diff --git a/.editorconfig b/.editorconfig index b6a8712f64..bd2580a27b 100644 --- a/.editorconfig +++ b/.editorconfig @@ -6,3 +6,7 @@ root = true # as some compilers complain about files not ending in newline [*] insert_final_newline = true + +# Part of readline test that needs file without final newline +[modules/libcom/test/multiline-input.txt] +insert_final_newline = false diff --git a/.gitattributes b/.gitattributes index 5221674ea8..8162ae0f20 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,9 +1,11 @@ -.ci/ export-ignore -.tools/ export-ignore -.github/ export-ignore -.appveyor/ export-ignore -.appveyor.yml export-ignore -README export-subst +.appveyor.yml export-ignore +.appveyor/ export-ignore +.ci/ export-ignore +.github/ export-ignore +.gitmodules export-ignore +.readthedocs.yml export-ignore +.tools/ export-ignore +README export-subst #Which files need CRLF handling # default to automatic diff --git a/.github/codeql/config.yml b/.github/codeql/config.yml new file mode 100644 index 0000000000..65d7feb361 --- /dev/null +++ b/.github/codeql/config.yml @@ -0,0 +1,22 @@ +name: "CodeQL Config" + +# Queries and the suites they belong to are documented at +# https://codeql.github.com/codeql-query-help/cpp/ + +queries: + - uses: security-and-quality + # Choose the above from 3 query suites: + # default + # The default set of queries + # security-extended + # `default` suite plus lower severity and precision queries + # security-and-quality + # `security-extended`, plus maintainability and reliability queries + +query-filters: +- + exclude: + id: cpp/use-of-goto +- + exclude: + problem.severity: recommendation diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 18996a42a3..0add1231c1 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -7,311 +7,267 @@ defaults: shell: bash jobs: - build: - name: ${{ matrix.name }} + manylinux: + name: py${{ matrix.py }} ${{ matrix.ml }} + + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + include: + # see compat matrix: https://github.com/pypa/manylinux + + - ml: manylinux1_x86_64 + py: cp27-cp27m + cy: "Cython<3.0" + skip: true + + - ml: manylinux2010_x86_64 + py: cp37-cp37m + skip: true + + - ml: manylinux2014_x86_64 + py: cp38-cp38 + + - ml: manylinux2014_x86_64 + py: cp39-cp39 + + - ml: manylinux2014_x86_64 + py: cp310-cp310 + + - ml: manylinux2014_x86_64 + py: cp311-cp311 + + - ml: manylinux2014_x86_64 + py: cp312-cp312 + + - ml: manylinux2014_x86_64 + py: cp313-cp313 + + - ml: manylinux2014_x86_64 + py: cp313-cp313t + + - ml: manylinux_2_28_x86_64 + py: cp314-cp314 + + - ml: manylinux_2_28_x86_64 + py: cp314-cp314t + src: true + + steps: + - uses: actions/checkout@v6 + with: + submodules: recursive + + - name: Detect pre-release + run: python ./gha-set-pre.py + + - name: Build + run: | + # emit script to be executed in container. + cat < runit.sh + #!/bin/sh + set -e -x + ls /opt/python/ + /opt/python/${{ matrix.py }}/bin/python --version + + export PATH="/opt/python/${{ matrix.py }}/bin:\$PATH" + export SETUPTOOLS_DSO_PLAT_NAME="${{ matrix.ml }}" + + cd /io + + pip install -U pip + # pre-download for later steps + pip download $PRE -d output \ + --only-binary numpy,Cython \ + "${{ matrix.cy || 'Cython' }}" \ + setuptools_dso wheel numpy ply nose2 + + # create source tar + pip install --no-index -f ./output setuptools_dso + python setup.py sdist -d ./output + ls -lh output + + # build wheel from source tar + pip wheel -w ./output --no-index -f ./output epicscorelibs + ls -lh output + + # install wheel for testing + pip install --no-index -f ./output epicscorelibs nose2 + + # switch away from root when running test to avoid inclusion in PYTHONPATH + cd output + python -m nose2 -v epicscorelibs + + EOF + # end of script. Now execute in container + + chmod +x runit.sh + mkdir output + podman run --rm \ + -v `pwd`:/io \ + quay.io/pypa/${{ matrix.ml }} \ + /io/runit.sh + + - name: Result + run: ls -lhtr output + + - name: Save wheels + if: ${{ ! matrix.skip }} + uses: actions/upload-artifact@v6 + with: + retention-days: 1 + name: whl-${{ matrix.py }}-${{ matrix.ml }} + path: output/epicscorelibs*.whl + + - name: Save source + if: ${{ matrix.src }} + uses: actions/upload-artifact@v6 + with: + retention-days: 1 + name: whl-source + path: output/epicscorelibs*.gz + + native: + name: py${{ matrix.py }} ${{ matrix.arch }} runs-on: ${{ matrix.os }} strategy: fail-fast: false matrix: include: - # Check build from .tar - - name: Source - os: ubuntu-latest - python: "3.11" - source: true - - # Linux py builds x64 - - name: linux 2.7 amd64 - os: ubuntu-latest - pyver: cp27-cp27m - piparch: manylinux1_x86_64 - - - name: linux 2.7u amd64 - os: ubuntu-latest - pyver: cp27-cp27mu - piparch: manylinux1_x86_64 - - - name: linux 3.5 amd64 - os: ubuntu-latest - pyver: cp35-cp35m - piparch: manylinux1_x86_64 - - - name: linux 3.6 amd64 - os: ubuntu-latest - pyver: cp36-cp36m - piparch: manylinux1_x86_64 - - - name: linux 3.7 amd64 - os: ubuntu-latest - pyver: cp37-cp37m - piparch: manylinux1_x86_64 - - - name: linux 3.8 amd64 - os: ubuntu-latest - pyver: cp38-cp38 - piparch: manylinux1_x86_64 - - - name: linux 3.9 amd64 - os: ubuntu-latest - pyver: cp39-cp39 - piparch: manylinux2010_x86_64 - - - name: linux 3.10 amd64 - os: ubuntu-latest - pyver: cp310-cp310 - piparch: manylinux2014_x86_64 - - - name: linux 3.11 amd64 - os: ubuntu-latest - pyver: cp311-cp311 - piparch: manylinux2014_x86_64 - - - name: linux 3.12 amd64 - os: ubuntu-latest - pyver: cp312-cp312 - piparch: manylinux2014_x86_64 - - - name: linux 3.13 amd64 - os: ubuntu-latest - pyver: cp313-cp313 - piparch: manylinux2014_x86_64 - - # Linux py builds x64 - - name: linux 2.7 i686 - os: ubuntu-latest - pyver: cp27-cp27m - piparch: manylinux1_i686 - pre: linux32 - - - name: linux 2.7u i686 - os: ubuntu-latest - pyver: cp27-cp27mu - piparch: manylinux1_i686 - pre: linux32 - - - name: linux 3.5 i686 - os: ubuntu-latest - pyver: cp35-cp35m - piparch: manylinux1_i686 - pre: linux32 - - - name: linux 3.6 i686 - os: ubuntu-latest - pyver: cp36-cp36m - piparch: manylinux1_i686 - pre: linux32 - - - name: linux 3.7 i686 - os: ubuntu-latest - pyver: cp37-cp37m - piparch: manylinux1_i686 - pre: linux32 - - - name: linux 3.8 i686 - os: ubuntu-latest - pyver: cp38-cp38 - piparch: manylinux1_i686 - pre: linux32 - - - name: linux 3.9 i686 - os: ubuntu-latest - pyver: cp39-cp39 - piparch: manylinux2010_i686 - pre: linux32 - - # numpy builds for >=3.10 do no include i686 - - # OSX py builds - - name: osx 3.6 intel - os: macos-13 - python: "3.6" - piparch: macosx_10_9_intel - - - name: osx 3.7 intel - os: macos-13 - python: "3.7" - piparch: macosx_10_9_intel - - - name: osx 3.8 arm64 - os: macos-latest - python: "3.8" - piparch: macosx_11_0_arm64 - - - name: osx 3.9 arm64 - os: macos-latest - python: "3.9" - piparch: macosx_11_0_arm64 - - - name: osx 3.10 arm64 - os: macos-latest - python: "3.10" - piparch: macosx_11_0_arm64 - - - name: osx 3.11 arm64 - os: macos-latest - python: "3.11" - piparch: macosx_11_0_arm64 - - - name: osx 3.12 arm64 - os: macos-latest - python: "3.12" - piparch: macosx_11_0_arm64 - - - name: osx 3.13 arm64 - os: macos-latest - python: "3.13" - piparch: macosx_11_0_arm64 - - # Windows py builds - - - name: win64 3.6 - os: windows-latest - python: "3.6" - piparch: win_amd64 - - - name: win64 3.7 - os: windows-latest - python: "3.7" - piparch: win_amd64 - - - name: win64 3.8 - os: windows-latest - python: "3.8" - piparch: win_amd64 - - - name: win64 3.9 - os: windows-latest - python: "3.9" - piparch: win_amd64 - - - name: win64 3.10 - os: windows-latest - python: "3.10" - piparch: win_amd64 - - - name: win64 3.11 - os: windows-latest - python: "3.11" - piparch: win_amd64 - - - name: win64 3.12 - os: windows-latest - python: "3.12" - piparch: win_amd64 - - - name: win64 3.13 - os: windows-latest - python: "3.13" - piparch: win_amd64 + - os: windows-latest + py: "3.8" + arch: win_amd64 + + # windows + 3.9 omitted due to numpy bug "invalid preprocessor command 'warning'" + + - os: windows-latest + py: "3.10" + arch: win_amd64 + + - os: windows-latest + py: "3.11" + arch: win_amd64 + + - os: windows-latest + py: "3.12" + arch: win_amd64 + + - os: windows-latest + py: "3.13" + arch: win_amd64 + + - os: windows-latest + py: "3.14" + arch: win_amd64 + + - os: macos-latest + py: "3.8" + arch: macosx_11_0_universal2 + + - os: macos-latest + py: "3.9" + arch: macosx_11_0_universal2 + + - os: macos-latest + py: "3.10" + arch: macosx_11_0_universal2 + + - os: macos-latest + py: "3.11" + arch: macosx_11_0_universal2 + + - os: macos-latest + py: "3.12" + arch: macosx_11_0_universal2 + + - os: macos-latest + py: "3.13" + arch: macosx_11_0_universal2 + + - os: macos-latest + py: "3.14" + arch: macosx_11_0_universal2 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 with: - submodules: recursive + submodules: recursive - - name: Setup native python - uses: actions/setup-python@v5 + - uses: actions/setup-python@v6 with: - python-version: ${{ matrix.python || '3.11' }} - #architecture: x64 + python-version: ${{ matrix.py }} - # TLS 1.0 and 1.1 support was removed from pypi so the cached pip won't work - - name: Python 3.5 Fix - if: ${{ matrix.python == '3.5' }} - run: | - curl https://bootstrap.pypa.io/pip/3.5/get-pip.py | python + - name: Prepare + run: python -m pip install -U pip virtualenv - - name: Native Prep/Info - run: | - python -m pip install --upgrade pip - python -m pip install setuptools wheel twine - which python - python --version - python -m pip --version - python -m twine -h - python ./gha-set-pre.py + - name: PIP Debug + run: pip debug -v - - name: PY Source - run: | - echo "PRE=$PRE" - python -m pip install setuptools wheel nose2 - python -m pip install $PRE setuptools-dso - python setup.py sdist --formats=gztar - ls dist/* - - - name: PEP517 Build - if: matrix.source - run: | - python -m pip wheel -v -w dist $PRE dist/*.tar.* + - name: Detect pre-release + run: python ./gha-set-pre.py - - name: Native PY build - if: matrix.python && !matrix.source + - name: Build run: | - python -m pip wheel -v -w dist --no-index --no-deps \ - --no-build-isolation --no-use-pep517 \ - --build-option --plat-name=${{ matrix.piparch }} \ - $PRE \ - dist/*.tar.* - - cd dist - python -m pip install epicscorelibs*.whl - python -c 'import epicscorelibs; print("IMPORT", epicscorelibs.__file__)' - python -m nose2 -v epicscorelibs - - - name: Docker PY build - if: matrix.pyver && !matrix.source - run: | - # can't use GHA native docker support since GHA magic binaries need .so absent from old manylinux images :( - cat < runit.sh - #!/bin/sh - set -e -x - ls /opt/python/ - cd /io - [ -d dist ] - ls dist/* - export PATH="/opt/python/${{ matrix.pyver }}/bin:\$PATH" - which python - python -m pip install -U pip - python -m pip install --only-binary numpy setuptools wheel nose2 numpy - python -m pip install $PRE setuptools-dso - python -m pip wheel -v -w dist --no-index --no-deps \ - --no-build-isolation --no-use-pep517 \ - --build-option --plat-name=${{ matrix.piparch }} \ - $PRE \ - dist/*.tar.* - - cd dist - python -m pip install epicscorelibs*.whl - python -c 'import epicscorelibs; print("IMPORT", epicscorelibs.__file__)' - python -m nose2 -v epicscorelibs - - EOF - cat runit.sh - chmod +x runit.sh - docker pull quay.io/pypa/${{ matrix.piparch }} - docker run --rm -v `pwd`:/io quay.io/pypa/${{ matrix.piparch }} ${{ matrix.pre }} /io/runit.sh - - - name: List Artifacts - run: ls dist/* - - - name: Save Artifacts - uses: actions/upload-artifact@v4 + set -x -e + python -m pip install -U pip + pip download $PRE -d output \ + --only-binary numpy,Cython \ + Cython setuptools_dso wheel numpy ply nose2 + + export SETUPTOOLS_DSO_PLAT_NAME=${{ matrix.arch }} + + pip install --no-index -f ./output setuptools_dso + python setup.py sdist -d ./output + ls -lh output + + # build wheel from source tar + pip wheel -w ./output --no-index -f ./output epicscorelibs + ls -lh output + + # install wheel for testing + pip install --no-index -f ./output epicscorelibs nose2 + + # switch away from root when running test to avoid inclusion in PYTHONPATH + cd output + python -m nose2 -v epicscorelibs + + - run: ls -lhtr output + + - name: Save wheels + if: ${{ ! matrix.skip }} + uses: actions/upload-artifact@v6 with: - name: epicscorelibs-${{ matrix.python || matrix.pyver }}-${{ matrix.piparch || 'source' }} - path: dist/* + retention-days: 1 + name: whl-${{ matrix.py }}-${{ matrix.os }} + path: output/epicscorelibs*.whl - - name: Upload wheels - if: github.event_name=='push' && github.ref=='refs/heads/master' && !matrix.source - env: - TWINE_USERNAME: ${{ secrets.PYPI_USERNAME }} - TWINE_PASSWORD: ${{ secrets.PYPI_PASSWORD }} + combine: + runs-on: ubuntu-latest + needs: [manylinux, native] + steps: + - name: Download + uses: actions/download-artifact@v7 + with: + merge-multiple: true + + - run: ls -lhtr + + - uses: actions/setup-python@v6 + with: + python-version: 3.11 + + - name: Setup run: | - python -m twine upload --skip-existing dist/epicscorelibs*.whl + pip install -U pip twine + python -m twine -h + + - name: Check + run: twine check epicscorelibs*.gz epicscorelibs*.whl - - name: Upload source - if: github.event_name=='push' && github.ref=='refs/heads/master' && matrix.source + - name: Upload wheels + if: env.TWINE_USERNAME && github.event_name=='push' && github.ref=='refs/heads/master' env: TWINE_USERNAME: ${{ secrets.PYPI_USERNAME }} TWINE_PASSWORD: ${{ secrets.PYPI_PASSWORD }} - run: | - python -m twine upload --skip-existing dist/epicscorelibs*.tar.* + run: twine upload --skip-existing epicscorelibs*.gz epicscorelibs*.whl diff --git a/.github/workflows/check-editorconfig.yml b/.github/workflows/check-editorconfig.yml deleted file mode 100644 index 07f6d6c945..0000000000 --- a/.github/workflows/check-editorconfig.yml +++ /dev/null @@ -1,13 +0,0 @@ -name: Check EditorConfig - -on: - push: - pull_request: - -jobs: - editorconfig: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - name: EditorConfig-Action - uses: greut/eclint-action@v0 diff --git a/.gitignore b/.gitignore index b7a25673aa..2f54c20c5c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,8 +1,11 @@ +# Before adding patterns here, please read the gitignore +# documentation at https://git-scm.com/docs/gitignore /cfg/ /bin/ /lib/ /db/ /dbd/ +/doc/ /html/ /include/ /templates/ @@ -10,10 +13,8 @@ /modules/RELEASE.*.local /modules/Makefile.local O.*/ -/QtC-* -/.qtc_* -/.vscode/ -*.orig *.log -.*.swp +.iocsh_history + +# Common files generated by other tools .DS_Store diff --git a/.lgtm.yml b/.lgtm.yml deleted file mode 100644 index e3027329ea..0000000000 --- a/.lgtm.yml +++ /dev/null @@ -1,50 +0,0 @@ -# Configuration for lgtm.com -# - -path_classifiers: - test: - - exclude: / - - test - - "modules/*/test*" - library: - - modules/libcom/src/yacc - - modules/libcom/src/flex - template: - - src/template - - modules/ca/src/template - - modules/database/src/template - -extraction: - cpp: - prepare: - packages: - - "libreadline-dev" - index: - build_command: - - "g++ --version" - - "make --version" - - "perl --version" - - "make -sj2 || echo '*** Build failed, ignored for lgtm ***'" - - python: - index: - include: - - src/tools - - # Interpreted languages to be excluded - javascript: - index: - exclude: - - "*" - - # Compiled languages to be excluded - java: - index: - build_command: "echo No Java code in this project" - csharp: - index: - build_command: "echo No C# code in this project" - go: - index: - build_command: "echo No Go code in this project" - diff --git a/.readthedocs.yaml b/.readthedocs.yaml new file mode 100644 index 0000000000..49b5b0d42a --- /dev/null +++ b/.readthedocs.yaml @@ -0,0 +1,36 @@ +# .readthedocs.yml +# Read the Docs configuration file +# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details + +# Required +version: 2 + +build: + os: ubuntu-22.04 + tools: + python: "3.9" + apt_packages: + - graphviz + - rsync + jobs: + pre_build: + - make inc + - make -C documentation rtd + +# Build documentation in the documentation/O.Common directory with Sphinx +sphinx: + configuration: documentation/O.Common/rtd-src/conf.py + +# Build documentation with MkDocs +#mkdocs: +# configuration: mkdocs.yml + +# Optionally build your docs in additional formats such as PDF and ePub +#formats: [] + +submodules: + exclude: all + +python: + install: + - requirements: documentation/requirements.txt diff --git a/.tools/make-tar.sh b/.tools/make-tar.sh index 276d8e204d..f018d9e8f5 100755 --- a/.tools/make-tar.sh +++ b/.tools/make-tar.sh @@ -136,19 +136,20 @@ sed -i -e 's|^\./||' "$TDIR"/list.1 # Exclude files sed \ - -e '/\/\.ci\//d' \ + -e '/\/\.appveyor\.yml$/d' \ + -e '/\/\.appveyor\//d' \ -e '/\/\.ci-local\//d' \ - -e '/\/\.tools\//d' \ - -e '/\/jenkins\//d' \ + -e '/\/\.ci\//d' \ + -e '/\/\.cproject$/d' \ -e '/\/\.github\//d' \ -e '/\/\.gitmodules$/d' \ -e '/\/\.hgtags$/d' \ - -e '/\/\.cproject$/d' \ - -e '/\/\.project$/d' \ -e '/\/\.lgtm\.yml$/d' \ - -e '/\/\.travis\.yml$/d' \ - -e '/\/\.appveyor\.yml$/d' \ + -e '/\/\.project$/d' \ -e '/\/\.readthedocs\.yml$/d' \ + -e '/\/\.tools\//d' \ + -e '/\/\.travis\.yml$/d' \ + -e '/\/jenkins\//d' \ "$TDIR"/list.1 > "$TDIR"/list.2 if ! diff -U 0 "$TDIR"/list.1 "$TDIR"/list.2 diff --git a/README b/README index 942d00cb31..da2492989a 100644 --- a/README +++ b/README @@ -13,15 +13,7 @@ this distribution. --------------------------------------------------------- -Installation and release information can be found in the -various files in the documentation subdirectory. - -Additional information about EPICS including mailing list -archives and subscription instructions, documentation and -training materials, additional components, links to other -websites etc. is available on the EPICS home page at - https://epics.anl.gov/ +For more information, see the README.md file. $Format:%cD$ $Format:%H$ -https://code.launchpad.net/epics-base diff --git a/README.epicscorelibs b/README.epicscorelibs index 964fec8c96..9f18b98068 100644 --- a/README.epicscorelibs +++ b/README.epicscorelibs @@ -13,6 +13,10 @@ setup.py.d/* include - module sub-directories containing internal headers source - list of source files +Compare file list before and after merge using: + +git ls-tree --name-only -r + Generated files rather than build/run antelope (modified yacc) and e_flex executables, diff --git a/README.md b/README.md index 695851e2dc..6c6305d8dd 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,6 @@ epicscorelibs ============= - - - Not an end user library. This repository contains the source used to build @@ -19,7 +16,7 @@ it is will be necessary to skip the pre-built wheel binaries. ```sh -pip install --no-binary :all: epicscorelibs +pip install --no-binary epicscorelibs epicscorelibs ``` Please report issues first to https://github.com/mdavidsaver/epicscorelibs/issues @@ -27,9 +24,7 @@ before code.launchpad.net or EPICS mailing lists. CI builds -https://travis-ci.org/mdavidsaver/epicscorelibs/builds - -https://ci.appveyor.com/project/mdavidsaver/epicscorelibs +https://github.com/mdavidsaver/epicscorelibs/actions Python API ---------- diff --git a/configure/CONFIG.gnuCommon b/configure/CONFIG.gnuCommon index f721b065eb..63a32eca17 100644 --- a/configure/CONFIG.gnuCommon +++ b/configure/CONFIG.gnuCommon @@ -27,13 +27,17 @@ RANLIB = $(GNU_BIN)/$(CMPLR_PREFIX)ranlib$(CMPLR_SUFFIX) ASAN_FLAGS_YES = -fsanitize=address ASAN_LDFLAGS_YES = $(ASAN_FLAGS_YES) +# It makes sense to include debugging symbols even in optimized builds +# in case you want to attach gdb to the process or examine a core-dump. +# This does cost disk space, but not memory as debug symbols are not +# loaded into RAM when the binary is loaded. PROF_CFLAGS_YES = -p GPROF_CFLAGS_YES = -pg CODE_CFLAGS = $(PROF_CFLAGS_$(PROFILE)) $(GPROF_CFLAGS_$(GPROF)) CODE_CFLAGS += $(ASAN_FLAGS_$(ENABLE_ASAN)) WARN_CFLAGS_YES = -Wall -Werror-implicit-function-declaration WARN_CFLAGS_NO = -w -OPT_CFLAGS_YES = -O3 +OPT_CFLAGS_YES = -O3 -g OPT_CFLAGS_NO = -g PROF_CXXFLAGS_YES = -p @@ -42,7 +46,7 @@ CODE_CXXFLAGS = $(PROF_CXXFLAGS_$(PROFILE)) $(GPROF_CXXFLAGS_$(GPROF)) CODE_CXXFLAGS += $(ASAN_FLAGS_$(ENABLE_ASAN)) WARN_CXXFLAGS_YES = -Wall WARN_CXXFLAGS_NO = -w -OPT_CXXFLAGS_YES = -O3 +OPT_CXXFLAGS_YES = -O3 -g OPT_CXXFLAGS_NO = -g CODE_LDFLAGS = $(PROF_CXXFLAGS_$(PROFILE)) $(GPROF_CXXFLAGS_$(GPROF)) @@ -61,7 +65,7 @@ LOADABLE_SHRLIB_LDFLAGS = -shared -fPIC -Wl,-h$@ GNU_LDLIBS_YES = -lgcc -# Use compiler flags to generate header dependancies files +# Use compiler flags to generate header dependencies files HDEPENDS_METHOD = COMP HDEPENDS_COMPFLAGS = -MM -MF $@ diff --git a/configure/CONFIG_ADDONS b/configure/CONFIG_ADDONS index d32d911656..5b581f48c6 100644 --- a/configure/CONFIG_ADDONS +++ b/configure/CONFIG_ADDONS @@ -24,7 +24,7 @@ # These rules apply to these Makefile-variables: # USR_CFLAGS C flags # USR_CXXFLAGS C++ flags -# USR_CPPFLAGS c preprocesser flags +# USR_CPPFLAGS c preprocessor flags # SRCS source files for building libraries and prods # USR_SRCS source files for building libraries and prods # PROD_SRCS source files for building prods diff --git a/configure/CONFIG_BASE b/configure/CONFIG_BASE index a860765200..a737b37fcc 100644 --- a/configure/CONFIG_BASE +++ b/configure/CONFIG_BASE @@ -45,10 +45,13 @@ FIND_PM = $(wildcard $(EPICS_BASE)/lib/perl/$(1)) #--------------------------------------------------------------- # EPICS Base build tools and tool flags +PODTOMD_pl = $(TOOLS)/podToMD.pl +PODTOMD = $(PERL) $(PODTOMD_pl) PODTOHTML_pl = $(TOOLS)/podToHtml.pl PODTOHTML_dep = $(PODTOHTML_pl) $(call FIND_PM,EPICS/PodHtml.pm) PODTOHTML = $(PERL) $(PODTOHTML_pl) CONVERTRELEASE = $(PERL) $(call FIND_TOOL,convertRelease.pl) +FILTERMAKEFLAGS = $(PERL) $(call FIND_TOOL,filterMakeflags.pl) FULLPATHNAME = $(PERL) $(TOOLS)/fullPathName.pl GENVERSIONHEADER = $(PERL) $(TOOLS)/genVersionHeader.pl $(QUIET_FLAG) $(QUESTION_FLAG) @@ -65,6 +68,24 @@ INSTALL_LIBRARY = $(INSTALL) MKMF = $(PERL) $(TOOLS)/mkmf.pl REPLACEVAR = $(PERL) $(TOOLS)/replaceVAR.pl +#--------------------------------------------------------------- +# How to portably check the flags to make +# GNUmake versions before 4.0 gave different values +makeflags := $(shell $(FILTERMAKEFLAGS) $(MAKEFLAGS)) +define checkflags + make-$1 := $(findstring $1,$(makeflags)) +endef +# This is extensible to most single letter flags: +$(foreach flag,s q, $(eval $(call checkflags,$(flag)))) + +# Silent builds - suppress messages during 'make -s' +NOP = : +ECHO = @$(if $(make-s),$(NOP),echo) +QUIET_FLAG := $(if $(make-s),-q,) + +# Convert 'make -q' flag into '-i' for genVersionHeader.pl +QUESTION_FLAG := $(if $(make-q),-i,) + #--------------------------------------------------------------- # tools for cleaning out unwanted files CVSCLEAN = $(call FIND_TOOL,cvsclean.pl) diff --git a/configure/CONFIG_BASE_VERSION b/configure/CONFIG_BASE_VERSION index 12120cddbf..a058619561 100644 --- a/configure/CONFIG_BASE_VERSION +++ b/configure/CONFIG_BASE_VERSION @@ -48,7 +48,7 @@ EPICS_VERSION = 7 EPICS_REVISION = 0 # EPICS_MODIFICATION must be a number >=0 and <256 -EPICS_MODIFICATION = 7 +EPICS_MODIFICATION = 10 # EPICS_PATCH_LEVEL must be a number (win32 resource file requirement) # Not included in the official EPICS version number if zero @@ -71,6 +71,3 @@ endif EPICS_SHORT_VERSION=$(EPICS_VERSION).$(EPICS_REVISION).$(EPICS_MODIFICATION)$(EPICS_PATCH_VSTRING) EPICS_VERSION_NUMBER=$(EPICS_SHORT_VERSION)$(EPICS_DEV_SNAPSHOT)$(EPICS_SITE_VSTRING) EPICS_VERSION_STRING="EPICS Version $(EPICS_VERSION_NUMBER)" - -# Provide this in case anyone is still using the old name -COMMIT_DATE="-no-date-" diff --git a/configure/CONFIG_CA_VERSION b/configure/CONFIG_CA_VERSION index 1fe4f1c254..de8fd6ba13 100644 --- a/configure/CONFIG_CA_VERSION +++ b/configure/CONFIG_CA_VERSION @@ -1,8 +1,8 @@ # Version number for the Channel Access API and shared library EPICS_CA_MAJOR_VERSION = 4 -EPICS_CA_MINOR_VERSION = 14 -EPICS_CA_MAINTENANCE_VERSION = 3 +EPICS_CA_MINOR_VERSION = 15 +EPICS_CA_MAINTENANCE_VERSION = 1 # Development flag, set to zero for release versions diff --git a/configure/CONFIG_COMMON b/configure/CONFIG_COMMON index 742e54ae4f..c37b472387 100644 --- a/configure/CONFIG_COMMON +++ b/configure/CONFIG_COMMON @@ -81,23 +81,6 @@ FINAL_LOCATION = $(INSTALL_ABSOLUTE) # IOC's view of install path IOCS_APPL_TOP = $(INSTALL_ABSOLUTE) -#------------------------------------------------------- -# How to portably check the flags to make -makeflags := $(firstword $(filter-out -,$(filter-out --%,$(MAKEFLAGS)))) -define checkflags - make-$1 := $(findstring $1,$(makeflags)) -endef -# This is extensible to most single letter flags: -$(foreach flag,s q, $(eval $(call checkflags,$(flag)))) - -# Silent builds - suppress messages during 'make -s' -NOP = : -ECHO = @$(if $(make-s),$(NOP),echo) -QUIET_FLAG := $(if $(make-s),-q,) - -# Convert 'make -q' flag into '-i' for genVersionHeader.pl -QUESTION_FLAG := $(if $(make-q),-i,) - #------------------------------------------------------- ifdef T_A @@ -164,7 +147,7 @@ CMPLR_SRC_DIRS += . $(foreach dir, .. $(SRC_DIRS), \ ALL_SRC_DIRS = $(CMPLR_SRC_DIRS) $(OS_SRC_DIRS) $(GENERIC_SRC_DIRS) #-------------------------------------------------- -# Directory for OS independant build created files +# Directory for OS independent build created files COMMON_DIR = ../O.Common # compile line include directories @@ -422,9 +405,9 @@ INSTALL_LIB_INSTALLS = $(addprefix $(INSTALL_LIB)/,$(notdir $(LIB_INSTALLS))) #--------------------------------------------------------------- # Installed file permissions -BIN_PERMISSIONS = 555 -LIB_PERMISSIONS = 444 -SHRLIB_PERMISSIONS = 555 +BIN_PERMISSIONS = 755 +LIB_PERMISSIONS = 644 +SHRLIB_PERMISSIONS = 755 INSTALL_PERMISSIONS = 444 #--------------------------------------------------------------- @@ -486,4 +469,9 @@ COMMON_INC += $(filter $(COMMON_DIR)/%, \ $(foreach dir, $(ALL_SRC_DIRS), \ $(addsuffix /$(file), $(dir)))) $(COMMON_DIR)/$(file)))) +COMMON_DOCS += $(filter $(COMMON_DIR)/%, \ + $(foreach file, $(DOCS), \ + $(firstword $(wildcard $(file) \ + $(foreach dir, $(ALL_SRC_DIRS), \ + $(addsuffix /$(file), $(dir)))) $(COMMON_DIR)/$(file)))) endif diff --git a/configure/CONFIG_DATABASE_MODULE b/configure/CONFIG_DATABASE_MODULE index 14c219c2cb..22f2bce6e3 100644 --- a/configure/CONFIG_DATABASE_MODULE +++ b/configure/CONFIG_DATABASE_MODULE @@ -12,6 +12,7 @@ DBDTORECTYPEH_pl = $(EPICS_BASE_HOST_BIN)/dbdToRecordtypeH.pl DBDTORECTYPEH_dep = $(DBDTORECTYPEH_pl) $(call FIND_PM,DBD/Rec*.pm) DBDTOMENUH_pl = $(EPICS_BASE_HOST_BIN)/dbdToMenuH.pl DBDTOMENUH_dep = $(DBDTOMENUH_pl) $(call FIND_PM,DBD/Menu.pm) +DBDTOMD_pl = $(EPICS_BASE_HOST_BIN)/dbdToMD.pl DBDTOHTML_pl = $(EPICS_BASE_HOST_BIN)/dbdToHtml.pl DBDTOHTML_dep = $(DBDTOHTML_pl) $(call FIND_PM,EPICS/Pod*Html.pm) REGRECDEVDRV_pl = $(EPICS_BASE_HOST_BIN)/registerRecordDeviceDriver.pl @@ -21,6 +22,7 @@ REGRECDEVDRV_dep = $(REGRECDEVDRV_pl) DBEXPAND = $(PERL) $(DBDEXPAND_pl) DBTORECORDTYPEH = $(PERL) $(DBDTORECTYPEH_pl) DBTOMENUH = $(PERL) $(DBDTOMENUH_pl) +DBDTOMD = $(PERL) $(DBDTOMD_pl) DBDTOHTML = $(PERL) $(DBDTOHTML_pl) REGISTERRECORDDEVICEDRIVER = $(PERL) $(REGRECDEVDRV_pl) diff --git a/configure/CONFIG_DATABASE_VERSION b/configure/CONFIG_DATABASE_VERSION index 8b4002ae6a..94f4872e75 100644 --- a/configure/CONFIG_DATABASE_VERSION +++ b/configure/CONFIG_DATABASE_VERSION @@ -1,7 +1,7 @@ # Version number for the database APIs and shared library EPICS_DATABASE_MAJOR_VERSION = 3 -EPICS_DATABASE_MINOR_VERSION = 22 +EPICS_DATABASE_MINOR_VERSION = 25 EPICS_DATABASE_MAINTENANCE_VERSION = 1 # Development flag, set to zero for release versions diff --git a/configure/CONFIG_ENV b/configure/CONFIG_ENV index 6d0d52a79a..3ec50c020d 100644 --- a/configure/CONFIG_ENV +++ b/configure/CONFIG_ENV @@ -53,3 +53,5 @@ EPICS_IOC_IGNORE_SERVERS="" # EPICS_IOC_LOG_PORT Log server port number etc. EPICS_IOC_LOG_PORT=7004 +# Posix priority scheduling +EPICS_ALLOW_POSIX_THREAD_PRIORITY_SCHEDULING=YES diff --git a/configure/CONFIG_LIBCOM_VERSION b/configure/CONFIG_LIBCOM_VERSION index 898df2c67f..4059068136 100644 --- a/configure/CONFIG_LIBCOM_VERSION +++ b/configure/CONFIG_LIBCOM_VERSION @@ -1,7 +1,7 @@ # Version number for the libcom APIs and shared library EPICS_LIBCOM_MAJOR_VERSION = 3 -EPICS_LIBCOM_MINOR_VERSION = 22 +EPICS_LIBCOM_MINOR_VERSION = 25 EPICS_LIBCOM_MAINTENANCE_VERSION = 1 # Development flag, set to zero for release versions diff --git a/configure/CONFIG_SITE_ENV b/configure/CONFIG_SITE_ENV index bacbc14a53..ba90e2f849 100644 --- a/configure/CONFIG_SITE_ENV +++ b/configure/CONFIG_SITE_ENV @@ -71,7 +71,7 @@ EPICS_TS_NTP_INET= # Number of lines of command history to keep. # IOCSH_HISTEDIT_DISABLE # Prevents use of readline or equivalent if defined. -IOCSH_PS1="epics> " +IOCSH_PS1=ANSI_GREEN("epics> ") IOCSH_HISTSIZE=50 IOCSH_HISTEDIT_DISABLE= @@ -92,3 +92,6 @@ EPICS_IOC_LOG_FILE_NAME= EPICS_IOC_LOG_FILE_COMMAND= EPICS_IOC_LOG_FILE_LIMIT=1000000 +# Set to 'YES' to call abort() rather than suspend the current thread +# when an assert() fails +EPICS_ABORT_ON_ASSERT=NO diff --git a/configure/Makefile b/configure/Makefile index ba77d50898..3c2c0b53a0 100644 --- a/configure/Makefile +++ b/configure/Makefile @@ -34,5 +34,9 @@ CFG += TOOLCHAIN.$(EPICS_HOST_ARCH).$(T_A) include $(TOP)/configure/RULES +ifeq ($(GNU),YES) + # Pass compiler flags to preprocessor to enable _FORTIFY_SOURCE + TOOLCHAIN.$(EPICS_HOST_ARCH).$(T_A): CPPFLAGS += $(CFLAGS) +endif TOOLCHAIN.$(EPICS_HOST_ARCH).$(T_A): toolchain.c $(PREPROCESS.cpp) diff --git a/configure/RULES.Db b/configure/RULES.Db index f08af423d3..ae023cee1c 100644 --- a/configure/RULES.Db +++ b/configure/RULES.Db @@ -55,7 +55,7 @@ DBD += $(foreach type, $(CROSS_TARGET_OS_TYPES), $(DBD_$(type))) # DBD_solaris += abcSolaris.dbd # # --------------------------------------------------- -# DBD concatination files +# DBD concatenation files COMMON_DBDCATS += $(addprefix $(COMMON_DIR)/,$(DBDCAT)) DBDCAT_SOURCES += $(foreach file, $($*_DBD), $(DBDCAT_SOURCE) ) @@ -194,10 +194,10 @@ ifneq (,$(strip $(DBDDEPENDS_FILES))) endif #--------------------------------------------------------------- -# build dependancies, clean rule +# build dependencies, clean rule inc: $(COMMON_INC) $(INSTALL_INC) $(COMMON_DBDS) $(COMMON_DBDCATS) \ - $(INSTALL_DBDS) $(INSTALL_DBD_INSTALLS) + $(INSTALL_DBDS) $(INSTALL_DBD_INSTALLS) $(COMMON_DOCS) build: $(COMMON_DBS) $(INSTALL_DBS) \ $(DBDDEPENDS_FILES) $(TARGETS) \ @@ -427,6 +427,26 @@ $(foreach file, $(DBD_INSTALLS), $(eval $(call DBD_INSTALLS_template, $(file)))) .PRECIOUS: $(COMMON_DBDS) $(COMMON_DIR)/%.dbd +#--------------------------------------------------------------- +# Markdown files + +$(COMMON_DIR)/%.md: %.dbd.pod $(DBDTOMD_pl) + @$(RM) $(notdir $@) + $(DBDTOMD) $(DBDFLAGS) -o $(notdir $@) $< + @$(MV) $(notdir $@) $@ + +$(COMMON_DIR)/%.md: %.pod $(PODTOMD_pl) + @$(RM) $(notdir $@) + $(PODTOMD) -o $(notdir $@) $< + @$(MV) $(notdir $@) $@ + +$(COMMON_DIR)/%.md: ../%.pl $(PODTOMD_pl) + @$(RM) $(notdir $@) + $(PODTOMD) -o $(notdir $@) $< + @$(MV) $(notdir $@) $@ + +.PRECIOUS: $(COMMON_DIR)/%.md + #--------------------------------------------------------------- # HTML files diff --git a/configure/RULES_ARCHS b/configure/RULES_ARCHS index 4aaa758708..0d4d944d4f 100644 --- a/configure/RULES_ARCHS +++ b/configure/RULES_ARCHS @@ -32,7 +32,7 @@ ifneq ($(RELEASE_CFG_DIR_RULES),) include $(RELEASE_CFG_DIR_RULES) endif -# Create EPICS_HOST_ARCH dependancies for GNU make -j option. +# Create EPICS_HOST_ARCH dependencies for GNU make -j option. # Needed in dirs where EPICS_HOST_ARCH build creates a tool used in # cross arch builds diff --git a/configure/RULES_BUILD b/configure/RULES_BUILD index 869fba17e9..242a61b315 100644 --- a/configure/RULES_BUILD +++ b/configure/RULES_BUILD @@ -22,7 +22,8 @@ vpath %.cpp $(USR_VPATH) $(ALL_SRC_DIRS) vpath %.rc $(USR_VPATH) $(ALL_SRC_DIRS) vpath %.h $(USR_VPATH) $(ALL_SRC_DIRS) vpath %.hpp $(USR_VPATH) $(ALL_SRC_DIRS) -vpath %.html $(USR_VPATH) $(ALL_SRC_DIRS) +vpath %.html $(USR_VPATH) $(ALL_SRC_DIRS) $(COMMON_DIR) +vpath %.md $(USR_VPATH) $(ALL_SRC_DIRS) $(COMMON_DIR) vpath %.skel.static $(USR_VPATH) $(ALL_SRC_DIRS) vpath %.y $(USR_VPATH) $(ALL_SRC_DIRS) vpath %.l $(USR_VPATH) $(ALL_SRC_DIRS) @@ -111,7 +112,7 @@ endif #--------------------------------------------------------------- # Read dependency files -ifneq (inc,$(strip $(MAKECMDGOALS))) +ifneq ($(filter-out inc,$(strip $(MAKECMDGOALS))),) ifneq (,$(strip $(HDEPENDS_FILES))) $(filter-out $(wildcard *$(DEP)), $(HDEPENDS_FILES)): | $(COMMON_INC) -include $(HDEPENDS_FILES) @@ -165,12 +166,12 @@ build: $(OBJSNAME) $(LIBTARGETS) $(PRODTARGETS) $(TESTPRODTARGETS) \ $(TARGETS) $(TESTSCRIPTS) $(INSTALL_LIB_INSTALLS) inc: $(COMMON_INC) $(INSTALL_INC) $(INSTALL_CONFIGS) $(INSTALLS_CFG) \ - $(INSTALL_HTMLS) $(INSTALLS_PERL_MODULES) $(INSTALL_SCRIPTS) + $(INSTALL_HTMLS) $(INSTALLS_PERL_MODULES) $(INSTALL_SCRIPTS) \ + $(INSTALL_DOCS) buildInstall: \ $(INSTALL_PROD) $(INSTALL_MUNCHS) \ $(INSTALL_TCLLIBS) $(INSTALL_TCLINDEX) \ - $(INSTALL_DOCS) \ $(INSTALL_OBJS) \ $(INSTALL_TEMPLATE) \ $(INSTALL_BIN_INSTALLS) @@ -532,11 +533,11 @@ endif # LOADABLE_SHRLIB_SUFFIX ifneq ($(INSTALL_CONFIGS),) $(INSTALL_CONFIG)/%: % $(ECHO) "Installing config file $@" - @$(INSTALL) -d -m $(INSTALL_PERMISSIONS) $< $(@D) + @$(INSTALL) -d -m $(INSTALL_PERMISSIONS) $(abspath $< $(@D)) $(INSTALL_CONFIG)/%: ../% $(ECHO) "Installing config file $@" - @$(INSTALL) -d -m $(INSTALL_PERMISSIONS) $< $(@D) + @$(INSTALL) -d -m $(INSTALL_PERMISSIONS) $(abspath $< $(@D)) endif $(INSTALL_INCLUDE)/%: $(COMMON_DIR)/% @@ -555,13 +556,17 @@ $(INSTALL_INCLUDE)/compiler/$(CMPLR_CLASS)/%: % $(ECHO) "Installing compiler dependent include file $@" @$(INSTALL) -d -m $(INSTALL_PERMISSIONS) $< $(@D) +$(INSTALL_DOC)/%: $(COMMON_DIR)/% + $(ECHO) "Installing generated doc $@" + @$(INSTALL) -d -m $(INSTALL_PERMISSIONS) $< $(@D) + $(INSTALL_DOC)/%: % $(ECHO) "Installing doc $@" - @$(INSTALL) -d -m $(INSTALL_PERMISSIONS) $< $(INSTALL_DOC) + @$(INSTALL) -d -m $(INSTALL_PERMISSIONS) $< $(@D) $(INSTALL_DOC)/%: ../% $(ECHO) "Installing doc $@" - @$(INSTALL) -d -m $(INSTALL_PERMISSIONS) $< $(INSTALL_DOC) + @$(INSTALL) -d -m $(INSTALL_PERMISSIONS) $< $(@D) $(INSTALL_HTML)/$(HTMLS_DIR)/%: $(COMMON_DIR)/% $(ECHO) "Installing generated html $@" diff --git a/configure/RULES_DIRS b/configure/RULES_DIRS index f47df4a539..6314c530fb 100644 --- a/configure/RULES_DIRS +++ b/configure/RULES_DIRS @@ -44,7 +44,7 @@ $(foreach dir, $(DIRS), $(dir)$(DIVIDER)install): \ rebuild: $(foreach dir, $(DIRS), $(dir)$(DIVIDER)install) endif -# Create directory dependancies lines for GNU make -j option +# Create directory dependencies lines for GNU make -j option # Only works with GNU make 3.81 or later (uses eval function) define DEP_template1 $(1): $$($(1)_DEPEND_DIRS) diff --git a/configure/RULES_MODULES b/configure/RULES_MODULES index b8e18aecb0..7ae8ffcbc3 100644 --- a/configure/RULES_MODULES +++ b/configure/RULES_MODULES @@ -41,7 +41,7 @@ RELEASE.host: $(RELEASE_LOCAL) $(RELEASE_LOCAL): Makefile $(CONFIG)/CONFIG_SITE \ $(wildcard $(CONFIG)/CONFIG_SITE.local) - $(ECHO) Creating $@ with + $(ECHO) "Creating $@ with" $(ECHO) " $(PARENT_MODULE) = $(INSTALL_ABSOLUTE)" @echo $(PARENT_MODULE) = $(INSTALL_ABSOLUTE)> $@ diff --git a/configure/Sample.Makefile b/configure/Sample.Makefile index e0e9675bf3..0c0b313e25 100644 --- a/configure/Sample.Makefile +++ b/configure/Sample.Makefile @@ -82,7 +82,7 @@ INC = file.h # Platform specific files can also be put in # separate os/OS_CLASS directories! # -# For almost every file the seach order is: +# For almost every file the search order is: # ./os/OS_CLASS # ./os/generic # . @@ -136,11 +136,11 @@ PROD_SRCS = ppp.c qqq.c a_file_SRCS = aa.c bb.c # -# EPICS libs needed to link PROD, TESTPROD and sharable library +# EPICS libs needed to link PROD, TESTPROD and shareable library # # note that DLL_LIBS (the libraries needed to link a shareable # library) is created by default from the PROD/SYS libraries specified -# below minus the name of the sharable library (LIBRARY) +# below minus the name of the shareable library (LIBRARY) # # # ---------- libraries for a specific product pppp @@ -169,7 +169,7 @@ USR_LIBS_DEFAULT = foolib USR_LIBS_WIN32 = -nil- foolib_DIR = $(FOO_LIB) -# system libs needed to link PROD, TESTPROD and sharable library +# system libs needed to link PROD, TESTPROD and shareable library # # ---------- system libraries for all products # for all systems: diff --git a/configure/os/CONFIG.Common.RTEMS b/configure/os/CONFIG.Common.RTEMS index a736addaa9..9a2f4823b2 100644 --- a/configure/os/CONFIG.Common.RTEMS +++ b/configure/os/CONFIG.Common.RTEMS @@ -65,7 +65,7 @@ CXXFLAGS = $($(BUILD_CLASS)_CXXFLAGS) $(POSIX_CXXFLAGS) $(OPT_CXXFLAGS)\ $(USR_CXXFLAGS) $(CMD_CXXFLAGS) $(ARCH_DEP_CXXFLAGS) $(CODE_CXXFLAGS)\ $(STATIC_CXXFLAGS) $(OP_SYS_CXXFLAGS) $(LIBRARY_SRC_CFLAGS) -LDFLAGS = $(OPT_LDFLAGS) $(TARGET_LDFLAGS) $(USR_LDFLAGS) $(CMD_LDFLAGS)\ +LDFLAGS += $(OPT_LDFLAGS) $(TARGET_LDFLAGS) $(USR_LDFLAGS) $(CMD_LDFLAGS)\ $(POSIX_LDFLAGS) $(ARCH_DEP_LDFLAGS) $(DEBUG_LDFLAGS) $(OP_SYS_LDFLAGS)\ $($(BUILD_CLASS)_LDFLAGS) $(RUNTIME_LDFLAGS) $(CODE_LDFLAGS) @@ -102,15 +102,47 @@ OS_CLASS = RTEMS # Operating system compile & link flags OP_SYS_CFLAGS += -D__LINUX_ERRNO_EXTENSIONS__ -OP_SYS_CFLAGS_NET_yes = -DRTEMS_LEGACY_STACK -OP_SYS_CFLAGS += $(OP_SYS_CFLAGS_NET_$(RTEMS_HAS_NETWORKING)) +# Has RTEMS been built with the internal legacy stack? +ifeq ($(RTEMS_LEGACY_NETWORKING_INTERNAL),yes) +RTEMS_HAS_NETWORKING = yes +RTEMS_NETWORKING = legacy_internal +endif + +# Has RTEMS been built with the legacy stack as a separate package? +ifeq ($(RTEMS_LEGACY_NETWORKING),yes) +RTEMS_HAS_NETWORKING = yes +RTEMS_NETWORKING = legacy +endif -ifeq ($(RTEMS_HAS_POSIX_API),yes) -POSIX_CPPFLAGS = -D_GNU_SOURCE -D_DEFAULT_SOURCE +# Has RTEMS been built with the libbsd stack as a separate package? +ifeq ($(RTEMS_BSD_NETWORKING),yes) +RTEMS_HAS_NETWORKING = yes +RTEMS_NETWORKING = bsd endif -OP_SYS_LDLIBS_posix_NET_yes = -ltftpfs -lnfs -lz -ltelnetd -OP_SYS_LDLIBS_posix_NET_no = -ltftpfs -lbsd -lz +RTEMS_LEGACY_NET_LIB_no= + +# Legacy network with RTEMS 5 and earlier +RTEMS_NET_LIB_legacy_internal=-lnfs +OP_SYS_CFLAGS_NET_legacy_internal = -DRTEMS_LEGACY_STACK + +# Legacy network with RTEMS 6 is a separate package and library +RTEMS_NET_LIB_legacy=-lnfs -lnetworking -lnfs +OP_SYS_CFLAGS_NET_legacy = -DRTEMS_LEGACY_STACK + +# LibBSD network with RTEMS 5 and 6 is a separate package and library +RTEMS_NET_LIB_bsd=-lbsd +OP_SYS_CFLAGS_NET_bsd = -DRTEMS_LIBBSD_STACK + +# Set the networking flags +OP_SYS_CFLAGS += $(OP_SYS_CFLAGS_NET_$(RTEMS_NETWORKING)) + +POSIX_CPPFLAGS_posix = -D_GNU_SOURCE -D_DEFAULT_SOURCE +POSIX_CPPFLAGS = $(POSIX_CPPFLAGS_$(OS_API)) + +OP_SYS_LDLIBS_posix_NET_yes = -ltftpfs -lz -ltelnetd +OP_SYS_LDLIBS_posix_NET_yes += $(RTEMS_NET_LIB_$(RTEMS_NETWORKING)) +OP_SYS_LDLIBS_posix_NET_no = -ltftpfs -lz OP_SYS_LDLIBS_score_NET_yes = -lnfs OP_SYS_LDLIBS_score_NET_no = -lnfs OP_SYS_LDLIBS += -lrtemsCom -lCom @@ -133,7 +165,8 @@ MOD_SYS_LDFLAGS += $(CPU_CFLAGS) -Wl,-r -nostdlib GESYS_LIBS += -lgcc GESYS_LIBS += -lc -lm -lrtemscpu -lrtemsbsp -lrtems++ GESYS_LIBS += -lcexp -ltecla_r -lspencer_regexp -lpmelf -lpmbfd -GESYS_LIBS += -lnfs -ltelnetd -lrtems-gdb-stub +GESYS_LIBS += -lnfs -ltelnetd -lrtems-gdb-stub -lbsd -ltftpfs -lz +GESYS_LIBS += -lnetworking # While not part of the Generic Image it provides symbols which # would conflict. diff --git a/configure/os/CONFIG.Common.RTEMS-beagleboneblack b/configure/os/CONFIG.Common.RTEMS-beagleboneblack index 4b6107b052..edda91b9d0 100644 --- a/configure/os/CONFIG.Common.RTEMS-beagleboneblack +++ b/configure/os/CONFIG.Common.RTEMS-beagleboneblack @@ -9,7 +9,7 @@ RTEMS_BSP = beagleboneblack RTEMS_TARGET_CPU = arm GNU_TARGET = arm-rtems -OP_SYS_LDLIBS += -Wl,--gc-sections +OP_SYS_LDFLAGS += -Wl,--gc-sections ARCH_DEP_LDFLAGS = -L$(RTEMS_BASE)/$(GNU_TARGET)$(RTEMS_VERSION)/beagleboneblack/lib/ include $(CONFIG)/os/CONFIG.Common.RTEMS diff --git a/configure/os/CONFIG.Common.RTEMS-mvme2700 b/configure/os/CONFIG.Common.RTEMS-mvme2700 index 82715277eb..3b43ae45be 100644 --- a/configure/os/CONFIG.Common.RTEMS-mvme2700 +++ b/configure/os/CONFIG.Common.RTEMS-mvme2700 @@ -1,7 +1,6 @@ # # Author: Matt Rippa # -RTEMS_BSP = mvme2307 RTEMS_TARGET_CPU = powerpc ARCH_DEP_CFLAGS += -DMY_DO_BOOTP=NULL ARCH_DEP_CFLAGS += -DHAVE_PPCBUG @@ -23,3 +22,9 @@ define MUNCH_CMD endef include $(CONFIG)/os/CONFIG.Common.RTEMS + +ifeq ($(shell test $(RTEMS_VERSION) -ge 5; echo $$?),0) +RTEMS_BSP = mvme2700 +else +RTEMS_BSP = mvme2307 +endif diff --git a/configure/os/CONFIG.Common.RTEMS-pc386 b/configure/os/CONFIG.Common.RTEMS-pc386 index 9fc518d6b5..77867f0046 100644 --- a/configure/os/CONFIG.Common.RTEMS-pc386 +++ b/configure/os/CONFIG.Common.RTEMS-pc386 @@ -25,7 +25,7 @@ include $(CONFIG)/os/CONFIG.Common.RTEMS OP_SYS_LDFLAGS += -Wl,-Ttext,0x100000 # This check must appear after the above include -ifeq ($(RTEMS_VERSION),5) +ifeq ($(shell test $(RTEMS_VERSION) -ge 5; echo $$?),0) $(info *** This target is not compatible with the configured RTEMS version.) $(info *** Build the RTEMS-pc686 (-qemu) target for RTEMS 5.x) $(error Can't continue) diff --git a/configure/os/CONFIG.Common.RTEMS-pc686 b/configure/os/CONFIG.Common.RTEMS-pc686 index 882a2f3cb3..5d30b17d8f 100644 --- a/configure/os/CONFIG.Common.RTEMS-pc686 +++ b/configure/os/CONFIG.Common.RTEMS-pc686 @@ -20,7 +20,6 @@ define MUNCH_CMD $(RTEMS_TOOLS)/bin/$(OBJCOPY_FOR_TARGET) -O binary -R .comment -S $< $@ endef -OP_SYS_LDLIBS += -Wl,--gc-sections ARCH_DEP_LDFLAGS = -L$(RTEMS_BASE)/$(GNU_TARGET)$(RTEMS_VERSION)/pc686/lib/ include $(CONFIG)/os/CONFIG.Common.RTEMS @@ -28,11 +27,11 @@ include $(CONFIG)/os/CONFIG.Common.RTEMS # # Put text segment where it will work with etherboot # -OP_SYS_LDFLAGS += -Wl,-Ttext,0x100000 +OP_SYS_LDFLAGS += -Wl,-Ttext,0x100000 -Wl,--gc-sections # This check must appear after the above include -ifneq ($(RTEMS_VERSION),5) +ifeq ($(shell test $(RTEMS_VERSION) -lt 5; echo $$?),0) $(info *** This target is not compatible with the configured RTEMS version.) $(info *** Build the RTEMS-pc386 (-qemu) target for RTEMS 4.x) $(error Can't continue) diff --git a/configure/os/CONFIG.Common.RTEMS-qoriq_e500 b/configure/os/CONFIG.Common.RTEMS-qoriq_e500 index 7f5c42e8e2..234c2e4f4e 100644 --- a/configure/os/CONFIG.Common.RTEMS-qoriq_e500 +++ b/configure/os/CONFIG.Common.RTEMS-qoriq_e500 @@ -20,9 +20,9 @@ ARCH_DEP_CFLAGS += -DRTEMS_HAS_ALTIVEC #ARCH_DEP_CFLAGS += -I$(RTEMS_BASE)/powerpc-rtems5/qoriq_e500/lib/include #OP_SYS_LDLIBS += -lbspExt #does not use posix stuff ... want to ignore -OP_SYS_LDLIBS += -Wl,--gc-sections +OP_SYS_LDFLAGS += -Wl,--gc-sections #ARCH_DEP_LDFLAGS = -mcpu=8540 -meabi -msdata=sysv -mstrict-align -mspe -mabi=spe -mfloat-gprs=double -ARCH_DEP_LDFLAGS = -L$(RTEMS_BASE)/powerpc-rtems5/qoriq_e500/lib +ARCH_DEP_LDFLAGS = -L$(RTEMS_BASE)/$(GNU_TARGET)$(RTEMS_VERSION)/$(RTEMS_BSP)/lib MUNCH_SUFFIX = .img MUNCHNAME = $(PRODNAME:%$(EXE)=%$(MUNCH_SUFFIX)) diff --git a/configure/os/CONFIG.Common.RTEMS-xilinx_zynq_a9_qemu b/configure/os/CONFIG.Common.RTEMS-xilinx_zynq_a9_qemu index 83b518b693..51cd286ae4 100644 --- a/configure/os/CONFIG.Common.RTEMS-xilinx_zynq_a9_qemu +++ b/configure/os/CONFIG.Common.RTEMS-xilinx_zynq_a9_qemu @@ -12,7 +12,7 @@ GNU_TARGET = arm-rtems #use dhcp/bootp ARCH_DEP_CFLAGS += -DMY_DO_BOOTP=NULL -OP_SYS_LDLIBS += -Wl,--gc-sections +OP_SYS_LDFLAGS += -Wl,--gc-sections ARCH_DEP_LDFLAGS = -L$(RTEMS_BASE)/$(GNU_TARGET)$(RTEMS_VERSION)/xilinx_zynq_a9_qemu/lib/ diff --git a/configure/os/CONFIG.Common.RTEMS-xilinx_zynq_microzed b/configure/os/CONFIG.Common.RTEMS-xilinx_zynq_microzed new file mode 100644 index 0000000000..212b1b16f3 --- /dev/null +++ b/configure/os/CONFIG.Common.RTEMS-xilinx_zynq_microzed @@ -0,0 +1,16 @@ +# +# CONFIG.Common.RTEMS-xilinx_zynq_microzed +# Author: Chris Johns +# +# All RTEMS targets use the same Makefile fragment +# +#EXE = .elf +RTEMS_BSP = xilinx_zynq_microzed +RTEMS_TARGET_CPU = arm +GNU_TARGET = arm-rtems + +OP_SYS_LDLIBS += -Wl,--gc-sections +ARCH_DEP_LDFLAGS = -L$(RTEMS_BASE)/$(GNU_TARGET)$(RTEMS_VERSION)/xilinx_zynq_microzed/lib/ + + +include $(CONFIG)/os/CONFIG.Common.RTEMS diff --git a/configure/os/CONFIG.Common.RTEMS-xilinx_zynq_zedboard b/configure/os/CONFIG.Common.RTEMS-xilinx_zynq_zedboard index bac33eb912..f745b15d69 100644 --- a/configure/os/CONFIG.Common.RTEMS-xilinx_zynq_zedboard +++ b/configure/os/CONFIG.Common.RTEMS-xilinx_zynq_zedboard @@ -9,7 +9,7 @@ RTEMS_BSP = xilinx_zynq_zedboard RTEMS_TARGET_CPU = arm GNU_TARGET = arm-rtems -OP_SYS_LDLIBS += -Wl,--gc-sections +OP_SYS_LDFLAGS += -Wl,--gc-sections ARCH_DEP_LDFLAGS = -L$(RTEMS_BASE)/$(GNU_TARGET)$(RTEMS_VERSION)/xilinx_zynq_zedboard/lib/ diff --git a/configure/os/CONFIG.Common.iosCommon b/configure/os/CONFIG.Common.iosCommon index 5c248f38aa..03d3cab8e8 100644 --- a/configure/os/CONFIG.Common.iosCommon +++ b/configure/os/CONFIG.Common.iosCommon @@ -42,12 +42,6 @@ ARCH_DEP_LDFLAGS += $(ARCH_DEP_FLAGS) OP_SYS_CFLAGS += -isysroot $(SDK_DIR) OP_SYS_LDFLAGS += -isysroot $(SDK_DIR) -#-------------------------------------------------- -# Always compile in debugging symbol table information -# -OPT_CFLAGS_YES += -g -OPT_CXXFLAGS_YES += -g - #------------------------------------------------------- # Compiler definitions: diff --git a/configure/os/CONFIG.darwin-aarch64-debug.Common b/configure/os/CONFIG.darwin-aarch64-debug.Common new file mode 100644 index 0000000000..49b58c879d --- /dev/null +++ b/configure/os/CONFIG.darwin-aarch64-debug.Common @@ -0,0 +1,11 @@ +# CONFIG.darwin-aarch64-debug.Common +# +# Definitions for darwin-aarch64-debug host builds - darwin-aarch64 target build with debug compiler flags +# Sites may override these definitions in CONFIG_SITE.darwin-aarch64-debug.Common +#------------------------------------------------------- + +include $(CONFIG)/os/CONFIG.darwin-aarch64.Common + +# Removes -O optimization and adds -g compile option +HOST_OPT=NO + diff --git a/configure/os/CONFIG.darwin-aarch64.darwin-aarch64-debug b/configure/os/CONFIG.darwin-aarch64.darwin-aarch64-debug new file mode 100644 index 0000000000..25d27b9968 --- /dev/null +++ b/configure/os/CONFIG.darwin-aarch64.darwin-aarch64-debug @@ -0,0 +1,14 @@ +# CONFIG.darwin-aarch64.darwin-aarch64-debug +# +# Definitions for darwin-aarch64 host - darwin-aarch64-debug target build with debug compiler flags +# Sites may override these definitions in CONFIG_SITE.darwin-aarch64.darwin-aarch64-debug +#------------------------------------------------------- + +-include $(CONFIG)/os/CONFIG.Common.darwin-aarch64 +-include $(CONFIG)/os/CONFIG.darwin-aarch64.darwin-aarch64 +-include $(CONFIG)/os/CONFIG_SITE.Common.darwin-aarch64 +-include $(CONFIG)/os/CONFIG_SITE.darwin-aarch64.darwin-aarch64 + + +BUILD_CLASS=HOST +HOST_OPT = NO diff --git a/configure/os/CONFIG.darwin-x86-debug.Common b/configure/os/CONFIG.darwin-x86-debug.Common new file mode 100644 index 0000000000..4e9aca82b3 --- /dev/null +++ b/configure/os/CONFIG.darwin-x86-debug.Common @@ -0,0 +1,11 @@ +# CONFIG.darwin-x86-debug.Common +# +# Definitions for darwin-x86-debug host builds - darwin-x86 target build with debug compiler flags +# Sites may override these definitions in CONFIG_SITE.darwin-x86-debug.Common +#------------------------------------------------------- + +include $(CONFIG)/os/CONFIG.darwin-x86.Common + +# Removes -O optimization and adds -g compile option +HOST_OPT=NO + diff --git a/configure/os/CONFIG.darwinCommon.darwinCommon b/configure/os/CONFIG.darwinCommon.darwinCommon index c0a69f46cd..2c12b2fbbf 100644 --- a/configure/os/CONFIG.darwinCommon.darwinCommon +++ b/configure/os/CONFIG.darwinCommon.darwinCommon @@ -39,12 +39,6 @@ OP_SYS_CFLAGS += -fno-common # OP_SYS_CPPFLAGS += -Ddarwin -# -# Always compile in debugging symbol table information -# -OPT_CFLAGS_YES += -g -OPT_CXXFLAGS_YES += -g - # # Libraries for command-line editing. # diff --git a/configure/os/CONFIG.freebsd-x86_64.freebsd-x86_64 b/configure/os/CONFIG.freebsd-x86_64.freebsd-x86_64 index 0ad2bfcf86..5e8b691d8b 100644 --- a/configure/os/CONFIG.freebsd-x86_64.freebsd-x86_64 +++ b/configure/os/CONFIG.freebsd-x86_64.freebsd-x86_64 @@ -2,13 +2,23 @@ # Definitions for freebsd-x86_64 host - freebsd-x86_64 target builds # Sites may override these definitions in CONFIG_SITE.freebsd-x86_64.freebsd-x86_64 #------------------------------------------------------- -GNU_DIR=/usr/local +GNU_DIR=/usr # Include common gnu compiler definitions include $(CONFIG)/CONFIG.gnuCommon +GNU_BIN = $(GNU_DIR)/bin +GNU_LIB = $(GNU_DIR)/lib + +CMPLR_CLASS = clang + +CC = $(GNU_BIN)/$(CMPLR_PREFIX)cc$(CMPLR_SUFFIX) +CCC = $(GNU_BIN)/$(CMPLR_PREFIX)c++$(CMPLR_SUFFIX) +CPP = $(CC) -x c -E + +GNU_LDLIBS_YES = + STATIC_LDFLAGS_YES= -Wl,-Bstatic STATIC_LDFLAGS_NO= STATIC_LDLIBS_YES= -Wl,-Bdynamic STATIC_LDLIBS_NO= - diff --git a/configure/os/CONFIG.win32-x86.win32-x86 b/configure/os/CONFIG.win32-x86.win32-x86 index 767f4c7fac..abf552ba91 100644 --- a/configure/os/CONFIG.win32-x86.win32-x86 +++ b/configure/os/CONFIG.win32-x86.win32-x86 @@ -16,7 +16,7 @@ OPT_WHOLE_PROGRAM = YES WINLINK = link -RCCMD = rc -l 0x409 $(INCLUDES) -fo $@ $< +RCCMD = rc -nologo -l 0x409 $(INCLUDES) -fo $@ $< ARCMD = lib -nologo -verbose -out:$@ $(LIB_OPT_LDFLAGS) $(LIBRARY_LD_OBJS) @@ -222,7 +222,7 @@ INSTALL_SHRLIB = $(INSTALL_BIN) #-------------------------------------------------- -# Products dependancy definitions +# Products dependency definitions PROD_DEPLIBS = $(foreach lib, $(PROD_LIBS) $(USR_LIBS), \ $(firstword $(wildcard \ @@ -247,10 +247,10 @@ PROD_LDLIBS += $(STATIC_LDLIBS) \ $(LDLIBS_SHARED_$(SHARED_LIBRARIES)))) #-------------------------------------------------- -# Libraries dependancy definitions +# Libraries dependency definitions # libs that we need to link the DLL with -# (it isnt necessary to rebuild the dll if these change) +# (it isn't necessary to rebuild the dll if these change) SHRLIB_DEPLIBS = $(foreach lib, $(LIB_LIBS) $(USR_LIBS), \ $(firstword $(wildcard \ diff --git a/configure/os/CONFIG_SITE.cygwin-x86.cygwin-x86 b/configure/os/CONFIG_SITE.cygwin-x86.cygwin-x86 index 46a1ecceac..3125c7a64a 100644 --- a/configure/os/CONFIG_SITE.cygwin-x86.cygwin-x86 +++ b/configure/os/CONFIG_SITE.cygwin-x86.cygwin-x86 @@ -6,11 +6,3 @@ # GNU_DIR used when COMMANDLINE_LIBRARY is READLINE #GNU_DIR=C:/cygwin - -# It makes sense to include debugging symbols even in optimized builds -# in case you want to attach gdb to the process or examine a core-dump. -# This does cost disk space, but not memory as debug symbols are not -# loaded into RAM when the binary is loaded. -OPT_CFLAGS_YES += -g -OPT_CXXFLAGS_YES += -g - diff --git a/configure/os/CONFIG_SITE.darwinCommon.darwinCommon b/configure/os/CONFIG_SITE.darwinCommon.darwinCommon index d45422401e..85ccbad382 100644 --- a/configure/os/CONFIG_SITE.darwinCommon.darwinCommon +++ b/configure/os/CONFIG_SITE.darwinCommon.darwinCommon @@ -6,24 +6,17 @@ # These settings are designed for users of Homebrew. # Users of other third-party package managers are welcome to # provide patches appropriate for their manager. -ifneq (,$(wildcard /opt/homebrew)) - # Default location on aarch64 - HOMEBREW_DIR = /opt/homebrew -else ifneq (,$(wildcard /usr/local/Homebrew)) - # Default location on x86_64 - HOMEBREW_DIR = /usr/local -else ifneq (,$(wildcard /opt/local/include/readline)) - # MacPorts - READLINE_DIR = /opt/local -endif +HOMEBREW_DIR := $(shell brew --prefix 2>/dev/null) +INCLUDES += $(HOMEBREW_DIR:%=-I%/include) +LDFLAGS += $(HOMEBREW_DIR:%=-L%/lib) # Look for Homebrew's readline ifneq (,$(wildcard $(HOMEBREW_DIR)/opt/readline)) - READLINE_DIR = $(HOMEBREW_DIR)/opt/readline + INCLUDES_READLINE += -I$(HOMEBREW_DIR)/opt/readline/include + LDFLAGS_READLINE += -L$(HOMEBREW_DIR)/opt/readline/lib endif -# Use GNU readline if it's avaiilable -ifneq (,$(wildcard $(READLINE_DIR)/include/readline/readline.h)) - INCLUDES_READLINE = -I$(READLINE_DIR)/include - LDFLAGS_READLINE = -L$(READLINE_DIR)/lib -endif +# Look for MacPorts +MACPORT_DIR := $(patsubst %/bin/port,%,$(shell which port)) +INCLUDES += $(MACPORT_DIR:%=-I%/include) +LDFLAGS += $(MACPORT_DIR:%=-L%/lib) diff --git a/configure/os/CONFIG_SITE.linux-aarch64.linux-aarch64 b/configure/os/CONFIG_SITE.linux-aarch64.linux-aarch64 index e196135b76..f569951f19 100644 --- a/configure/os/CONFIG_SITE.linux-aarch64.linux-aarch64 +++ b/configure/os/CONFIG_SITE.linux-aarch64.linux-aarch64 @@ -3,10 +3,3 @@ # Site specific definitions for native linux-aarch64 builds #------------------------------------------------------- -# It makes sense to include debugging symbols even in optimized builds -# in case you want to attach gdb to the process or examine a core-dump. -# This does cost disk space, but not memory as debug symbols are not -# loaded into RAM when the binary is loaded. -OPT_CFLAGS_YES += -g -OPT_CXXFLAGS_YES += -g - diff --git a/configure/os/CONFIG_SITE.linux-x86.linux-x86 b/configure/os/CONFIG_SITE.linux-x86.linux-x86 index 5b2fa7c65a..ee7484e048 100644 --- a/configure/os/CONFIG_SITE.linux-x86.linux-x86 +++ b/configure/os/CONFIG_SITE.linux-x86.linux-x86 @@ -3,14 +3,7 @@ # Site specific definitions for linux-x86 host - linux-x86 target builds #------------------------------------------------------- -# It makes sense to include debugging symbols even in optimized builds -# in case you want to attach gdb to the process or examine a core-dump. -# This does cost disk space, but not memory as debug symbols are not -# loaded into RAM when the binary is loaded. -OPT_CFLAGS_YES += -g -OPT_CXXFLAGS_YES += -g - -# Uncomment the followings lines to build with CLANG instead of GCC. +# Uncomment the following lines to build with CLANG instead of GCC. # #GNU = NO #CMPLR_CLASS = clang diff --git a/configure/os/CONFIG_SITE.linux-x86_64.linux-x86_64 b/configure/os/CONFIG_SITE.linux-x86_64.linux-x86_64 index 9178afd43d..bdedf4f2cd 100644 --- a/configure/os/CONFIG_SITE.linux-x86_64.linux-x86_64 +++ b/configure/os/CONFIG_SITE.linux-x86_64.linux-x86_64 @@ -3,14 +3,7 @@ # Site specific definitions for linux-x86_64 host - linux-x86_64 target builds #------------------------------------------------------- -# It makes sense to include debugging symbols even in optimized builds -# in case you want to attach gdb to the process or examine a core-dump. -# This does cost disk space, but not memory as debug symbols are not -# loaded into RAM when the binary is loaded. -OPT_CFLAGS_YES += -g -OPT_CXXFLAGS_YES += -g - -# Uncomment the followings lines to build with CLANG instead of GCC. +# Uncomment the following lines to build with CLANG instead of GCC. # #GNU = NO #CMPLR_CLASS = clang diff --git a/configure/toolchain.c b/configure/toolchain.c index da4e8bb2cd..0a52760457 100644 --- a/configure/toolchain.c +++ b/configure/toolchain.c @@ -30,9 +30,26 @@ MSVC_VER = _MSC_VER #ifdef __rtems__ #include # if __RTEMS_MAJOR__>=5 -OS_API = posix + OS_API = posix # else -OS_API = score + OS_API = score +# endif +# if defined(RTEMS_NETWORKING) + /* legacy stack circa RTEMS <= 5 and networking internal to RTEMS */ + RTEMS_LEGACY_NETWORKING_INTERNAL = yes +# else +# if !defined(__has_include) + /* assume old GCC implies RTEMS < 5 with mis-configured BSP */ +# error rebuild BSP with --enable-network +# elif __has_include() + /* legacy stack circa RTEMS > 5 */ + RTEMS_LEGACY_NETWORKING = yes +# elif __has_include() + /* libbsd stack */ + RTEMS_BSD_NETWORKING = yes +# else +# error Cannot determine RTEMS network configuration +# endif # endif #endif diff --git a/documentation/.gitignore b/documentation/.gitignore deleted file mode 100644 index 52230970ac..0000000000 --- a/documentation/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -*.db -epics-base.tag -html/ diff --git a/documentation/Doxyfile@ b/documentation/Doxyfile@ index a657db19d5..ef2377119a 100644 --- a/documentation/Doxyfile@ +++ b/documentation/Doxyfile@ @@ -759,10 +759,10 @@ WARN_LOGFILE = # spaces. # Note: If this tag is empty the current directory is searched. -INPUT = ../mainpage.dox \ - ../RELEASE_NOTES.md \ +INPUT = ../RELEASE_NOTES.md \ ../README.md \ - @TOP@/include + @TOP@/include \ + @TOP@/doc # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses @@ -1105,7 +1105,7 @@ HTML_STYLESHEET = # cascading style sheets that are included after the standard style sheets # created by doxygen. Using this option one can overrule certain style aspects. # This is preferred over using HTML_STYLESHEET since it does not replace the -# standard style sheet and is therefor more robust against future updates. +# standard style sheet and is therefore more robust against future updates. # Doxygen will copy the style sheet files to the output directory. # Note: The order of the extra stylesheet files is of importance (e.g. the last # stylesheet in the list overrules the setting of the previous ones in the @@ -1648,8 +1648,8 @@ EXTRA_PACKAGES = # Note: Only use a user-defined header if you know what you are doing! The # following commands have a special meaning inside the header: $title, # $datetime, $date, $doxygenversion, $projectname, $projectnumber, -# $projectbrief, $projectlogo. Doxygen will replace $title with the empy string, -# for the replacement values of the other commands the user is refered to +# $projectbrief, $projectlogo. Doxygen will replace $title with the empty string, +# for the replacement values of the other commands the user is referred to # HTML_HEADER. # This tag requires that the tag GENERATE_LATEX is set to YES. @@ -1833,7 +1833,7 @@ MAN_LINKS = NO # captures the structure of the code including all documentation. # The default value is: NO. -GENERATE_XML = NO +GENERATE_XML = YES # The XML_OUTPUT tag is used to specify where the XML pages will be put. If a # relative path is entered the value of OUTPUT_DIRECTORY will be put in front of @@ -1841,7 +1841,7 @@ GENERATE_XML = NO # The default directory is: xml. # This tag requires that the tag GENERATE_XML is set to YES. -XML_OUTPUT = xml +XML_OUTPUT = @RTD_SRC@/xml # If the XML_PROGRAMLISTING tag is set to YES doxygen will dump the program # listings (including syntax highlighting and cross-referencing information) to @@ -1946,7 +1946,7 @@ ENABLE_PREPROCESSING = YES # The default value is: NO. # This tag requires that the tag ENABLE_PREPROCESSING is set to YES. -MACRO_EXPANSION = NO +MACRO_EXPANSION = YES # If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES then # the macro expansion is limited to the macros specified with the PREDEFINED and @@ -1986,7 +1986,20 @@ INCLUDE_FILE_PATTERNS = # recursively expanded use the := operator instead of the = operator. # This tag requires that the tag ENABLE_PREPROCESSING is set to YES. -PREDEFINED = __cplusplus +PREDEFINED = __cplusplus \ + LIBCOM_API \ + DBCORE_API \ + LIBCA_API \ + DBRECSTD_API \ + EPICS_ALWAYS_INLINE \ + EPICS_NORETURN \ + EPICS_PRINTF_FMT(x)=x \ + EPICS_PRINTF_STYLE(a,b)= \ + epicsShareExtern \ + epicsShareClass \ + epicsShareFunc \ + epicsShareAPI \ + epicsStdCall # If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this # tag can be used to specify a list of macro names that should be expanded. The diff --git a/documentation/HEADER_h.md b/documentation/HEADER_h.md new file mode 100644 index 0000000000..8fd0ba69c5 --- /dev/null +++ b/documentation/HEADER_h.md @@ -0,0 +1,4 @@ +# @HEADER@.h + +```{doxygenfile} @HEADER@.h +``` diff --git a/documentation/KnownProblems.html b/documentation/KnownProblems.html deleted file mode 100644 index 5c29cc2a78..0000000000 --- a/documentation/KnownProblems.html +++ /dev/null @@ -1,34 +0,0 @@ - - - - - - Known Problems in EPICS 7.0.3.1 - - - -

EPICS 7.0.3.1: Known Problems

- -

Any patch files linked below should be applied at the root of the -base-7.0.3.1 tree. Download them, then use the GNU Patch program as -follows:

- -
% cd /path/to/base-7.0.3.1
-% patch -p1 < /path/to/file.patch
- -

The following problems were known by the developers at the time of this -release:

- -
    - - - -
- - - diff --git a/documentation/Makefile b/documentation/Makefile index f2a75dab15..0594740169 100644 --- a/documentation/Makefile +++ b/documentation/Makefile @@ -7,25 +7,121 @@ EXPAND_ME += EPICS_REVISION EXPAND_ME += EPICS_MODIFICATION EXPAND_ME += EPICS_PATCH_LEVEL EXPAND_ME += OS_CLASS CMPLR_CLASS +EXPAND_ME += RTD_SRC -HTMLS += ComponentReference.html +DOXYGEN ?= doxygen -TARGETS += doxygen +include $(TOP)/documentation/Makefile.headers -DOXYGEN = doxygen -ME = documentation/O.$(T_A)/html -GH_FILES = $(ME)/ $(ME)/.nojekyll $(ME)/*.* $(ME)/*/*.* +HEADER_MD_FILES = $(foreach t, $(HEADER_TYPES), \ + $(addsuffix _h.md, $($t_HEADERS))) +API_RST_FILES = $(addsuffix -api.rst, $(HEADER_TYPES)) + +RTD_SRC = $(COMMON_DIR)/rtd-src + +DOCS += README.md +DOCS += ca-cli.md +DOCS += ReleaseChecklist.md + +OLD_NOTES = $(wildcard ../RELEASE-*.md) +DOCS += $(OLD_NOTES:../%=%) + +ifneq ($(EPICS_DEV_SNAPSHOT),) + # Between releases the release target is disabled + DOCS += RELEASE_NOTES.md + REASON = EPICS_DEV_SNAPSHOT not empty +else + # Not a snapshot, user may be creating a new release + ifdef T_A + DOC = ../ + endif + # Could we be in the middle of anything? + NOTES_FILE = RELEASE-$(EPICS_SHORT_VERSION).md + NOTES_PATH = $(DOC)$(NOTES_FILE) + NOTES_GIT := $(firstword $(shell git status --porcelain $(NOTES_PATH))) + ifeq ($(NOTES_GIT),A) + # New file was added to the Git index; allow unrelease + else ifeq ($(NOTES_GIT),AD) + # File was added to the Git index but deleted; allow unrelease + else ifeq ($(wildcard $(NOTES_PATH)),) + # No file yet, allow release + else + # File exists but isn't in Git + REASON := $(NOTES_FILE) exists but isn't in Git + NOTES_FILE = + NOTES_PATH = + endif +endif + +NEW_DIR = ../new-notes +NEW_NOTES = $(wildcard $(NEW_DIR)/*.md) include $(TOP)/configure/RULES -doxygen: Doxyfile ../mainpage.dox $(INSTALL_HTMLS) +ifndef T_A + release unrelease doxygen rtd sphinx: inc + $(MAKE) -C O.$(EPICS_HOST_ARCH) $@ +else + +MAKENOTES = ../make-notes.pl + +ifneq ($(NOTES_PATH),) + $(NOTES_PATH): $(NEW_NOTES) $(MAKENOTES) + @$(RM) $@ + $(PERL) $(MAKENOTES) -o $@ -V $(EPICS_SHORT_VERSION) \ + -d $(abspath $(NEW_DIR)) + release-git: $(NOTES_PATH) + $(if $(NEW_NOTES), \ + git rm -q $(NEW_NOTES)) + git add $< + release: $(INSTALL_DOC)/RELEASE_NOTES.md $(INSTALL_DOC)/$(NOTES_FILE) + + unrelease: + $(if $(wildcard $(NOTES_PATH)), \ + git restore --staged $(NOTES_PATH); \ + $(RM) $(NOTES_PATH)) + git restore --staged $(NEW_DIR) + git restore $(NEW_DIR) + + REL_DEP = release-git $(NOTES_PATH) +else + REL_DEV = -D + release unrelease: + $(error "make $@" not available, $(REASON)) +endif + +$(COMMON_DIR)/RELEASE_NOTES.md: $(REL_DEP) $(NEW_NOTES) $(MAKENOTES) + @$(RM) $@ + $(PERL) $(MAKENOTES) -o $@ -V $(EPICS_SHORT_VERSION) $(REL_DEV) \ + -d $(abspath $(NEW_DIR)) $(OLD_NOTES) $(NOTES_PATH) + +$(HEADER_MD_FILES): %_h.md: ../HEADER_h.md + $(EXPAND_TOOL) -t $(INSTALL_LOCATION) -DHEADER=$* $< $@ + +$(API_RST_FILES): %-api.rst: ../%-API.rst + @$(RM) $@ + @$(ECHO) Creating $@ + @$(CP) $< $@ + @$(foreach h, $($*_HEADERS), \ + echo " $h_h.rst" >> $@;) + +doxygen: Doxyfile + @$(MKDIR) $(RTD_SRC) $(DOXYGEN) - rsync -av $(TOP)/html/ html/ +DOX = doxygen -.PHONY: doxygen +# Use "make sphinx DOX=" to skip running doxygen +rtd: $(DOX) $(API_RST_FILES) $(HEADER_MD_FILES) + rsync -av --exclude=RELEASE-*.md $(INSTALL_DOC)/ $(RTD_SRC)/ + rsync -av $(HEADER_MD_FILES) $(RTD_SRC)/ + rsync -av $(API_RST_FILES) $(RTD_SRC)/ + rsync -av ../index.rst ../conf.py $(RTD_SRC)/ +RTD = rtd -commit: doxygen - $(TOUCH) html/.nojekyll - (cd $(TOP) && $(CURDIR)/../commit-gh.sh $(GH_FILES)) +# Use "make sphinx RTD=" to skip earlier steps +sphinx: $(RTD) + cd $(COMMON_DIR); $(PYTHON) -m sphinx rtd-src readthedocs + rsync -av $(COMMON_DIR)/readthedocs $(INSTALL_HTML)/ +endif -.PHONY: commit +.PHONY: release release-git unrelease doxygen rtd sphinx diff --git a/documentation/Makefile.headers b/documentation/Makefile.headers new file mode 100644 index 0000000000..016af88cad --- /dev/null +++ b/documentation/Makefile.headers @@ -0,0 +1,130 @@ +# Makefile.headers +# +# Lists of C/C++ header files to include in each +# section of the documentation + +HEADER_TYPES = libcom ca database record menu + +libcom_HEADERS += adjustment +libcom_HEADERS += alarm +libcom_HEADERS += alarmString +libcom_HEADERS += asTrapWrite +libcom_HEADERS += bucketLib +libcom_HEADERS += cantProceed +libcom_HEADERS += compilerDependencies +libcom_HEADERS += cvtFast +libcom_HEADERS += dbmf +libcom_HEADERS += devLibVME +libcom_HEADERS += devLibVMEImpl +libcom_HEADERS += ellLib +libcom_HEADERS += envDefs +libcom_HEADERS += epicsAlgorithm +libcom_HEADERS += epicsAssert +libcom_HEADERS += epicsAtomic +libcom_HEADERS += epicsEvent +libcom_HEADERS += epicsExit +libcom_HEADERS += epicsExport +libcom_HEADERS += epicsGeneralTime +libcom_HEADERS += epicsGuard +libcom_HEADERS += epicsMessageQueue +libcom_HEADERS += epicsMutex +libcom_HEADERS += epicsReadline +libcom_HEADERS += epicsRingBytes +libcom_HEADERS += epicsRingPointer +libcom_HEADERS += epicsSignal +libcom_HEADERS += epicsSpin +libcom_HEADERS += epicsStackTrace +libcom_HEADERS += epicsStdio +libcom_HEADERS += epicsStdlib +libcom_HEADERS += epicsString +libcom_HEADERS += epicsTempFile +libcom_HEADERS += epicsThread +libcom_HEADERS += epicsTime +libcom_HEADERS += epicsTypes +libcom_HEADERS += epicsUnitTest +libcom_HEADERS += errlog +libcom_HEADERS += freeList +libcom_HEADERS += iocsh +libcom_HEADERS += ipAddrToAsciiAsynchronous +libcom_HEADERS += logClient +libcom_HEADERS += macLib +libcom_HEADERS += osiPoolStatus +libcom_HEADERS += osiProcess +libcom_HEADERS += osiSock +libcom_HEADERS += postfix +libcom_HEADERS += shareLib +libcom_HEADERS += testMain +libcom_HEADERS += yajl_alloc +libcom_HEADERS += yajl_common +libcom_HEADERS += yajl_gen +libcom_HEADERS += yajl_parse + +ca_HEADERS += cadef +ca_HEADERS += caerr +ca_HEADERS += caeventmask +ca_HEADERS += db_access + +database_HEADERS += callback +database_HEADERS += chfPlugin +database_HEADERS += dbChannel +database_HEADERS += dbCommon +database_HEADERS += dbDefs +database_HEADERS += dbExtractArray +database_HEADERS += dbLink +database_HEADERS += dbServer +database_HEADERS += dbState +database_HEADERS += dbStaticLib +database_HEADERS += dbUnitTest +database_HEADERS += devLib +database_HEADERS += devSup +database_HEADERS += drvSup +database_HEADERS += initHooks + +record_HEADERS += aaiRecord +record_HEADERS += aaoRecord +record_HEADERS += aiRecord +record_HEADERS += aoRecord +record_HEADERS += aSubRecord +record_HEADERS += biRecord +record_HEADERS += boRecord +record_HEADERS += calcRecord +record_HEADERS += calcoutRecord +record_HEADERS += compressRecord +record_HEADERS += dfanoutRecord +record_HEADERS += eventRecord +record_HEADERS += fanoutRecord +record_HEADERS += histogramRecord +record_HEADERS += int64inRecord +record_HEADERS += int64outRecord +record_HEADERS += longinRecord +record_HEADERS += longoutRecord +record_HEADERS += lsiRecord +record_HEADERS += lsoRecord +record_HEADERS += mbbiRecord +record_HEADERS += mbbiDirectRecord +record_HEADERS += mbboRecord +record_HEADERS += mbboDirectRecord +record_HEADERS += permissiveRecord +record_HEADERS += printfRecord +record_HEADERS += selRecord +record_HEADERS += seqRecord +record_HEADERS += stateRecord +record_HEADERS += stringinRecord +record_HEADERS += stringoutRecord +record_HEADERS += subRecord +record_HEADERS += subArrayRecord +record_HEADERS += waveformRecord + +menu_HEADERS += menuAlarmSevr +menu_HEADERS += menuAlarmStat +menu_HEADERS += menuConvert +menu_HEADERS += menuFtype +menu_HEADERS += menuIvoa +menu_HEADERS += menuOmsl +menu_HEADERS += menuPini +menu_HEADERS += menuPost +menu_HEADERS += menuPriority +menu_HEADERS += menuScan +menu_HEADERS += menuSimm +menu_HEADERS += menuYesNo + diff --git a/documentation/README.md b/documentation/README.md index 85913891ad..a74266db82 100644 --- a/documentation/README.md +++ b/documentation/README.md @@ -1,27 +1,7 @@ -# Installation Instructions {#install} +# Installation instructions ## EPICS Base Release 7.0.x ------ - -### Table of Contents - - - [What is EPICS base?](#what-is-epics-base?) - - [What is new in this release?](#what-is-new-in-this-release?) - - [Copyright](#copyright) - - [Supported platforms](#supported-platforms) - - [Supported compilers](#supported-compilers) - - [Software requirements](#software-requirements) - - [Host system storage requirements](#host-system-storage-requirements) - - [Documentation](#documentation) - - [Directory Structure](#directory-structure) - - [Site-specific build configuration](#site-specific-build-configuration) - - [Building EPICS base](#building-epics-base) - - [Example application and extension](#example-application-and-extension) - - [Multiple host platforms](#multiple-host-platforms) - ------ - ### What is EPICS base? The Experimental Physics and Industrial Control Systems (EPICS) is an @@ -36,8 +16,8 @@ interfaces) of various types. ### What is new in this release? -Please check the `documentation/RELEASE_NOTES.md` file for -description of changes and release migration details. +Please check the [release notes](RELEASE_NOTES.md) +for description of changes and release migration details. ### Copyright @@ -80,8 +60,7 @@ executable must be found through your normal search path. #### Unzip and tar (Winzip on WIN32 systems) -You must have tools available to unzip and untar the EPICS base -distribution file. +You may need tools to unzip and untar the EPICS base distribution file. #### Target systems @@ -91,7 +70,7 @@ running as processes on the host platform. #### vxWorks -You must have vxWorks 6.8 or later installed if any of your target +You must have vxWorks 6.8 or 6.9 installed if any of your target systems are vxWorks systems; the C++ compiler from older versions cannot compile recently developed code. The vxWorks installation provides the cross-compiler and header files needed to build for these targets. The @@ -120,16 +99,10 @@ readline is available and will then use it. RTEMS targets may be configured to use `LIBTECLA` if available, and on vxWorks the OS's ledLib line-editing library is normally used. -### Host system storage requirements - -The compressed tar file is approximately 3 MB in size. The -distribution source tree takes up approximately 21 MB. A 64-bit host -architecture may need around 610 MB to compile, while cross-compiled -targets are somewhat smaller. - ### Documentation -EPICS documentation is available through the [EPICS website](https://epics.anl.gov/) at Argonne. +EPICS documentation is available from the +[EPICS Documentation Website](https://docs.epics-controls.org/). Release specific documentation can also be found in the `base/documentation` directory of the distribution. @@ -143,8 +116,10 @@ Release specific documentation can also be found in the base/configure Build rules and OS-independent config files base/configure/os OS-dependent build config files base/documentation Distribution documentation - base/src Source code in various subdirectories + base/src Source code for templates and build tools + base/modules Source code for distribution submodules base/startup Scripts for setting up path and environment + base/test Unit tests for build tools ``` #### Directories created by the build @@ -158,7 +133,8 @@ configuration variable if that has been set. cfg Installed build configuration files db Installed database files dbd Installed database definition files - html Installed html documentation + doc Installed Markdown documentation + html Installed HTML documentation include Installed header files include/os Installed OS-specific header files in subdirs include/compiler Installed compiler-specific header files @@ -169,12 +145,11 @@ configuration variable if that has been set. #### `base/documentation` Directory -This contains documents on how to setup, build, and install EPICS. +This contains EPICS reference documentation. ``` README.md This file RELEASE_NOTES.md Notes on release changes - KnownProblems.html List of known problems and workarounds ``` #### `base/startup` Directory @@ -257,14 +232,16 @@ required path and other environment variables. * **`EPICS_HOST_ARCH`** Some host builds of EPICS require that the environment variable -`EPICS_HOST_ARCH` be defined. The perl script `EpicsHostArch.pl` in the -`base/startup` directory prints the value which the build will use if -the variable is not set before the build starts. Architecture names -start with the operating system followed by a dash and the host CPU -architecture, e.g. `linux-x86_64`. Some architecture names have another -dash followed by another keyword, for example when building for Windows -but using the MinGW compiler the name must be `windows-x64-mingw`. See -`configure/CONFIG_SITE` for a list of supported host architecture names. +`EPICS_HOST_ARCH` be defined. The shell script `EpicsHostArch` in the +`base/startup` directory (which finds and calls the perl script +`EpicsHostArch.pl` in the `base/src/tools` directory) prints the +value which the build will use if the variable is not set before +the build starts. Architecture names start with the operating system +followed by a dash and the host CPU architecture, e.g. `linux-x86_64`. +Some architecture names have another dash followed by another keyword, +for example when building for Windows but using the MinGW compiler the +name must be `windows-x64-mingw`. See `configure/CONFIG_SITE` for a +list of supported host architecture names. * **`PATH`** As already mentioned, you must have the `perl` executable and you may @@ -272,8 +249,7 @@ need C and C++ compilers in your search path. When building base you must have `echo` in your search path. For Unix host builds you will also need `cp`, `rm`, `mv`, and `mkdir` in your search path. Some Unix systems may also need `ar` and `ranlib`, and the C/C++ compilers may -require `as` and `ld` in your path. On Solaris systems you need -`uname` in your path. +require `as` and `ld` in your path. * **`LD_LIBRARY_PATH`** EPICS shared libraries and executables normally contain the full path @@ -290,7 +266,6 @@ Shared libraries are now built by default on all Unix type hosts. To configure EPICS, you may want to modify some values set in the following files: ->>>>>>> mirror/3.15 ``` configure/CONFIG_SITE Build settings. Specify target archs. diff --git a/documentation/RELEASE-3.15.md b/documentation/RELEASE-3.15.md new file mode 100644 index 0000000000..aa31805ee7 --- /dev/null +++ b/documentation/RELEASE-3.15.md @@ -0,0 +1,890 @@ +# Changes incorporated from the 3.15 branch + +## Changes from the 3.15 branch since 3.15.9 + +### Support for Apple M1/M2 (arm64) Processors + +Thanks to Jeong Han Lee this release comes with build support for Apple's new +M1/M2 CPUs running macOS, using the target name `darwin-aarch64`. + +### Set thread names on Windows + +On MS Windows, epicsThread names are made available to the OS and debugger +using `SetThreadDescription()` if available as well as using the older +exception mechanism. + +### Fix timers on MS Windows for non-EPICS threads + +The waitable timer changes in 3.15.9 broke calls to `epicsThreadSleep()` and +similar routines that used timers (including `ca_pend_event()`) when made from +threads that were not started using the epicsThread APIs. +[This problem](https://github.com/epics-base/epics-base/pull/200) +[has now been fixed](https://github.com/epics-base/epics-base/pull/201). + +## Changes made between 3.15.8 and 3.15.9 + +### Use waitable timers on Microsoft Windows + +The `epicsEventWaitWithTimeout()` and `epicsThreadSleep()` functions have +been changed to use waitable timers. On Windows 10 version 1803 or higher +they will use high resolution timers for more consistent timing. + +See [this Google Groups thread](https://groups.google.com/a/chromium.org/g/scheduler-dev/c/0GlSPYreJeY) +for a comparison of the performance of different timers. + +### Build target for documentation + +The build target `inc` now works again after a very long hiatus. It now +generates and installs just the dbd, header and html files, without compiling +any C/C++ code. This can be used to speed up CI jobs that only generate +documentation. + +### Bug fixes + +- The error status returned by a record support's `special()` method is now propagated out of the `dbPut()` routine again (broken since 3.15.0). +- [gh: #80](https://github.com/epics-base/epics-base/issues/80), VS-2015 and +later have working strtod() +- [lp: #1776141](https://bugs.launchpad.net/epics-base/+bug/1776141), Catch +buffer overflow from long link strings +- [lp: #1899697](https://bugs.launchpad.net/epics-base/+bug/1899697), Records +in wrong PHAS order + +### Change to the `junitfiles` self-test build target + +The names of the generated junit xml test output files have been changed +from `.xml` to `-results.xml`, to allow better +distinction from other xml files. (I.e., for easy wildcard matching.) + +### Fixes and code cleanups + +Issues reported by various static code checkers. + +## Changes made between 3.15.7 and 3.15.8 + +### Bug fixes + +The following launchpad bugs have fixes included in this release: + +- [lp: 1812084](https://bugs.launchpad.net/epics-base/+bug/1812084), Build + failure on RTEMS 4.10.2 +- [lp: 1829770](https://bugs.launchpad.net/epics-base/+bug/1829770), event + record device support broken with constant INP +- [lp: 1829919](https://bugs.launchpad.net/epics-base/+bug/1829919), IOC + segfaults when calling dbLoadRecords after iocInit +- [lp: 1838792](https://bugs.launchpad.net/epics-base/+bug/1838792), epicsCalc + bit-wise operators on aarch64 +- [lp: 1841608](https://bugs.launchpad.net/epics-base/+bug/1841608), logClient + falsely sends error logs on all connections +- [lp: 1853168](https://bugs.launchpad.net/epics-base/+bug/1853168), undefined + reference to `clock_gettime()` +- [lp: 1862328](https://bugs.launchpad.net/epics-base/+bug/1862328), Race + condition on IOC start leaves rsrv unresponsive +- [lp: 1868486](https://bugs.launchpad.net/epics-base/+bug/1868486), + epicsMessageQueue lost messages + +### Improvements to the self-test build targets + +This release contains changes that make it possible to integrate another test +running and reporting system (such as Google's gtest) into the EPICS build +system. The built-in test-runner and reporting system will continue to be used +by the test programs inside Base however. + +These GNUmake `tapfiles` and `test-results` build targets now collect a list of +the directories that experienced test failures and display those at the end of +running and/or reporting all of the tests. The GNUmake process will also only +exit with an error status after running and/or reporting all of the test +results; previously the `-k` flag to make was needed and even that didn't always +work. + +Continuous Integration systems are recommended to run `make tapfiles` (or if +they can read junittest output instead of TAP `make junitfiles`) followed by +`make -s test-results` to display the results of the tests. If multiple CPUs are +available the `-j` flag can be used to run tests in parallel, giving the maximum +jobs that should be allowed so `make -j4 tapfiles` for a system with 4 CPUs say. +Running many more jobs than you have CPUs is likely to be slower and is not +recommended. + +### Calc Engine Fixes and Enhancements + +The code that implements bit operations for Calc expressions has been reworked +to better handle some CPU architectures and compilers. As part of this work a +new operator has been added: `>>>` performs a logical right-shift, inserting +zero bits into the most significant bits (the operator `>>` is an arithmetic +right-shift which copies the sign bit as it shifts the value rightwards). + +### IOC logClient Changes + +The IOC's error logging system has been updated significantly to fix a number +of issues including: + + - Only send errlog messages to iocLogClient listeners + - Try to minimize lost messages while the log server is down: + + Detect disconnects sooner + + Don't discard the buffer on disconnect + + Flush the buffer immediately after a server reconnects + +### epicsThread: Main thread defaults to allow blocking I/O + +VxWorks IOCs (and potentially RTEMS IOCs running GeSys) have had problems with +garbled error messages from dbStaticLib routines for some time — messages +printed before `iocInit` were being queued through the errlog thread instead of +being output immediately. This has been fixed by initializing the main thread +with its `OkToBlock` flag set instead of cleared. IOCs running on other +operating systems that use iocsh to execute the startup script previously had +that set anyway in iocsh so were not affected, but this change might cause other +programs that don't use iocsh to change their behavior slightly if they use +`errlogPrintf()`, `epicsPrintf()` or `errPrintf()`. + +### catools: Handle data type changes in camonitor + +The camonitor program didn't properly cope if subscribed to a channel whose data +type changed when its IOC was rebooted without restarting the camonitor program. +This has now been fixed. + +### More Record Reference Documentation + +The remaining record types have had their reference pages moved from the Wiki, +and some new reference pages have been written to cover the analog array and +long string input and output record types plus the printf record type, none of +which were previously documented. The wiki reference pages covering the fields +common to all, input, and output record types have also been added, thanks to +Rolf Keitel. The POD conversion scripts have also been improved and they now +properly support linking to subsections in a different document, although the +POD changes to add the cross-links that appeared in the original wiki pages +still needs to be done in most cases. + +### Fix build issues with newer MinGW versions + +The `clock_gettime()` routine is no longer used under MinGW since newer versions +don't provide it any more. + +### Fix race for port in RSRV when multiple IOCs start simultaneously + +If multiple IOCs were started at the same time, by systemd say, they could race +to obtain the Channel Access TCP port number 5064. This issue has been fixed. + +----- + +## Changes made between 3.15.6 and 3.15.7 + +### GNU Readline detection on Linux + +Most Linux architectures should now configure themselves automatically to use +the GNU Readline library if its main header file can be found in the expected +place, and not try to use Readline if the header file isn't present. For older +Linux architectures where libncurses or libcurses must also be linked with, the +manual configuration of the `COMMANDLINE_LIBRARY` variable in the appropriate +`configure/os/CONFIG_SITE.Common.` file will still be necessary. + +### Replace `EPICS_TIMEZONE` with `EPICS_TZ` + +The `EPICS_TIMEZONE` environment parameter provided time-zone information for +the IOC's locale in the old ANSI format expected by VxWorks for its `TIMEZONE` +environment variable, and can also used by RTEMS to set its `TZ` environment +variable. However the `TIMEZONE` value has to be updated every year since it +contains the exact dates of the daylight-savings time changes. The Posix TZ +format that RTEMS uses contains rules that for calculating those dates, thus its +value would only need updating if the rules (or the locale) are changed. + +This release contains changes that replace the `EPICS_TIMEZONE` environment +parameter with one called `EPICS_TZ` and a routine for VxWorks that calculates +the `TIMEZONE` environment variable from the current `TZ` value. This routine +will be run once at start-up, when the EPICS clock has synchronized to its NTP +server. The calculations it contains were worked out and donated to EPICS by +Larry Hoff in 2009; it is unfortunate that it has taken 10 years for them to be +integrated into Base. + +The default value for the `EPICS_TZ` environment parameter is set in the Base +`configure/CONFIG_SITE_ENV` file, which contains example settings for most EPICS +sites that use VxWorks, and a link to a page describing the Posix TZ format for +any locations that I missed. + +If a VxWorks IOC runs continuously without being rebooted from December 31st to +the start of daylight savings time the following year, its `TIMEZONE` value will +be wrong as it was calculated for the previous year. This only affects times +that are converted to a string on the IOC however and is easily fixed; just run +the command `tz2timezone()` on the VxWorks shell and the calculation will be +redone for the current year. IOCs that get rebooted at least once before the +start of summer time will not need this to be done. + +### Added new decimation channel filter + +A new server-side filter has been added to the IOC for reducing the number +and frequency of monitor updates from a channel by a client-specified factor. +The filter's behaviour is quite simplistic, it passes the first monitor event it +sees to the client and then drops the next N-1 events before passing another +event. For example to sample a 60Hz channel at 1Hz, a 10Hz channel every 6 +seconds, or a 1Hz channel once every minute: + +``` + Hal$ camonitor 'test:channel.{"dec":{"n":60}}' + ... +``` + +More information is included in the filters documentation, which can be found +in the `html/filters.html` document that is generated during the build. + +### Imported Record Reference Documentation from Wiki + +The remaining record types that had 3.14 reference documentation in the EPICS +Wiki have had that documentation converted and imported into their DBD files. +The preferred form for future updates to the record type descriptions is now an +emailed patch file, a Pull Request through GitHub, or a Merge Request through +Launchpad. Note that in some cases the behavior of a record type in a 7.0.x +release may differ from that of the same record type in a 3.15 release, although +this would be unusual, so it may be important to indicate the branch that your +changes apply to. + +**NOTE:** *These documentation changes have modified the order of the fields in +some record definitions. As a result this release is not compatible with record +or device support binaries that were compiled against earlier releases.* + +### `make test-results` for Windows + +The make target `test-results` should now work properly on Windows. Some Perl +installations used versions of `prove.bat` that would only display the results of +up to 3 tests or didn't return an error status in the event of tests failing. The +build system now calls its own perl script to summarize the results instead of +passing a list of TAP filenames to `prove`. + +### Add option to avoid CALLBACK conflict + +If a macro `EPICS_NO_CALLBACK` is defined, then callback.h will no longer +(re)define CALLBACK. The name `CALLBACK` is used by the WIN32 API, and +redefinition in callback.h cause errors if some windows headers are later +included. + +Code which defines `EPICS_NO_CALLBACK`, but still wishes to use callbacks, +should use the alternate name `epicsCallback` introduced in 3.15.6, 3.16.2, and +7.0.2. It is also possible, though not encouraged, to use `struct callbackPvt` +which has been present since the callback API was introduced. + +### Cleaning up with Multiple CA contexts in a Process + +Bruno Martins reported a problem with the CA client library at shutdown in a +process that uses multiple CA client contexts. The first context that triggers +the CA client exit handler prevents any others from being able to clean up +because it resets the ID of an internal epicsThreadPrivate variable which is +shared by all clients. This action has been removed from the client library, +which makes cleanup of clients like this possible. + +### Perl CA bindings fixed for macOS Mojave + +Apple removed some Perl header files from macOS Mojave that were available +in their SDK, requiring a change to the include paths used when compiling the +CA bindings. The new version should build on new and older macOS versions, and +these changes may also help other targets that have an incomplete installation +of Perl (the build will continue after printing a warning that the Perl CA +bindings could not be built). + +### Routine `epicsTempName()` removed from libCom + +This routine was a simple wrapper around the C89 function `tmpnam()` +which is now seen as unsafe and causes warning messages to be generated by +most modern compilers. The two internal uses of this function have been +modified to call `epicsTempFile()` instead. We were unable to find any +published code that used this function, so it was removed immediately instead +of being deprecated. + +### DBD Parsing of Record Types + +The Perl DBD file parser has been made slightly more liberal; the order in +which DBD files must be parsed is now more flexible, so that a record type +definition can now be parsed after a device support that referred to that +record type. A warning message will be displayed when the device support is +seen, but the subsequent loading of the record type will be accepted without +triggering an error. See +[Launchpad bug 1801145](https://bugs.launchpad.net/epics-base/+bug/1801145). + +### menuScan and several record types documented with POD + +The EPICS Wiki pages describing a number of standard record types has been +converted into the Perl POD documentation format and added to the DBD files, +so at build-time an HTML version of these documents is generated and installed +into the htmls directory. Thanks to Tony Pietryla. + +### CA client tools learned `-V` option + +This displays the version numbers of EPICS Base and the CA protocol. + +----- + +## Changes made between 3.15.5 and 3.15.6 + +### Unsetting environment variables + +The new command `epicsEnvUnset varname` can be used to +unset an environment variable. + +### Warning indicators in msi (and macLib) output + +The libCom macro expansion library has been modified so that when the +`SUPPRESS_WARNINGS` flag is set it will no longer include any `,undefined` +or `,recursive` indicators in its output when undefined or recursive +macros are encountered. These indicators were harmless when the output was fed +into an IOC along with a definition for the macro, but when the `msi` +tool was used to generate other kinds of files they caused problems. If the +`msi -V` flag is used the markers will still be present in the output +whenever the appropriate condition is seen. + +### Improvements to msi + +In addition to fixing its response to discovering parsing errors in its +substitution input file (reported as Launchpad +[bug 1503661](https://bugs.launchpad.net/epics-base/+bug/1503661)) +so it now deletes the incomplete output file, the msi program has been cleaned +up a little bit internally. + +### All array records now post monitors on their array-length fields + +The waveform record has been posting monitors on its NORD field since Base +3.15.0.1; we finally got around to doing the equivalent in all the other +built-in record types, which even required modifying device support in some +cases. This fixes +[Launchpad bug 1730727](https://bugs.launchpad.net/epics-base/+bug/1730727). + +### HOWTO: Converting Wiki Record Reference to POD + +Some documentation has been added to the `dbdToHtml.pl` script +explaining how Perl POD (Plain Old Documentation) markup can be added to +`.dbd` files to generate HTML documentation for the record types. To see +these instructions, run `perl bin//dbdToHtml.pl -H` +or `perldoc bin//dbdToHtml.pl`. + +### Fix problem with numeric soft events + +Changing from numeric to named soft events introduced an incompatibility +when a numeric event 1-255 is converted from a DOUBLE, e.g. from a calc record. +The `post_event()` API is not marked deprecated any more. + +Also `scanpel` has been modified to accept a glob pattern for +event name filtering and to show events with no connected records as well. + +### Add `osiSockOptMcastLoop_t` and osiSockTest + +Added a new OS-independent typedef for multicast socket options, and a test +file to check their correct operation. + +### Support for `CONFIG_SITE.local` in Base + +This feature is mostly meant for use by developers; configuration +settings that would normally appear in `base/configure/CONFIG_SITE` can now +be put in a locally created `base/configure/CONFIG_SITE.local` file instead +of having go modify or replace the original. A new `.gitignore` pattern +tells git to ignore all `configure/*.local` files. + +### Fix broken `EPICS_IOC_LOG_FILE_LIMIT=0` setting + +The Application Developers' Guide says this is allowed and disables the +limit on the log-file, but it hasn't actually worked for some time (if ever). +Note that the iocLogServer will be removed from newer Base release sometime +soon as its functionality can be implemented by other dedicated log servers +such as logstash or syslog-ng. + +Fixes [lp:1786858](https://bugs.launchpad.net/bugs/1786858) +and part of [lp:1786966](https://bugs.launchpad.net/bugs/1786966). + +### Cleanup of startup directory + +The files in the startup directory have not been maintained in recent years +and have grown crufty (technical term). This release includes the following +updates to these files: + + - The Perl `EpicsHostArch.pl` script has been rewritten, and support + for a few previously missing host architectures has been added to it. + - The `EpicsHostArch.pl` script has also been moved into the standard + `src/tools` directory, from where it will be installed into + `lib/perl`. In this new location it is no longer executable, so it must + be run by the `perl` executable. + - The build system has been adjusted to look for `EpicsHostArch.pl` in + both places if the `EPICS_HOST_ARCH` environment variable has not been + set at build-time. + - Sites that used the original Perl script to set `EPICS_HOST_ARCH` as part of + their standard environment will need to adjust their scripts when they + upgrade to this release. + - The `EpicsHostArch` shell script has been replaced with a wrapper + routine that calls the Perl `EpicsHostArch.pl` script. Sites that rely on + this script to set `EPICS_HOST_ARCH` should consider switching to the + Perl script instead. + - The `Site.cshrc` and `Site.profile` files have been renamed to + `unix.csh` and `unix.sh`, respectively. + - The existing `win32.bat` file has been cleaned up and a new + `windows.bat` file added for 64-bit targets. The contents of these files + should be seen as examples, don't uncomment or install parts for software + that you don't explicitly know that you need. + +### Recent Apple XCode Build Issues + +The latest version of XCode will not compile calls to `system()` or +`clock_settime()` for iOS targets. There were several places in Base +where these were being compiled, although there were probably never called. The +code has now been modified to permit iOS builds to complete again. + +### Prevent illegal alarm severities + +A check has been added to `recGblResetAlarms()` that prevents records +from getting an alarm severity higher than `INVALID_ALARM`. It is still possible +for a field like HSV to get set to a value that is not a legal alarm severity, +but the core IOC code should never copy such a value into a record's SEVR or +ACKS fields. With this fix the record's alarm severity will be limited to +`INVALID_ALARM`. + +### Fixes for Launchpad bugs + +The following launchpad bugs have fixes included: + + - [lp: 1786320](https://bugs.launchpad.net/epics-base/+bug/1786320), dbCa + subscribes twice to ENUM + - [lp: 541221](https://bugs.launchpad.net/epics-base/+bug/541221), + `assert (pca->pgetNative)` failed in ../dbCa.c + - [lp: 1747091](https://bugs.launchpad.net/epics-base/+bug/1747091), + epicsTimeGetEvent() / generalTime bug + - [lp: 1743076](https://bugs.launchpad.net/epics-base/+bug/1743076), Segfault + in `ca_attach_context()` during exits + - [lp: 1751380](https://bugs.launchpad.net/epics-base/+bug/1751380), Deadlock + in `ca_clear_subscription()` + - [lp: 1597809](https://bugs.launchpad.net/epics-base/+bug/1597809), Setting + NAME field in DB file may break IOC + - [lp: 1770292](https://bugs.launchpad.net/epics-base/+bug/1770292), + `get_alarm_double()` inconsistent across record types + - [lp: 1771298](https://bugs.launchpad.net/epics-base/+bug/1771298), + Conversion of NaN to integer relies on undefined behavior + +### Updated VxWorks Timezone settings + +Removed the settings for 2017; fixed the hour of the change for MET. + +### Fixed camonitor server side relative timestamps bug + +Initialize the first time-stamp from the first monitor, not the client-side +current time in this configuration. + +### Build changes for MSVC + +Windows builds using Visual Studio 2015 and later now use the `-FS` +compiler option to allow parallel builds to work properly. + +We now give the `-FC` option to tell the compiler to print absolute +paths for source files in diagnostic messages. + +### Extend maximum Posix epicsEventWaitWithTimeout() delay + +The Posix implementation of epicsEventWaitWithTimeout() was limiting the +timeout delay to at most 60 minutes (3600.0 seconds). This has been changed to +10 years; significantly longer maximum delays cause problems on systems where +`time_t` is still a signed 32-bit integer so cannot represent absolute +time-stamps after 2038-01-19. Our assumption is that such 32-bit systems will +have been retired before the year 2028, but some additional tests have been +added to the epicsTimeTest program to detect and fail if this assumption is +violated. + +### New test-related make targets + +This release adds several new make targets intended for use by developers +and Continuous Integration systems which simplify the task of running the +built-in self-test programs and viewing the results. Since these targets are +intended for limited use they can have requirements for the build host which +go beyond the standard minimum set needed to build and run Base. + +#### `test-results` - Summarize test results + +The new make target `test-results` will run the self-tests if +necessary to generate a TAP file for each test, then summarizes the TAP output +files in each test directory in turn, displaying the details of any failures. +This step uses the program `prove` which comes with Perl, but also needs +`cat` to be provided in the default search path so will not work on most +Windows systems. + +#### `junitfiles` - Convert test results to JUnit XML Format + +The new make target `junitfiles` will run the self-tests if necessary +and then convert the TAP output files into the more commonly-supported JUnit +XML format. The program that performs this conversion needs the Perl module +`XML::Generator` to have been installed. + +#### `clean-tests` - Delete test result files + +The new make target `clean-tests` removes any test result files from +previous test runs. It cleans both TAP and JUnit XML files. + +### Fix DNS related crash on exit + +The attempt to fix DNS related delays for short lived CLI programs (eg. caget) +in [lp:1527636](https://bugs.launchpad.net/epics-base/+bug/1527636) introduced a +bug which cased these short lived clients to crash on exit. This bug should now +be fixed. + +### Server bind issue on Windows + +When a National Instruments network variables CA server is already running on +a Windows system and an IOC or PCAS server is started, the IOC's attempt to +bind a TCP socket to the CA server port number fails, but Windows returns a +different error status value than the IOC is expecting in that circumstance +(because the National Instruments code requests exclusive use of that port, +unlike the EPICS code) so the IOC fails to start properly. The relevant EPICS +bind() checks have now been updated so the IOC will request that a dynamic port +number be allocated for this TCP socket instead when this happens. + +### Checking Periodic Scan Rates + +Code has been added to the IOC startup to better protect it against bad +periodic scan rates, including against locales where `.` is not +accepted as a decimal separator character. If the scan period in a menuScan +choice string cannot be parsed, the associated periodic scan thread will no +longer be started by the IOC and a warning message will be displayed at iocInit +time. The `scanppl` command will also flag the faulty menuScan value. + +----- + +## Changes made between 3.15.4 and 3.15.5 + +### dbStatic Library Speedup and Cleanup + +Loading of database files has been optimized to avoid over-proportionally +long loading times for large databases. As a part of this, the alphabetical +ordering of records instances (within a record type) has been dropped. In the +unexpected case that applications were relying on the alphabetic order, setting +`dbRecordsAbcSorted = 1` before loading the databases will retain the +old behavior. + +The routine `dbRenameRecord()` has been removed, as it was intended +to be used by database configuration tools linked against a host side version +of the dbStatic library that is not being built anymore. + +### Launchpad Bug-fixes + +In addition to the more detailed change descriptions below, the following +Launchpad bugs have also been fixed in this release: + + - [lp:1440186](https://bugs.launchpad.net/epics-base/+bug/1440186) Crash due + to a too small buffer being provided in `dbContextReadNotifyCache()` + - [lp:1479316](https://bugs.launchpad.net/epics-base/+bug/1479316) Some data + races found using Helgrind + - [lp:1495833](https://bugs.launchpad.net/epics-base/+bug/1495833) biRecord + prompt groups are nonsensical + - [lp:1606848](https://bugs.launchpad.net/epics-base/+bug/1606848) WSAIoctl + `SIO_GET_INTERFACE_LIST` failed in Windows + +### Whole-Program Optimization for MS Visual Studio Targets + +When using the Microsoft compilers a new build system variable is provided that +controls whether whole program optimization is used or not. For static builds +using Visual Studio 2010 this optimization must be disabled. This is controlled +in the files `configure/os/CONFIG_SITE.Common.windows-x64-static` and +`configure/os/CONFIG_SITE.Common.win32-x86-static` by setting the variable +`OPT_WHOLE_PROGRAM=NO` to override the default value `YES` that would otherwise +be used. + +Note that enabling this optimization slows down the build process. It is not +possible to selectively disable this optimization, when building a particular +module say; Microsoft's linker will restart itself automatically with the +`-LTCG` flag set and display a warning if it is asked to link any object +files that were compiled with the `-GL` flag. + +### Add dynamic (variable length) array support to PCAS + +Dynamic array sizing support was added to the IOC server (RSRV) in the +Base-3.14.12 release, but has not until now been supported in the Portable +Channel Access Server (PCAS). Channel Access server applications using the +PCAS may not need to be modified at all; if they already push monitors with +different gdd array lengths, those variable sizes will be forwarded to any CA +clients who have requested variable length updates. The example CAS server +application has been modified to demonstrate this feature. + +In implementing the above, the gdd method `gdd::put(const gdd *)` now +copies the full-sized array from the source gdd if the destination gdd is of +type array, has no allocated memory and a boundary size of 0. + +### Additional epicsTime conversion + +The EPICS timestamp library (epicsTime) inside libCom's OSI layer has +been extended by routines that convert from `struct tm` to the EPICS +internal `epicsTime` type, assuming UTC - i.e. without going through +the timezone mechanism. This solves issues with converting from the structured +type to the EPICS timestamp at driver level from multiple threads at a high +repetition rate, where the timezone mechanism was blocking on file access. + +### MinGW Cross-builds from Linux + +The build configuration files that allow cross-building of the 32-bit +win32-x86-mingw cross-target have been adjusted to default to building shared +libraries (DLLs) as this is now supported by recent MinGW compilers. The 64-bit +windows-x64-mingw cross-target was already being built that way by default. The +configuration options to tell the minGW cross-compiler to link programs with +static versions of the compiler support libraries have now been moved into the +`CONFIG_SITE.linux-x86.` files. + +### General Time updates + +The `iocInit` code now performs a sanity check of the current time +returned by the generalTime subsystem and will print a warning if the wall-clock +time returned has not been initialized yet. This is just a warning message; when +a time provider does synchronize the IOC will subsequently pick up and use the +correct time. This check code also primes the registered event system provider +if there is one so the `epicsTimeGetEventInt()` routine will work on IOCs +that ask for event time within an interrupt service routine. + +The osiClockTime provider's synchronization thread (which is only used on +some embedded targets) will now poll the other time providers at 1Hz until the +first time it manages to get a successful timestamp, after which it will poll +for updates every 60 seconds as before. + +The routine `generalTimeGetExceptPriority()` was designed for use by +backup (lower priority) time providers like the osiClockTime provider which do +not have their own absolute time reference and rely on other providers for an +absolute time source. This routine no longer implements the ratchet mechanism +that prevented the time it returned from going backwards. If the backup clock's +tick-timer runs fast the synchronization of the backup time provider would never +allow it to be corrected backwards when the ratchet was in place. The regular +`epicsTimeGetCurrent()` API still uses the ratchet mechanism, so this +change will not cause the IOC to see time going backwards. + +### Microsoft Visual Studio builds + +The build configuration files for builds using the Microsoft compilers have been +updated, although there should be no noticeable difference at most sites. One +extra compiler warning is now being suppressed for C++ code, `C4344: behavior +change: use of explicit template arguments results in ...` which is gratuitous +and was appearing frequently in builds of the EPICS V4 modules. + +Cross-builds of the windows-x64 target from a win32-x86 host have been +removed as they don't actually work within the context of a single `make` +run. Significant changes to the build configuration files would be necessary for +these kinds of cross-builds to work properly, which could be done if someone +needs them (email Andrew Johnson before working on this, and see +[this stack-overflow answer](http://stackoverflow.com/questions/5807647/how-do-you-compile-32-bit-and-64-bit-applications-at-the-same-time-in-visual-stu) for a starting point). + +### Bazaar keywords such as 'Revision-Id' removed + +In preparation for moving to git in place of the Bazaar revision control +system we have removed all the keywords from the Base source code. + +### Linux systemd service file for CA Repeater + +Building this version of Base on a Linux system creates a systemd service +file suitable for starting the Channel Access Repeater under systemd. The file +will be installed into the target bin directory, from where it can be copied +into the appropriate systemd location and modified as necessary. Installation +instructions are included as comments in the file. + +----- + +## Changes made between 3.15.3 and 3.15.4 + +### New string input device support "getenv" + +A new "getenv" device support for both the stringin and lsi (long string +input) record types can be used to read the value of an environment variable +from the IOC at runtime. See base/db/softIocExit.db for sample usage. + +### Build rules and `DELAY_INSTALL_LIBS` + +A new order-only prerequisite build rule has been added to ensure that +library files (and DLL stubs on Windows) get installed before linking any +executables, which resolves parallel build problems on high-powered CPUs. There +are some (rare) cases though where a Makefile has to build an executable and run +it to be able to compile code for a library built by the same Makefile. With +this new build rule GNUmake will complain about a circular dependency and the +build will probably fail in those cases. To avoid this problem the failing +Makefile should set `DELAY_INSTALL_LIBS = YES` before including the +`$(TOP)/configure/RULES` file, disabling the new build rule. + +### IOC environment variables and build parameters + +The IOC now sets a number of environment variables at startup that provide the +version of EPICS Base it was built against (`EPICS_VERSION_...`) and its build +architecture (ARCH). In some cases this allows a single iocBoot/ioc directory to +be used to run the same IOC on several different architectures without any +changes. + +There are also 3 new environment parameters (`EPICS_BUILD_...`) available that +C/C++ code can use to find out the target architecture, OS class and compiler +class it was built with. These may be useful when writing interfaces to other +languages. + +### New implementation of `promptgroup`/`gui_group` field property + +The mechanism behind the `promptgroup()` field property inside a record type +definition has been changed. Instead of using a fixed set of choices, +the static database access library now collects the used gui group names +while parsing DBD information. Group names should start with a two-digit number +plus space-dash-space to allow proper sorting of groups. + +The include file `guigroup.h` that defined the fixed set of choices +has been deprecated. Instead, use the conversion functions between index number +and group string that have been added to dbStaticLib. + +When a DBD file containing record-type descriptions is expanded, any +old-style `GUI_xxx` group names will be replaced by a new-style +string for use by the IOC. This permits an older record type to be used with +the 3.15.4 release, although eventually record types should be converted by +hand with better group names used. + +### CA server configuration changes + +RSRV now honors `EPICS_CAS_INTF_ADDR_LIST` and binds only to the provided list +of network interfaces. Name searches (UDP and TCP) on other network interfaces +are ignored. For example on a computer with interfaces 10.5.1.1/24, 10.5.2.1/24, +and 10.5.3.1/24, setting `EPICS_CAS_INTF_ADDR_LIST='10.5.1.1 10.5.2.1'` will +accept traffic on the .1.1 and .2.1, but ignore from .3.1 + +RSRV now honors `EPICS_CAS_IGNORE_ADDR_LIST` and ignores UDP messages received +from addresses in this list. + +Previously, CA servers (RSRV and PCAS) would build the beacon address list using +`EPICS_CA_ADDR_LIST` if `EPICS_CAS_BEACON_ADDR_LIST` was no set. This is no +longer done. Sites depending on this should set both environment variables to +the same value. + +### IPv4 multicast for name search and beacons + +libca, RSRV, and PCAS may now use IPv4 multicasting for UDP traffic (name search +and beacons). This is disabled by default. To enable multicast address(s) must +be listed in `EPICS_CA_ADDR_LIST` for clients and `EPICS_CAS_INTF_ADDR_LIST` for +servers (IOCs should set both). For example: + + EPICS_CAS_INTF_ADDR_LIST='224.0.2.9' EPICS_CA_ADDR_LIST=224.0.2.9 + +Please note that no IPv4 multicast address is officially assigned for Channel +Access by IANA. The example 224.0.2.9 is taken from the AD-HOC Block I range. + +### Moved `mlockall()` into its own epicsThread routine + +Since EPICS Base 3.15.0.2 on Posix OSs the initialization of the epicsThread +subsystem has called `mlockall()` when the OS supports it and thread +priority scheduling is enabled. Doing so has caused problems in third-party +applications that call the CA client library, so the functionality has been +moved to a separate routine `epicsThreadRealtimeLock()` which will be +called by the IOC at iocInit (unless disabled by setting the global variable +`dbThreadRealtimeLock` to zero). + +### Added dbQuietMacroWarnings control + +When loading database files, macros get expanded even on comment lines. If a +comment contains an undefined macro, the load still continues but an error +message gets printed. For this release the error message has been changed to a +warning, but even this warning can be made less verbose by setting this new +variable to a non-zero value before loading the file, like this: + +``` + var dbQuietMacroWarnings 1 iocsh + dbQuietMacroWarnings=1 VxWorks +``` + +This was [Launchpad bug +541119](https://bugs.launchpad.net/bugs/541119). + +----- + +## Changes from the 3.14 branch between 3.15.3 and 3.15.4 + +### NTP Time Provider adjusts to OS tick rate changes + +Dirk Zimoch provided code that allows the NTP Time provider (used on VxWorks +and RTEMS only) to adapt to changes in the OS clock tick rate after the provider +has been initialized. Note that changing the tick rate after iocInit() is not +advisable, and that other software might still misbehave if initialized before +an OS tick rate change. This change was back-ported from the 3.15 branch. + +### Making IOC `ca_get` operations atomic + +When a CA client gets data from an IOC record using a compound data type such +as `DBR_TIME_DOUBLE` the value field is fetched from the database in a +separate call than the other metadata, without keeping the record locked. This +allows some other thread such as a periodic scan thread a chance to interrupt +the get operation and process the record in between. CA monitors have always +been atomic as long as the value data isn't a string or an array, but this race +condition in the CA get path has now been fixed so the record will stay locked +between the two fetch operations. + +This fixes +[Launchpad bug 1581212](https://bugs.launchpad.net/epics-base/+bug/1581212), +thanks to Till Strauman and Dehong Zhang. + +### New `CONFIG_SITE` variable for running self-tests + +The 'make runtests' and 'make tapfiles' build targets normally only run the +self-tests for the main `EPICS_HOST_ARCH` architecture. If the host is +able to execute self-test programs for other target architectures that are being +built by the host, such as when building a `-debug` version of the host +architecture for example, the names of those other architectures can be added to +the new `CROSS_COMPILER_RUNTEST_ARCHS` variable in either the +`configure/CONFIG_SITE` file or in an appropriate +`configure/os/CONFIG_SITE..Common` file to have the test +programs for those targets be run as well. + +### Additional RELEASE file checks + +An additional check has been added at build-time for the contents of the +`configure/RELEASE` file(s), which will mostly only affect users of the Debian +EPICS packages published by NSLS-2. Support modules may share an install path, +but all such modules must be listed adjacent to each other in any `RELEASE` +files that point to them. For example the following will fail the new checks: + +``` + AUTOSAVE = /usr/lib/epics + ASYN = /home/mdavidsaver/asyn + EPICS_BASE = /usr/lib/epics +``` + +giving the compile-time error + +``` + This application's RELEASE file(s) define + EPICS_BASE = /usr/lib/epics + after but not adjacent to + AUTOSAVE = /usr/lib/epics + Module definitions that share paths must be grouped together. + Either remove a definition, or move it to a line immediately + above or below the other(s). + Any non-module definitions belong in configure/CONFIG_SITE. +``` + +In many cases such as the one above the order of the `AUTOSAVE` and +`ASYN` lines can be swapped to let the checks pass, but if the +`AUTOSAVE` module depended on `ASYN` and hence had to appear +before it in the list this error indicates that `AUTOSAVE` should also be +built in its own private area; a shared copy would likely be incompatible with +the version of `ASYN` built in the home directory. + +### String field buffer overflows + +Two buffer overflow bugs that can crash the IOC have been fixed, caused by +initializing a string field with a value larger than the field size +([Launchpad bug 1563191](https://bugs.launchpad.net/bugs/1563191)). + +### Fixed stack corruption bug in epicsThread C++ API + +The C++ interface to the epicsThread API could corrupt the stack on thread +exit in some rare circumstances, usually at program exit. This bug has been +fixed ([Launchpad bug 1558206](https://bugs.launchpad.net/bugs/1558206)). + +### RTEMS NTP Support Issue + +On RTEMS the NTP Time Provider could in some circumstances get out of sync +with the server because the `osdNTPGet()` code wasn't clearing its input socket +before sending out a new request. This +([Launchpad bug 1549908](https://bugs.launchpad.net/bugs/1549908)) +has now been fixed. + +### CALC engine bitwise operator fixes + +The bitwise operators in the CALC engine have been modified to work properly +with values that have bit 31 (0x80000000) set. This modification involved +back-porting some earlier changes from the 3.15 branch, and fixes +[Launchpad bug 1514520](https://code.launchpad.net/bugs/1514520). + +### Fix `ipAddrToAsciiAsync()`: Don't try to join the daemon thread + +On process exit, don't try to stop the worker thread that makes DNS lookups +asynchronous. Previously this would wait for any lookups still in progress, +delaying the exit unnecessarily. This was most obvious with catools (eg. +cainfo). +[lp:1527636](https://bugs.launchpad.net/bugs/1527636) + +### Fix `epicsTime_localtime()` on Windows + +Simpler versions of the `epicsTime_gmtime()` and `epicsTime_localtime()` +routines have been included in the Windows implementations, and a new test +program added. The original versions do not report DST status properly. Fixes +[Launchpad bug 1528284](https://bugs.launchpad.net/bugs/1528284). diff --git a/documentation/RELEASE-3.16.md b/documentation/RELEASE-3.16.md new file mode 100644 index 0000000000..5f9602ca3f --- /dev/null +++ b/documentation/RELEASE-3.16.md @@ -0,0 +1,756 @@ +# Changes incorporated from the 3.16 branch + +## Changes made between 3.16.1 and 3.16.2 + +### Launchpad Bugs + +The list of tracked bugs fixed in this release can be found on the +[Launchpad Milestone page for EPICS Base 3.16.2](https://launchpad.net/epics-base/+milestone/3.16.2). + +### Status reporting for the callback and scanOnce task queues + +Two new iocsh commands and some associated underlying APIs have been added to +show the state of the queues that feed the three callback tasks and the +scanOnce task, including a high-water mark which can optionally be reset. The +new iocsh commands are `callbackQueueShow` and `scanOnceQueueShow`; both take +an optional integer argument which must be non-zero to reset the high-water +mark. + +### Support for event codes greater than or equal to `NUM_TIME_EVENTS` + +Event numbers greater than or equal to `NUM_TIME_EVENTS` are now allowed if +supported by the registered event time provider, which must provide its own +advancing timestamp validation for such events. + +Time events numbered 0 through `(NUM_TIME_EVENTS-1)` are still validated by code +in epicsGeneralTime.c that checks for advancing timestamps and enforces that +restriction. + +### Type-safe Device and Driver Support Tables + +Type-safe versions of the device and driver support structures `dset` and +`drvet` have been added to the devSup.h and drvSup.h headers respectively. The +original structure definitions have not been changed so existing support +modules will still build normally, but older modules can be modified and new +code written to be compatible with both. + +The old structure definitions will be replaced by the new ones if the macros +`USE_TYPED_DSET` and/or `USE_TYPED_DRVET` are defined when the appropriate +header is included. The best place to define these is in the Makefile, as with +the `USE_TYPED_RSET` macro that was introduced in Base-3.16.1 and described +below. See the comments in devSup.h for a brief usage example, or look at +[this commit](https://github.com/epics-modules/ipac/commit/a7e0ff4089b9aa39108bc8569e95ba7fcf07cee9) +to the ipac module to see a module conversion. + +A helper function `DBLINK* dbGetDevLink(dbCommon *prec)` has also been added +to devSup.h which fetches a pointer to the INP or OUT field of the record. + +### RTEMS build configuration update, running tests under QEMU + +This release includes the ability to run the EPICS unit tests built for a +special version of the RTEMS-pc386 target architecture on systems that have an +appropriate QEMU emulator installed (`qemu-system-i386`). It is also now +possible to create sub-architectures of RTEMS targets, whereas previously the +EPICS target architecture name had to be `RTEMS-$(RTEMS_BSP)`. + +The new target `RTEMS-pc386-qemu` builds binaries that can be run in the +`qemu-system-i386` PC System emulator. This target is a derivative of the +original `RTEMS-pc386` target but with additional software to build an in- +memory file-system, and some minor modifications to allow the unit tests to +work properly under QEMU. When this target is enabled, building any of the +make targets that cause the built-in self-tests to be run (such as `make +runtests`) will also run the tests for RTEMS using QEMU. + +To allow the new 3-component RTEMS target name, the EPICS build system for +RTEMS was modified to allow a `configure/os/CONFIG.Common.` file to set +the `RTEMS_BSP` variable to inform the build what RTEMS BSP to use. Previously +this was inferred from the value of the `T_A` make variable, but that prevents +having multiple EPICS targets that build against the same BSP. All the +included RTEMS target configuration files have been updated; build +configuration files for out-of-tree RTEMS targets will continue to work as the +original rules are used to set `RTEMS_BSP` if it hasn't been set when needed. + +### Link type enhancements + +This release adds three new link types: "state", "debug" and "trace". The +"state" link type gets and puts boolean values from/to the dbState library +that was added in the 3.15.1 release. The "debug" link type sets the +`jlink::debug` flag in its child link, while the "trace" link type also causes +the arguments and return values for all calls to the child link's jlif and +lset routines to be printed on stdout. The debug flag can no longer be set +using an info tag. The addition of the "trace" link type has allowed over 200 +lines of conditional diagnostic printf() calls to be removed from the other +link types. + +The "calc" link type can now be used for output links as well as input links. +This allows modification of the output value and even combining it with values +from other input links. See the separate JSON Link types document for details. + +A new `start_child()` method was added to the end of the jlif interface table. + +The `lset` methods have now been properly documented in the dbLink.h header +file using Doxygen annotations, although we do not run Doxygen on the source +tree yet to generate API documentation. + +Link types that utilize child links must now indicate whether the child will +be used for input, output or forward linking by the return value from its +`parse_start_map()` method. The `jlif_key_result` enum now contains 3 values +`jlif_key_child_inlink`, `jlif_key_child_outlink` and `jlif_key_child_fwdlink` +instead of the single `jlif_key_child_link` that was previously used for this. + +### GNUmake targets for debugging + +Some additional build rules have been added to help debug configuration +problems with the build system. Run `make show-makefiles` to get a sorted list +of all the files that the build system includes when building in the current +directory. + +A new pattern rule for `PRINT.%` can be used to show the value of any GNUmake +variable for the current build directory (make sure you are in the right +directory though, many variables are only set when inside the `O.` build +directory). For example `make PRINT.T_A` will display the build target +architecture name from inside a `O.` directory but the variable will be +empty from an application top or src directory. `make PRINT.EPICS_BASE` will +show the path to Base from any EPICS application directory though. + +### Propagate PUTF across Asynchronous record processing + +The IOC contains a mechanism involving the PUTF and RPRO fields of each record +to ensure that if a record is busy when it receives a put to one of its +fields, the record will be processed again to ensure that the new field value +has been correctly acted on. Until now that mechanism only worked if the put +was to the asynchronous record itself, so puts that were chained from some +other record via a DB link did not cause reprocessing. + +In this release the mechanism has been extended to propagate the PUTF state +across DB links until all downstream records have been reprocessed. Some +additional information about the record state can be shown by setting the TPRO +field of an upstream record, and even more trace data is displayed if the +debugging variable `dbAccessDebugPUTF` is set in addition to TPRO. + +### Finding info fields + +A new iocsh command `dbli` lists the info fields defined in the database, and +can take a glob pattern to limit output to specific info names. The newly +added dbStaticLib function `dbNextMatchingInfo()` iterates through the info +fields defined in the current record, and is used to implement the new +command. + +### Output from `dbpr` command enhanced + +The "DataBase Print Record" command `dbpr` now generates slightly better +output, with more field types having their own display methods. This release +also includes additional protection against buffer overflows while printing +long links in `dbpr`, and corrects the output of long strings from the `dbgf` +command. + +### Record types mbbiDirect and mbboDirect upgraded to 32 bit + +The VAL fields and related fields of these records are now `DBF_LONG`. (Not +`DBF_ULONG` in order to prevent Channel Access from promoting them to +`DBF_DOUBLE`.) Additional bit fields `B10`...`B1F` have been added. + +Device support that accesses `VAL` or the bit fields directly (most don't) and +aims for compatibility with old and new versions of these records should use +at least 32 bit integer types to avoid bit loss. The number of bit fields can +be calculated using `8 * sizeof(prec->val)` which is correct in both versions. + +### Restore use of ledlib for VxWorks command editing + +The epicsReadline refactoring work described below unfortunately disabled the +VxWorks implementation of the osdReadline.c API that uses ledlib for command +editing and history. This functionality has now been restored, see Launchpad +[bug #1741578](https://bugs.launchpad.net/bugs/1741578). + +### Constant link types + +Constant links can now hold 64-bit integer values, either as scalars or + arrays. Only base 10 is supported by the JSON parser though, the JSON standard +doesn't allow for hexadecimal numbers. + +### Upgraded the YAJL JSON Library + +The third-party YAJL library that has been included in libCom for several +years has been upgraded to version 2.1.0 and several bugs fixed. This has an +updated API, requiring any code that uses it to parse its own JSON files to be +modified to match. The changes are mainly that it uses `size_t` instead +`unsigned int` for string lengths, but it also uses `long long` instead of +`long` for JSON integer values, which was the main motivation for the upgrade. + +The self-tests that YAJL comes with have been imported and are now run as an +EPICS Unit Test program, and the JSON syntax accepted by the parser was +extended to permit trailing commas in both arrays and maps. The difference +between the old and new YAJL APIs can be detected at compile time by looking +for the macro `EPICS_YAJL_VERSION` which is defined in the `yajl_common.h` +header file along with a brief description of the API changes. + +### Timestamp support for the calc link type + +A new optional parameter can be given when specifying a calc JSON link. The +`time` parameter is a string containing a single letter `A..L` that selects +one of the input links to be used for the timestamp of calculation if +requested. The timestamp will be fetched atomically with the value from the +chosen input link (providing that input link type supports the readLocked() +method). + +### Silence errors from puts to constant link types + +A soft channel output record with the OUT link unset uses the CONSTANT link +type. The new link type code was causing some soft channel device supports to +return an error status from the write method of that link type, which would +cause a `ca_put()` operation to such a record to generate an exception. This has +been silenced by giving the constant link types a dummy putValue method. A new +test program has been added to prevent regressions of this behaviour. + +### RSRV expanding large buffer causes crash + +In the 3.16.1 release a crash can occur in the IOC's RSRV server when a large +array is made even larger; the previous array buffer was not being released +correctly. See Launchpad +[bug #1706703](https://bugs.launchpad.net/epics-base/+bug/1706703). + +----- + +## Changes made between 3.16.0.1 and 3.16.1 + +### IOC Database Support for 64-bit integers + +The IOC now supports the 64-bit integer field types `DBF_INT64` and +`DBF_UINT64`, and there are new record types `int64in` and `int64out` derived +from the `longin` and `longout` types respectively that use the `DBF_INT64` +data type for their VAL and related fields. The usual range of Soft Channel +device support are included for these new record types. + +All internal IOC APIs such as dbAccess can handle the new field types and +their associated request values `DBR_INT64` and `DBR_UINT64`, which are +implemented using the `epicsInt64` and `epicsUInt64` typedef's from the +`epicsTypes.h` header. + +The waveform record type has been updated to support these new field types. +**All waveform device support layers must be updated to recognize the new type +enumeration values**, which had to be inserted before the `FLOAT` value in the +enum `dbfType` and in `menuFtype`. C or C++ code can detect at compile-time +whether this version of base provides 64-bit support by checking for the +presence of the `DBR_INT64` macro as follows (Note that `DBF_INT64` is an +enum tag and not a preprocessor macro): + +``` + #ifdef DBR_INT64 + /* Code where Base has INT64 support */ + #else + /* Code for older versions */ + #endif +``` + +If the code uses the old `db_access.h` types (probably because it's calling +Channel Access APIs) then it will have to test against the EPICS version +number instead, like this: + +``` + #include + + #ifndef VERSION_INT + # define VERSION_INT(V,R,M,P) ( ((V)<<24) | ((R)<<16) | ((M)<<8) | (P)) + #endif + #ifndef EPICS_VERSION_INT + # define EPICS_VERSION_INT VERSION_INT(EPICS_VERSION, EPICS_REVISION, EPICS_MODIFICATION, EPICS_PATCH_LEVEL) + #endif + + #if EPICS_VERSION_INT >= VERSION_INT(3,16,1,0) + /* Code where Base has INT64 support */ + #else + /* Code for older versions */ + #endif +``` + +Channel Access does not (and probably never will) directly support 64-bit +integer types, so the new field types are presented to the CA server as +`DBF_DOUBLE` values. This means that field values larger than 2^52 +(0x10\_0000\_0000\_0000 = 4503599627370496) cannot be transported over Channel +Access without their least significant bits being truncated. The EPICS V4 +pvAccess network protocol _can_ transport 64-bit data types however, and a +future release of the pvaSrv module will connect this ability to the fields of +the IOC. + +Additional 64-bit support will be provided in later release. For instance the +JSON parser for the new Link Support feature only handles integers up to 32 +bits wide, so constant array initializer values cannot hold larger values in +this release. + +### Add `EPICS_CA_MCAST_TTL` + +A new environment parameter `EPICS_CA_MCAST_TTL` is used to set the Time To Live +(TTL) value of any IP multi-cast CA search or beacon packets sent. + +### `EPICS_CA_MAX_ARRAY_BYTES` is optional + +A new environment parameter `EPICS_CA_AUTO_ARRAY_BYTES` is now used by libca and +RSRV (CA clients and the IOC CA server). The default is equivalent to setting +`EPICS_CA_AUTO_ARRAY_BYTES=YES` which removes the need to set +`EPICS_CA_MAX_ARRAY_BYTES` and always attempts to allocate sufficiently large +network buffers to transfer large arrays properly over the network. In this case +the value of the `EPICS_CA_MAX_ARRAY_BYTES` parameter is ignored. + +Explicitly setting `EPICS_CA_AUTO_ARRAY_BYTES=NO` will continue to honor the +buffer setting in `EPICS_CA_AUTO_ARRAY_BYTES` as in previous releases. + +The default setting for `EPICS_CA_AUTO_ARRAY_BYTES` can be changed by adding the +line + +```makefile + EPICS_CA_AUTO_ARRAY_BYTES=NO +``` + +to the `configure/CONFIG_SITE_ENV` file before building Base. Sites that wish to +override this only for specific IOC architectures can create new files for each +architecture named `configure/os/CONFIG_SITE_ENV.` with the above +setting in before building Base. The configuration can also be explicitly +changed by setting the environment variable in the IOC's startup script, +anywhere above the `iocInit` line. + +The PCAS server (used by the PV Gateway and other CA servers) now always behaves +as if `EPICS_CA_AUTO_ARRAY_BYTES` is set to `YES` (it ignores the configuration +parameter and environment variable). + +### Channel Access "modernization" + +Drop support for CA clients advertising protocol versions less than 4. + +This effects clients from Base older than 3.12.0-beta1. Newer clients will +continue to be able to connect to older servers. Older clients will be ignored +by newer servers. + +This allows removal of UDP echo and similar protocol features which are not +compatible with secure protocol design practice. + +### Lookup-tables using the subArrray record + +The subArray record can now be used as a lookup-table from a constant array +specified in its INP field. For example: + +``` + record(subArray, "powers-of-2") { + field(FTVL, "LONG") + field(MALM, 12) + field(INP, [1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048]) + field(INDX, 0) + field(NELM, 1) + } +``` + +The INDX field selects which power of 2 to set the VAL field to. In previous +releases the INP field would have to have been pointed to a separate waveform +record that was initialized with the array values somehow at initialization +time. + +### Synchronized Timestamps with TSEL=-2 + +Most Soft Channel input device support routines have supported fetching the +timestamp through the INP link along with the input data. However before now +there was no guarantee that the timestamp provided by a CA link came from the +same update as the data, since the two were read from the CA input buffer at +separate times without maintaining a lock on that buffer in between. This +shortcoming could be fixed as a result of the new link support code, which +allows code using a link to pass a subroutine to the link type which will be +run with the link locked. The subroutine may make multiple requests for +metadata from the link, but must not block. + +### Extensible Link Types + +A major new feature introduced with this release of EPICS Base is an +Extensible Link Type mechanism, also known as Link Support or JSON Link Types. +This addition permits new kinds of link I/O to be added to an IOC in a similar +manner to the other extension points already supported (e.g. record, device +and driver support). + +A new link type must implement two related APIs, one for parsing the JSON +string which provides the link address and the other which implements the link +operations that get called at run-time to perform I/O. The link type is built +into the IOC by providing a new `link` entry in a DBD file. + +#### New Link Types Added + +This release contains two new JSON link types, `const` and `calc`: + + * The `const` link type is almost equivalent to the old CONSTANT link type +with the updates described below to accept arrays and strings, except that +there is no need to wrap a scalar string constant inside array brackets since +a constant string will never be confused with a PV name. + + * The `calc` link type allows CALC expressions to be used to combine +values from other JSON links to produce its value. Until additional JSON link +types are created though, the `calc` link type has little practical utility as +it can currently only fetch inputs from other `calc` links or from `const` +links. + +``` + field(INP, {calc:{expr:"A+B+1", + args:[5, # A + {const:6}] # B + } + } + ) +``` + +The new link types are documented in a separate document that gets generated at build time and installed as `html/links.html`. + +#### Device Support Addressing using `JSON_LINK` + +The API to allow device support to use JSON addresses is currently +incomplete; developers are advised not to try creating device support that +specifies a `JSON_LINK` address type. + +#### Support Routine Modifications for Extensible Link Types + +For link fields in external record types and soft device support to be able +to use the new link types properly, various changes are required to utilize +the new Link Support API as defined in the dbLink.h header file and outlined +below. The existing built-in Database and Channel Access link types have been +altered to implement the link APIs, so will work properly after these +conversions: + + * Make all calls to `recGblInitConstantLink()` unconditional on the link +type, i.e. change this code: + +```C + if (prec->siml.type == CONSTANT) { + recGblInitConstantLink(&prec->siml, DBF_USHORT, &prec->simm); + } +``` + + into this: + +```C + recGblInitConstantLink(&prec->siml, DBF_USHORT, &prec->simm); +``` + + Note that `recGblInitConstantLink()` still returns TRUE if the field was + successfully initialized from the link (implying the link is constant). + This change will work properly with all Base releases currently in use. + + * Code that needs to identify a constant link should be modified to use +the new routine `dbLinkIsConstant()` instead, which returns TRUE for constant +or undefined links, FALSE for links whose `dbGetLink()` routine may return +different values on different calls. For example this: + +```C + if (prec->dol.type != CONSTANT) +``` + + should become this: + +```C + if (!dbLinkIsConstant(&prec->dol)) +``` + + When the converted software is also required to build against older versions + of Base, this macro definition may be useful: + +```C + #define dbLinkIsConstant(lnk) ((lnk)->type == CONSTANT) +``` + + * Any code that calls dbCa routines directly, or that explicitly checks if +a link has been resolved as a CA link using code such as + +```C + if (prec->inp.type == CA_LINK) +``` + + will still compile and run, but will only work properly with the old CA link + type. To operate with the new extensible link types such code must be + modified to use the new generic routines defined in dbLink.h and should + never attempt to examine or modify data inside the link. After conversion + the above line would probably become: + +```C + if (dbLinkIsVolatile(&prec->inp)) +``` + + A volatile link is one like a Channel Access link which may disconnect and + reconnect without notice at runtime. Database links and constant links are + not volatile; unless their link address is changed they will always remain + in the same state they started in. For compatibility when building against + older versions of Base, this macro definition may be useful: + +```C + #define dbLinkIsVolatile(lnk) ((lnk)->type == CA_LINK) +``` + + * The current connection state of a volatile link can be found using the +routine `dbIsLinkConnected()` which will only return TRUE for a volatile link +that is currently connected. Code using the older dbCa API returning this +information used to look like this: + +```C + stat = dbCaIsLinkConnected(plink); +``` + + which should become: + +```C + stat = dbIsLinkConnected(plink); +``` + + Similar changes should be made for calls to the other dbCa routines. + + * A full example can be found by looking at the changes to the calcout +record type, which has been modified in this release to use the new dbLink +generic API. + +### Constant Link Values + +Previously a constant link (i.e. a link that did not point to another PV, +either locally or over Channel Access) was only able to provide a single +numeric value to a record initialization; any string given in a link field +that was not recognized as a number was treated as a PV name. In this release, +constant links can be expressed using JSON array syntax and may provide array +initialization of values containing integers, doubles or strings. An array +containing a single string value can also be used to initialize scalar +strings, so the stringin, stringout, lsi (long string input), lso (long string +output), printf, waveform, subArray and aai (analog array input) record types +and/or their soft device supports have been modified to support this. + +Some examples of constant array and string initialized records are: + +``` + record(stringin, "const:string") { + field(INP, ["Not-a-PV-name"]) + } + record(waveform, "const:longs") { + field(FTVL, LONG) + field(NELM, 10) + field(INP, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]) + } + record(aai, "const:doubles") { + field(FTVL, DOUBLE) + field(NELM, 10) + field(INP, [0, 1, 1.6e-19, 2.718, 3.141593]) + } + record(aSub, "select") { + field(FTA, STRING) + field(NOA, 4) + field(INPA, ["Zero", "One", "Two", "Three"]) + field(FTB, SHORT) + field(NOB, 1) + field(FTVA, STRING) + field(NOVA, 1) + field(SNAM, "select_asub") + } +``` + +Reminder: Link initialization with constant values normally only occurs at +record initialization time. The calcout and printf record types are the only +exceptions in the Base record types to this rule, so it is generally not +useful to change a const link value after iocInit. + +### Database Parsing of "Relaxed JSON" Values + +A database file can now provide a "relaxed JSON" value for a database field +value or an info tag. Only a few field types can currently accept such values, +but the capability is now available for use in other places in the future. +When writing to a JSON-capable field at run-time however, only strictly +compliant JSON may be used (the dbStaticLib parser rewrites relaxed JSON +values into strict JSON before passing them to the datase for interpretation, +where the strict rules must be followed). + +"Relaxed JSON" was developed to maximize compatibility with the previous +database parser rules and reduce the number of double-quotes that would be +needed for strict JSON syntax. The parser does accept strict JSON too though, +which should be used when machine-generating database files. The differences +are: + + * Strings containing only the characters `a-z A-Z 0-9 _ - + .` do not have to +be enclosed in double-quote characters. + + * The above rule applies to map keys as well as to regular string values. + + * The JSON keywords `null`, `true` and `false` (all lower-case) will be +recognized as keywords, so they must be quoted to use any of these single words +as a string. + + * Comments may be used, introduced as usual by the `#` character and extending +to the end of the line. + +A JSON field or info value is only enclosed in quotes when the value being +provided is a single string, and even here the quotes can be omitted in some +cases as described above. The following shows both correct and incorrect +excerpts from a database file: + +``` + record(ai, math:pi) { + field(INP, {const: 3.14159265358979}) # Correct + field(SIOL, "{const: 3.142857}") # Wrong + + info(autosave, { # White-space and comments are allowed + fields:[DESC, SIMM], + pass0:[VAL] + }) # Correct + } +``` + +Note that the record, field and info-tag names do *not* accept JSON values, so +they follows the older bareword rules for quoting where the colon `:` and +several additional characters are legal in a bareword string. Only the value +(after the comma) is parsed as JSON. The autosave module has not been modified +to accept JSON syntax, the above is only an example of how JSON might be used. + +### Echoless comments in iocsh + +The way comments are parsed by the iocsh interpreter has changed. The +interpreter can be selectively disabled from echoing comments coming from a +script by starting those lines with `#-` rather than just `#`. + +### Typed record support methods + +The table of record support functions (rset methods for short) no longer has +entries of type `RECSUPFUN` (which says: any number and type of arguments). +Instead, rset methods are now typed by default. The `RECSUPFUN` typedef has +been deprecated and casts to it as well as using the untyped `struct rset` +will create compilation warnings. + +Existing code (e.g. external record supports) will generate such warnings when +compiled against this version of Base, but it will work without changes. + +For a conversion period, the new typed rset definitions are activated by +defining `USE_TYPED_RSET`, preferably by setting `USR_CPPFLAGS += +-DUSE_TYPED_RSET` inside a Makefile. After activating the new typed rset in +this way and making the following changes, the result should still compile and +work properly against older versions of Base. + +The first parameter of `init_record` and `process` has been changed to `struct +dbCommon *`. Record types that use `void*` here should be changed to use +`struct dbCommon*`, and cast the argument to their own `xxxRecord *`. + +When compiled against this release, compiler warnings about incompatible types +for the method pointers should be taken seriously. When compiled against older +versions of base, such warnings are unavoidable. + +Record types written in C++ need to take more drastic measures because of the +stricter type checking in C++. To remain compatible with older versions of +base you will need to use something like: + +``` + #include "epicsVersion.h" + #ifdef VERSION_INT + # if EPICS_VERSION_INT < VERSION_INT(3,16,0,2) + # define RECSUPFUN_CAST (RECSUPFUN) + # else + # define RECSUPFUN_CAST + # endif + #else + # define RECSUPFUN_CAST (RECSUPFUN) + #endif +``` + +and then replace `(RECSUPFUN)` with `RECSUPFUN_CAST` when initializing the +rset. Further changes might also be needed, e.g. to adapt `const`-ness of +method parameters. + +----- + +## Changes made between 3.15.3 and 3.16.0.1 + +### Build support for CapFast and dbst removed + +The build rules associated with the CapFast-related tools `sch2edif` and +`e2db` and the database optimization tool `dbst` have been removed, along with +the `DB_OPT` build configuration variable. + +### compressRecord buffering order + +The compressRecord has a new field `BALG` which can select between FIFO +(append) and LIFO (prepend) ordering for insertion of new elements. FIFO +ordering is the default, matching the behaviour of previous versions. + +### Valgrind Instrumentation + +Valgrind is a software debugging suite provided by many Linux distributions. +The header valgrind/valgrind.h is now included in, and installed by, Base. +When included by a C or C++ source file this header defines some macros which +expand to provide hints to the Valgrind runtime. These have no effect on +normal operation of the software, but when run using the valgrind tool they +can help to find memory leaks and buffer overflows. Suitable hints have been +added to several free-lists within libCom, including freeListLib, allowing +valgrind to provide more accurate information about the source of potential +leaks. + +valgrind.h automatically disables itself when the build target is not +supported by the valgrind tool. It can also explicitly be disabled by defining +the macro `NVALGRIND`. See `src/libCom/Makefile` for a commented-out example. + +As a matter of policy valgrind.h will never be included by any header file +installed by Base, so its use will remain purely an implementation detail +hidden from application software. Support modules which choose to use +valgrind.h are advised to do likewise. + +### Database Multi-locking + +The IOC record locking code has been re-written with an expanded API; global +locks are no longer required by the IOC database implementation. + +The new API functions center around `dbScanLockMany()`, which behaves like +`dbScanLock()` applied to an arbitrary group of records. `dbLockerAlloc()` is +used to prepare a list or record pointers, then `dbScanLockMany()` is called. +When it returns, all of the records listed may be accessed (in any order) until +`dbScanUnlockMany()` is called. + +The Application Developer's Guide has been updated to describe the API and +implementation is more detail. + +Previously a global mutex `lockSetModifyLock` was locked and unlocked during +`dbScanLock()`, acting as a sequencing point for otherwise unrelated calls. The +new dbLock.c implementation does not include any global mutex in `dbScanLock()` +or `dbScanLockMany()`. Locking and unlocking of unrelated lock sets is now +completely concurrent. + +### Generate Version Header + +A Perl script and Makefile rules have been added to allow modules to generate +a C header file with a macro defined with an automatically updated identifier. +This is a VCS revision ID (Darcs, Git, Mercurial, Subversion, and Bazaar are +supported) or the date/time of the build if no VCS system is in use. + +The makeBaseApp example template has been updated with a new device support +which makes this identifier visible via a lsi (long string input) record. + +### epicsTime API return status + +The epicsTime routines that used to return epicsTimeERROR now return a +specific `S_time_` status value, allowing the caller to discover the reason for +any failure. The identifier `epicsTimeERROR` is no longer defined, so any +references to it in source code will no longer compile. The identifier +epicsTimeOK still exists and has the value 0 as before, so most code that uses +these APIs can be changed in a way that is backwards-compatible with the +previous return status. + +Time providers that have to return a status value and still need to be built +with earlier versions of Base can define the necessary status symbols like +this: + +``` + #include "epicsTime.h" + + #ifndef M_time + /* S_time_... status values were not provided before Base 3.16 */ + #define S_time_unsynchronized epicsTimeERROR + #define S_time_...whatever... epicsTimeERROR + #endif +``` + +### Refactoring of epicsReadline + +The epicsReadline code has been reorganized to allow the commandline history +editor to be disabled at runtime. The `EPICS_COMMANDLINE_LIBRARY` build setting +still selects the preferred editor, but the new `IOCSH_HISTEDIT_DISABLE` +environment variable can be set at runtime to disable history editing and make +the IOC or other program use the basic editor instead. This is useful when +starting and controlling an IOC from another program through its stdin and +stdout streams since history editors often insert invisible escape codes into +the stdout stream, making it hard to parse. + +### Callback subsystem API + +Added a new macro `callbackGetPriority(prio, callback)` to the callback.h +header and removed the need for dbScan.c to reach into the internals of its +`CALLBACK` objects. diff --git a/documentation/RELEASE-7.0.1.1.md b/documentation/RELEASE-7.0.1.1.md new file mode 100644 index 0000000000..3e0bbd4671 --- /dev/null +++ b/documentation/RELEASE-7.0.1.1.md @@ -0,0 +1,65 @@ +# Release Notes + +This document describes the changes that were included in the release of EPICS +noted below. Release entries are now provided in a separate document for each +version in the EPICS 7 series, but all are combined into a single page for +publishing on the EPICS website. Separate release documents are also included +from the older Base 3.15 and 3.16 series. + +## EPICS Release 7.0.1.1 + +### Changed SIML failure behavior + +A failure when fetching the simulation mode through `SIML` will not put the +record into INVALID alarm state anymore. Instead, as long as the record's +current alarm severity (`SEVR`)is `NO_ALARM`, its alarm status (`STAT`) will be +set to `LINK_ALARM` without increasing the severity. This allows clients to get +some notification of a failing or bad `SIML` link without otherwise affecting +record processing. + +### `dbVerify()` has been restored to dbStaticLib + +This routine was removed in Base-3.16.1 but has been reimplemented in this +release by special request. Note that the error message strings that it +returns when verification fails have changed, but are still designed for +display to the user. + +### Simulation mode improvements + +Records that support simulation mode have two new fields, `SSCN` (Simulation +Scan Mode) and `SDLY` (Simulation Delay). `SSCN` is a menu field that provides +an alternate value for the `SCAN` field to be used while the record is in +simulation mode. This is especially useful for I/O scanned records, for which +simulation mode was not working at all. Setting `SDLY` to a positive value +makes the record process asynchronously in simulation mode, with the second +stage processing happening after the specified time (in seconds). + +### Extend the dbServer API with init/run/pause/stop methods + +This change permits IOCs to be built that omit the CA server (RSRV) by +removing its registrar entry which is now provided in the new `rsrv.dbd` file. +Other server layers can be built into the IOC (alongside RSRV or in place of +it) by registering them in a similar manner. The dbServer API is documented +with Doxygen comments in the header file. + +Specific IOC server layers can be disabled at runtime by adding their name to +the environment variable `EPICS_IOC_IGNORE_SERVERS` (separated by spaces if more +than one should be ignored). + +### Grand source-code reorganization + +EPICS 7.0.1 contains the IOC Database, RSRV server and the Channel Access +client code from EPICS Base 3.16.1 along with all the original record types +and soft device support, but GDD and the Portable Channel Access Server have +been unbundled and are now available separately. In their place we have +brought in the more recently written EPICS V4 C++ libraries (collectively +referred to as the PVA modules). The directory tree for EPICS is somewhat +larger as a result, and the original structure of the Base directories has +been split into 4 separate Git repositories. External modules should build +against this new structure with little or no changes needed, except that some +allowance may be needed for the merging of the V4 modules. + +There should be rather more description and documentation of these changes +than is currently available, but as developers we generally much prefer to +write code than documentation. Send questions to the tech-talk mailing list +and we'll be happy to try and answer them! diff --git a/documentation/RELEASE-7.0.10.md b/documentation/RELEASE-7.0.10.md new file mode 100644 index 0000000000..e626bc0da7 --- /dev/null +++ b/documentation/RELEASE-7.0.10.md @@ -0,0 +1,431 @@ +# Release Notes + +This document describes the changes that were included in the release of EPICS +noted below. Release entries are now provided in a separate document for each +version in the EPICS 7 series, but all are combined into a single page for +publishing on the EPICS website. Separate release documents are also included +from the older Base 3.15 and 3.16 series. + +The external PVA submodules continue to maintain their own release notes files +as before, but the entries describing changes in those submodules since version +7.0.5 have been copied into the associated EPICS Release Notes files; they will +also be manually added to new EPICS Release Notes published in the future. + + +## EPICS Release 7.0.10 + +### Reduce symbol and macro pollution from epicsAtomic.h on WIN32 + +`epicsAtomic.h` no longer pulls in as many unneeded declarations and macros from +`windows.h`. Prior to this change, including `epicsAtomic.h` at the wrong time +could result in unexpected compiler errors. Due to the nature of `windows.h`, +some unneeded declarations are still pulled in, however the number is greatly reduced. +Code that needs these declarations should explicitly include `windows.h` before `epicsAtomic.h`. + +### New `afterIocRunning` IOC Shell Command Added + +This release incorporates [PR #558](https://github.com/epics-base/epics-base/pull/558) which added a new IOC shell command `afterIocRunning`. This command allows startup scripts to schedule arbitrary commands to be executed automatically after the IOC initialization phase (`iocInit`). + +`afterIocRunning` allows you to write better-structured IOC shell files to include in your startup scripts without tracking where `iocInit` is located (and how IOC is deployed) e.g.: +- to achieve the best maintainability (e.g. encapsulation of the context into one file), +- to improve writing boot sequences, +- to improve IOC startup flexibility and scripting capabilities, +- it replaces the community [`afterInit`](https://github.com/paulscherrerinstitute/iocsh_utilities/blob/master/afterInit.c) and [`doAfterIocInit`](https://github.com/epics-modules/std/blob/master/stdApp/src/delayCmd.cpp) IOC shell commands, +- community usage examples: + - [`pf4filters.iocsh`](https://github.com/epics-modules/optics/blob/master/opticsApp/iocsh/pf4filters.iocsh) - enable/disable with a single comment ([full description](https://github.com/epics-base/epics-base/pull/558#issuecomment-2430057167)) + - [ALS-U autosave management](https://github.com/epics-base/epics-base/pull/558#issuecomment-2430447220) + +#### Features + +- Define commands that run after IOC initialization completes. +- Executes following `iocInit` and `autosave` initialization (important for proper PV configuration). +- Supports any valid IOC shell command as an argument. +- Example usages: + - `afterIocRunning "dbpf "` + - `afterIocRunning "date"` + - `afterIocRunning "dbpf $(P)EvtClkSource-Sel 'Upstream (fanout)'"` + - `afterIocRunning "dbpf $(P)Enable-Sel Enabled"` + +### fdManager file descriptor limit removed + +In order to support file descriptors above 1023, fdManager now uses +`poll()` instead of `select()` on all architectures that support it +(Linux, MacOS, Windows, newer RTEMS). + +### New `dbServerStats()` API for iocStats + +A new routine provides the ability to request channel and client counts from +named server layers that implement the `stats()` method, or to get a summary +of the counts from all registered server layers. A preprocessor macro +`HAS_DBSERVER_STATS` macro is defined in the `dbServer.h` header file to +simplify code that needs to support older versions of Base as well. + +### epicsExport simplifications + +`epicsExportAddress()`, `epicsExportRegistrar()` and `epicsRegisterFunction()` +no longer require to be wrapped in `extern "C" { }` in C++ code. + +### Support for multiline strings in iocsh files + +GitHub [PR #603](https://github.com/epics-base/epics-base/pull/603) + +This update introduces support for multiline strings in IOC shell (iocsh) files. +Previously, string values in iocsh files were limited to a single line, making +it difficult to include longer or formatted text. With this change, users can +now define strings that span multiple lines, improving readability and +flexibility when configuring IOC shell scripts. + +To create a multiline string, end a line with a backslash (`\`). The following +line, including any leading whitespace, will be joined to the previous line. +If the backslash is immediately followed by any character other than a newline, +it will not be treated as a multiline continuation. + +### Enhancement to IOC `dbgrep` command + +`dbgrep` now takes an optional second string argument consisting of a list of field names +separated by spaces, e.g. `dbgrep "*PRESSURE*", "VAL DESC"` + +### ACF Syntax Forward Compatibility + +This release modifies the Access Security Configuration File (ACF) parser to +**standardize the ACF grammar for forward compatibility**. +It does not change the syntax that was accepted by earlier versions of the parser, +so **existing access security configuration files will not need to be modified.** +All ACF definitions will adhere to a consistent syntax format, +which will allow future additions to the access security language +without breaking existing configurations. +In practice, this means the structure of ACF files is now formally defined +and will remain stable going forward, +so any new grammar features will fit into the same pattern. +(Existing ACF files continue to work as-is under the new parser, +so no changes are required for legacy configurations or tools.). + +**Generic ACF Syntax:** +The ACF file consists of definitions for User Access Groups (UAG), +Host Access Groups (HAG), +and Access Security Groups (ASG), +using the following general format +(angle brackets below denote placeholders): + +```text + +UAG() [{ [, ...] }] +... + +HAG() [{ [, ...] }] +... + +ASG() [{ + [INP() + ...] + + RULE(, NONE | READ | WRITE [, NOTRAPWRITE | TRAPWRITE]) { + [UAG( [, ...])] + [HAG( [, ...])] + [CALC()] + } + ... +}] +... + +``` + +Under this schema each definition comprises a keyword, +a name in parentheses, +and (optionally) a braced block of contents. +This uniform structure ensures that +**future keywords or sections** +can be introduced in the same form, +maintaining compatibility with the parser. +For example, if a new type of condition or group is added in a later release, +it would follow the `KEYWORD(name) { ... }` pattern, +so 7.0.10-era parsers can handle or ignore it gracefully +instead of failing on unknown syntax. + +**Supported Syntax in EPICS 7.0.10:** +The current release defines the following specific elements +within the above generic format: + +- **UAG** -- *User Access Group*. +Defines a group of user names. +- **HAG** -- *Host Access Group*. +Defines a group of host names +(or IP addresses) that clients can connect from. +- **ASG** -- *Access Security Group*. +Defines a security group which records can be assigned to. +An ASG entry may contain a block with input definitions and access rules. +For example: + +```text +ASG(MyGroup) { + INPA(myPV1) + INPB(myPV2) + RULE(1, WRITE) { ... } + RULE(1, READ) { ... } +} +``` + +If no rules are defined for an ASG, +the access permissions default to always allowed. + +- **INP()** -- *Input link*. +Declares an input process variable whose value can be used in a CALC condition. +- **RULE(, [, ]) { ... }** -- +Defines an access rule for the ASG. + +Inside the curly braces of a RULE, +**optional conditions** can restrict when that rule applies. +All conditions that are present must be satisfied +(they function as a logical AND): + +- **UAG(, ...)** -- User-group condition. +The rule only applies if the Channel Access client's user +is a member of one of the listed UAGs. +- **HAG(, ...)** -- Host-group condition. +The rule only applies if the client's host +(as determined by its IP or hostname) is in one of the listed HAGs +- **CALC("")** -- Calculation condition. +The rule only applies if the given expression evaluates to true (non-zero). + +**Special Semantics for RULEs:** +Rules will continue to allow the prescribed access if and only if +all the predicates the rule contains are satisfied. +- If the rule contains predicates that are unknown to the parser +(indicating future functionality), +then the rule will NOT not match, +but no syntax error will be reported as long as the syntax is correct. +- If the rule contains predicates that the parser does not recognise +which are malformed (e.g. missing parentheses), +then the rule will not match and the parser will report a syntax error. +- In this way rules can be extended with new predicates +without breaking older clients or giving those older clients elevated privileges. + +**Special Semantics for unrecognised ACF file elements:** +Any elements that are included in an ACF file will be ignored silently +by a parser that does not understand them. +- If an element is seen in an ACF file that is not understood by the parser, +the parser will simply ignore it silently, +without reporting an error, +as long as its syntax is correct. +- If elements are added to the ACF file that are malformed +(e.g. missing parentheses), +the parser will report a syntax error. +- Thus new elements can be added to ACF files in new EPICS releases +without breaking older clients that loads those files. + +In summary, **ACF forward compatibility** +means that from EPICS 7.0.10 onward, +any new access security features will use this established syntax. +The parser will recognize new group types or rule options using the same +`(...) { ... }` convention, +ensuring they can be used in files loaded by IOCs running EPICS 7.0.10 or later +without breaking those IOCs or requiring their parser to be modified. +This change **does not require any modifications to existing ACF files +or downstream tools** -- all legacy syntax remains valid, +and the new standardized grammar provides a robust foundation for future extensions. + +A full EBNF grammar for the new syntax can be found in the +[IOC Access Security](ACF-Language.md) document added to this release. + +### Add `dbglob` to replace `dbgrep` + +A new IOC shell command, `dbglob` has been added, with `dbgrep` becoming +an alias of this new function, with the intent of deprecating it in a +future release. + +### Conflict-free release note entries for GitHub pull requests + +GitHub [PR #628](https://github.com/epics-base/epics-base/pull/628) + +This release replaces the developer-edited `documentation/RELEASE_NOTES.md` +source file in the EPICS tree with a process which generates that file from a +series of individual files added for each changeset in the release, thus +preventing merge conflicts when entries are added by many different pull +requests. + +For this new approach each pull request must add its own Markdown file to the +`documentation/new-notes` directory, using a unique filename. +When a release is made, all these files will be combined into a single +`RELEASE-.md` file and the `new-notes` directory emptied to prepare +for development of the next release. + +Developers can generate the `RELEASE_NOTES.md` file by running `make` in the +`documentation` directory, which will install the result in the `doc` top-level +directory along with the `RELEASE-.md` files describing older EPICS +releases going back to 3.15. +The `documentation/Makefile` provides some other targets which can also be +requested, but they require additional non-EPICS software such as Doxygen and +Sphinx to have been installed first. + +Between releases the generated `RELEASE_NOTES.md` file contains the text from +any `new-notes` files added to document changes already merged. +It also provides links to the older `RELEASE-.md` files, so it remains +the starting point for documentation on all release changes. + +Detailed instructions on creating new entries are provided in a `README.txt` +file in the `documentation/new-notes` directory. +The release-time process that generates a new `RELEASE-.md` file is +described in the developers' [Release Checklist](ReleaseChecklist.md). + +### Add support for `EPICS_DB_INCLUDE_PATH` to `dbLoadTemplate` + +GitHub [PR #636](https://github.com/epics-base/epics-base/pull/636) + +Allow finding the substitution file through a path in `EPICS_DB_INCLUDE_PATH` or +an additional parameter to the iocsh `dbLoadTemplate` command. + +### Expand the use of colour in the IOCs output + +This release includes various changes to iocsh.cpp and elsewhere to add and +expand the use of color: + +- When loading a startup script, the IOC Shell now displays comment lines in +blue, and uses bold to make command lines stand out from other text. + +- The `softIoc -v` output also uses the above color scheme for the commands it +prints. + +- The default IOC Shell prompt is now displayed in green; this color can be +modified in the `configure/CONFIG_SITE_ENV` file for all targets, or set for +a specific target by adding a `configure/os/CONFIG_SITE_ENV.` file. +The value of the `IOCSH_PS1` environment parameter in those files can use the +`ANSI_ENV_*` and `ANSI_*()` color macros found in errlog.h to configure the +appearance of the prompt. The C string literal concatenation syntax can be +used to construct the prompt string: + +```Makefile + IOCSH_PS1 = ANSI_ESC_RED "e" ANSI_ESC_YELLOW "p" ANSI_ESC_GREEN "i" \ + ANSI_ESC_CYAN "c" ANSI_BLUE("s") "> " +``` + +- More error messages printed by IOC Shell commands now appear in red, or use +the red `ERROR` prefix that was introduced in previous releases. + +- The word "Illegal" has been replaced with "Invalid" in several Shell error +messages. + +- The iocsh `var` command now shows the data type of the registered variables +as well as their values. + +### Documentation Updates + +The reference documentation for the [event](eventRecord.md) record type +has been updated to cover the use of named events which were added in Base +3.14.12.3 and 3.15.1. + +Documentation for CALC expression evaluation has been updated for format +enhancements and to add some missing operators. +The best documentation for these expressions can be found in the +[postfix.h](postfix_h.md) header in libCom, but both the +[calc](calcRecord.md) and [calcout](calcoutRecord.md) record reference +pages also cover the infix expressions supported. + +### Records calc, calcout and sub extended + +The record types calc, calcout and sub have been extended from 12 inputs +A - L to 21 inputs A - U. +The macro `CALCPERFORM_NARGS` reflects this change. +The new inputs can be used in calc links and access security as well. +The size of CALC and OCAL fields has been doubled to 160 chars. + +### Allow hex and octal strings in dbPut and dbGet + +It is now possible to convert hex and octal strings to integer fields +with `dbPut()`, `dbGet()` and related functions like the iocsh command +`dbpf` or through database links. + +Possible incompatibility: Up to now, leading `0`s have been ignored, +now they switch to octal mode. + +For backward compatibility, this behavior can be switched off, returning +to the old decimal only conversions, by setting the environment variable +`EPICS_DB_CONVERT_DECIMAL_ONLY` to `YES` (case insensitive) before `iocInit`. + +### `dfanout` improvements + +The [dfanout](dfanoutRecord.md) record now has invalid output handling with the usual fields +`IVOA` and `IVOV` just like many other output record types. + +The number of output links has also been increased from 8 to 16. + +### CA Client Library Enhancement + +GitHub [PR #711](https://github.com/epics-base/epics-base/pull/711) + +* Allow CA clients to determine a server's protocol version. + + Adds a call to the CA client API that allows a client to determine the + server's protocol minor version number. + This is needed to allow the ca-nameserver to report a server's protocol + version correctly to a client. + +* `ca_host_minor_protocol()` return for disconnected channels + + `ca_host_minor_protocol()` now explicitly returns `CA_UKN_MINOR_VERSION` + for a disconnected channel. + +### Add environment variable to opt out of POSIX Real-Time scheduling + +On POSIX systems, processes with real-time capabilities can opt out of using +Posix thread priority scheduling and memory locking. +Set `EPICS_ALLOW_POSIX_THREAD_PRIORITY_SCHEDULING=NO` to achieve this. + +### Avoid early expiration of timers on non-RTOS IOCs + +Previously the epicsTimer code rounded down user requested delays +by subtracting one half of the sleep "quantum". +On RTEMS and vxWorks, +this allowed periodic timers which expired on every tick. +However this also resulted in timers expiring slightly +[earlier than requested](https://github.com/epics-base/epics-base/issues/106). + +With [PR 744](https://github.com/epics-base/epics-base/pull/744) +rounding is only done for RTEMS and vxWorks, which still have tick timers. + +This affects several facilities which use epicsTimer, +including record delays. +For example, `calcout.ODLY` becomes more +[accurate](https://github.com/epics-base/epics-base/issues/106#issuecomment-1260232765) +on non-RTOS IOCs. + +### normativeTypes Module + +Changes to this module since the previous release: + +#### Release 6.0.2 + +- Fix potential NULL pointer dereference in `NTNDArray::getValueSize()` + +### pvAccess Module + +Changes to this module since the previous release: + +#### Release 7.1.8 + +- Compatible changes + - Capped the number and age of PVA beacons to avoid a resource leak. Beacons + older than 360 seconds will be destroyed automatically, new beacons will be + ignored if >=2048 exist already. + - Various Clang, MSVC and GCC compiler warnings cleaned up. + +### pvData Module + +Changes to this module since the previous release: + +#### Release 8.0.7 + +- Compatible changes + - Allow epics::pvData::Timer to be cancelled during callback execution. + - Clang compiler warnings cleaned up. + - Limit periodic timers to one catch-up after missing many events. + + +### pvaClient Module + +Changes to this module since the previous release: + +## Release 4.8.1 + +* Fix error message generation code. + + diff --git a/documentation/RELEASE-7.0.2.1.md b/documentation/RELEASE-7.0.2.1.md new file mode 100644 index 0000000000..edcc19b93b --- /dev/null +++ b/documentation/RELEASE-7.0.2.1.md @@ -0,0 +1,58 @@ +# Release Notes + +This document describes the changes that were included in the release of EPICS +noted below. Release entries are now provided in a separate document for each +version in the EPICS 7 series, but all are combined into a single page for +publishing on the EPICS website. Separate release documents are also included +from the older Base 3.15 and 3.16 series. + +## EPICS Release 7.0.2.1 + +### Linking shared libraries on macOS + +The linker flag `-flat_namespace` has been restored for creating shared +libraries, although not for loadable libraries (bundles). This was required +for building using the latest versions of Apple XCode. + +### Fix `DB_LINK` loop breaking + +A regression was introduced in 7.0.2 which caused record chains with loops to +be incorrectly broken. Processing should be skipped when a `DB_LINK` with +Process Passive (PP) closes a loop to a synchronous record. + +Instead in 7.0.2 the targeted record would be processed if processing began +with a remote action (or some other caller of `dbPutField()`). This would +result in the loop running a second time. The loop would be broken on the +second iteration. + +[See lp: #1809570](https://bugs.launchpad.net/epics-base/+bug/1809570) + +### Old dbStaticLib APIs removed + +Support for some obsolete dbStaticLib Database Configuration Tool (DCT) APIs +was removed some time ago, but vestiges of them still remained. The following +routines and macros and have now finally been removed: + + * `int dbGetFieldType(DBENTRY *pdbentry)` + * `int dbGetLinkType(DBENTRY *pdbentry)` + * `DCT_STRING` + * `DCT_INTEGER` + * `DCT_REAL` + * `DCT_MENU` + * `DCT_MENUFORM` + * `DCT_INLINK` + * `DCT_OUTLINK` + * `DCT_FWDLINK` + * `DCT_NOACCESS` + * `DCT_LINK_CONSTANT` + * `DCT_LINK_FORM` + * `DCT_LINK_PV` + +### Fix for `dbhcr` before `iocInit` + +The `dbhcr` command used to work before `iocInit` as well as afterwards. It +displays all records that have hardware addresses (`VME_IO`, `CAMAC_IO`, +`GPIB_IO`, `INST_IO` etc.) but stopped working if run before iocInit due to the +rewrite of the link address parser code in dbStaticLib. This release fixes that +issue, although in some cases the output may be slightly different than it used +to be. diff --git a/documentation/RELEASE-7.0.2.2.md b/documentation/RELEASE-7.0.2.2.md new file mode 100644 index 0000000000..27adf0610f --- /dev/null +++ b/documentation/RELEASE-7.0.2.2.md @@ -0,0 +1,41 @@ +# Release Notes + +This document describes the changes that were included in the release of EPICS +noted below. Release entries are now provided in a separate document for each +version in the EPICS 7 series, but all are combined into a single page for +publishing on the EPICS website. Separate release documents are also included +from the older Base 3.15 and 3.16 series. + +## EPICS Release 7.0.2.2 + +### Build System changes + + * The GNUmake build targets `cvsclean` and `depclean` are now available from +any directory; previously they were only available from application top +directories. + + * The approach that EPICS Base uses for building submodules inside the parent +module looks useful for support modules too. The rules for building submodules +have been modified and extracted into a new `RULES_MODULES` file, so a support +module will be able to use them too without having to copy them into its own +`modules/Makefile`. There are some specific requirements that support modules +and their submodules must follow, which are described as comments in the new +`base/configure/RULES_MODULES` file itself. + +### `EPICS_BASE_VERSION` Update Policy change + +In the past, a build of EPICS using sources checked out from the repository +branch between official releases would have shown the version number of the +previous release, followed by a -DEV suffix, for example 7.0.2.1-DEV. + +The policy that controls when the number gets updated has been changed, and +now immediately after a release has been tagged the version number will be +updated to the next patch release version, plus the -DEV suffix as before. +Thus following 7.0.2.2 the version number will show as 7.0.2.3-DEV. This does +not require the next official release to be numbered 7.0.2.3 though, it could +become 7.0.3 or even 7.1.0 if the changes incorporated into it are more +substantial than bug fixes. + +### Drop `CLOCK_MONOTONIC_RAW` from posix/osdMonotonic.c + +Turns out this is ~10x slower to query than `CLOCK_MONOTONIC`. diff --git a/documentation/RELEASE-7.0.2.md b/documentation/RELEASE-7.0.2.md new file mode 100644 index 0000000000..a5b368cd04 --- /dev/null +++ b/documentation/RELEASE-7.0.2.md @@ -0,0 +1,24 @@ +# Release Notes + +This document describes the changes that were included in the release of EPICS +noted below. Release entries are now provided in a separate document for each +version in the EPICS 7 series, but all are combined into a single page for +publishing on the EPICS website. Separate release documents are also included +from the older Base 3.15 and 3.16 series. + +## EPICS Release 7.0.2 + +### Launchpad Bugs + +The list of tracked bugs fixed in this release can be found on the +[Launchpad Milestone page for EPICS Base 7.0.2](https://launchpad.net/epics-base/+milestone/7.0.2). + +### Git Branches Recombined + +The four separate Git branches `core/master`, `libcom/master`, `ca/master` and +`database/master` have been recombined into one branch called `7.0`. Keeping +these as 4 separate branches in the same repository made it impossible to +create merge requests that contained changes in more than one of these +modules. The layout of the source files has not changed at all however, so the +source code for libcom, ca and the database are still found separately under +the module subdirectory. diff --git a/documentation/RELEASE-7.0.3.1.md b/documentation/RELEASE-7.0.3.1.md new file mode 100644 index 0000000000..ed1998f72e --- /dev/null +++ b/documentation/RELEASE-7.0.3.1.md @@ -0,0 +1,200 @@ +# Release Notes + +This document describes the changes that were included in the release of EPICS +noted below. Release entries are now provided in a separate document for each +version in the EPICS 7 series, but all are combined into a single page for +publishing on the EPICS website. Separate release documents are also included +from the older Base 3.15 and 3.16 series. + +## EPICS Release 7.0.3.1 + +**IMPORTANT NOTE:** *Some record types in this release will not be compatible +with device support binaries compiled against earlier versions of those record +types, because importing the record documentation from the EPICS Wiki +[as described below](#imported-record-reference-documentation-from-wiki) +also modified the order of some of the fields in the record definitions.* +As long as all support modules and IOCs are rebuilt from source after updating +them to use this release of EPICS Base, these changes should not have any +affect. + + +### logClient reliability + +On supported targets (Linux, Mac, Windows) logClient will attempt to avoid dropping +undelivered log messages when the connection to the log server is closed/reset. + +### Timers and delays use monotonic clock + +Many internal timers and delay calculations use a monotonic clock +epicsTimeGetMonotonic() instead of the realtime epicsTimeGetCurrent(). This is +intended to make IOCs less susceptible to jumps in system time. + +### Iocsh `on error ...` + +A new statement is added to enable IOC shell commands to signal error +conditions, and for scripts to respond. This first is through the new function + +```C + int iocshSetError(int err); +``` + +A script may be prefixed with eg. "on error break" to stop at the failed +command. + +```sh + on error continue | break | wait [value] | halt +``` + +A suggested form for IOC shell commands is: + +```C + static void doSomethingCallFunc(const iocshArgBuf *args) + { + iocshSetError(doSomething(...)); /* return 0 == success */ + } +``` + +### Relocatable Builds + +Allows built trees to be copied or moved without invalidating RPATH entries. + +The `LINKER_USE_RPATH` Makefile variable (see `configure/CONFIG_SITE`) may be +set to `YES`, `NO`, and a new third option `ORIGIN`. This is limited to +targets using the ELF executable format (eg. Linux). + +When `LINKER_USE_RPATH=ORIGIN`, the variable `LINKER_ORIGIN_ROOT` is set to +one of the parents of the build directory. Any libraries being linked +to which are found under this root will have a relative RPATH entry. +Other libraries continue to result in absolute RPATH entries. + +An effect of this might change a support library from being linked with +`-Wl,-rpath /build/epics-base/lib/linux-x86` +to being linked with +`-Wl,-rpath \$ORIGIN/../../../epics-base/lib/linux-x86` +if the support module directory is `/build/mymodule` +and `LINKER_ORIGIN_ROOT=/build`. + +The API functions `epicsGetExecDir()` and `epicsGetExecName()` are also +added to `osiFileName.h` to provide runtime access to the directory or +filename of the executable with which the process was started. + +### Decouple `LINKER_USE_RPATH` and `STATIC_BUILD` + +Previously, setting `STATIC_BUILD=NO` implied `LINKER_USE_RPATH=NO`. +This is no longer the case. Setting `LINKER_USE_RPATH=YES` will +always emit RPATH entries. This was found to be helpful when linking +against some 3rd party libraries which are only available as shared objects. + +### Channel Access Security: Check Hostname Against DNS + +Host names given in a `HAG` entry of an IOC's Access Security Configuration +File (ACF) have to date been compared against the hostname provided by the CA +client at connection time, which may or may not be the actual name of that +client. This allows rogue clients to pretend to be a different host, and the +IOC would believe them. + +An option is now available to cause an IOC to ask its operating system to look +up the IP address of any hostnames listed in its ACF (which will normally be +done using the DNS or the `/etc/hosts` file). The IOC will then compare the +resulting IP address against the client's actual IP address when checking +access permissions at connection time. This name resolution is performed at +ACF file load time, which has a few consequences: + + 1. If the DNS is slow when the names are resolved this will delay the process +of loading the ACF file. + + 2. If a host name cannot be resolved the IOC will proceed, but this host name +will never be matched. + + 3. Any changes in the hostname to IP address mapping will not be picked up by +the IOC unless and until the ACF file gets reloaded. + +Optionally, IP addresses may be added instead of, or in addition to, host +names in the ACF file. + +This feature can be enabled before `iocInit` with + +``` + var("asCheckClientIP",1) +``` + +or with the VxWorks target shell use + +```C + asCheckClientIP = 1 +``` + +### New and modified epicsThread APIs + +#### `epicsThreadCreateOpt()` + +A new routine `epicsThreadCreateOpt()` is an alternative to +`epicsThreadCreate()` which takes some arguments via a structure (`struct +epicsThreadOpts`) to allow for future extensions. + +```C + typedef struct epicsThreadOpts { + unsigned int priority; + unsigned int stackSize; + unsigned int joinable; + } epicsThreadOpts; + #define EPICS_THREAD_OPTS_INIT { \ + epicsThreadPriorityLow, epicsThreadStackMedium, 0} + epicsThreadId epicsThreadCreateOpt(const char * name, + EPICSTHREADFUNC funptr, void * parm, const epicsThreadOpts *opts); +``` + +The final `opts` parameter may be `NULL` to use the default values of thread +priority (low) and stack size (medium). Callers wishing to provide alternative +settings for these thread options or to create a joinable thread (see below) +should create and pass in an `epicsThreadOpts` structure as shown below. +Always initialize one of these structures using the `EPICS_THREAD_OPTS_INIT` +macro to ensure that any additional fields that get added in the future are +set to their default values. + +```C + void startitup(void) { + epicsThreadOpts opts = EPICS_THREAD_OPTS_INIT; + epicsThreadId tid; + + opts.priority = epicsThreadPriorityMedium; + tid = epicsThreadCreateOpt("my thread", &threadMain, NULL, &opts); + } +``` + +C or C++ Code that also needs to build on earlier versions of Base can use +`#ifdef EPICS_THREAD_OPTS_INIT` to determine whether the +`epicsThreadCreateOpt()` API is available on this Base version. + +#### Thread stack sizes + +The `stackSize` member of the `epicsThreadOpts` structure and the equivalent +parameters to the `epicsThreadCreate()` and `epicsThreadMustCreate()` routines +can now be passed either one of the `epicsThreadStackSizeClass` enum values or +a value returned from the `epicsThreadGetStackSize()` routine. + +#### `epicsThreadMustJoin()` + +If the new `joinable` flag of an `epicsThreadOpts` structure is non-zero (the +default value is zero), the new API routine `epicsThreadMustJoin()` *must* be +called with the thread's `epicsThreadId` when/after the thread exits, to free +up thread resources. This function will block until the thread's main function +has returned, allowing the parent to wait for its child thread. The child's +`epicsThreadId` will no longer be valid and should not be used after the +`epicsThreadMustJoin()` routine returns. + +A thread that was originally created with its joinable flag set may itself +call `epicsThreadMustJoin()`, passing in its own epicsThreadId. This marks the +thread as no longer being joinable, so it will then free the thread resources +itself when its main function returns. The `epicsThreadId` of a thread that is +not joinable gets invalidated as soon as its main function returns. + +### Non-VME RTEMS targets now define pdevLibVME + +Previously IOC executables that made calls to devLib routines would fail to +link when built for some non-VME based RTEMS targets, which would have to be +explicitly filtered out by sites that build Base for those targets. [This +fix](https://bugs.launchpad.net/epics-base/+bug/1841692) makes that no longer +necessary, all RTEMS targets should now link although the IOC won't be able to +be used with the VME I/O on those systems (that we don't have VMEbus I/O +support for in libCom). diff --git a/documentation/RELEASE-7.0.3.md b/documentation/RELEASE-7.0.3.md new file mode 100644 index 0000000000..785899a367 --- /dev/null +++ b/documentation/RELEASE-7.0.3.md @@ -0,0 +1,27 @@ +# Release Notes + +This document describes the changes that were included in the release of EPICS +noted below. Release entries are now provided in a separate document for each +version in the EPICS 7 series, but all are combined into a single page for +publishing on the EPICS website. Separate release documents are also included +from the older Base 3.15 and 3.16 series. + +## EPICS Release 7.0.3 + +### `epicsTimeGetCurrent()` optimization + +Add a fast path to epicsTimeGetCurrent() and related calls in the common case +where only the default OS current time provider is registered. This path does +not take the global mutex guarding the time providers list, potentially +reducing lock contention. + +### dbEvent tweak Queue size + +The size of the queue used by dbEvent to push monitor updates has been +slightly increased based on `DBR_TIME_DOUBLE` to better fill an ethernet frame. +This may result in slightly fewer, but larger frames being sent. + +### mbbo/mbbiDirect number of bits as precision + +Report NOBT as "precision" through the dbAccess API. This is not accessible +through CA, but is planned to be used through QSRV. diff --git a/documentation/RELEASE-7.0.4.1.md b/documentation/RELEASE-7.0.4.1.md new file mode 100644 index 0000000000..038f06f439 --- /dev/null +++ b/documentation/RELEASE-7.0.4.1.md @@ -0,0 +1,54 @@ +# Release Notes + +This document describes the changes that were included in the release of EPICS +noted below. Release entries are now provided in a separate document for each +version in the EPICS 7 series, but all are combined into a single page for +publishing on the EPICS website. Separate release documents are also included +from the older Base 3.15 and 3.16 series. + +## EPICS Release 7.0.4.1 + +### ARM Architecture Changes + +Build configuration files for a new cross-build architecture `linux-aarch64` +have been added, and the targets `linux-arm_el` and `linux-arm_eb` removed. +The 64-bit ARM architecture target doesn't have build files for self-hosting +yet but they should be relatively easy to add, contributions welcome! + +### Bug fixes + +The following bugs/issues have fixes included in this release: + +- [lp: 1884339](https://bugs.launchpad.net/epics-base/+bug/1884339), + Inaccessible CA servers on Windows +- [github: 83](https://github.com/epics-base/epics-base/issues/83) + osdTimeGetCurrent doesn't work for subprocess on macOS +- Recent Cygwin build problem with a missing `TCP_NODELAY` declaration. + +### Perl CA Bindings under Conda + +Builds of the Perl CA bindings weren't working properly when the Perl +installation was from Conda. This release also fixed the capr.pl script +to handle the INT64 data types, and to be able to properly handle missing +fields, as happens if the IOC is running an older EPICS version for example. + +### epicsMessageQueue implementation on RTEMS + +The implementation of the `epicsMessageQueue` used on RTEMS has switched from +the native RTEMS-specific one to the EPICS generic version, avoiding a bug +in the RTEMS Kernel message queue code. + +### Record Name Validation + +Historically, there have been very few restrictions on which characters +may be present in record and alias names. Base 3.14.12.3 added a warning +for names containing space, single or double quote, period/dot, or +dollar sign. + +``` +Bad character ' ' in record name "bad practice" +``` + +7.0.4.1 Turns this warning into an error, and adds a new warning +if a record name begins with a minus, plus, left square bracket, +or left curly bracket. diff --git a/documentation/RELEASE-7.0.4.md b/documentation/RELEASE-7.0.4.md new file mode 100644 index 0000000000..1567d2c7be --- /dev/null +++ b/documentation/RELEASE-7.0.4.md @@ -0,0 +1,191 @@ +# Release Notes + +This document describes the changes that were included in the release of EPICS +noted below. Release entries are now provided in a separate document for each +version in the EPICS 7 series, but all are combined into a single page for +publishing on the EPICS website. Separate release documents are also included +from the older Base 3.15 and 3.16 series. + +## EPICS Release 7.0.4 + +### Bug fixes + +The following launchpad bugs have fixes included in this release: + +- [lp: 1812084](https://bugs.launchpad.net/bugs/1812084), Build failure on + RTEMS 4.10.2 +- [lp: 1829919](https://bugs.launchpad.net/bugs/1829919), IOC segfaults when + calling dbLoadRecords after iocInit +- [lp: 1838792](https://bugs.launchpad.net/bugs/1838792), epicsCalc bit-wise + operators on aarch64 +- [lp: 1853148](https://bugs.launchpad.net/bugs/1853148), mingw compiler + problem with printf/scanf formats +- [lp: 1852653](https://bugs.launchpad.net/bugs/1852653), `USE_TYPED_DSET` + incompatible with C++ +- [lp: 1862328](https://bugs.launchpad.net/bugs/1862328), Race condition on + IOC start leaves rsrv unresponsive +- [lp: 1866651](https://bugs.launchpad.net/bugs/1866651), thread joinable race +- [lp: 1868486](https://bugs.launchpad.net/bugs/1868486), epicsMessageQueue + lost messages +- [lp: 1868680](https://bugs.launchpad.net/bugs/1868680), Access Security file + reload (asInit) fails + +### `*_API` macros in EPICS headers + +Internally, the Com and ca libraries now express dllimport/export (Windows) +and symbol visibility (GCC) using library-specific macros (eg. `LIBCOM_API`) +instead of the macros `epicsShareFunc`, `epicsShareClass`, `epicsShareDef` etc. +that are defined in the `shareLib.h` header. +This change may affect some user code which uses the `epicsShare*` macros +without having explicitly included the `shareLib.h` header themselves. +Such code should be changed to include `shareLib.h` directly. + +A new helper script `makeAPIheader.pl` and build rules to generate a +library-specific `*API.h` header file has been added. Run `makeAPIheader.pl -h` +for information on how to use this in your own applications, but note that the +resulting sources will not be able to be compiled using earlier versions of +EPICS Base. + +### IOCsh usage messages + +At the iocShell prompt `help ` now prints a descriptive usage message +for many internal IOCsh commands in addition to the command parameters. +Try `help *` to see all commands, or a glob pattern such as `help db*` to see +a subset. + +External code may provide usage messages when registering commands using a +new `const char *usage` member of the `iocshFuncDef` structure. +The `iocsh.h` header also now defines a macro `IOCSHFUNCDEF_HAS_USAGE` which +can be used to detect Base versions that support this feature at compile-time. + +### Variable names in RELEASE files + +`configure/RELEASE` files are parsed by both GNUmake and the `convertRelease.pl` +script. While GNUmake is quite relaxed about what characters may be used in a +RELEASE variable name, the `convertRelease.pl` script parser has only recognized +variable names that match the Perl regular expression `\w+`, i.e. upper and +lower-case letters, digits and underscore characters. + +The script has been modified so now RELEASE variable names must start with a +letter or underscore, and be followed by any number of letters, digits, +underscore or hyphen characters, matching the regular expression +`[A-Za-z_][A-Za-z_0-9-]*`. The hyphen character `-` was not previously allowed +and if used would have prevented a build from finding include files and +libraries in any module using that in its RELEASE variable name. + +This change does disallow names that start with a digit which used to be +allowed, but hopefully nobody has been relying on that ability. The regular +expression used for names can be found in the file `src/tools/EPICS/Release.pm` +and can be adjusted locally if necessary. + +### caRepeater /dev/null + +On \*NIX targets caRepeater will now partially daemonize by redirecting +stdin/out/err to /dev/null. This prevents caRepeater from inheriting +the stdin/out of a process, like caget, which has spawned it in the +background. This has been known to cause problems in some cases when +caget is itself being run from a shell script. + +caRepeater will now understand the `-v` argument to retain stdin/out/err +which may be necessary to see any error messages it may emit. + +### `state` record deprecated + +IOCs now emit a warning when a database file containing the `state` record is +loaded. This record has been deprecated for a while and will be removed +beginning with EPICS 7.1. Consider using the `stringin` record instead. + +### Record types publish dset's + +The record types in Base now define their device support entry table (DSET) +structures in the record header file. While still optional, developers of +external support modules are encouraged to start converting their code to use +the record's new definitions instead of the traditional approach of copying the +structure definitions into each source file that needs them. By following the +instructions below it is still possible for the converted code to build and +work with older Base releases. + +This would also be a good time to modify the device support to use the type-safe +device support entry tables that were introduced in Base-3.16.2 -- see +[this entry below](#type-safe-device-and-driver-support-tables) for the +description of that change, which is also optional for now. + +Look at the aiRecord for example. Near the top of the generated `aiRecord.h` +header file is a new section that declares the `aidset`: + +```C +/* Declare Device Support Entry Table */ +struct aiRecord; +typedef struct aidset { + dset common; + long (*read_ai)(struct aiRecord *prec); + long (*special_linconv)(struct aiRecord *prec, int after); +} aidset; +#define HAS_aidset +``` + +Notice that the common members (`number`, `report()`, `init()`, `init_record()` +and `get_ioint_info()` don't appear directly but are included by embedding the +`dset common` member instead. This avoids the need to have separate definitions +of those members in each record dset, but does require those members to be +wrapped inside another set of braces `{}` when initializing the data structure +for the individual device supports. It also requires changes to code that +references those common members, but that code usually only appears inside the +record type implementation and very rarely in device supports. + +An aiRecord device support that will only be built against this or later +versions of EPICS can now declare its dset like this: + +```C +aidset devAiSoft = { + { 6, NULL, NULL, init_record, NULL }, + read_ai, NULL +}; +epicsExportAddress(dset, devAiSoft); +``` + +However most device support that is not built into EPICS itself will need to +remain compatible with older EPICS versions, which is why the ai record's header +file also declares the preprocessor macro `HAS_aidset`. This makes it easy to +define the `aidset` in the device support code when it's needed, and not when +it's provided in the header: + +```C +#ifndef HAS_aidset +typedef struct aidset { + dset common; + long (*read_ai)(aiRecord *prec); + long (*special_linconv)(aiRecord *prec, int after); +} aidset; +#endif +aidset devAiSoft = { + { 6, NULL, NULL, init_record, NULL }, + read_ai, NULL +}; +epicsExportAddress(dset, devAiSoft); +``` + +The above `typedef struct` declaration was copied directly from the new +aiRecord.h file and wrapped in the `#ifndef HAS_aidset` conditional. + +This same pattern should be followed for all record types except for the lsi, +lso and printf record types, which have published their device support entry +table structures since they were first added to Base but didn't previously embed +the `dset common` member. Device support for these record types therefore can't +use the dset name since the new definitions are different from the originals and +will cause a compile error, so this pattern should be used instead: + +```C +#ifndef HAS_lsidset +struct { + dset common; + long (*read_string)(lsiRecord *prec); +} +#else +lsidset +#endif +devLsiEtherIP = { + {5, NULL, lsi_init, lsi_init_record, get_ioint_info}, + lsi_read +}; +``` diff --git a/documentation/RELEASE-7.0.5.md b/documentation/RELEASE-7.0.5.md new file mode 100644 index 0000000000..afe1cab54b --- /dev/null +++ b/documentation/RELEASE-7.0.5.md @@ -0,0 +1,326 @@ +# Release Notes + +This document describes the changes that were included in the release of EPICS +noted below. Release entries are now provided in a separate document for each +version in the EPICS 7 series, but all are combined into a single page for +publishing on the EPICS website. Separate release documents are also included +from the older Base 3.15 and 3.16 series. + +The external PVA submodules continue to maintain their own release notes files +as before, but the entries describing changes in those submodules since version +7.0.5 have been copied into the associated EPICS Release Notes files, and will +be added to new EPICS Release Notes published in the future. + +## EPICS Release 7.0.5 + +### Fix aai's Device Support Initialization + +Krisztian Loki [reported](https://github.com/epics-base/epics-base/issues/97) +segfaults occurring when a Soft Channel aai record INP field was a DB link to +an array field of a compress record. This was caused by the aai record's +pass-0 device support initialization clashing with the semantics of the new +link support API. + +The aai record +[has been modified](https://github.com/epics-base/epics-base/pull/114) to +allow the Soft Channel device support to request a pass-1 initialization +callback. See the Device Support section of the Array Analogue Input Record +Reference pages in this release for the API changes, which are fully backwards +compatible for existing aai device support. + +### Prevent default DTYPs from changing + +[Kay Kasemir reported](https://bugs.launchpad.net/epics-base/+bug/1908305) that +it is possible to change the Base record type's default DTYP if a `device()` +entry is seen before the `recordtype()` definition to which it refers. The +default DTYP is the first device loaded, which is normally the `Soft Channel` +support from Base. A warning was being displayed by dbdExpand when a `device()` +entry was see first, but that was easily missed. + +The DBD file parser in dbdExpand.pl has now been modified to make this an error, +although the registerRecordDeviceDriver.pl script will still accept `device()` +entries without having their `recordtype()` loaded since this is necessary to +compile device supports as loadable modules. + + +### Priority inversion safe Posix mutexes + +On Posix systems, epicsMutex now support priority inheritance if available. +The IOC needs to run with `SCHED_FIFO` engaged to use these. +Support for Posix implementations before POSIX.1-2001 (`_XOPEN_SOURCE < 500`, +glibc version < 2.3.3) has been dropped. + +The IOC shell's `epicsMutexShowAll` command prints "PI is enabled" if both +libc and kernel support is present. + +### Fix for Periodic Scan threads hanging on Windows + +Since 7.0.3.1 a Windows IOC could not run for more than 49.7 days; at that +time the periodic scan threads would stop processing. This issue should now +have been fixed and the Monotonic time functions on Windows should return +values which count at nanosecond resolution. However we have not waited 49.7 +days to test the final software, so there is a small chance that it's still +broken. + +This fixes [lauchpad bug #1896295](https://bugs.launchpad.net/bugs/1896295). + +### Support for Apple M1/M2 (arm64) Processors + +Thanks to Jeong Han Lee this release comes with build support for Apple's new +M1/M2 CPUs running macOS, using the target name `darwin-aarch64`. + +It should also be possible to build universal binaries containing code for +both the Intel and arm64 processors under either target name: In the +appropriate `configure/os/CONFIG_SITE.Common.darwin-*` file add the other +architecture class name to the `ARCH_CLASS` variable (after a space). + +### New String Comparison Routine `epicsStrSimilarity()` + +The new `epicsStrSimilarity()` routine in epicsString.h uses a modified +Levenshtein distance to compare two strings, with a character case difference +being half the weight of a full substitution. The double return value falls in +the range 0.0 (identical) through 1.0 (no characters matching), or -1.0 for +error. This is used to provide a new "Did you mean ..." suggestion when a .db +file provides an invalid choice string for a `DBF_MENU` or `DBF_DEVICE` field. + +### Build System: New `VALID_BUILDS` type "Command" + +Target architectures that support command-line programs that run the `main()` +routine can now be marked as such in their `VALID_BUILDS` definition. This +enables a new set of Makefile target variables `PROD_CMD` (similar to +`PROD_HOST`), `LIBRARY_CMD` (like `LIBRARY_HOST`, etc.), `LOADABLE_LIBRARY_CMD`, +`OBJS_CMD`, `SCRIPTS_CMD`, `TARGETS_CMD`, `TESTLIBRARY_CMD`, `TESTSCRIPTS_CMD` +and `TESTPROD_CMD`. The CA client tools and programs such as `caRepeater` are now built for all such targets (previously they were built for all targets except where the OS was VxWorks, RTEMS and iOS). + +If you have created your own site-specific target architectures you may need to +update the `VALID_BUILDS` variable if it gets set in your locally added +`configure/os/CONFIG.Common.` files. This is usually only needed for +cross-compiled targets though since `CONFIG.Common.UnixCommon` sets it. + +The other `VALID_BUILDS` types are "Host" for target architectures that can +compile and run their own programs (`PROD_HOST` etc.), and "Ioc" for targets +that can run IOCs (`PROD_IOC` etc.). + +### Support for JSON5 + +The YAJL parser and generator routines in libcom and in the IOC's dbStatic +parser now support the JSON5 standard. This adds various features to JSON +without altering the API for the code other than adding a new option to the +YAJL parser which can be used to disable JSON5 support if desired. The new +features include: + +- The ability to handle numeric values `Infinity`, `-Infinity` and `NaN`. +- String values and map keys may be enclosed in single quotes `'`, inside which + the double-quote character `"` doesn't have to be escaped with a back-slash + `\`, although a single-quote character `'` (or apostrophy) must be escaped + inside a single-quoted string. +- Numbers may start with a plus sign, `+`. +- Integers may be expressed in hexadecimal with a leading `0x` or `0X`. +- Floating-point numbers may start or end with their decimal point `.` + (after the sign or before the exponent respectively if present). +- Map keys that match the regex `[A-Za-z_][A-Za-z_0-9]*` don't have to be + enclosed in quotes at all. The dbStatic parser adds `.+-` to the characters + allowed but will add quotes around such keys before passing them to YAJL. +- Arrays and maps allow a comma before the closing bracket/brace character. +- The YAJL parser will elide a backslash followed by a newline characters from + a string value. The dbStatic parser doesn't allow that however. + +Code that must also compile against the older API can use the new C macro +`HAS_JSON5` to detect the new version. This macro is defined on including +either the `yajl_parse.h` or `yajl_gen.h` headers, which also provide the +new configuration options to turn on JSON5 support. + +All APIs in the IOC that previously accepted JSON will now accept JSON5. +This includes JSON field modifiers (channel filters), JSON link addresses, +constant input link array values and database info-tag values. JSON values +that get parsed by the dbLoadRecords() routine are still more liberal than +the other uses as the ability to use unquoted strings that was called +"relaxed JSON" is still supported, whereas the JSON5 standard and the YAJL +parser only allow unquoted strings to be used for keys in a JSON map. + +This also fixes [lauchpad bug #1714455](https://bugs.launchpad.net/bugs/1714455). + + +### Character Escape Changes + +- The libCom routines `epicsStrnRawFromEscaped()` and `dbTranslateEscape()` + declared in epicsString.h no longer accept octal escaped characters such as + `\123` or `\41`. +- The routine `epicsStrnEscapedFromRaw()` now generates hex + escaped characters for unprintable characters such as `\x1f`. +- Hex escape character sequences `\xXX` must now contain exactly 2 hex digits. +- An escape sequence `\0` now generates a zero byte in the raw string, but the + other digits `1-9` should not appear after a back-slash. + +These changes are to more closely follow the JSON5 standard, which doesn't +support octal character escapes or the `\a` (Bel, `\x07`) escape sequence. + +### Filters in database input links + +Input database links can now use channel filters, it is not necessary to +make them CA links for the filters to work. + +### ai Soft Channel support + +The Soft Channel device support for ai records now returns failure when +fetching the INP link fails. + +### Support for zero-length arrays + +Several modifications have been made to properly support zero-length +array values inside the IOC and over Channel Access. Some of these changes +may affect external code that interfaces with the IOC, either directly or +over the CA client API so we recommend thorough testing of any external +code that handles array fields when upgrading to this release. + +Since these changes affect the Channel Access client-side API they will +require rebuilding any CA Gateways against this version or Base to +properly handle zero-length arrays. The `caget`, `caput` and `camonitor` +client programs are known to work with empty arrays as long as they were +built with this or a later version of EPICS. + +#### Change to the db\_access.h `dbr_size_n(TYPE, COUNT)` macro + +When called with COUNT=0 this macro no longer returns the number of bytes +required for a scalar (1 element) but for an empty array (0 elements). +Make sure code that uses this doesn't call it with COUNT=0 when it really +means COUNT=1. + +Note that the db\_access.h header file is included by cadef.h so the change +can impact Channel Access client programs that use this macro. + +#### Channel Access support for zero-length arrays + +The `ca_array_put()` and `ca_array_put_callback()` routines now accept an +element count of zero, and will write a zero-length array to the PV if +possible. No error will be raised if the target is a scalar field though, +and the field's value will not be changed. + +The `ca_array_get_callback()` and `ca_create_subscription()` routines +still accept a count of zero to mean fetch as many elements as the PV +currently holds. + +Client programs should be prepared for the `count` fields of any +`struct event_handler_args` or `struct exception_handler_args` passed to +their callback routines to be zero. + +#### Array records + +The soft device support for the array records aai, waveform, and subArray +as well as the aSub record type now correctly report reading 0 elements +when getting an empty array from an input link. + +#### Array support for dbpf + +The dbpf command now accepts array values, including empty arrays, when +provided as a JSON string. This must be enclosed in quotes so the iocsh +argument parser sees the JSON as a single argument: + +``` +epics> dbpf wf10:i32 '[1, 2, 3, 4, 5]' +DBF_LONG[5]: 1 = 0x1 2 = 0x2 3 = 0x3 4 = 0x4 5 = 0x5 +``` + +#### Reading empty arrays as scalar values + +Record links that get a scalar value from an array that is currently +empty will cause the record that has the link field to be set to an +`INVALID/LINK` alarm status. +The record code must call `dbGetLink()` with `pnRequest=NULL` for it to +be recognized as a request for a scalar value though. + +This changes the semantics of passing `pnRequest=NULL` to `dbGetLink()`, +which now behaves differently than passing it a pointer to a long integer +containing the value 1, which was previously equivalent. +The latter can successfully fetch a zero-element array without triggering +a LINK alarm. + +#### Writing empty arrays to scalar fields + +Record links that put a zero-element array into a scalar field will now set +the target record to `INVALID/LINK` alarm without changing the field's value. +Previously the field was set to 0 in this case (with no alarm). +The target field must be marked as `special(SPC_DBADDR)` to be recognized +as an array field, and its record support must define a `put_array_info()` +routine. + +### Timestamp before processing output links + +The record processing code for records with output links has been modified to +update the timestamp via recGblGetTimeStamp() _before_ processing the output +links. This ensures that other records which get processed via an output link +can use TSEL links to fetch the timestamp corresponding to the data processed +by the output link. + +This change could result in a slightly earlier timestamp for records whose +output link is handled by a device driver, but only if the device driver does +not handle its own timestamping via TSE -2 and instead uses TSE 0 or TSE -1 to +get current time or best time, and the time spent in the device driver is +greater than your timestamp provider resolution. For these situations it is +recommended to set TSE to -2 and set the timestamp in the driver code. + +### Add registerAllRecordDeviceDrivers() + +A new iocsh command `registerAllRecordDeviceDrivers` is provided and also +defined as a function in iocshRegisterCommon.h. This uses dynamic symbol +lookup with `epicsFindSymbol()` to perform the same function as a generated +`*_registerRecordDeviceDriver()` function. This allows for an alternative +approach to dynamic loading of support modules without code generation. + +This feature is not intended for use by IOCs constructed using the standard +EPICS application build process and booted from a startup script in an iocBoot +subdirectory, although it might work in some of those cases — the +generated registerRecordDeviceDriver.cpp file is normally required to link +everything referred to in the DBD file into the IOC's executable. It also +won't work with some static build configurations, or if the symbol table has +been stripped from the executable. + +### Using a `{const:"string"}` to initialize an array of `DBF_CHAR` + +It is now possible to use a JSON Const link with a string value to initialize +an aai or waveform record that has `FTVL` set to `CHAR` through the INP link. +The string length is not limited to 40 characters. This should also work for +aSub record inputs similarly configured as long strings. + +``` + record(waveform, "wf") { + field(NELM, 100) + field(FTVL, CHAR) + field(INP, {const:"This is a waveform and more than 40 characters"}) + } +``` + +### RELEASE files may use `undefine` + +GNUmake added the directive `undefine` in version 3.82 to allow variables to +be undefined. Support for this has been added to the EPICS Release file parser, +so `undefine` can now be used in configure/RELEASE files to unset variables. + + +### Submodule updates + +The pvData module was updated to version 8.0.4: + +- Incompatible changes + - Remove `ByteBuffer::align()` +- Compatible changes + - Deprecate `SerializableControl::alignBuffer()` and + `DeserializableControl::alignData()` + - `shared_vector_convert<>()` fix convert of empty, untyped, array + +The pvAccess module was updated to version 7.1.3: + +- Bug fixes + - Increase default TCP timeout to 40 seconds. + Applies a 4/3 multiplier on `$EPICS_PVA_CONN_TMO` for compatibility. + - CA Provider implementation restructured to simplify, reduce duplication + and fix issues #163 and #165. +- Changes + - Enable building of pvtools to all except vxWorks, RTEMS and iOS. + +The pva2pva module was updated to version 1.3.0: + +- Changes + - Add `dbLoadGroup()` iocsh function to read group JSON definitions + from a file. Mappings in files must refer to full record names + instead of fields. eg. 'recname.VAL' instead of 'VAL'. diff --git a/documentation/RELEASE-7.0.6.1.md b/documentation/RELEASE-7.0.6.1.md new file mode 100644 index 0000000000..82108bf6a6 --- /dev/null +++ b/documentation/RELEASE-7.0.6.1.md @@ -0,0 +1,85 @@ +# Release Notes + +This document describes the changes that were included in the release of EPICS +noted below. Release entries are now provided in a separate document for each +version in the EPICS 7 series, but all are combined into a single page for +publishing on the EPICS website. Separate release documents are also included +from the older Base 3.15 and 3.16 series. + +The external PVA submodules continue to maintain their own release notes files +as before, but the entries describing changes in those submodules since version +7.0.5 have been copied into the associated EPICS Release Notes files, and will +be added to new EPICS Release Notes published in the future. + +## EPICS Release 7.0.6.1 + +### `mbboDirectRecord` enhancements + +The bit fields `B0` - `B1F` of this record are now always updated and have a +monitor posted when the `VAL` field is set and the record processed. It is now +possible to initialize the record's value by setting the bit fields inside a +database file as long as no other method was used to initialize it (suc as +setting `VAL` directly, using `DOL`, or by an initial readback from device +support). A new internal field `OBIT` was added to store information about +monitors posted on the bit fields. + +### Minimum Perl Version is now 5.10.1 + +Some scripts now make use of features that were introduced to this Perl version +that was released in 2009. + +### DB Links to `DBF_MENU` fields fixed + +[GH:183](https://github.com/epics-base/epics-base/issues/183) +These were broken in a previous release, but now work again. + +### Long String access to CALC fields fixed + +[GH:194](https://github.com/epics-base/epics-base/issues/194) +This was broken in a previous release, but now works again. + +### Minor Changes + ++ Many code comments have been spell-checked and corrected. ++ Passing a `-DDEBUG` compiler flag no longer breaks the build. ++ Parallel builds of RTEMS-mvme2100 and RTEMS-mvme2700 targets now work. ++ Illegal characters seen in JSON strings in a database file should now get a +better error message. + +### Other Launchpad Bugs and GitHub Issues Fixed + ++ [lp:1938459](https://bugs.launchpad.net/epics-base/+bug/1938459) + [GH:191](https://github.com/epics-base/epics-base/pull/191) int64in only + checks lower 32 bits for change ++ [lp:1941875](https://bugs.launchpad.net/epics-base/+bug/1941875) Buggy + warning message "Record/Alias name '...' should not contain non-printable ... ++ [GH:187](https://github.com/epics-base/epics-base/issues/187) waveformRecord + missing PACT=true? ++ [GH:189](https://github.com/epics-base/epics-base/pull/189) Fix a couple + memory leaks and a segfault ++ [GH:200](https://github.com/epics-base/epics-base/pull/200) and + [GH:201](https://github.com/epics-base/epics-base/pull/201) Fix timers on MS + Windows for non-EPICS threads + +### Compiler interface for epicsAtomic tidied up + +[GH:192](https://github.com/epics-base/epics-base/pull/192) +Both GCC and CLANG compiler intrisics used for the epicsAtomic APIs have been revised; implementations using CLANG should now run faster as they now use the compiler's built-in atomic functions instead of taking a mutex. + +### The epicsTime code has been reimplemented + +[GH:185](https://github.com/epics-base/epics-base/pull/185) +This was done to simplify the code and may have improved performance slightly for some uses. Support for the old NTP-specific `struct l_fp` has been dropped but all other routines and methods of the `class epicsTime` function as before. + +### Updates to Record Reference documentation + +Many of the built-in record types have had improvements to their documentation with additional fields added to the tables, rewrites of descriptions and links to other documents added or fixed. + +### Submodule updates + +The pvAccess module was updated to version 7.1.4: + +- Changes to caProvider + - Resolve issues with pv structures that don't have a value field + - Add NULL checks for handling unusual structures + - Speed up channel creation when using large numbers of channels diff --git a/documentation/RELEASE-7.0.6.md b/documentation/RELEASE-7.0.6.md new file mode 100644 index 0000000000..a0285bd76f --- /dev/null +++ b/documentation/RELEASE-7.0.6.md @@ -0,0 +1,283 @@ +# Release Notes + +This document describes the changes that were included in the release of EPICS +noted below. Release entries are now provided in a separate document for each +version in the EPICS 7 series, but all are combined into a single page for +publishing on the EPICS website. Separate release documents are also included +from the older Base 3.15 and 3.16 series. + +The external PVA submodules continue to maintain their own release notes files +as before, but the entries describing changes in those submodules since version +7.0.5 have been copied into the associated EPICS Release Notes files, and will +be added to new EPICS Release Notes published in the future. + +## EPICS Release 7.0.6 + +### Support for obsolete architectures removed + +These target architectures have been removed: + ++ darwin-ppc, darwin-ppcx86 ++ linux-386, linux-486, linux-586, linux-686, linux-athlon (cross-build) ++ linux-cris, linux-cris\_v10, linux-cris\_v32 (cross-build) ++ RTEMS-at91rm9200ek, RTEMS-gen68360, RTEMS-mcp750, RTEMS-mvme167, +RTEMS-psim (cross-build) + +### Experimental Support for RTEMS 5 + +The new major release of the RTEMS real-time OS contains many changes +including the ability to support SMP systems. This release of EPICS +can still be built with RTEMS 4.9.x or 4.10.x and should work just +the same as earlier releases, although due to code having moved around +we recommend thorough testing before this release is first used in +production systems. + +This release of EPICS comes with support for several new RTEMS targets +running on RTEMS 5: + +- RTEMS-beagleboneblack +- RTEMS-pc686 +- RTEMS-qoriq\_e500 (MVME2500) +- RTEMS-xilinx\_zynq\_a9\_qemu +- RTEMS-xilinx\_zynq\_zedboard + +The EPICS support for RTEMS 4 has always relied on RTEMS-specific +kernel APIs which cannot be used on an SMP system, so a new port was +created to use the Posix real-time APIs that are now recommended for +RTEMS 5. Note that a single installation of EPICS cannot build both +RTEMS 4 and RTEMS 5 targets, if you need to support targets running +on both versions you must use a separate installation, and be sure +to run `make distclean` if switching a single source tree from one +to the other (both header files and dependency files are different +between the two and must be cleaned out). + +The configuration variable `RTEMS_VERSION` in the EPICS config file +`configure/os/CONFIG_SITE.Common.RTEMS` must be set to the full 3- +part version number for RTEMS 4 releases, e.g. `4.9.1`, `4.10.2` +but for RTEMS 5.1 and later it must only contain the major version +number e.g. `5`. + +Some RTEMS BSPs can be built and may work with the newer libbsd +network stack which RTEMS is moving over to, but most of the MVME +boards (and the uC5282) still require the legacy network stack. + +The dependency on bspExt has been removed, EPICS now provides its +own routine for VMEbus probing (or uses one built into the BSP). + +Anyone using this release on RTEMS is advised to discuss problems +building or running it on either the tech-talk or core-talk email +lists so the core developers can help with and find out about any +problems with the old or new port. + +Known Issues: +- MVME2100 and MVME2700 need changes to the RTEMS 5 BSP to build. +- VMEBus support is not yet available for the MVME2500 BSP. +- There are some known issues with floating point on MVME2500, + probably related to its newer e500 FPU. +- Changed network driver for beatnik to work with libbsd. Some + issues with DHCP, but network stack usable. Can load env from + NVRAM. + +### `epicsEnvShow` accepts a glob pattern + +The optional argument to epicsEnvShow can now be a glob pattern. + +### New function `epicsStrnGlobMatch()` + +The function `epicsStrnGlobMatch(char* str, size_t len, char* pattern)` +works exactly the same as `epicsStrGlobMatch()` but takes an additional +length arguments which limits the number of characters of `str` to match. + +### Automatic fallback to thread when unable to exec caRepeater + +A process using libca which does not find an existing caRepeater process +will attempt to start one by running the caRepeater executable. +This is not always possible, usually when caRepeater is not in `$PATH`. +Now, instead of printing a warning, an internal caRepeater thread +will be started (as is done be RTEMS and vxWorks targets). + +If this fallback occurs, the lifetime of the caRepeater thread +may be shorter than the lifetime of a separate caRepeater process +would have been. + +It remains the recommended practice to explicitly start a caRepeater +instance. Examples of both systemd (`caRepeater.service`) and sysv +(`S99caRepeater`) scripts may be found under `bin/`. + +### Glob pattern allowed in `var` command + +When used with one argument, the `var` command can be used with a glob pattern +for printing matching variables. + +### Formalize/fix `FINAL_LOCATION` + +The `FINAL_LOCATION` make variable has for some time been an undocumented +means of performing a staged build. This is a build which "installs" to +a temporary location, which will later be moved to a final location. + +This has now been added to `configure/CONFIG_SITE`. + +Usage analogous to the autotools recipe + +```sh +./configure --prefix=/usr/lib/epics +make install DESTDIR=/tmp/build +``` + +would be + +```sh +make INSTALL_LOCATION=/tmp/build FINAL_LOCATION=/usr/lib/epics +``` + +`FINAL_LOCATION` is now correctly used in systemd and sysv init scripts +`caRepeater.service`, `S99caRepeater`, and `S99logServer`. + +### IOCsh sets `${PWD}` + +IOC shell will now ensure `${PWD}` is set on startup, +and updated by the `cd` iocsh function. + +### Add Alarm Message and Time Tag Fields + +Two new fields have been added to `dbCommon` so will be present in all +records: `AMSG` and `UTAG`. + +#### `AMSG` + +`AMSG` can hold an arbitrary 40-character string, providing additional +information about the alarm condition indicated in `STAT` and `SEVR`. With no +alarm it will hold an empty string. The new `recGblSetSevrMsg()` function can +be used in place of `recGblSetSevr()` to signal an alarm while providing a +message. + +For example, a device support's `read_bi()` routine for a hypothetical +multi-channel ethernet attached device might flag a communication error +between the IOC and controller, or an error involving a certain channel like +this: + +```c +static long read_bi(biRecord* prec) { + ... + if (!priv->connected) { + recGblSetSevrMsg(prec, COMM_ALARM, INVALID_ALARM, + "No controller connected"); + return S_dev_noDevice; + } + if (!priv->err) { + recGblSetSevrMsg(prec, READ_ALARM, INVALID_ALARM, + "Channel %u disconnexted", priv->chan); + return S_dev_noDevice; + } + return status; +} +``` + +#### `UTAG` + +`UTAG` holds an `epicsUInt64` value which is semantically part of the record's +timestamp (`TIME`). The value defaults to zero if not explicitly set. Device +support or an event time provider which supports this feature may write a tag +value directly to the `dbCommon::utag` field. + +`TSEL` links will copy both `TIME` and `UTAG` between records if the link type +supports this (CA links do not). + +A `utag` server side channel filter has been added which can be configured to +filter out monitor updates which don't pass the test `(UTAG & M) == V` where +`M` and `V` are client specified integers. For example running the command +`camonitor BPM0:X.{utag:{M:1,V:1}}` will only show updates for which +`(UTAG & 1) == 1` i.e. the least significant bit of the `UTAG` field is set. + +This feature is intended for use by intelligent devices which can provide +contextual information along with a value/alarm/time. For example, a beam +diagnostic device which is aware of whether a beam signal should be present +(eg. from a global timing system). + +#### Link Support + +Two new optional methods have been added to the Link Support Entry Table +(`struct lset`): `lset::getAlarmMsg()` and `lset::getTimeStampTag()`. See +comments in dbLink.h for details on implementing these. + +Two new accessor functions have also been added which call these methods: +`dbGetAlarmMsg()` and `dbGetTimeStampTag()`. + +#### Compatibility + +User code wishing to call these interfaces while maintaining compatibility with older +versions of Base may add some of the following macro definitions, and ensure +that the variables referenced by output pointers are initialized. + +```c +#ifndef HAS_ALARM_MESSAGE +# define recGblSetSevrMsg(REC, STAT, SEVR, ...) recGblSetSevr(REC, STAT, SEVR) +#endif +#ifndef dbGetAlarmMsg +# define dbGetAlarmMsg(LINK, STAT, SEVR, BUF, BUFLEN) dbGetAlarm(LINK, STAT, SEVR) +#endif +#ifndef dbGetTimeStampTag +# define dbGetTimeStampTag(LINK, STAMP, TAG) dbGetTimeStamp(LINK, STAMP) +#endif +``` + + +### Timeouts for Unit Test Programs + +The unit test programs that are run by the `make runtests` or `make tapfiles` +commands get executed by a `.t` wrapper script which is normally generated by +the EPICS `makeTestfile.pl` program. Those generated wrapper scripts now +impose a time-limit on the test program they execute, and will kill it if it +runs for longer than 500 seconds (8 minutes 20) without exiting. That +time-limit can be changed for any such test by modifying the Makefile which +creates and runs the `.t` wrapper script. + +Setting the environment variable `EPICS_UNITTEST_TIMEOUT` to the desired +number of seconds while the Makefile is generating the test script changes the +timeout in that script. For example: + +``` + TESTSCRIPTS_HOST += hourLongTest.t + hourLongTest.t: export EPICS_UNITTEST_TIMEOUT=3600 +``` + +When selecting such a timeout remember that different Continuous Integration +systems such as GitHub Actions and Appveyor run on processors with different +speeds, so allow enough head-room for slower systems to complete the test. + +Test programs written directly in Perl as a `.plt` script should implement a +similar timeout for themselves. The "netget" test in Base does this in a way +that works on Windows as well as Unix-like hosts. + +### Submodule updates + +The pvAccess module was updated to version 7.1.4: + +- Changes + - Adjust argument parsing with pvput (Jesus Vasquez). + +The pva2pva module was updated to version 1.3.1: + +- Bug Fixes + - Correct handling for server side filters. +- Changes + - Syncing softMain.cpp with epics-base + +The pvDatabase module was updated to version 4.6.0: + +* Access Security is now supported. +* special has been revised and extended. +* addRecord, removeRecord, processRecord, and traceRecord are replaced by pvdbcr versions. +* support is DEPRECATED + +The pvaClient module was updated to version 4.8.0: + +* `PvaClientNTMultiData::getChannelChangeFlags` is a new method. It fixes + issue #66. +* Fix for issue #68. Both `PvaClientArray` and `PvaClientField` are not longer + present. Neither was previously implemented. +* Several public methods are now protected. They were never meant to be called + by clients. +* Issue #70 has been fixed. +* Changes was made to increase the performance of `pvaMultiChannel`. +* doxygen changes were made. diff --git a/documentation/RELEASE-7.0.7.md b/documentation/RELEASE-7.0.7.md new file mode 100644 index 0000000000..8c9043c58b --- /dev/null +++ b/documentation/RELEASE-7.0.7.md @@ -0,0 +1,298 @@ +# Release Notes + +This document describes the changes that were included in the release of EPICS +noted below. Release entries are now provided in a separate document for each +version in the EPICS 7 series, but all are combined into a single page for +publishing on the EPICS website. Separate release documents are also included +from the older Base 3.15 and 3.16 series. + +The external PVA submodules continue to maintain their own release notes files +as before, but the entries describing changes in those submodules since version +7.0.5 have been copied into the associated EPICS Release Notes files, and will +be added to new EPICS Release Notes published in the future. + +## EPICS Release 7.0.7 + +### Doxygen Annotations + +Thanks to several attendees at the 2022 EPICS Codeathon the number of header +files with Doxygen annotations in the EPICS Core has again increased. + +### Build System updates + +The top-level make targets `uninstall`, `archuninstall` and similar no +longer trigger the `clean` target which empties build directories, this +was a bug introduced in 7.0.5. + +The `make distclean` target now properly deletes the generated file(s) +`modules/RELEASE..local` which are essential to build the external +submodules under the `modules` directory, and should not crash if the +build is configured with `INSTALL_LOCATION` pointing to an empty external directory (i.e. if you run `make distclean` twice in succession). When +`INSTALL_LOCATION` is set in the files `configure/CONFIG_SITE` or +`configure/CONFIG_SITE.local` the `modules/RELEASE..local` file +will now be regenerated in case the install path has been modified. + +Note that passing `INSTALL_LOCATION=` on the make command-line will +only work if you have run `make distclean` immediately beforehand, as the +`modules/RELEASE..local` file must be recreated using the new path. + +### Enhancements to `capr.pl` + +The `capr.pl` script can now display records from older Base versions to +which fields have since been added, and shows long strings and array data +up to 10 elements, use the new `-n` option to increase that number. +The script is fully event-driven and prints all the field data received by +the end of the CA wait time (`-w` option which defaults to 2 seconds). +The interest level can now be specified using the `-l` option before the +PV name, and the new `-D` flag outputs debugging information. + +### Time Synchronization on VxWorks + +VxWorks 6.9 can do its own OS clock time synchronization, if it has been +configured by setting `SNTPC_PRIMARY_IPV4_ADDR`. Since EPICS 3.15.3 the +IOC time support code has checked for the existence of the VxWorks time +synchronization task and avoided starting the EPICS one if the OS task +exists and the OS clock gives a "recent" time (i.e. after when EPICS was +compiled), unless the environment variable `EPICS_TS_FORCE_NTPTIME` is +also set. However a logic error in that code required the environment +variable to be set in more cases than it should have. + +This error has been fixed and the IOC should work normally if the VxWorks +task is configured and running. The `TIMEZONE` value for the year is also +now calculated at initialization in this configuration, previously it was +only done when the IOC synchronzation task was used. Setting the above +environment variable will now cause the IOC support code to shut down the +VxWorks synchronization thread (if running) before starting the EPICS one. + +Running the iocsh command `ClockTime_Report` now shows whether the VxWorks +task is running as well as giving the state of the IOC synchronization task. +The `ClockTime_Init` command can also be used to stop or restart the IOC +time synchronization task while the IOC is running, depending on the `0` or +`1` parameter passed to it. This last change also applies to RTEMS IOCs. + +### Incompatible change to `struct db_field_log` + +This change may cause channel filters which manipulate array updates +to fail to compile. + +To avoid potential speculation issues arising from overlapping code pointers +with data values, `union dbfl_ref` is modified to remove the `dtor` member. +`dtor` is moved out into the enclosing `struct db_field_log`. + +So eg. using a `db_field_log* p`, the expression `p->u.r.dtor` must be +changed to `(p)->dtor`. + +### Fix undef ts on first camonitor update of NORD from waveformRecord + +The order over operations when processing a waveformRecord is adjusted +so that updates to NORD is posted with the correct timestamp. + +### Automatic `COMMANDLINE_LIBRARY` with newer compilers + +When built with a compiler supporting `__has_include<>`, the presence +of a `readline/readline.h` header will be used to automatically determine +a default value for `COMMANDLINE_LIBRARY`. + +Mingw builds with readline support now link `-ltermcap` instead of `-lcurses`. + +This should not effect sites which set explicitly set `COMMANDLINE_LIBRARY` +as the only definition in Base now has the form `COMMANDLINE_LIBRARY ?= ...`. + +### Perl CA support for empty long strings + +The Perl CA bindings have been fixed to handle zero-length long string data +properly. + +### `aao` gains `OMSL` and `DOL` + +The `aao` record types gains the same `DOL` functionality found +in other output record types (`ao`, `longout`, etc.) + +### Server exports `RSRV_SERVER_PORT` + +During `iocInit()`, the environment variable `RSRV_SERVER_PORT` is set +with the TCP port number selected. + +### `dbdExpand.pl` sorts all items by name + +DBD files generated by the `dbdExpand.pl` script are now sorted within each +item type by the primary name of the item. The result should resolve any +issues with reproducible builds. No option is provided to prevent the sorting, +previously the order was essentially random and varied each time. + +### `dbExpand.pl` sorts records by name + +Records are now output by this program in order, sorted by name. The new flag +`-s` can be given to output the records in the same order they were read in, +instead of sorting them. + +Note that there are currently no build rules provided with Base which make use +of this program. + +### Simulation Mode RAW Support for Output Record Types + +SIMM=RAW support has been added for the relevant output record types +(ao, bo, mbbo, mbboDirect). +RAW simulation mode will have those records do the appropriate conversion +and write RVAL to the location pointed to by SIOL. + +### Fixed leak from a non-EPICS thread + +On some targets, if a thread not created by `epicsThreadCreate*()` directly +or indirectly calls an `epicsThread*()` function, a specific tracking struct +is allocated. + +Prior to this release, on POSIX and WIN32 targets, this +struct would not be `free()`d, resulting in a memory leak. + +This release fixed the leak on POSIX targets. + +See the associated github [issue 241](https://github.com/epics-base/epics-base/issues/241) +for WIN32 status. + +### Fixed leak from a non-EPICS thread + +On some targets, if a thread not created by `epicsThreadCreate*()` directly +or indirectly calls an `epicsThread*()` function, a specific tracking struct +is allocated. + +Prior to this release, on POSIX and WIN32 targets, this +allocation would not be `free()`d, resulting in a memory leak. + +This release fixed the leak on POSIX and WIN32 targets (excluding +MSVC before vs2012, and the WINE runtime). + +### Fixed leak from a non-EPICS thread + +On some targets, if a thread not created by `epicsThreadCreate*()` directly +or indirectly calls an `epicsThread*()` function, a specific tracking struct +is allocated. + +Prior to this release, on POSIX and WIN32 targets, this +struct would not be `free()`d, resulting in a memory leak. + +This release fixed the leak on POSIX targets. + +See the associated github [issue 241](https://github.com/epics-base/epics-base/issues/241) +for WIN32 status. + +### Fix `CHECK_RELEASE = WARN` + +This now works again, it was broken in 2019 (7.0.3.1) by an errant commit. + +### Document `DISP` as design-time field + +The DISP field can be set to a non-zero value to prevent records being changed +from outside the IOC (this is ancient behavior), but has never been documented +as being usable at design-time (DCT=Yes in the Record Reference tables). This +has now been changed. + +### Make `epicsInt8` signed on all architectures + +The `epicsInt8` and thus `DBF_CHAR` types have always been unsigned on +architectures where `char` is unsigned, for example on many PowerPC CPU +architectures. This was counter-intuitive, and resulted in IOC behavior +differing between architectures when converting `DBF_CHAR` values into a +signed integer or floating point type. + +**WARNING**: This fix may change behavior of existing databases on target +architectures with unsigned `char` (mainly PowerPC) when using input links to +read from `CHAR` arrays. Architectures with signed `char` (usually x86) should +be unaffected, although some compilers might generate new warnings. + +### Allow hexadecimal and octal numbers in hardware links + +[GH:213](https://github.com/epics-base/epics-base/pull/213) + +Several types of hardware links (`VME_IO`, `CAMAC_IO`, etc) now accept +hexadecimal and octal numbers. (Hexadecimal numbers had already been valid +up to EPICS R3.15.) This change may introduce incompatibilities when using +numbers with leading `0` as they will now be parsed as octal. + +### Fix embedded implementations of `epicsEvent` + +[GH:202](https://github.com/epics-base/epics-base/issues/202) and +[GH:206](https://github.com/epics-base/epics-base/pull/206) + +Heinz Junkes provided a new implementation of the `epicsEvent` API suitable for +RTEMS Posix targets (RTEMS 5.1 and later). In review a few issues related to +overflow of timeout values surfaced in this and other embedded implementations, +and these were also been fixed in this Pull Request. The API documentation for +this and some other routines has also been updated. + +### Breakpoint Table Names + +The names of breakpoint tables were made unnecessarily strict when DBD file +processing was moved to Perl for the 3.15 release series. Table names may now +contain the special characters `_` `-` `:` `;` `.` `[` `]` `<` `>` in addition +to letters and digits. + +### Fix for `undefined` in configure/RELEASE files + +Prevents `Use of uninitialized value` warnings from convertRelease.pl. + +### Colorized Messages for errlog + +Many internal error messages now emit ANSI escape sequences to highlight the +words "ERROR" and "WARNING" in an attempt to make occurrences more noticeable +during IOC startup. + +The macros `ERL_ERROR` and `ERL_WARNING` are defined for external usage, +and expand as string constants. eg. + +```c +#include +#ifndef ERL_ERROR +# define ERL_ERROR "ERROR" +#endif +void fn() { + ... + errlogPrintf(ERL_ERROR ": something bad happens :(\n"); +``` + +ANSI escapes are automatically removed from errlog output not destined +for a terminal. For example, for logClient, if stderr is redirected, +or if unsupported (`$TERM` not set, or Windows < 10). + +### `dbnd` filter pass through `DBE_ALARM|DBE_PROPERTY` + +The `dbnd` server side filter now passes through alarm and property +change events, even when not exceeding the deadband. + +### Submodule updates + +The pvData module was updated to version 8.0.5: + +- Compatible changes + - Internal changes to use the YAJL API for generating JSON and JSON-5 output. + +The pvAccess module was updated to version 7.1.6: + +- Changes to caProvider + - Bug fix related to enum values. + - More internal changes to improve performance when connecting tens of + thousands of CA channels. +- Several minor internal improvements. + +The pva2pva module was updated to version 1.4.0: + +- Bug Fixes + - Apply ACF when writing to atomic group +- Additions + - Add new "structure" to @ref qsrv_group_map_types +- Changes + - Add Access Security hooks for single and group writes. + - Enable "Async Soft Channel" for output links + - When built against Base 7.0.6.1, set timeStamp.userTag from UTAG field. + - Add DTYP="QSRV Set UTag" for longin, which sets UTAG=VAL. + +The pvDatabase module was updated to version 4.7.0: + +* Added support for the whole structure (master field) server side plugins. + The whole structure is identified as the `_` string, and a pvRequest string + that applies a plugin to it takes the form: + + `field(_[XYZ=A:3;B:uniqueId])` + + where `XYZ` is the name of a specific filter plugin that takes parameters + `A` and `B` with values `3` and `uniqueId` respectively. diff --git a/documentation/RELEASE-7.0.8.1.md b/documentation/RELEASE-7.0.8.1.md new file mode 100644 index 0000000000..4fed2b3def --- /dev/null +++ b/documentation/RELEASE-7.0.8.1.md @@ -0,0 +1,60 @@ +# Release Notes + +This document describes the changes that were included in the release of EPICS +noted below. Release entries are now provided in a separate document for each +version in the EPICS 7 series, but all are combined into a single page for +publishing on the EPICS website. Separate release documents are also included +from the older Base 3.15 and 3.16 series. + +The external PVA submodules continue to maintain their own release notes files +as before, but the entries describing changes in those submodules since version +7.0.5 have been copied into the associated EPICS Release Notes files, and will +be added to new EPICS Release Notes published in the future. + +## EPICS Release 7.0.8.1 + +### Limit to `_FORTIFY_SOURCE=2` + +GCC versions 12 and beyond and glibc have added some aggressive runtime +checks for buffer overflows in libc functions at runtime, and the +[Ubuntu 2024.04](https://wiki.ubuntu.com/ToolChain/CompilerFlags) release +increased their default gcc fortification level from 2 to 3. +This has started causing EPICS Base builds to fail on that version, and +other OS releases may make that configuration change with similar results. +This release detects a compiler configured with `_FORTIFY_SOURCE=3` and +overrides it to 2. +Later releases of Base will adjust the code, providing information to the +compiler to avoid triggering these incorrect protections. + +### Fix issue with compress record + +In Base 7.0.8, an update to the compress record was added to allow for certain +algorithms to use partially filled buffers in their computations. Unfortunately, +this broke the behaviour of the records in certain cases. This has been fixed. + +### Various minor changes + +These included fixing minor memory leaks and documentation corrections. The +`SIZV` field of lsi, lso and printf record VAL fields now can't exceed 32767 +characters, to match an internal limit. + +### `epicsSocketAccept()` now returns `SOCKET`, not `int` + +This might have some effect on downstream modules still using `int`, but the +OS-specific osdSock.h headers which osiSock.h includes have all declared +`SOCKET` (in most casese as a typedef for `int`) for many releases. +This change removes a compiler warning on WIN32. +Further details and the discussion about this change can be found +[here](https://github.com/epics-base/epics-base/pull/458). + +### `dbLoadRecords` allows macros with default values + +Previously the parser assumed that files containing macro substitutions were +bad if no macro definitions were provided; that assumption was made incorrect +once macro substitutions were allowed to provide a default value. + +### Hostname length limit in CA removed + +Before this release, the CA client library only handled hostnames in address +list environment variables up to 255 characters long. +This limit has been removed. diff --git a/documentation/RELEASE-7.0.8.md b/documentation/RELEASE-7.0.8.md new file mode 100644 index 0000000000..f8477a61be --- /dev/null +++ b/documentation/RELEASE-7.0.8.md @@ -0,0 +1,243 @@ +# Release Notes + +This document describes the changes that were included in the release of EPICS +noted below. Release entries are now provided in a separate document for each +version in the EPICS 7 series, but all are combined into a single page for +publishing on the EPICS website. Separate release documents are also included +from the older Base 3.15 and 3.16 series. + +The external PVA submodules continue to maintain their own release notes files +as before, but the entries describing changes in those submodules since version +7.0.5 have been copied into the associated EPICS Release Notes files, and will +be added to new EPICS Release Notes published in the future. + +## EPICS Release 7.0.8 + +### bi "Raw Soft Channel" use MASK + +If MASK is non-zero, The raw device support will now apply MASK to the +value read into RVAL. +eg. allows extraction of a bit from an input integer. + +``` +record(longin, "integer") { + field(VAL, "0xff") +} +record(bi, "bit1") { + field(DESC, "extract bit 1") + field(DTYP, "Raw Soft Channel") + field(INP , "integer") + field(MASK, "0x2") + field(ZNAM, "Clear") + field(ONAM, "Set") +} +``` + +### ANSI escapes in stderr + +ANSI escape character sequences may now be printed to the stderr stream. +These escapes will appear in logs captured from that stream. +Tools which parse and/or render these logs may need to be adjusted to +either strip out the escapes, or to translate them into markup. +(see [ansi2html](https://pypi.org/project/ansi2html/) for example) + +### Allow explicit append with `dbRecordsOnceOnly!=0` + +Previously setting `dbRecordsOnceOnly!=0` prevented any further changes to a record via a .db file. eg. + +``` +record(ai, "myrec") {} +``` + +`dbRecordsOnceOnly!=0` previously disallowed appending fields with either form: + +``` +record("*", "myrec") {} # error +record(ai, "myrec") {} # error +``` + +Beginning with this release, `dbRecordsOnceOnly!=0` allows appending when explicitly intended (when record type is `"*"`). + +``` +record("*", "myrec") {} # allowed +record(ai, "myrec") {} # error +``` + +### Add `$EPICS_CLI_TIMEOUT` + +Add support for CA tools timeout from environment variable `$EPICS_CLI_TIMEOUT` +which sets the default the default timeout for `caget` et al. +The `-w` argument continues to take precedence. + +### Fixed leak from a non-EPICS thread on WIN32 + +On Windows targets, if a thread not created by `epicsThreadCreate*()` directly +or indirectly calls an `epicsThread*()` function, a specific tracking struct +is allocated. Prior to this release the allocation would not be `free()`d, +resulting in a memory leak. + +A similar issue on POSIX targets was previously fixed. + +### Change compiler for FreeBSD to clang + +The default compiler for FreeBSD targets changes from GCC to clang. + +### Expose `dbCreateAlias` in IOC shell + +Add a new IOC shell command `dbCreateAlias` allow record aliases to be added. +Intended for use before `iocInit`. eg. to add an alias "bar" for a record "foo". + +``` +dbLoadRecords("some.db") # includes: record(ai, "foo") { ... +dbCreateAlias("foo", "bar") +iocInit() +``` + +### dbEvent eventsRemaining missed on cancel + +In some cases, RSRV may queue a subscription update, but not flush it. +This partially addresses this issue. + +### subRecord on bad INP links + +Previously, if a subRecord has an invalid `INP*` link, it was silently failing +(and not running the proc function). Now the the status code returned by the +subroutine is returned from `dbProcess()`. + +### COMMANDLINE\_LIBRARY fallback to GNU\_DIR + +Fall back to the previous behavior when searching for `readline.h` with older compilers. + +### Search for readline installed via HomeBrew. + +Look for `/opt/local/include/readline` on OSX. + +### Always stop worker threads + +The SCAN and callback threads are now stopped during normal IOC shutdown. + +### Allow runtime bypass of free list allocator + +The environment variable `$EPICS_FREELIST_BYPASS` may be set to `YES` to cause the `freeListLib` functions to always call directly to `malloc()`/`free()`. May be useful when troubleshooting some kinds of memory allocation bugs which would otherwise be "hidden". eg. use-after-free data races. This may also improve the results of dynamic analysis tools which are not aware of this internal free list. + +### `compress` record enhancement + +The compress record now supports the use of partially-filled buffers when using +any of the N-to-one algorithms. This is achieved by setting the new field `PBUF` +to `YES`. + +### Extended timestamp channel filter + +The `"ts"` filter can now retrieve the record's timestamp in several numeric +and string formats, some of which support full nanosecond precision. + + Hal$ caget -a test:channel + test:channel 2021-03-11 18:23:48.265386 42 + Hal$ caget -f9 'test:channel.{"ts": {"num": "dbl"}}' + test:channel.{"ts": {"num": "dbl"}} 984331428.265386105 + Hal$ caget 'test:channel.{"ts": {"str": "iso"}}' + test:channel.{"ts": {"str": "iso"}} 2021-03-11T18:23:48.265386+0100 + Hal$ caget -f1 'test:channel.{"ts": {"num": "ts"}}' + test:channel.{"ts": {"num": "ts"}} 2 984331428.0 265386163.0 + +More information is included in the filters documentation, which can be found in +the `html/filters.html` document that is generated during the build + +### Allow adding new error symbols at any time + +`errSymbolAdd` can now be called after early initialization. + +### Add conditional output (OOPT) to the longout record + +The longout record can now be configured using its new OOPT and OOCH fields +to (not) write to its output link depending on the contents of VAL, in a +similar manner to the calcout record. More information can be found on the +reference page for the longout record type that accompanies this release. + +This functionality was suggested in +[lp# 1398215](https://bugs.launchpad.net/epics-base/+bug/1398215) and may +be added to other output record types if the community finds it useful, +please send feedback about the feature to tech-talk. + +### IOC Shell + +#### Tab completion + +When built with optional GNU libreadline support, the interactive IOC shell +will perform tab completion for command names as well as for some arguments +of the built-in commands. For example, the record name argument of `dbpr`, +and the path name argument of `cd`. + +Externally defined commands have a limited ability to opt into completion by +using the new `iocshArgStringRecord` and `iocshArgStringPath` argument types. +Both function identically to `iocshArgString` but indicate how to suggest +completion strings. + +Builds on macOS (`darwin-x86` or `darwin-aarch64` targets) normally use Apple's +libedit library in readline compatibility mode, which doesn't support the tab +completion API that GNU readline provides. You can use Homebrew or some other +third-party package manager to install the GNU readline package, then edit the +`configure/os/CONFIG_SITE.darwinCommon.darwinCommon` file to have EPICS use the +real thing to get tab completion in the IOC shell. The default settings in that +file currently look for and use a Homebrew-installed readline if present. + +#### Persist history + +Attempt to read and write command to a file (`./.iocsh_history` by default). +Name may be overwritten with by setting `$EPICS_IOCSH_HISTFILE` to an +alternate path, or disabled by setting to an empty string. + +#### Changes to help output + +Rework the `help` command output to improve formatting and readability, +and include a visual marker (a line of underlines) between different help commands. + +### Add FMOD as CALC Expression + +The floating point modulo function `FMOD(NUM,DEN)` has been added to the CALC +expression engine and is available to all software using that (calc and calcout +record types, access security library and some extensions). + +### Submodule updates + +The pvData module was updated to version 8.0.6: + +- Compatible changes + - Actually enable JSON-5 output in PVStructure::Formatter::JSON when available. + - Fix unaligned access issues for some ARM/Linux targets. + +The pvAccess module was updated to version 7.1.7: + +- Changes + - Registering the PVA server with the IOC now sets the `PVAS_SERVER_PORT` + variable in the environment. + +The pva2pva module was updated to version 1.4.1: + +- Bug Fixes + - `dbLoadGroup` was fixed +- Additions + - Support for "meta" member at top of array of structs + +The pvDatabase module was updated to version 4.7.1: + +* Added data distributor plugin which can be used for distributing data between + a group of clients. The plugin is triggered by the request string of the + form: + + `_[distributor=group:;set:;trigger:;updates:;mode:]` + + The plugin parameters are optional and are described below: + + - group: this parameter indicates a group that client application belongs to (default value: "default"); groups of clients are completely independent of each other + + - set: this parameter designates a client set that application belongs to within its group (default value: "default") + + - trigger: this is the PV structure field that distinguishes different channel updates (default value: "timeStamp"); for example, for area detector images one could use the "uniqueId" field of the NTND structure + + - updates: this parameter configures how many sequential updates a client (or a set of clients) will receive before the data distributor starts updating the next one (default value: "1") + + - mode: this parameter configures how channel updates are to be distributed between clients in a set: + - one: update goes to one client per set + - all: update goes to all clients in a set + - default is "one" if client set id is not specified, and "all" if set id is specified diff --git a/documentation/RELEASE-7.0.9.md b/documentation/RELEASE-7.0.9.md new file mode 100644 index 0000000000..71883905d0 --- /dev/null +++ b/documentation/RELEASE-7.0.9.md @@ -0,0 +1,134 @@ +# Release Notes + +This document describes the changes that were included in the release of EPICS +noted below. Release entries are now provided in a separate document for each +version in the EPICS 7 series, but all are combined into a single page for +publishing on the EPICS website. Separate release documents are also included +from the older Base 3.15 and 3.16 series. + +The external PVA submodules continue to maintain their own release notes files +as before, but the entries describing changes in those submodules since version +7.0.5 have been copied into the associated EPICS Release Notes files, and will +be added to new EPICS Release Notes published in the future. + +## EPICS Release 7.0.9 + +### Core documentation published at ReadTheDocs + +The `documentation` directory's `Makefile` can now run various publication scripts including Sphinx and Doxygen to generate formatted documentation that is now being published +[at docs.epics-controls.org](https://docs.epics-controls.org/projects/base/en/latest/index.html) +and integrated into the main [EPICS Documentation website](https://docs.epics-controls.org/en/latest/index.html). +The best place to find out more about these mechanisms is the +[Contribution Guide](https://docs.epics-controls.org/en/latest/CONTRIBUTING.html) +although it doesn't currently cover the new processes added to epics-base. + +Much of the documentation generated from .dbd.pod files at build time is now +also being converted into MarkDown (.md) files and installed into the top-level +`doc` directory. Some users might find it quicker to look up information about a +record type by opening these files in a text editor instead of opening a browser +and loading the HTML versions or finding and opening the files from the EPICS +Documentation site. + +### Post monitors from compress record when it's reset + +Writing into a compress record's `RES` field now posts a monitor event instead +of only changing `VAL`. Monitor clients will therefore receive an empty array. + +### The AMSG error message propagates through MSS links + +A database link with the MSS attribute will now propagate not only SEVR and +STAT, but also AMSG. This field contains additional information that complements +STAT. Links with MS or MSI attributes do not propagate STAT, and therefore do +not propagate AMSG, either. + +Channel Access links do not propagate AMSG, regardless of the MSS attribute, +because the message is not available as Channel metadata. + +### Reloading record aliases + +Aliases can now be defined more than once as long as they still refer to the +same record, unless the global variable `dbRecordsOnceOnly` is non-zero. +This allows database files to be loaded multiple times, even if they contain +alias definitions. + +### `DBE_PROPERTY` event rate changed + +Updating property fields now only posts `DBE_PROPERTY` events if the +field actually changed. + +### Changes to msi related to include paths + +There are two changes to `msi` included here. + +`msi` now treats files included by .template or .substutiions files in a more +consistent way: for relative paths, it will always look relative to the current +working directory if no `-I` flags are passed, and if they are passed then it +will search for the _relative_ path from each of those flags. That is, the +following will now find the file `bar.template` located at +`/some/path/rel/path/bar.template` +``` +$ cat foo.substitutions +file rel/path/bar.template { + # contents +} +$ msi -I /some/path foo.substitutions +``` + +Note that this does provide one change from previous behaviour: when opening a +file from the command line, `msi` will not use the `-I`-specified paths to +search for the file, but will only work relative to the current working +directory, consistent with most commandline utilities. + +### Allow users to delete previously created records from the database + +From this release, record instances and aliases that have already been loaded +by an IOC can be removed from the database again before the call to iocInit +by loading a second instance of the named records but using `"#"` in place of +the record type. Values for the fields are not required or advised, just use +an empty record body `{}`. This is useful when a template defines records that +are not wanted in some IOCs, without having to split or duplicate the original +template. + +For example this will remove the record named "unwanted": + +``` +record("#", "unwanted") {} +``` + +### Only keep readline history for interactive sessions + +Previously, all IOCsh commands were persisted in the libreadline history +(when readline support is included). +Going forward, only interactive commands are saved. + +### Type change to asTrap serverSpecific data + +Change `void*` to `dbChannel*` in `asTrapWriteBeforeWithData()` and +`asTrapWriteMessage::serverSpecific` to reflect the reality since +the `dbAddr*` to `dbChannel*` migration. +External code wishing to support both before and after 3.15 should +already be conditionally casting to/from the appropriate type. + +### Fix issues with `_FORTIFY_SOURCE=3` + +This release fixes the false positives failures when building with `_FORTIFY_SOURCE` level 3. +The override introduced in 7.0.8.1 has been removed. + +### Other + +- genVersionHeader: work with git submodules and worktrees. +- avoid UB with self `pthread_join()` +- freebsd: Add support for x86 and amd64 builds +- Clear AMSG when SEVR becomes zero. +- `seqRecord` fix support for link `DLY0` +- Add `ABORT_ON_ASSERT` flag to `CONFIG_SITE_ENV` +- rationalize osdMutex + +### Submodule updates + +The pvDatabase module was updated to version 4.7.2: + +* Resolved issue with changed field set in the case where the top level (master) +field ("_") is not requested by the client, but the master field callback causes +all fields to be marked as updated, rather than only those fields that have +actually been modified. diff --git a/documentation/RELEASE_NOTES.md b/documentation/RELEASE_NOTES.md deleted file mode 100644 index e0ec809b82..0000000000 --- a/documentation/RELEASE_NOTES.md +++ /dev/null @@ -1,3256 +0,0 @@ -# EPICS 7.0 Release Notes {#releasenotes} - -These release notes describe changes that have been made since the previous -release of this series of EPICS Base. **Note that changes which were merged up -from commits to the 3.15 branch are described -[further down this file](#changes-incorporated-from-the-315-branch) -under the 3.15 release to which they were originally committed.** Thus it is -important to read more than just the first section to understand everything that -has changed in each release. - -The PVA submodules each have their own individual sets of release notes which -should also be read to understand what has changed since earlier releases: - -- [normativeTypes](https://github.com/epics-base/normativeTypesCPP/blob/master/documentation/RELEASE_NOTES.md) -- [pvAccess](http://epics-base.github.io/pvAccessCPP/pvarelease_notes.html) -- [pvData](http://epics-base.github.io/pvDataCPP/release_notes.html) -- [pvDatabase](https://github.com/epics-base/pvDatabaseCPP/blob/master/documentation/RELEASE_NOTES.md) -- [pva2pva](https://epics-base.github.io/pva2pva/release_notes.html) -- [pvaClient](https://github.com/epics-base/pvaClientCPP/blob/master/documentation/RELEASE_NOTES.md) - -**This version of EPICS has not been released yet.** - -## Changes made on the 7.0 branch since 7.0.7 - -### Add `$EPICS_CLI_TIMEOUT` - -Add support for CA tools timeout from environment variable `$EPICS_CLI_TIMEOUT` -which sets the default the default timeout for `caget` et al. -The `-w` argument continues to take precedence. - -### Fixed leak from a non-EPICS thread on WIN32 - -On Windows targets, if a thread not created by `epicsThreadCreate*()` directly -or indirectly calls an `epicsThread*()` function, a specific tracking struct -is allocated. Prior to this release the allocation would not be `free()`d, -resulting in a memory leak. - -A similar issue on POSIX targets was previously fixed. - -### Change compiler for FreeBSD to clang - -The default compiler for FreeBSD targets changes from GCC to clang. - -### Expose `dbCreateAlias` in IOC shell - -Add a new IOC shell command `dbCreateAlias` allow record aliases to be added. -Intended for use before `iocInit`. eg. to add an alias "bar" for a record "foo". - -``` -dbLoadRecords("some.db") # includes: record(ai, "foo") { ... -dbCreateAlias("foo", "bar") -iocInit() -``` - -### dbEvent eventsRemaining missed on cancel - -In some cases, RSRV may queue a subscription update, but not flush it. -This partially addresses this issue. - -### subRecord on bad INP links - -Previously, if a subRecord has an invalid `INP*` link, it was silently failing -(and not running the proc function). Now the the status code returned by the -subroutine is returned from `dbProcess()`. - -### COMMANDLINE_LIBRARY fallback to GNU_DIR - -Fall back to the previous behavior when searching for `readline.h` with older compilers. - -### Search for readline installed via HomeBrew. - -Look for `/opt/local/include/readline` on OSX. - -### Always stop worker threads - -The SCAN and callback threads are now stopped during normal IOC shutdown. - -### Allow runtime bypass of free list allocator - -The environment variable `$EPICS_FREELIST_BYPASS` may be set to `YES` to cause the `freeListLib` functions to always call directly to `malloc()`/`free()`. May be useful when troubleshooting some kinds of memory allocation bugs which would otherwise be "hidden". eg. use-after-free data races. This may also improve the results of dynamic analysis tools which are not aware of this internal free list. - -### `compress` record enhancement - -The compress record now supports the use of partially-filled buffers when using -any of the N-to-one algorithms. This is achieved by setting the new field `PBUF` -to `YES`. - -### Extended timestamp channel filter - -The `"ts"` filter can now retrieve the record's timestamp in several numeric -and string formats, some of which support full nanosecond precision. - - Hal$ caget -a test:channel - test:channel 2021-03-11 18:23:48.265386 42 - Hal$ caget -f9 'test:channel.{"ts": {"num": "dbl"}}' - test:channel.{"ts": {"num": "dbl"}} 984331428.265386105 - Hal$ caget 'test:channel.{"ts": {"str": "iso"}}' - test:channel.{"ts": {"str": "iso"}} 2021-03-11T18:23:48.265386+0100 - Hal$ caget -f1 'test:channel.{"ts": {"num": "ts"}}' - test:channel.{"ts": {"num": "ts"}} 2 984331428.0 265386163.0 - -More information is included in the filters documentation, which can be found in -the `html/filters.html` document that is generated during the build - -### Add conditional output (OOPT) to the longout record - -The longout record can now be configured using its new OOPT and OOCH fields -to (not) write to its output link depending on the contents of VAL, in a -similar manner to the calcout record. More information can be found on the -reference page for the longout record type that accompanies this release. - -This functionality was suggested in -[lp# 1398215](https://bugs.launchpad.net/epics-base/+bug/1398215) and may -be added to other output record types if the community finds it useful, -please send feedback about the feature to tech-talk. - -### Tab completion for IOC shell - -When built with optional GNU libreadline support, the interactive IOC shell -will perform tab completion for command names as well as for some arguments -of the built-in commands. For example, the record name argument of `dbpr`, -and the path name argument of `cd`. - -Externally defined commands have a limited ability to opt into completion by -using the new `iocshArgStringRecord` and `iocshArgStringPath` argument types. -Both function identically to `iocshArgString` but indicate how to suggest -completion strings. - -Builds on macOS (darwin-x86 or darwin-aarch64 targets) normally use Apple's -libedit library in readline compatibility mode, which doesn't support the tab -completion API that GNU readline provides. You can use Homebrew or some other -third-party package manager to install the GNU readline package, then edit the -configure/os/CONFIG_SITE.darwinCommon.darwinCommon file to have EPICS use the -real thing to get tab completion in the IOC shell. The default settings in that -file currently look for and use a Homebrew-installed readline if present. - -### Add FMOD as CALC Expression - -The floating point modulo function `FMOD(NUM,DEN)` has been added to the CALC -expression engine and is available to all software using that (calc and calcout -record types, access security library and some extensions). - ------ - -## EPICS Release 7.0.7 - -### Doxygen Annotations - -Thanks to several attendees at the 2022 EPICS Codeathon the number of header -files with Doxygen annotations in the EPICS Core has again increased. - -### Build System updates - -The top-level make targets `uninstall`, `archuninstall` and similar no -longer trigger the `clean` target which empties build directories, this -was a bug introduced in 7.0.5. - -The `make distclean` target now properly deletes the generated file(s) -`modules/RELEASE..local` which are essential to build the external -submodules under the `modules` directory, and should not crash if the -build is configured with `INSTALL_LOCATION` pointing to an empty external directory (i.e. if you run `make distclean` twice in succession). When -`INSTALL_LOCATION` is set in the files `configure/CONFIG_SITE` or -`configure/CONFIG_SITE.local` the `modules/RELEASE..local` file -will now be regenerated in case the install path has been modified. - -Note that passing `INSTALL_LOCATION=` on the make command-line will -only work if you have run `make distclean` immediately beforehand, as the -`modules/RELEASE..local` file must be recreated using the new path. - -### Enhancements to `capr.pl` - -The `capr.pl` script can now display records from older Base versions to -which fields have since been added, and shows long strings and array data -up to 10 elements, use the new `-n` option to increase that number. -The script is fully event-driven and prints all the field data received by -the end of the CA wait time (`-w` option which defaults to 2 seconds). -The interest level can now be specified using the `-l` option before the -PV name, and the new `-D` flag outputs debugging information. - -### Time Synchronization on VxWorks - -VxWorks 6.9 can do its own OS clock time synchronization, if it has been -configured by setting `SNTPC_PRIMARY_IPV4_ADDR`. Since EPICS 3.15.3 the -IOC time support code has checked for the existence of the VxWorks time -synchronization task and avoided starting the EPICS one if the OS task -exists and the OS clock gives a "recent" time (i.e. after when EPICS was -compiled), unless the environment variable `EPICS_TS_FORCE_NTPTIME` is -also set. However a logic error in that code required the environment -variable to be set in more cases than it should have. - -This error has been fixed and the IOC should work normally if the VxWorks -task is configured and running. The `TIMEZONE` value for the year is also -now calculated at initialization in this configuration, previously it was -only done when the IOC synchronzation task was used. Setting the above -environment variable will now cause the IOC support code to shut down the -VxWorks synchronization thread (if running) before starting the EPICS one. - -Running the iocsh command `ClockTime_Report` now shows whether the VxWorks -task is running as well as giving the state of the IOC synchronization task. -The `ClockTime_Init` command can also be used to stop or restart the IOC -time synchronization task while the IOC is running, depending on the `0` or -`1` parameter passed to it. This last change also applies to RTEMS IOCs. - -### Incompatible change to `struct db_field_log` - -This change may cause channel filters which manipulate array updates -to fail to compile. - -To avoid potential speculation issues arising from overlapping code pointers -with data values, `union dbfl_ref` is modified to remove the `dtor` member. -`dtor` is moved out into the enclosing `struct db_field_log`. - -So eg. using a `db_field_log* p`, the expression `p->u.r.dtor` must be -changed to `(p)->dtor`. - -### Fix undef ts on first camonitor update of NORD from waveformRecord - -The order over operations when processing a waveformRecord is adjusted -so that updates to NORD is posted with the correct timestamp. - -### Automatic COMMANDLINE_LIBRARY w/ newer compilers - -When built with a compiler supporting `__has_include<>`, the presence -of the `` will be used to automatically determine -a default value for `COMMANDLINE_LIBRARY`. - -Mingw builds with readline support now link `-ltermcap` instead of `-lcurses`. - -This should not effect sites which set explicitly set `COMMANDLINE_LIBRARY` -as the only definition in Base now has the form `COMMANDLINE_LIBRARY ?= ...`. - -### Perl CA support for empty long strings - -The Perl CA bindings have been fixed to handle zero-length long string data -properly. - -### `aao` gains `OMSL` and `DOL` - -The `aao` record types gains the same `DOL` functionality found -in other output record types (`ao`, `longout`, etc.) - -### Server exports `RSRV_SERVER_PORT` - -During `iocInit()`, the environment variable `RSRV_SERVER_PORT` is set -with the TCP port number selected. - -### `dbdExpand.pl` sorts all items by name - -DBD files generated by the `dbdExpand.pl` script are now sorted within each -item type by the primary name of the item. The result should resolve any -issues with reproducable builds. No option is provided to prevent the sorting, -previously the order was essentially random and varied each time. - -### `dbExpand.pl` sorts records by name - -Records are now output by this program in order, sorted by name. The new flag -`-s` can be given to output the records in the same order they were read in, -instead of sorting them. - -Note that there are currently no build rules provided with Base which make use -of this program. - -### Simulation Mode RAW Support for Output Record Types - -SIMM=RAW support has been added for the relevant output record types -(ao, bo, mbbo, mbboDirect). -RAW simulation mode will have those records do the appropriate conversion -and write RVAL to the location pointed to by SIOL. - -### Fixed leak from a non-EPICS thread - -On some targets, if a thread not created by `epicsThreadCreate*()` directly -or indirectly calls an `epicsThread*()` function, a specific tracking struct -is allocated. - -Prior to this release, on POSIX and WIN32 targets, this -struct would not be `free()`d, resulting in a memory leak. - -This release fixed the leak on POSIX targets. - -See the associated github [issue 241](https://github.com/epics-base/epics-base/issues/241) -for WIN32 status. - -### Fixed leak from a non-EPICS thread - -On some targets, if a thread not created by `epicsThreadCreate*()` directly -or indirectly calls an `epicsThread*()` function, a specific tracking struct -is allocated. - -Prior to this release, on POSIX and WIN32 targets, this -allocation would not be `free()`d, resulting in a memory leak. - -This release fixed the leak on POSIX and WIN32 targets (excluding -MSVC before vs2012, and the WINE runtime). - -### Fixed leak from a non-EPICS thread - -On some targets, if a thread not created by `epicsThreadCreate*()` directly -or indirectly calls an `epicsThread*()` function, a specific tracking struct -is allocated. - -Prior to this release, on POSIX and WIN32 targets, this -struct would not be `free()`d, resulting in a memory leak. - -This release fixed the leak on POSIX targets. - -See the associated github [issue 241](https://github.com/epics-base/epics-base/issues/241) -for WIN32 status. - -### Fix `CHECK_RELEASE = WARN` - -This now works again, it was broken in 2019 (7.0.3.1) by an errant commit. - -### Document `DISP` as design-time field - -The DISP field can be set to a non-zero value to prevent records being changed -from outside the IOC (this is ancient behavior), but has never been documented -as being usable at design-time (DCT=Yes in the Record Reference tables). This -has now been changed. - -### Make `epicsInt8` signed on all architectures - -The `epicsInt8` and thus `DBF_CHAR` types have always been unsigned on -architectures where `char` is unsigned, for example on many PowerPC CPU -architectures. This was counter-intuitive, and resulted in IOC behavior -differing between architectures when converting `DBF_CHAR` values into a -signed integer or floating point type. - -**WARNING**: This fix may change behavior of existing databases on target -architectures with unsigned `char` (mainly PowerPC) when using input links to -read from `CHAR` arrays. Architectures with signed `char` (usually x86) should -be unaffected, although some compilers might generate new warnings. - -### Allow hexadecimal and octal numbers in hardware links - -[GH:213](https://github.com/epics-base/epics-base/pull/213) - -Several types of hardware links (`VME_IO`, `CAMAC_IO`, etc) now accept -hexadecimal and octal numbers. (Hexadecimal numbers had already been valid -up to EPICS R3.15.) This change may introduce incompatibilities when using -numbers with leading `0` as they will now be parsed as octal. - -### Fix embedded implementations of `epicsEvent` - -[GH:202](https://github.com/epics-base/epics-base/issues/202) and -[GH:206](https://github.com/epics-base/epics-base/pull/206) - -Heinz Junkes provided a new implementation of the `epicsEvent` API suitable for -RTEMS Posix targets (RTEMS 5.1 and later). In review a few issues related to -overflow of timeout values surfaced in this and other embedded implementations, -and these were also been fixed in this Pull Request. The API documentation for -this and some other routines has also been updated. - -### Breakpoint Table Names - -The names of breakpoint tables were made unnecessarily strict when DBD file -processing was moved to Perl for the 3.15 release series. Table names may now -contain the special characters `_` `-` `:` `;` `.` `[` `]` `<` `>` in addition -to letters and digits. - -### Fix for `undefined` in configure/RELEASE files - -Prevents `Use of uninitialized value` warnings from convertRelease.pl. - -### Colorized Messages for errlog - -Many internal error messages now emit ANSI escape sequences to highlight the -words "ERROR" and "WARNING" in an attempt to make occurrences more noticeable -during IOC startup. - -The macros `ERL_ERROR` and `ERL_WARNING` are defined for external usage, -and expand as string constants. eg. - -```c -#include -#ifndef ERL_ERROR -# define ERL_ERROR "ERROR" -#endif -void fn() { - ... - errlogPrintf(ERL_ERROR ": something bad happens :(\n"); -``` - -ANSI escapes are automatically removed from errlog output not destined -for a terminal. For example, for logClient, if stderr is redirected, -or if unsupported (`$TERM` not set, or Windows < 10). - -### `dbnd` filter pass through `DBE_ALARM|DBE_PROPERTY` - -The `dbnd` server side filter now passes through alarm and property -change events, even when not exceeding the deadband. - ------ - -## EPICS Release 7.0.6.1 - -### `mbboDirectRecord` enhancements - -The bit fields `B0` - `B1F` of this record are now always updated and have a -monitor posted when the `VAL` field is set and the record processed. It is now -possible to initialize the record's value by setting the bit fields inside a -database file as long as no other method was used to initialize it (suc as -setting `VAL` directly, using `DOL`, or by an initial readback from device -support). A new internal field `OBIT` was added to store information about -monitors posted on the bit fields. - -### Minimum Perl Version is now 5.10.1 - -Some scripts now make use of features that were introduced to this Perl version -that was released in 2009. - -### DB Links to `DBF_MENU` fields fixed - -[GH:183](https://github.com/epics-base/epics-base/issues/183) -These were broken in a previous release, but now work again. - -### Long String access to CALC fields fixed - -[GH:194](https://github.com/epics-base/epics-base/issues/194) -This was broken in a previous release, but now works again. - -### Minor Changes - -+ Many code comments have been spell-checked and corrected. -+ Passing a `-DDEBUG` compiler flag no longer breaks the build. -+ Parallel builds of RTEMS-mvme2100 and RTEMS-mvme2700 targets now work. -+ Illegal characters seen in JSON strings in a database file should now get a -better error message. - -### Other Launchpad Bugs and GitHub Issues Fixed - -+ [lp:1938459](https://bugs.launchpad.net/epics-base/+bug/1938459) - [GH:191](https://github.com/epics-base/epics-base/pull/191) int64in only - checks lower 32 bits for change -+ [lp:1941875](https://bugs.launchpad.net/epics-base/+bug/1941875) Buggy - warning message "Record/Alias name '...' should not contain non-printable ... -+ [GH:187](https://github.com/epics-base/epics-base/issues/187) waveformRecord - missing PACT=true? -+ [GH:189](https://github.com/epics-base/epics-base/pull/189) Fix a couple - memory leaks and a segfault -+ [GH:200](https://github.com/epics-base/epics-base/pull/200) and - [GH:201](https://github.com/epics-base/epics-base/pull/201) Fix timers on MS - Windows for non-EPICS threads - -### Compiler interface for epicsAtomic tidied up - -[GH:192](https://github.com/epics-base/epics-base/pull/192) -Both GCC and CLANG compiler intrisics used for the epicsAtomic APIs have been revised; implementations using CLANG should now run faster as they now use the compiler's built-in atomic functions instead of taking a mutex. - -### The epicsTime code has been reimplemented - -[GH:185](https://github.com/epics-base/epics-base/pull/185) -This was done to simplify the code and may have improved performance slightly for some uses. Support for the old NTP-specific `struct l_fp` has been dropped but all other routines and methods of the `class epicsTime` function as before. - -### Updates to Record Reference documentation - -Many of the built-in record types have had improvements to their documentation with additional fields added to the tables, rewrites of descriptions and links to other documents added or fixed. - ------ - -## EPICS Release 7.0.6 - -### Support for obsolete architectures removed - -These target architectures have been removed: - -+ darwin-ppc, darwin-ppcx86 -+ linux-386, linux-486, linux-586, linux-686, linux-athlon (cross-build) -+ linux-cris, linux-cris_v10, linux-cris_v32 (cross-build) -+ RTEMS-at91rm9200ek, RTEMS-gen68360, RTEMS-mcp750, RTEMS-mvme167, -RTEMS-psim (cross-build) - -### Experimental Support for RTEMS 5 - -The new major release of the RTEMS real-time OS contains many changes -including the ability to support SMP systems. This release of EPICS -can still be built with RTEMS 4.9.x or 4.10.x and should work just -the same as earlier releases, although due to code having moved around -we recommend thorough testing before this release is first used in -production systems. - -This release of EPICS comes with support for several new RTEMS targets -running on RTEMS 5: - -- RTEMS-beagleboneblack -- RTEMS-pc686 -- RTEMS-qoriq_e500 (MVME2500) -- RTEMS-xilinx_zynq_a9_qemu -- RTEMS-xilinx_zynq_zedboard - -The EPICS support for RTEMS 4 has always relied on RTEMS-specific -kernel APIs which cannot be used on an SMP system, so a new port was -created to use the Posix real-time APIs that are now recommended for -RTEMS 5. Note that a single installation of EPICS cannot build both -RTEMS 4 and RTEMS 5 targets, if you need to support targets running -on both versions you must use a separate installation, and be sure -to run `make distclean` if switching a single source tree from one -to the other (both header files and dependency files are different -between the two and must be cleaned out). - -The configuration variable RTEMS_VERSION in the EPICS config file -`configure/os/CONFIG_SITE.Common.RTEMS` must be set to the full 3- -part version number for RTEMS 4 releases, e.g. `4.9.1`, `4.10.2` -but for RTEMS 5.1 and later it must only contain the major version -number e.g. `5`. - -Some RTEMS BSPs can be built and may work with the newer libbsd -network stack which RTEMS is moving over to, but most of the MVME -boards (and the uC5282) still require the legacy network stack. - -The dependency on bspExt has been removed, EPICS now provides its -own routine for VMEbus probing (or uses one built into the BSP). - -Anyone using this release on RTEMS is advised to discuss problems -building or running it on either the tech-talk or core-talk email -lists so the core developers can help with and find out about any -problems with the old or new port. - -Known Issues: -- MVME2100 and MVME2700 need changes to the RTEMS 5 BSP to build. -- VMEBus support is not yet available for the MVME2500 BSP. -- There are some known issues with floating point on MVME2500, - probably related to its newer e500 FPU. -- Changed network driver for beatnik to work with libbsd. Some - issues with DHCP, but network stack usable. Can load env from - NVRAM. - -### `epicsEnvShow` accepts a glob pattern - -The optional argument to epicsEnvShow can now be a glob pattern. - -### New function `epicsStrnGlobMatch()` - -The function `epicsStrnGlobMatch(char* str, size_t len, char* pattern)` -works exactly the same as `epicsStrGlobMatch()` but takes an additional -length arguments which limits the number of characters of `str` to match. - -### Automatic fallback to thread when unable to exec caRepeater - -A process using libca which does not find an existing caRepeater process -will attempt to start one by running the caRepeater executable. -This is not always possible, usually when caRepeater is not in `$PATH`. -Now, instead of printing a warning, an internal caRepeater thread -will be started (as is done be RTEMS and vxWorks targets). - -If this fallback occurs, the lifetime of the caRepeater thread -may be shorter than the lifetime of a separate caRepeater process -would have been. - -It remains the recommended practice to explicitly start a caRepeater -instance. Examples of both systemd (`caRepeater.service`) and sysv -(`S99caRepeater`) scripts may be found under `bin/`. - -### Glob pattern allowed in `var` command - -When used with one argument, the `var` command can be used with a glob pattern -for printing matching variables. - -### Formalize/fix `FINAL_LOCATION` - -The `FINAL_LOCATION` make variable has for some time been an undocumented -means of performing a staged build. This is a build which "installs" to -a temporary location, which will later be moved to a final location. - -This has now been added to `configure/CONFIG_SITE`. - -Usage analogous to the autotools recipe - -```sh -./configure --prefix=/usr/lib/epics -make install DESTDIR=/tmp/build -``` - -would be - -```sh -make INSTALL_LOCATION=/tmp/build FINAL_LOCATION=/usr/lib/epics -``` - -`FINAL_LOCATION` is now correctly used in systemd and sysv init scripts -`caRepeater.service`, `S99caRepeater`, and `S99logServer`. - -### IOCsh sets `${PWD}` - -IOC shell will now ensure `${PWD}` is set on startup, -and updated by the `cd` iocsh function. - -### Add Alarm Message and Time Tag Fields - -Two new fields have been added to `dbCommon` so will be present in all -records: `AMSG` and `UTAG`. - -#### `AMSG` - -`AMSG` can hold an arbitrary 40-character string, providing additional -information about the alarm condition indicated in `STAT` and `SEVR`. With no -alarm it will hold an empty string. The new `recGblSetSevrMsg()` function can -be used in place of `recGblSetSevr()` to signal an alarm while providing a -message. - -For example, a device support's `read_bi()` routine for a hypothetical -multi-channel ethernet attached device might flag a communication error -between the IOC and controller, or an error involving a certain channel like -this: - -```c -static long read_bi(biRecord* prec) { - ... - if (!priv->connected) { - recGblSetSevrMsg(prec, COMM_ALARM, INVALID_ALARM, - "No controller connected"); - return S_dev_noDevice; - } - if (!priv->err) { - recGblSetSevrMsg(prec, READ_ALARM, INVALID_ALARM, - "Channel %u disconnexted", priv->chan); - return S_dev_noDevice; - } - return status; -} -``` - -#### `UTAG` - -`UTAG` holds an `epicsUInt64` value which is semantically part of the record's -timestamp (`TIME`). The value defaults to zero if not explicitly set. Device -support or an event time provider which supports this feature may write a tag -value directly to the `dbCommon::utag` field. - -`TSEL` links will copy both `TIME` and `UTAG` between records if the link type -supports this (CA links do not). - -A `utag` server side channel filter has been added which can be configured to -filter out monitor updates which don't pass the test `(UTAG & M) == V` where -`M` and `V` are client specified integers. For example running the command -`camonitor BPM0:X.{utag:{M:1,V:1}}` will only show updates for which -`(UTAG & 1) == 1` i.e. the least significant bit of the `UTAG` field is set. - -This feature is intended for use by intelligent devices which can provide -contextual information along with a value/alarm/time. For example, a beam -diagnostic device which is aware of whether a beam signal should be present -(eg. from a global timing system). - -#### Link Support - -Two new optional methods have been added to the Link Support Entry Table -(`struct lset`): `lset::getAlarmMsg()` and `lset::getTimeStampTag()`. See -comments in dbLink.h for details on implementing these. - -Two new accessor functions have also been added which call these methods: -`dbGetAlarmMsg()` and `dbGetTimeStampTag()`. - -#### Compatibility - -User code wishing to call these interfaces while maintaining compatibility with older -versions of Base may add some of the following macro definitions, and ensure -that the variables referenced by output pointers are initialized. - -```c -#ifndef HAS_ALARM_MESSAGE -# define recGblSetSevrMsg(REC, STAT, SEVR, ...) recGblSetSevr(REC, STAT, SEVR) -#endif -#ifndef dbGetAlarmMsg -# define dbGetAlarmMsg(LINK, STAT, SEVR, BUF, BUFLEN) dbGetAlarm(LINK, STAT, SEVR) -#endif -#ifndef dbGetTimeStampTag -# define dbGetTimeStampTag(LINK, STAMP, TAG) dbGetTimeStamp(LINK, STAMP) -#endif -``` - - -### Timeouts for Unit Test Programs - -The unit test programs that are run by the `make runtests` or `make tapfiles` -commands get executed by a `.t` wrapper script which is normally generated by -the EPICS `makeTestfile.pl` program. Those generated wrapper scripts now -impose a time-limit on the test program they execute, and will kill it if it -runs for longer than 500 seconds (8 minutes 20) without exiting. That -time-limit can be changed for any such test by modifying the Makefile which -creates and runs the `.t` wrapper script. - -Setting the environment variable `EPICS_UNITTEST_TIMEOUT` to the desired -number of seconds while the Makefile is generating the test script changes the -timeout in that script. For example: - -``` - TESTSCRIPTS_HOST += hourLongTest.t - hourLongTest.t: export EPICS_UNITTEST_TIMEOUT=3600 -``` - -When selecting such a timeout remember that different Continuous Integration -systems such as GitHub Actions and Appveyor run on processors with different -speeds, so allow enough head-room for slower systems to complete the test. - -Test programs written directly in Perl as a `.plt` script should implement a -similar timeout for themselves. The "netget" test in Base does this in a way -that works on Windows as well as Unix-like hosts. - ------ - -## EPICS Release 7.0.5 - -### Fix aai's Device Support Initialization - -Krisztian Loki [reported](https://github.com/epics-base/epics-base/issues/97) -segfaults occurring when a Soft Channel aai record INP field was a DB link to -an array field of a compress record. This was caused by the aai record's -pass-0 device support initialization clashing with the semantics of the new -link support API. - -The aai record -[has been modified](https://github.com/epics-base/epics-base/pull/114) to -allow the Soft Channel device support to request a pass-1 initialization -callback. See the Device Support section of the Array Analogue Input Record -Reference pages in this release for the API changes, which are fully backwards -compatible for existing aai device support. - -### Prevent default DTYPs from changing - -[Kay Kasemir reported](https://bugs.launchpad.net/epics-base/+bug/1908305) that -it is possible to change the Base record type's default DTYP if a `device()` -entry is seen before the `recordtype()` definition to which it refers. The -default DTYP is the first device loaded, which is normally the `Soft Channel` -support from Base. A warning was being displayed by dbdExpand when a `device()` -entry was see first, but that was easily missed. - -The DBD file parser in dbdExpand.pl has now been modified to make this an error, -although the registerRecordDeviceDriver.pl script will still accept `device()` -entries without having their `recordtype()` loaded since this is necessary to -compile device supports as loadable modules. - - -### Priority inversion safe Posix mutexes - -On Posix systems, epicsMutex now support priority inheritance if available. -The IOC needs to run with SCHED_FIFO engaged to use these. -Support for Posix implementations before POSIX.1-2001 (`_XOPEN_SOURCE < 500`, -glibc version < 2.3.3) has been dropped. - -The IOC shell's `epicsMutexShowAll` command prints "PI is enabled" if both -libc and kernel support is present. - -### Fix for Periodic Scan threads hanging on Windows - -Since 7.0.3.1 a Windows IOC could not run for more than 49.7 days; at that -time the periodic scan threads would stop processing. This issue should now -have been fixed and the Monotonic time functions on Windows should return -values which count at nanosecond resolution. However we have not waited 49.7 -days to test the final software, so there is a small chance that it's still -broken. - -This fixes [lauchpad bug #1896295](https://bugs.launchpad.net/bugs/1896295). - -### Support for Apple M1/M2 (arm64) Processors - -Thanks to Jeong Han Lee this release comes with build support for Apple's new -M1/M2 CPUs running macOS, using the target name `darwin-aarch64`. - -It should also be possible to build universal binaries containing code for -both the Intel and arm64 processors under either target name: In the -appropriate `configure/os/CONFIG_SITE.Common.darwin-*` file add the other -architecture class name to the `ARCH_CLASS` variable (after a space). - -### New String Comparison Routine `epicsStrSimilarity()` - -The new `epicsStrSimilarity()` routine in epicsString.h uses a modified -Levenshtein distance to compare two strings, with a character case difference -being half the weight of a full substitution. The double return value falls in -the range 0.0 (identical) through 1.0 (no characters matching), or -1.0 for -error. This is used to provide a new "Did you mean ..." suggestion when a .db -file provides an invalid choice string for a `DBF_MENU` or `DBF_DEVICE` field. - -### Build System: New `VALID_BUILDS` type "Command" - -Target architectures that support command-line programs that run the `main()` -routine can now be marked as such in their `VALID_BUILDS` definition. This -enables a new set of Makefile target variables `PROD_CMD` (similar to -`PROD_HOST`), `LIBRARY_CMD` (like `LIBRARY_HOST`, etc.), `LOADABLE_LIBRARY_CMD`, -`OBJS_CMD`, `SCRIPTS_CMD`, `TARGETS_CMD`, `TESTLIBRARY_CMD`, `TESTSCRIPTS_CMD` -and `TESTPROD_CMD`. The CA client tools and programs such as `caRepeater` are now built for all such targets (previously they were built for all targets except where the OS was VxWorks, RTEMS and iOS). - -If you have created your own site-specific target architectures you may need to -update the `VALID_BUILDS` variable if it gets set in your locally added -`configure/os/CONFIG.Common.` files. This is usually only needed for -cross-compiled targets though since `CONFIG.Common.UnixCommon` sets it. - -The other `VALID_BUILDS` types are "Host" for target architectures that can -compile and run their own programs (`PROD_HOST` etc.), and "Ioc" for targets -that can run IOCs (`PROD_IOC` etc.). - -### Support for JSON5 - -The YAJL parser and generator routines in libcom and in the IOC's dbStatic -parser now support the JSON5 standard. This adds various features to JSON -without altering the API for the code other than adding a new option to the -YAJL parser which can be used to disable JSON5 support if desired. The new -features include: - -- The ability to handle numeric values `Infinity`, `-Infinity` and `NaN`. -- String values and map keys may be enclosed in single quotes `'`, inside which - the double-quote character `"` doesn't have to be escaped with a back-slash - `\`, although a single-quote character `'` (or apostrophy) must be escaped - inside a single-quoted string. -- Numbers may start with a plus sign, `+`. -- Integers may be expressed in hexadecimal with a leading `0x` or `0X`. -- Floating-point numbers may start or end with their decimal point `.` - (after the sign or before the exponent respectively if present). -- Map keys that match the regex `[A-Za-z_][A-Za-z_0-9]*` don't have to be - enclosed in quotes at all. The dbStatic parser adds `.+-` to the characters - allowed but will add quotes around such keys before passing them to YAJL. -- Arrays and maps allow a comma before the closing bracket/brace character. -- The YAJL parser will elide a backslash followed by a newline characters from - a string value. The dbStatic parser doesn't allow that however. - -Code that must also compile against the older API can use the new C macro -`HAS_JSON5` to detect the new version. This macro is defined on including -either the `yajl_parse.h` or `yajl_gen.h` headers, which also provide the -new configuration options to turn on JSON5 support. - -All APIs in the IOC that previously accepted JSON will now accept JSON5. -This includes JSON field modifiers (channel filters), JSON link addresses, -constant input link array values and database info-tag values. JSON values -that get parsed by the dbLoadRecords() routine are still more liberal than -the other uses as the ability to use unquoted strings that was called -"relaxed JSON" is still supported, whereas the JSON5 standard and the YAJL -parser only allow unquoted strings to be used for keys in a JSON map. - -This also fixes [lauchpad bug #1714455](https://bugs.launchpad.net/bugs/1714455). - - -### Character Escape Changes - -- The libCom routines `epicsStrnRawFromEscaped()` and `dbTranslateEscape()` - declared in epicsString.h no longer accept octal escaped characters such as - `\123` or `\41`. -- The routine `epicsStrnEscapedFromRaw()` now generates hex - excaped characters for unprintable characters such as `\x1f`. -- Hex escape character sequences `\xXX` must now contain exactly 2 hex digits. -- An escape sequence `\0` now generates a zero byte in the raw string, but the - other digits `1-9` should not appear after a back-slash. - -These changes are to more closely follow the JSON5 standard, which doesn't -support octal character escapes or the `\a` (Bel, `\x07`) escape sequence. - -### Filters in database input links - -Input database links can now use channel filters, it is not necessary to -make them CA links for the filters to work. - -### ai Soft Channel support - -The Soft Channel device support for ai records now returns failure when -fetching the INP link fails. - -### Support for zero-length arrays - -Several modifications have been made to properly support zero-length -array values inside the IOC and over Channel Access. Some of these changes -may affect external code that interfaces with the IOC, either directly or -over the CA client API so we recommend thorough testing of any external -code that handles array fields when upgrading to this release. - -Since these changes affect the Channel Access client-side API they will -require rebuilding any CA Gateways against this version or Base to -properly handle zero-length arrays. The `caget`, `caput` and `camonitor` -client programs are known to work with empty arrays as long as they were -built with this or a later version of EPICS. - -#### Change to the db_access.h `dbr_size_n(TYPE, COUNT)` macro - -When called with COUNT=0 this macro no longer returns the number of bytes -required for a scalar (1 element) but for an empty array (0 elements). -Make sure code that uses this doesn't call it with COUNT=0 when it really -means COUNT=1. - -Note that the db_access.h header file is included by cadef.h so the change -can impact Channel Access client programs that use this macro. - -#### Channel Access support for zero-length arrays - -The `ca_array_put()` and `ca_array_put_callback()` routines now accept an -element count of zero, and will write a zero-length array to the PV if -possible. No error will be raised if the target is a scalar field though, -and the field's value will not be changed. - -The `ca_array_get_callback()` and `ca_create_subscription()` routines -still accept a count of zero to mean fetch as many elements as the PV -currently holds. - -Client programs should be prepared for the `count` fields of any -`struct event_handler_args` or `struct exception_handler_args` passed to -their callback routines to be zero. - -#### Array records - -The soft device support for the array records aai, waveform, and subArray -as well as the aSub record type now correctly report reading 0 elements -when getting an empty array from an input link. - -#### Array support for dbpf - -The dbpf command now accepts array values, including empty arrays, when -provided as a JSON string. This must be enclosed in quotes so the iocsh -argument parser sees the JSON as a single argument: - -``` -epics> dbpf wf10:i32 '[1, 2, 3, 4, 5]' -DBF_LONG[5]: 1 = 0x1 2 = 0x2 3 = 0x3 4 = 0x4 5 = 0x5 -``` - -#### Reading empty arrays as scalar values - -Record links that get a scalar value from an array that is currently -empty will cause the record that has the link field to be set to an -`INVALID/LINK` alarm status. -The record code must call `dbGetLink()` with `pnRequest=NULL` for it to -be recognized as a request for a scalar value though. - -This changes the semantics of passing `pnRequest=NULL` to `dbGetLink()`, -which now behaves differently than passing it a pointer to a long integer -containing the value 1, which was previously equivalent. -The latter can successfully fetch a zero-element array without triggering -a LINK alarm. - -#### Writing empty arrays to scalar fields - -Record links that put a zero-element array into a scalar field will now set -the target record to `INVALID/LINK` alarm without changing the field's value. -Previously the field was set to 0 in this case (with no alarm). -The target field must be marked as `special(SPC_DBADDR)` to be recognized -as an array field, and its record support must define a `put_array_info()` -routine. - -### Timestamp before processing output links - -The record processing code for records with output links has been modified to -update the timestamp via recGblGetTimeStamp() _before_ processing the output -links. This ensures that other records which get processed via an output link -can use TSEL links to fetch the timestamp corresponding to the data processed -by the output link. - -This change could result in a slightly earlier timestamp for records whose -output link is handled by a device driver, but only if the device driver does -not handle its own timestamping via TSE -2 and instead uses TSE 0 or TSE -1 to -get current time or best time, and the time spent in the device driver is -greater than your timestamp provider resolution. For these situations it is -recommended to set TSE to -2 and set the timestamp in the driver code. - -### Add registerAllRecordDeviceDrivers() - -A new iocsh command `registerAllRecordDeviceDrivers` is provided and also -defined as a function in iocshRegisterCommon.h. This uses dynamic symbol -lookup with `epicsFindSymbol()` to perform the same function as a generated -`*_registerRecordDeviceDriver()` function. This allows for an alternative -approach to dynamic loading of support modules without code generation. - -This feature is not intended for use by IOCs constructed using the standard -EPICS application build process and booted from a startup script in an iocBoot -subdirectory, although it might work in some of those cases — the -generated registerRecordDeviceDriver.cpp file is normally required to link -everything referred to in the DBD file into the IOC's executable. It also -won't work with some static build configurations, or if the symbol table has -been stripped from the executable. - -### Using a `{const:"string"}` to initialize an array of `DBF_CHAR` - -It is now possible to use a JSON Const link with a string value to initialize -an aai or waveform record that has `FTVL` set to `CHAR` through the INP link. -The string length is not limited to 40 characters. This should also work for -aSub record inputs similarly configured as long strings. - -``` - record(waveform, "wf") { - field(NELM, 100) - field(FTVL, CHAR) - field(INP, {const:"This is a waveform and more than 40 characters"}) - } -``` - -### RELEASE files may use `undefine` - -GNUmake added the directive `undefine` in version 3.82 to allow variables to -be undefined. Support for this has been added to the EPICS Release file parser, -so `undefine` can now be used in configure/RELEASE files to unset variables. - ------ - -## EPICS Release 7.0.4.1 - -### ARM Architecture Changes - -Build configuration files for a new cross-build architecture `linux-aarch64` -have been added, and the targets `linux-arm_el` and `linux-arm_eb` removed. -The 64-bit ARM architecture target doesn't have build files for self-hosting -yet but they should be relatively easy to add, contributions welcome! - -### Bug fixes - -The following bugs/issues have fixes included in this release: - -- [lp: 1884339](https://bugs.launchpad.net/epics-base/+bug/1884339), - Inaccessible CA servers on Windows -- [github: 83](https://github.com/epics-base/epics-base/issues/83) - osdTimeGetCurrent doesn't work for subprocess on macOS -- Recent Cygwin build problem with a missing `TCP_NODELAY` declaration. - -### Perl CA Bindings under Conda - -Builds of the Perl CA bindings weren't working properly when the Perl -installation was from Conda. This release also fixed the capr.pl script -to handle the INT64 data types, and to be able to properly handle missing -fields, as happens if the IOC is running an older EPICS version for example. - -### epicsMessageQueue implementation on RTEMS - -The implementation of the `epicsMessageQueue` used on RTEMS has switched from -the native RTEMS-specific one to the EPICS generic version, avoiding a bug -in the RTEMS Kernel message queue code. - -### Record Name Validation - -Historically, there have been very few restrictions on which characters -may be present in record and alias names. Base 3.14.12.3 added a warning -for names containing space, single or double quote, period/dot, or -dollar sign. - -``` -Bad character ' ' in record name "bad practice" -``` - -7.0.4.1 Turns this warning into an error, and adds a new warning -if a record name begins with a minus, plus, left square bracket, -or left curly bracket. - ------ - -## EPICS Release 7.0.4 - -### Bug fixes - -The following launchpad bugs have fixes included in this release: - -- [lp: 1812084](https://bugs.launchpad.net/bugs/1812084), Build failure on - RTEMS 4.10.2 -- [lp: 1829919](https://bugs.launchpad.net/bugs/1829919), IOC segfaults when - calling dbLoadRecords after iocInit -- [lp: 1838792](https://bugs.launchpad.net/bugs/1838792), epicsCalc bit-wise - operators on aarch64 -- [lp: 1853148](https://bugs.launchpad.net/bugs/1853148), mingw compiler - problem with printf/scanf formats -- [lp: 1852653](https://bugs.launchpad.net/bugs/1852653), USE_TYPED_DSET - incompatible with C++ -- [lp: 1862328](https://bugs.launchpad.net/bugs/1862328), Race condition on - IOC start leaves rsrv unresponsive -- [lp: 1866651](https://bugs.launchpad.net/bugs/1866651), thread joinable race -- [lp: 1868486](https://bugs.launchpad.net/bugs/1868486), epicsMessageQueue - lost messages -- [lp: 1868680](https://bugs.launchpad.net/bugs/1868680), Access Security file - reload (asInit) fails - -### \*_API macros in EPICS headers - -Internally, the Com and ca libraries now express dllimport/export (Windows) -and symbol visibility (GCC) using library-specific macros (eg. `LIBCOM_API`) -instead of the macros `epicsShareFunc`, `epicsShareClass`, `epicsShareDef` etc. -that are defined in the `shareLib.h` header. -This change may affect some user code which uses the `epicsShare*` macros -without having explicitly included the `shareLib.h` header themselves. -Such code should be changed to include `shareLib.h` directly. - -A new helper script `makeAPIheader.pl` and build rules to generate a -library-specific `*API.h` header file has been added. Run `makeAPIheader.pl -h` -for information on how to use this in your own applications, but note that the -resulting sources will not be able to be compiled using earlier versions of -EPICS Base. - -### IOCsh usage messages - -At the iocShell prompt `help ` now prints a descriptive usage message -for many internal IOCsh commands in addition to the command parameters. -Try `help *` to see all commands, or a glob pattern such as `help db*` to see -a subset. - -External code may provide usage messages when registering commands using a -new `const char *usage` member of the `iocshFuncDef` structure. -The `iocsh.h` header also now defines a macro `IOCSHFUNCDEF_HAS_USAGE` which -can be used to detect Base versions that support this feature at compile-time. - -### Variable names in RELEASE files - -`configure/RELEASE` files are parsed by both GNUmake and the `convertRelease.pl` -script. While GNUmake is quite relaxed about what characters may be used in a -RELEASE variable name, the `convertRelease.pl` script parser has only recognized -variable names that match the Perl regular expression `\w+`, i.e. upper and -lower-case letters, digits and underscore characters. - -The script has been modified so now RELEASE variable names must start with a -letter or underscore, and be followed by any number of letters, digits, -underscore or hyphen characters, matching the regular expression -`[A-Za-z_][A-Za-z_0-9-]*`. The hyphen character `-` was not previously allowed -and if used would have prevented a build from finding include files and -libraries in any module using that in its RELEASE variable name. - -This change does disallow names that start with a digit which used to be -allowed, but hopefully nobody has been relying on that ability. The regular -expression used for names can be found in the file `src/tools/EPICS/Release.pm` -and can be adjusted locally if necessary. - -### caRepeater /dev/null - -On \*NIX targets caRepeater will now partially daemonize by redirecting -stdin/out/err to /dev/null. This prevents caRepeater from inheriting -the stdin/out of a process, like caget, which has spawned it in the -background. This has been known to cause problems in some cases when -caget is itself being run from a shell script. - -caRepeater will now understand the `-v` argument to retain stdin/out/err -which may be necessary to see any error messages it may emit. - -### `state` record deprecated - -IOCs now emit a warning when a database file containing the `state` record is -loaded. This record has been deprecated for a while and will be removed -beginning with EPICS 7.1. Consider using the `stringin` record instead. - -### Record types publish dset's - -The record types in Base now define their device support entry table (DSET) -structures in the record header file. While still optional, developers of -external support modules are encouraged to start converting their code to use -the record's new definitions instead of the traditional approach of copying the -structure definitions into each source file that needs them. By following the -instructions below it is still possible for the converted code to build and -work with older Base releases. - -This would also be a good time to modify the device support to use the type-safe -device support entry tables that were introduced in Base-3.16.2 -- see -[this entry below](#type-safe-device-and-driver-support-tables) for the -description of that change, which is also optional for now. - -Look at the aiRecord for example. Near the top of the generated `aiRecord.h` -header file is a new section that declares the `aidset`: - -```C -/* Declare Device Support Entry Table */ -struct aiRecord; -typedef struct aidset { - dset common; - long (*read_ai)(struct aiRecord *prec); - long (*special_linconv)(struct aiRecord *prec, int after); -} aidset; -#define HAS_aidset -``` - -Notice that the common members (`number`, `report()`, `init()`, `init_record()` -and `get_ioint_info()` don't appear directly but are included by embedding the -`dset common` member instead. This avoids the need to have separate definitions -of those members in each record dset, but does require those members to be -wrapped inside another set of braces `{}` when initializing the data structure -for the individual device supports. It also requires changes to code that -references those common members, but that code usually only appears inside the -record type implementation and very rarely in device supports. - -An aiRecord device support that will only be built against this or later -versions of EPICS can now declare its dset like this: - -```C -aidset devAiSoft = { - { 6, NULL, NULL, init_record, NULL }, - read_ai, NULL -}; -epicsExportAddress(dset, devAiSoft); -``` - -However most device support that is not built into EPICS itself will need to -remain compatible with older EPICS versions, which is why the ai record's header -file also declares the preprocessor macro `HAS_aidset`. This makes it easy to -define the `aidset` in the device support code when it's needed, and not when -it's provided in the header: - -```C -#ifndef HAS_aidset -typedef struct aidset { - dset common; - long (*read_ai)(aiRecord *prec); - long (*special_linconv)(aiRecord *prec, int after); -} aidset; -#endif -aidset devAiSoft = { - { 6, NULL, NULL, init_record, NULL }, - read_ai, NULL -}; -epicsExportAddress(dset, devAiSoft); -``` - -The above `typedef struct` declaration was copied directly from the new -aiRecord.h file and wrapped in the `#ifndef HAS_aidset` conditional. - -This same pattern should be followed for all record types except for the lsi, -lso and printf record types, which have published their device support entry -table structures since they were first added to Base but didn't previously embed -the `dset common` member. Device support for these record types therefore can't -use the dset name since the new definitions are different from the originals and -will cause a compile error, so this pattern should be used instead: - -```C -#ifndef HAS_lsidset -struct { - dset common; - long (*read_string)(lsiRecord *prec); -} -#else -lsidset -#endif -devLsiEtherIP = { - {5, NULL, lsi_init, lsi_init_record, get_ioint_info}, - lsi_read -}; -``` - ------ - -## EPICS Release 7.0.3.1 - -**IMPORTANT NOTE:** *Some record types in this release will not be compatible -with device support binaries compiled against earlier versions of those record -types, because importing the record documentation from the EPICS Wiki -[as described below](#imported-record-reference-documentation-from-wiki) -also modified the order of some of the fields in the record definitions.* -As long as all support modules and IOCs are rebuilt from source after updating -them to use this release of EPICS Base, these changes should not have any -affect. - - -### logClient reliability - -On supported targets (Linux, Mac, Windows) logClient will attempt to avoid dropping -undelivered log messages when the connection to the log server is closed/reset. - -### Timers and delays use monotonic clock - -Many internal timers and delay calculations use a monotonic clock -epicsTimeGetMonotonic() instead of the realtime epicsTimeGetCurrent(). This is -intended to make IOCs less susceptible to jumps in system time. - -### Iocsh `on error ...` - -A new statement is added to enable IOC shell commands to signal error -conditions, and for scripts to respond. This first is through the new function - -```C - int iocshSetError(int err); -``` - -A script may be prefixed with eg. "on error break" to stop at the failed -command. - -```sh - on error continue | break | wait [value] | halt -``` - -A suggested form for IOC shell commands is: - -```C - static void doSomethingCallFunc(const iocshArgBuf *args) - { - iocshSetError(doSomething(...)); /* return 0 == success */ - } -``` - -### Relocatable Builds - -Allows built trees to be copied or moved without invalidating RPATH entires. - -The `LINKER_USE_RPATH` Makefile variable (see `configure/CONFIG_SITE`) may be -set to `YES`, `NO`, and a new third option `ORIGIN`. This is limited to -targets using the ELF executable format (eg. Linux). - -When `LINKER_USE_RPATH=ORIGIN`, the variable `LINKER_ORIGIN_ROOT` is set to -one of the parents of the build directory. Any libraries being linked -to which are found under this root will have a relative RPATH entry. -Other libraries continue to result in absolute RPATH entries. - -An effect of this might change a support library from being linked with -`-Wl,-rpath /build/epics-base/lib/linux-x86` -to being linked with -`-Wl,-rpath \$ORIGIN/../../../epics-base/lib/linux-x86` -if the support module directory is `/build/mymodule` -and `LINKER_ORIGIN_ROOT=/build`. - -The API functions `epicsGetExecDir()` and `epicsGetExecName()` are also -added to `osiFileName.h` to provide runtime access to the directory or -filename of the executable with which the process was started. - -### Decouple LINKER_USE_RPATH and STATIC_BUILD - -Previously, setting `STATIC_BUILD=NO` implied `LINKER_USE_RPATH=NO`. -This is no longer the case. Setting `LINKER_USE_RPATH=YES` will -always emit RPATH entries. This was found to be helpful when linking -against some 3rd party libraries which are only available as shared objects. - -### Channel Access Security: Check Hostname Against DNS - -Host names given in a `HAG` entry of an IOC's Access Security Configuration -File (ACF) have to date been compared against the hostname provided by the CA -client at connection time, which may or may not be the actual name of that -client. This allows rogue clients to pretend to be a different host, and the -IOC would believe them. - -An option is now available to cause an IOC to ask its operating system to look -up the IP address of any hostnames listed in its ACF (which will normally be -done using the DNS or the `/etc/hosts` file). The IOC will then compare the -resulting IP address against the client's actual IP address when checking -access permissions at connection time. This name resolution is performed at -ACF file load time, which has a few consequences: - - 1. If the DNS is slow when the names are resolved this will delay the process -of loading the ACF file. - - 2. If a host name cannot be resolved the IOC will proceed, but this host name -will never be matched. - - 3. Any changes in the hostname to IP address mapping will not be picked up by -the IOC unless and until the ACF file gets reloaded. - -Optionally, IP addresses may be added instead of, or in addition to, host -names in the ACF file. - -This feature can be enabled before `iocInit` with - -``` - var("asCheckClientIP",1) -``` - -or with the VxWorks target shell use - -```C - asCheckClientIP = 1 -``` - -### New and modified epicsThread APIs - -#### `epicsThreadCreateOpt()` - -A new routine `epicsThreadCreateOpt()` is an alternative to -`epicsThreadCreate()` which takes some arguments via a structure (`struct -epicsThreadOpts`) to allow for future extensions. - -```C - typedef struct epicsThreadOpts { - unsigned int priority; - unsigned int stackSize; - unsigned int joinable; - } epicsThreadOpts; - #define EPICS_THREAD_OPTS_INIT { \ - epicsThreadPriorityLow, epicsThreadStackMedium, 0} - epicsThreadId epicsThreadCreateOpt(const char * name, - EPICSTHREADFUNC funptr, void * parm, const epicsThreadOpts *opts); -``` - -The final `opts` parameter may be `NULL` to use the default values of thread -priority (low) and stack size (medium). Callers wishing to provide alternative -settings for these thread options or to create a joinable thread (see below) -should create and pass in an `epicsThreadOpts` structure as shown below. -Always initialize one of these structures using the `EPICS_THREAD_OPTS_INIT` -macro to ensure that any additional fields that get added in the future are -set to their default values. - -```C - void startitup(void) { - epicsThreadOpts opts = EPICS_THREAD_OPTS_INIT; - epicsThreadId tid; - - opts.priority = epicsThreadPriorityMedium; - tid = epicsThreadCreateOpt("my thread", &threadMain, NULL, &opts); - } -``` - -C or C++ Code that also needs to build on earlier versions of Base can use -`#ifdef EPICS_THREAD_OPTS_INIT` to determine whether the -`epicsThreadCreateOpt()` API is available on this Base version. - -#### Thread stack sizes - -The `stackSize` member of the `epicsThreadOpts` structure and the equivalent -parameters to the `epicsThreadCreate()` and `epicsThreadMustCreate()` routines -can now be passed either one of the `epicsThreadStackSizeClass` enum values or -a value returned from the `epicsThreadGetStackSize()` routine. - -#### `epicsThreadMustJoin()` - -If the new `joinable` flag of an `epicsThreadOpts` structure is non-zero (the -default value is zero), the new API routine `epicsThreadMustJoin()` *must* be -called with the thread's `epicsThreadId` when/after the thread exits, to free -up thread resources. This function will block until the thread's main function -has returned, allowing the parent to wait for its child thread. The child's -`epicsThreadId` will no longer be valid and should not be used after the -`epicsThreadMustJoin()` routine returns. - -A thread that was originally created with its joinable flag set may itself -call `epicsThreadMustJoin()`, passing in its own epicsThreadId. This marks the -thread as no longer being joinable, so it will then free the thread resources -itself when its main function returns. The `epicsThreadId` of a thread that is -not joinable gets invalidated as soon as its main function returns. - -### Non-VME RTEMS targets now define pdevLibVME - -Previously IOC executables that made calls to devLib routines would fail to -link when built for some non-VME based RTEMS targets, which would have to be -explicitly filtered out by sites that build Base for those targets. [This -fix](https://bugs.launchpad.net/epics-base/+bug/1841692) makes that no longer -necessary, all RTEMS targets should now link although the IOC won't be able to -be used with the VME I/O on those systems (that we don't have VMEbus I/O -support for in libCom). - ------ - -## EPICS Release 7.0.3 - -### `epicsTimeGetCurrent()` optimization - -Add a fast path to epicsTimeGetCurrent() and related calls in the common case -where only the default OS current time provider is registered. This path does -not take the global mutex guarding the time providers list, potentially -reducing lock contention. - -### dbEvent tweak Queue size - -The size of the queue used by dbEvent to push monitor updates has been -slightly increased based on `DBR_TIME_DOUBLE` to better fill an ethernet frame. -This may result in slightly fewer, but larger frames being sent. - -### mbbo/mbbiDirect number of bits as precision - -Report NOBT as "precision" through the dbAccess API. This is not accessible -through CA, but is planned to be used through QSRV. - ------ - -## EPICS Release 7.0.2.2 - -### Build System changes - - * The GNUmake build targets `cvsclean` and `depclean` are now available from -any directory; previously they were only available from application top -directories. - - * The approach that EPICS Base uses for building submodules inside the parent -module looks useful for support modules too. The rules for building submodules -have been modified and extracted into a new `RULES_MODULES` file, so a support -module will be able to use them too without having to copy them into its own -`modules/Makefile`. There are some specific requirements that support modules -and their submodules must follow, which are described as comments in the new -`base/configure/RULES_MODULES` file itself. - -### `EPICS_BASE_VERSION` Update Policy change - -In the past, a build of EPICS using sources checked out from the repository -branch between official releases would have shown the version number of the -previous release, followed by a -DEV suffix, for example 7.0.2.1-DEV. - -The policy that controls when the number gets updated has been changed, and -now immediately after a release has been tagged the version number will be -updated to the next patch release version, plus the -DEV suffix as before. -Thus following 7.0.2.2 the version number will show as 7.0.2.3-DEV. This does -not require the next official release to be numbered 7.0.2.3 though, it could -become 7.0.3 or even 7.1.0 if the changes incorporated into it are more -substantial than bug fixes. - -### Drop `CLOCK_MONOTONIC_RAW` from posix/osdMonotonic.c - -Turns out this is ~10x slower to query than `CLOCK_MONOTONIC`. - ------ - -## EPICS Release 7.0.2.1 - -### Linking shared libraries on macOS - -The linker flag `-flat_namespace` has been restored for creating shared -libraries, although not for loadable libraries (bundles). This was required -for building using the latest versions of Apple XCode. - -### Fix `DB_LINK` loop breaking - -A regression was introduced in 7.0.2 which caused record chains with loops to -be incorrectly broken. Processing should be skipped when a `DB_LINK` with -Process Passive (PP) closes a loop to a synchronous record. - -Instead in 7.0.2 the targeted record would be processed if processing began -with a remote action (or some other caller of `dbPutField()`). This would -result in the loop running a second time. The loop would be broken on the -second iteration. - -[See lp: #1809570](https://bugs.launchpad.net/epics-base/+bug/1809570) - -### Old dbStaticLib APIs removed - -Support for some obsolete dbStaticLib Database Configuration Tool (DCT) APIs -was removed some time ago, but vestiges of them still remained. The following -routines and macros and have now finally been removed: - - * `int dbGetFieldType(DBENTRY *pdbentry)` - * `int dbGetLinkType(DBENTRY *pdbentry)` - * `DCT_STRING` - * `DCT_INTEGER` - * `DCT_REAL` - * `DCT_MENU` - * `DCT_MENUFORM` - * `DCT_INLINK` - * `DCT_OUTLINK` - * `DCT_FWDLINK` - * `DCT_NOACCESS` - * `DCT_LINK_CONSTANT` - * `DCT_LINK_FORM` - * `DCT_LINK_PV` - -### Fix for `dbhcr` before `iocInit` - -The `dbhcr` command used to work before `iocInit` as well as afterwards. It -displays all records that have hardware addresses (`VME_IO`, `CAMAC_IO`, -`GPIB_IO`, `INST_IO` etc.) but stopped working if run before iocInit due to the -rewrite of the link address parser code in dbStaticLib. This release fixes that -issue, although in some cases the output may be slightly different than it used -to be. - ------ - -## EPICS Release 7.0.2 - -### Launchpad Bugs - -The list of tracked bugs fixed in this release can be found on the -[Launchpad Milestone page for EPICS Base 7.0.2](https://launchpad.net/epics-base/+milestone/7.0.2). - -### Git Branches Recombined - -The four separate Git branches `core/master`, `libcom/master`, `ca/master` and -`database/master` have been recombined into one branch called `7.0`. Keeping -these as 4 separate branches in the same repository made it impossible to -create merge requests that contained changes in more than one of these -modules. The layout of the source files has not changed at all however, so the -source code for libcom, ca and the database are still found separately under -the module subdirectory. - ------ - -## EPICS Release 7.0.1.1 - -### Changed SIML failure behavior - -A failure when fetching the simulation mode through `SIML` will not put the -record into INVALID alarm state anymore. Instead, as long as the record's -current alarm severity (`SEVR`)is `NO_ALARM`, its alarm status (`STAT`) will be -set to `LINK_ALARM` without increasing the severity. This allows clients to get -some notification of a failing or bad `SIML` link without otherwise affecting -record processing. - -### `dbVerify()` has been restored to dbStaticLib - -This routine was removed in Base-3.16.1 but has been reimplemented in this -release by special request. Note that the error message strings that it -returns when verification fails have changed, but are still designed for -display to the user. - -### Simulation mode improvements - -Records that support simulation mode have two new fields, `SSCN` (Simulation -Scan Mode) and `SDLY` (Simulation Delay). `SSCN` is a menu field that provides -an alternate value for the `SCAN` field to be used while the record is in -simulation mode. This is especially useful for I/O scanned records, for which -simulation mode was not working at all. Setting `SDLY` to a positive value -makes the record process asynchronously in simulation mode, with the second -stage processing happening after the specified time (in seconds). - -### Extend the dbServer API with init/run/pause/stop methods - -This change permits IOCs to be built that omit the CA server (RSRV) by -removing its registrar entry which is now provided in the new `rsrv.dbd` file. -Other server layers can be built into the IOC (alongside RSRV or in place of -it) by registering them in a similar manner. The dbServer API is documented -with Doxygen comments in the header file. - -Specific IOC server layers can be disabled at runtime by adding their name to -the environment variable `EPICS_IOC_IGNORE_SERVERS` (separated by spaces if more -than one should be ignored). - -### Grand source-code reorganization - -EPICS 7.0.1 contains the IOC Database, RSRV server and the Channel Access -client code from EPICS Base 3.16.1 along with all the original record types -and soft device support, but GDD and the Portable Channel Access Server have -been unbundled and are now available separately. In their place we have -brought in the more recently written EPICS V4 C++ libraries (collectively -referred to as the PVA modules). The directory tree for EPICS is somewhat -larger as a result, and the original structure of the Base directories has -been split into 4 separate Git repositories. External modules should build -against this new structure with little or no changes needed, except that some -allowance may be needed for the merging of the V4 modules. - -There should be rather more description and documantation of these changes -than is currently available, but as developers we generally much prefer to -write code than documentation. Send questions to the tech-talk mailing list -and we'll be happy to try and answer them! - ------ - -## Changes made between 3.16.1 and 3.16.2 - -### Launchpad Bugs - -The list of tracked bugs fixed in this release can be found on the -[Launchpad Milestone page for EPICS Base 3.16.2](https://launchpad.net/epics-base/+milestone/3.16.2). - -### Status reporting for the callback and scanOnce task queues - -Two new iocsh commands and some associated underlying APIs have been added to -show the state of the queues that feed the three callback tasks and the -scanOnce task, including a high-water mark which can optionally be reset. The -new iocsh commands are `callbackQueueShow` and `scanOnceQueueShow`; both take -an optional integer argument which must be non-zero to reset the high-water -mark. - -### Support for event codes greater than or equal to `NUM_TIME_EVENTS` - -Event numbers greater than or equal to `NUM_TIME_EVENTS` are now allowed if -supported by the registered event time provider, which must provide its own -advancing timestamp validation for such events. - -Time events numbered 0 through `(NUM_TIME_EVENTS-1)` are still validated by code -in epicsGeneralTime.c that checks for advancing timestamps and enforces that -restriction. - -### Type-safe Device and Driver Support Tables - -Type-safe versions of the device and driver support structures `dset` and -`drvet` have been added to the devSup.h and drvSup.h headers respectively. The -original structure definitions have not been changed so existing support -modules will still build normally, but older modules can be modified and new -code written to be compatible with both. - -The old structure definitions will be replaced by the new ones if the macros -`USE_TYPED_DSET` and/or `USE_TYPED_DRVET` are defined when the appropriate -header is included. The best place to define these is in the Makefile, as with -the `USE_TYPED_RSET` macro that was introduced in Base-3.16.1 and described -below. See the comments in devSup.h for a brief usage example, or look at -[this commit](https://github.com/epics-modules/ipac/commit/a7e0ff4089b9aa39108bc8569e95ba7fcf07cee9) -to the ipac module to see a module conversion. - -A helper function `DBLINK* dbGetDevLink(dbCommon *prec)` has also been added -to devSup.h which fetches a pointer to the INP or OUT field of the record. - -### RTEMS build configuration update, running tests under QEMU - -This release includes the ability to run the EPICS unit tests built for a -special version of the RTEMS-pc386 target architecture on systems that have an -appropriate QEMU emulator installed (`qemu-system-i386`). It is also now -possible to create sub-architectures of RTEMS targets, whereas previously the -EPICS target architecture name had to be `RTEMS-$(RTEMS_BSP)`. - -The new target `RTEMS-pc386-qemu` builds binaries that can be run in the -`qemu-system-i386` PC System emulator. This target is a derivative of the -original `RTEMS-pc386` target but with additional software to build an in- -memory file-system, and some minor modifications to allow the unit tests to -work properly under QEMU. When this target is enabled, building any of the -make targets that cause the built-in self-tests to be run (such as `make -runtests`) will also run the tests for RTEMS using QEMU. - -To allow the new 3-component RTEMS target name, the EPICS build system for -RTEMS was modified to allow a `configure/os/CONFIG.Common.` file to set -the `RTEMS_BSP` variable to inform the build what RTEMS BSP to use. Previously -this was inferred from the value of the `T_A` make variable, but that prevents -having multiple EPICS targets that build against the same BSP. All the -included RTEMS target configuration files have been updated; build -configuration files for out-of-tree RTEMS targets will continue to work as the -original rules are used to set `RTEMS_BSP` if it hasn't been set when needed. - -### Link type enhancements - -This release adds three new link types: "state", "debug" and "trace". The -"state" link type gets and puts boolean values from/to the dbState library -that was added in the 3.15.1 release. The "debug" link type sets the -`jlink::debug` flag in its child link, while the "trace" link type also causes -the arguments and return values for all calls to the child link's jlif and -lset routines to be printed on stdout. The debug flag can no longer be set -using an info tag. The addition of the "trace" link type has allowed over 200 -lines of conditional diagnostic printf() calls to be removed from the other -link types. - -The "calc" link type can now be used for output links as well as input links. -This allows modification of the output value and even combining it with values -from other input links. See the separate JSON Link types document for details. - -A new `start_child()` method was added to the end of the jlif interface table. - -The `lset` methods have now been properly documented in the dbLink.h header -file using Doxygen annotations, although we do not run Doxygen on the source -tree yet to generate API documentation. - -Link types that utilize child links must now indicate whether the child will -be used for input, output or forward linking by the return value from its -`parse_start_map()` method. The `jlif_key_result` enum now contains 3 values -`jlif_key_child_inlink`, `jlif_key_child_outlink` and `jlif_key_child_fwdlink` -instead of the single `jlif_key_child_link` that was previously used for this. - -### GNUmake targets for debugging - -Some additional build rules have been added to help debug configuration -problems with the build system. Run `make show-makefiles` to get a sorted list -of all the files that the build system includes when building in the current -directory. - -A new pattern rule for `PRINT.%` can be used to show the value of any GNUmake -variable for the current build directory (make sure you are in the right -directory though, many variables are only set when inside the `O.` build -directory). For example `make PRINT.T_A` will display the build target -architecture name from inside a `O.` directory but the variable will be -empty from an application top or src directory. `make PRINT.EPICS_BASE` will -show the path to Base from any EPICS application directory though. - -### Propagate PUTF across Asynchronous record processing - -The IOC contains a mechanism involving the PUTF and RPRO fields of each record -to ensure that if a record is busy when it receives a put to one of its -fields, the record will be processed again to ensure that the new field value -has been correctly acted on. Until now that mechanism only worked if the put -was to the asynchronous record itself, so puts that were chained from some -other record via a DB link did not cause reprocessing. - -In this release the mechanism has been extended to propagate the PUTF state -across DB links until all downstream records have been reprocessed. Some -additional information about the record state can be shown by setting the TPRO -field of an upstream record, and even more trace data is displayed if the -debugging variable `dbAccessDebugPUTF` is set in addition to TPRO. - -### Finding info fields - -A new iocsh command `dbli` lists the info fields defined in the database, and -can take a glob pattern to limit output to specific info names. The newly -added dbStaticLib function `dbNextMatchingInfo()` iterates through the info -fields defined in the current record, and is used to implement the new -command. - -### Output from `dbpr` command enhanced - -The "DataBase Print Record" command `dbpr` now generates slightly better -output, with more field types having their own display methods. This release -also includes additional protection against buffer overflows while printing -long links in `dbpr`, and corrects the output of long strings from the `dbgf` -command. - -### Record types mbbiDirect and mbboDirect upgraded to 32 bit - -The VAL fields and related fields of these records are now `DBF_LONG`. (Not -`DBF_ULONG` in order to prevent Channel Access from promoting them to -`DBF_DOUBLE`.) Additional bit fields `B10`...`B1F` have been added. - -Device support that accesses `VAL` or the bit fields directly (most don't) and -aims for compatibility with old and new versions of these records should use -at least 32 bit integer types to avoid bit loss. The number of bit fields can -be calculated using `8 * sizeof(prec->val)` which is correct in both versions. - -### Restore use of ledlib for VxWorks command editing - -The epicsReadline refactoring work described below unfortunately disabled the -VxWorks implementation of the osdReadline.c API that uses ledlib for command -editing and history. This functionality has now been restored, see Launchpad -[bug #1741578](https://bugs.launchpad.net/bugs/1741578). - -### Constant link types - -Constant links can now hold 64-bit integer values, either as scalars or - arrays. Only base 10 is supported by the JSON parser though, the JSON standard -doesn't allow for hexadecimal numbers. - -### Upgraded the YAJL JSON Library - -The third-party YAJL library that has been included in libCom for several -years has been upgraded to version 2.1.0 and several bugs fixed. This has an -updated API, requiring any code that uses it to parse its own JSON files to be -modified to match. The changes are mainly that it uses `size_t` instead -`unsigned int` for string lengths, but it also uses `long long` instead of -`long` for JSON integer values, which was the main motivation for the upgrade. - -The self-tests that YAJL comes with have been imported and are now run as an -EPICS Unit Test program, and the JSON syntax accepted by the parser was -extended to permit trailing commas in both arrays and maps. The difference -between the old and new YAJL APIs can be detected at compile time by looking -for the macro `EPICS_YAJL_VERSION` which is defined in the `yajl_common.h` -header file along with a brief description of the API changes. - -### Timestamp support for the calc link type - -A new optional parameter can be given when specifying a calc JSON link. The -`time` parameter is a string containing a single letter `A..L` that selects -one of the input links to be used for the timestamp of calculation if -requested. The timestamp will be fetched atomically with the value from the -chosen input link (providing that input link type supports the readLocked() -method). - -### Silence errors from puts to constant link types - -A soft channel output record with the OUT link unset uses the CONSTANT link -type. The new link type code was causing some soft channel device supports to -return an error status from the write method of that link type, which would -cause a `ca_put()` operation to such a record to generate an exception. This has -been silenced by giving the constant link types a dummy putValue method. A new -test program has been added to prevent regressions of this behaviour. - -### RSRV expanding large buffer causes crash - -In the 3.16.1 release a crash can occur in the IOC's RSRV server when a large -array is made even larger; the previous array buffer was not being released -correctly. See Launchpad -[bug #1706703](https://bugs.launchpad.net/epics-base/+bug/1706703). - ------ - -## Changes made between 3.16.0.1 and 3.16.1 - -### IOC Database Support for 64-bit integers - -The IOC now supports the 64-bit integer field types `DBF_INT64` and -`DBF_UINT64`, and there are new record types `int64in` and `int64out` derived -from the `longin` and `longout` types respectively that use the `DBF_INT64` -data type for their VAL and related fields. The usual range of Soft Channel -device support are included for these new record types. - -All internal IOC APIs such as dbAccess can handle the new field types and -their associated request values `DBR_INT64` and `DBR_UINT64`, which are -implemented using the `epicsInt64` and `epicsUInt64` typedef's from the -`epicsTypes.h` header. - -The waveform record type has been updated to support these new field types. -**All waveform device support layers must be updated to recognize the new type -enumeration values**, which had to be inserted before the `FLOAT` value in the -enum `dbfType` and in `menuFtype`. C or C++ code can detect at compile-time -whether this version of base provides 64-bit support by checking for the -presence of the `DBR_INT64` macro as follows (Note that `DBF_INT64` is an -enum tag and not a preprocessor macro): - -``` - #ifdef DBR_INT64 - /* Code where Base has INT64 support */ - #else - /* Code for older versions */ - #endif -``` - -If the code uses the old `db_access.h` types (probably because it's calling -Channel Access APIs) then it will have to test against the EPICS version -number instead, like this: - -``` - #include - - #ifndef VERSION_INT - # define VERSION_INT(V,R,M,P) ( ((V)<<24) | ((R)<<16) | ((M)<<8) | (P)) - #endif - #ifndef EPICS_VERSION_INT - # define EPICS_VERSION_INT VERSION_INT(EPICS_VERSION, EPICS_REVISION, EPICS_MODIFICATION, EPICS_PATCH_LEVEL) - #endif - - #if EPICS_VERSION_INT >= VERSION_INT(3,16,1,0) - /* Code where Base has INT64 support */ - #else - /* Code for older versions */ - #endif -``` - -Channel Access does not (and probably never will) directly support 64-bit -integer types, so the new field types are presented to the CA server as -`DBF_DOUBLE` values. This means that field values larger than 2^52 -(0x10_0000_0000_0000 = 4503599627370496) cannot be transported over Channel -Access without their least significant bits being truncated. The EPICS V4 -pvAccess network protocol _can_ transport 64-bit data types however, and a -future release of the pvaSrv module will connect this ability to the fields of -the IOC. - -Additional 64-bit support will be provided in later release. For instance the -JSON parser for the new Link Support feature only handles integers up to 32 -bits wide, so constant array initializer values cannot hold larger values in -this release. - -### Add `EPICS_CA_MCAST_TTL` - -A new environment parameter `EPICS_CA_MCAST_TTL` is used to set the Time To Live -(TTL) value of any IP multi-cast CA search or beacon packets sent. - -### `EPICS_CA_MAX_ARRAY_BYTES` is optional - -A new environment parameter `EPICS_CA_AUTO_ARRAY_BYTES` is now used by libca and -RSRV (CA clients and the IOC CA server). The default is equivalent to setting -`EPICS_CA_AUTO_ARRAY_BYTES=YES` which removes the need to set -`EPICS_CA_MAX_ARRAY_BYTES` and always attempts to allocate sufficiently large -network buffers to transfer large arrays properly over the network. In this case -the value of the `EPICS_CA_MAX_ARRAY_BYTES` parameter is ignored. - -Explicitly setting `EPICS_CA_AUTO_ARRAY_BYTES=NO` will continue to honor the -buffer setting in `EPICS_CA_AUTO_ARRAY_BYTES` as in previous releases. - -The default setting for `EPICS_CA_AUTO_ARRAY_BYTES` can be changed by adding the -line - -```makefile - EPICS_CA_AUTO_ARRAY_BYTES=NO -``` - -to the `configure/CONFIG_SITE_ENV` file before building Base. Sites that wish to -override this only for specific IOC architectures can create new files for each -architecture named `configure/os/CONFIG_SITE_ENV.` with the above -setting in before building Base. The configuration can also be explicitly -changed by setting the environment variable in the IOC's startup script, -anywhere above the `iocInit` line. - -The PCAS server (used by the PV Gateway and other CA servers) now always behaves -as if `EPICS_CA_AUTO_ARRAY_BYTES` is set to `YES` (it ignores the configuration -parameter and environment variable). - -### Channel Access "modernization" - -Drop support for CA clients advertising protocol versions less than 4. - -This effects clients from Base older than 3.12.0-beta1. Newer clients will -continue to be able to connect to older servers. Older clients will be ignored -by newer servers. - -This allows removal of UDP echo and similar protocol features which are not -compatible with secure protocol design practice. - -### Lookup-tables using the subArrray record - -The subArray record can now be used as a lookup-table from a constant array -specified in its INP field. For example: - -``` - record(subArray, "powers-of-2") { - field(FTVL, "LONG") - field(MALM, 12) - field(INP, [1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048]) - field(INDX, 0) - field(NELM, 1) - } -``` - -The INDX field selects which power of 2 to set the VAL field to. In previous -releases the INP field would have to have been pointed to a separate waveform -record that was initialized with the array values somehow at initialization -time. - -### Synchronized Timestamps with TSEL=-2 - -Most Soft Channel input device support routines have supported fetching the -timestamp through the INP link along with the input data. However before now -there was no guarantee that the timestamp provided by a CA link came from the -same update as the data, since the two were read from the CA input buffer at -separate times without maintaining a lock on that buffer in between. This -shortcoming could be fixed as a result of the new link support code, which -allows code using a link to pass a subroutine to the link type which will be -run with the link locked. The subroutine may make multiple requests for -metadata from the link, but must not block. - -### Extensible Link Types - -A major new feature introduced with this release of EPICS Base is an -Extensible Link Type mechanism, also known as Link Support or JSON Link Types. -This addition permits new kinds of link I/O to be added to an IOC in a similar -manner to the other extension points already supported (e.g. record, device -and driver support). - -A new link type must implement two related APIs, one for parsing the JSON -string which provides the link address and the other which implements the link -operations that get called at run-time to perform I/O. The link type is built -into the IOC by providing a new `link` entry in a DBD file. - -#### New Link Types Added - -This release contains two new JSON link types, `const` and `calc`: - - * The `const` link type is almost equivalent to the old CONSTANT link type -with the updates described below to accept arrays and strings, except that -there is no need to wrap a scalar string constant inside array brackets since -a constant string will never be confused with a PV name. - - * The `calc` link type allows CALC expressions to be used to combine -values from other JSON links to produce its value. Until additional JSON link -types are created though, the `calc` link type has little practical utility as -it can currently only fetch inputs from other `calc` links or from `const` -links. - -``` - field(INP, {calc:{expr:"A+B+1", - args:[5, # A - {const:6}] # B - } - } - ) -``` - -The new link types are documented in a separate document that gets generated at build time and installed as `html/links.html`. - -#### Device Support Addressing using `JSON_LINK` - -The API to allow device support to use JSON addresses is currently -incomplete; developers are advised not to try creating device support that -specifies a `JSON_LINK` address type. - -#### Support Routine Modifications for Extensible Link Types - -For link fields in external record types and soft device support to be able -to use the new link types properly, various changes are required to utilize -the new Link Support API as defined in the dbLink.h header file and outlined -below. The existing built-in Database and Channel Access link types have been -altered to implement the link APIs, so will work properly after these -conversions: - - * Make all calls to `recGblInitConstantLink()` unconditional on the link -type, i.e. change this code: - -```C - if (prec->siml.type == CONSTANT) { - recGblInitConstantLink(&prec->siml, DBF_USHORT, &prec->simm); - } -``` - - into this: - -```C - recGblInitConstantLink(&prec->siml, DBF_USHORT, &prec->simm); -``` - - Note that `recGblInitConstantLink()` still returns TRUE if the field was - successfully initialized from the link (implying the link is constant). - This change will work properly with all Base releases currently in use. - - * Code that needs to identify a constant link should be modified to use -the new routine `dbLinkIsConstant()` instead, which returns TRUE for constant -or undefined links, FALSE for links whose `dbGetLink()` routine may return -different values on different calls. For example this: - -```C - if (prec->dol.type != CONSTANT) -``` - - should become this: - -```C - if (!dbLinkIsConstant(&prec->dol)) -``` - - When the converted software is also required to build against older versions - of Base, this macro definition may be useful: - -```C - #define dbLinkIsConstant(lnk) ((lnk)->type == CONSTANT) -``` - - * Any code that calls dbCa routines directly, or that explicitly checks if -a link has been resolved as a CA link using code such as - -```C - if (prec->inp.type == CA_LINK) -``` - - will still compile and run, but will only work properly with the old CA link - type. To operate with the new extensible link types such code must be - modified to use the new generic routines defined in dbLink.h and should - never attempt to examine or modify data inside the link. After conversion - the above line would probably become: - -```C - if (dbLinkIsVolatile(&prec->inp)) -``` - - A volatile link is one like a Channel Access link which may disconnect and - reconnect without notice at runtime. Database links and constant links are - not volatile; unless their link address is changed they will always remain - in the same state they started in. For compatibility when building against - older versions of Base, this macro definition may be useful: - -```C - #define dbLinkIsVolatile(lnk) ((lnk)->type == CA_LINK) -``` - - * The current connection state of a volatile link can be found using the -routine `dbIsLinkConnected()` which will only return TRUE for a volatile link -that is currently connected. Code using the older dbCa API returning this -information used to look like this: - -```C - stat = dbCaIsLinkConnected(plink); -``` - - which should become: - -```C - stat = dbIsLinkConnected(plink); -``` - - Similar changes should be made for calls to the other dbCa routines. - - * A full example can be found by looking at the changes to the calcout -record type, which has been modified in this release to use the new dbLink -generic API. - -### Constant Link Values - -Previously a constant link (i.e. a link that did not point to another PV, -either locally or over Channel Access) was only able to provide a single -numeric value to a record initialization; any string given in a link field -that was not recognized as a number was treated as a PV name. In this release, -constant links can be expressed using JSON array syntax and may provide array -initialization of values containing integers, doubles or strings. An array -containing a single string value can also be used to initialize scalar -strings, so the stringin, stringout, lsi (long string input), lso (long string -output), printf, waveform, subArray and aai (analog array input) record types -and/or their soft device supports have been modified to support this. - -Some examples of constant array and string initialized records are: - -``` - record(stringin, "const:string") { - field(INP, ["Not-a-PV-name"]) - } - record(waveform, "const:longs") { - field(FTVL, LONG) - field(NELM, 10) - field(INP, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]) - } - record(aai, "const:doubles") { - field(FTVL, DOUBLE) - field(NELM, 10) - field(INP, [0, 1, 1.6e-19, 2.718, 3.141593]) - } - record(aSub, "select") { - field(FTA, STRING) - field(NOA, 4) - field(INPA, ["Zero", "One", "Two", "Three"]) - field(FTB, SHORT) - field(NOB, 1) - field(FTVA, STRING) - field(NOVA, 1) - field(SNAM, "select_asub") - } -``` - -Reminder: Link initialization with constant values normally only occurs at -record initialization time. The calcout and printf record types are the only -exceptions in the Base record types to this rule, so it is generally not -useful to change a const link value after iocInit. - -### Database Parsing of "Relaxed JSON" Values - -A database file can now provide a "relaxed JSON" value for a database field -value or an info tag. Only a few field types can currently accept such values, -but the capability is now available for use in other places in the future. -When writing to a JSON-capable field at run-time however, only strictly -compliant JSON may be used (the dbStaticLib parser rewrites relaxed JSON -values into strict JSON before passing them to the datase for interpretation, -where the strict rules must be followed). - -"Relaxed JSON" was developed to maximize compatibility with the previous -database parser rules and reduce the number of double-quotes that would be -needed for strict JSON syntax. The parser does accept strict JSON too though, -which should be used when machine-generating database files. The differences -are: - - * Strings containing only the characters `a-z A-Z 0-9 _ - + .` do not have to -be enclosed in double-quote characters. - - * The above rule applies to map keys as well as to regular string values. - - * The JSON keywords `null`, `true` and `false` (all lower-case) will be -recognized as keywords, so they must be quoted to use any of these single words -as a string. - - * Comments may be used, introduced as usual by the `#` character and extending -to the end of the line. - -A JSON field or info value is only enclosed in quotes when the value being -provided is a single string, and even here the quotes can be omitted in some -cases as described above. The following shows both correct and incorrect -excerpts from a database file: - -``` - record(ai, math:pi) { - field(INP, {const: 3.14159265358979}) # Correct - field(SIOL, "{const: 3.142857}") # Wrong - - info(autosave, { # White-space and comments are allowed - fields:[DESC, SIMM], - pass0:[VAL] - }) # Correct - } -``` - -Note that the record, field and info-tag names do *not* accept JSON values, so -they follows the older bareword rules for quoting where the colon `:` and -several additional characters are legal in a bareword string. Only the value -(after the comma) is parsed as JSON. The autosave module has not been modified -to accept JSON syntax, the above is only an example of how JSON might be used. - -### Echoless comments in iocsh - -The way comments are parsed by the iocsh interpreter has changed. The -interpreter can be selectively disabled from echoing comments coming from a -script by starting those lines with `#-` rather than just `#`. - -### Typed record support methods - -The table of record support functions (rset methods for short) no longer has -entries of type `RECSUPFUN` (which says: any number and type of arguments). -Instead, rset methods are now typed by default. The `RECSUPFUN` typedef has -been deprecated and casts to it as well as using the untyped `struct rset` -will create compilation warnings. - -Existing code (e.g. external record supports) will generate such warnings when -compiled against this version of Base, but it will work without changes. - -For a conversion period, the new typed rset definitions are activated by -defining `USE_TYPED_RSET`, preferably by setting `USR_CPPFLAGS += --DUSE_TYPED_RSET` inside a Makefile. After activating the new typed rset in -this way and making the following changes, the result should still compile and -work properly against older versions of Base. - -The first parameter of `init_record` and `process` has been changed to `struct -dbCommon *`. Record types that use `void*` here should be changed to use -`struct dbCommon*`, and cast the argument to their own `xxxRecord *`. - -When compiled against this release, compiler warnings about incompatible types -for the method pointers should be taken seriously. When compiled against older -versions of base, such warnings are unavoidable. - -Record types written in C++ need to take more drastic measures because of the -stricter type checking in C++. To remain compatible with older versions of -base you will need to use something like: - -``` - #include "epicsVersion.h" - #ifdef VERSION_INT - # if EPICS_VERSION_INT < VERSION_INT(3,16,0,2) - # define RECSUPFUN_CAST (RECSUPFUN) - # else - # define RECSUPFUN_CAST - # endif - #else - # define RECSUPFUN_CAST (RECSUPFUN) - #endif -``` - -and then replace `(RECSUPFUN)` with `RECSUPFUN_CAST` when initializing the -rset. Further changes might also be needed, e.g. to adapt `const`-ness of -method parameters. - ------ - -## Changes made between 3.15.3 and 3.16.0.1 - -### Build support for CapFast and dbst removed - -The build rules associated with the CapFast-related tools `sch2edif` and -`e2db` and the database optimization tool `dbst` have been removed, along with -the `DB_OPT` build configuration variable. - -### compressRecord buffering order - -The compressRecord has a new field `BALG` which can select between FIFO -(append) and LIFO (prepend) ordering for insertion of new elements. FIFO -ordering is the default, matching the behviour of previous versions. - -### Valgrind Instrumentation - -Valgrind is a software debugging suite provided by many Linux distributions. -The header valgrind/valgrind.h is now included in, and installed by, Base. -When included by a C or C++ source file this header defines some macros which -expand to provide hints to the Valgrind runtime. These have no effect on -normal operation of the software, but when run using the valgrind tool they -can help to find memory leaks and buffer overflows. Suitable hints have been -added to several free-lists within libCom, including freeListLib, allowing -valgrind to provide more accurate information about the source of potential -leaks. - -valgrind.h automatically disables itself when the build target is not -supported by the valgrind tool. It can also explicitly be disabled by defining -the macro `NVALGRIND`. See `src/libCom/Makefile` for a commented-out example. - -As a matter of policy valgrind.h will never be included by any header file -installed by Base, so its use will remain purely an implementation detail -hidden from application software. Support modules which choose to use -valgrind.h are advised to do likewise. - -### Database Multi-locking - -The IOC record locking code has been re-written with an expanded API; global -locks are no longer required by the IOC database implementation. - -The new API functions center around `dbScanLockMany()`, which behaves like -`dbScanLock()` applied to an arbitrary group of records. `dbLockerAlloc()` is -used to prepare a list or record pointers, then `dbScanLockMany()` is called. -When it returns, all of the records listed may be accessed (in any order) until -`dbScanUnlockMany()` is called. - -The Application Developer's Guide has been updated to describe the API and -implementation is more detail. - -Previously a global mutex `lockSetModifyLock` was locked and unlocked during -`dbScanLock()`, acting as a sequencing point for otherwise unrelated calls. The -new dbLock.c implementation does not include any global mutex in `dbScanLock()` -or `dbScanLockMany()`. Locking and unlocking of unrelated lock sets is now -completely concurrent. - -### Generate Version Header - -A Perl script and Makefile rules have been added to allow modules to generate -a C header file with a macro defined with an automatically updated identifier. -This is a VCS revision ID (Darcs, Git, Mercurial, Subversion, and Bazaar are -supported) or the date/time of the build if no VCS system is in use. - -The makeBaseApp example template has been updated with a new device support -which makes this identifier visible via a lsi (long string input) record. - -### epicsTime API return status - -The epicsTime routines that used to return epicsTimeERROR now return a -specific `S_time_` status value, allowing the caller to discover the reason for -any failure. The identifier `epicsTimeERROR` is no longer defined, so any -references to it in source code will no longer compile. The identifier -epicsTimeOK still exists and has the value 0 as before, so most code that uses -these APIs can be changed in a way that is backwards-compatible with the -previous return status. - -Time providers that have to return a status value and still need to be built -with earlier versions of Base can define the necessary status symbols like -this: - -``` - #include "epicsTime.h" - - #ifndef M_time - /* S_time_... status values were not provided before Base 3.16 */ - #define S_time_unsynchronized epicsTimeERROR - #define S_time_...whatever... epicsTimeERROR - #endif -``` - -### Refactoring of epicsReadline - -The epicsReadline code has been reorganized to allow the commandline history -editor to be disabled at runtime. The `EPICS_COMMANDLINE_LIBRARY` build setting -still selects the preferred editor, but the new `IOCSH_HISTEDIT_DISABLE` -environment variable can be set at runtime to disable history editing and make -the IOC or other program use the basic editor instead. This is useful when -starting and controlling an IOC from another program through its stdin and -stdout streams since history editors often insert invisible escape codes into -the stdout stream, making it hard to parse. - -### Callback subsystem API - -Added a new macro `callbackGetPriority(prio, callback)` to the callback.h -header and removed the need for dbScan.c to reach into the internals of its -`CALLBACK` objects. - - ------ - -# Changes incorporated from the 3.15 branch - - -## Changes from the 3.15 branch since 3.15.9 - -### Support for Apple M1/M2 (arm64) Processors - -Thanks to Jeong Han Lee this release comes with build support for Apple's new -M1/M2 CPUs running macOS, using the target name `darwin-aarch64`. - -### Set thread names on Windows - -On MS Windows, epicsThread names are made available to the OS and debugger -using `SetThreadDescription()` if available as well as using the older -exception mechanism. - -### Fix timers on MS Windows for non-EPICS threads - -The waitable timer changes in 3.15.9 broke calls to `epicsThreadSleep()` and -similar routines that used timers (including `ca_pend_event()`) when made from -threads that were not started using the epicsThread APIs. -[This problem](https://github.com/epics-base/epics-base/pull/200) -[has now been fixed](https://github.com/epics-base/epics-base/pull/201). - -## Changes made between 3.15.8 and 3.15.9 - -### Use waitable timers on Microsoft Windows - -The `epicsEventWaitWithTimeout()` and `epicsThreadSleep()` functions have -been changed to use waitable timers. On Windows 10 version 1803 or higher -they will use high resolution timers for more consistent timing. - -See [this Google Groups thread](https://groups.google.com/a/chromium.org/g/scheduler-dev/c/0GlSPYreJeY) -for a comparison of the performance of different timers. - -### Build target for documentation - -The build target `inc` now works again after a very long hiatus. It now -generates and installs just the dbd, header and html files, without compiling -any C/C++ code. This can be used to speed up CI jobs that only generate -documentation. - -### Bug fixes - -- The error status returned by a record support's `special()` method is now propagated out of the `dbPut()` routine again (broken since 3.15.0). -- [gh: #80](https://github.com/epics-base/epics-base/issues/80), VS-2015 and -later have working strtod() -- [lp: #1776141](https://bugs.launchpad.net/epics-base/+bug/1776141), Catch -buffer overflow from long link strings -- [lp: #1899697](https://bugs.launchpad.net/epics-base/+bug/1899697), Records -in wrong PHAS order - -### Change to the `junitfiles` self-test build target - -The names of the generated junit xml test output files have been changed -from `.xml` to `-results.xml`, to allow better -distinction from other xml files. (I.e., for easy wildcard matching.) - -### Fixes and code cleanups - -Issues reported by various static code checkers. - -## Changes made between 3.15.7 and 3.15.8 - -### Bug fixes - -The following launchpad bugs have fixes included in this release: - -- [lp: 1812084](https://bugs.launchpad.net/epics-base/+bug/1812084), Build - failure on RTEMS 4.10.2 -- [lp: 1829770](https://bugs.launchpad.net/epics-base/+bug/1829770), event - record device support broken with constant INP -- [lp: 1829919](https://bugs.launchpad.net/epics-base/+bug/1829919), IOC - segfaults when calling dbLoadRecords after iocInit -- [lp: 1838792](https://bugs.launchpad.net/epics-base/+bug/1838792), epicsCalc - bit-wise operators on aarch64 -- [lp: 1841608](https://bugs.launchpad.net/epics-base/+bug/1841608), logClient - falsely sends error logs on all connections -- [lp: 1853168](https://bugs.launchpad.net/epics-base/+bug/1853168), undefined - reference to `clock_gettime()` -- [lp: 1862328](https://bugs.launchpad.net/epics-base/+bug/1862328), Race - condition on IOC start leaves rsrv unresponsive -- [lp: 1868486](https://bugs.launchpad.net/epics-base/+bug/1868486), - epicsMessageQueue lost messages - -### Improvements to the self-test build targets - -This release contains changes that make it possible to integrate another test -running and reporting system (such as Google's gtest) into the EPICS build -system. The built-in test-runner and reporting system will continue to be used -by the test programs inside Base however. - -These GNUmake `tapfiles` and `test-results` build targets now collect a list of -the directories that experienced test failures and display those at the end of -running and/or reporting all of the tests. The GNUmake process will also only -exit with an error status after running and/or reporting all of the test -results; previously the `-k` flag to make was needed and even that didn't always -work. - -Continuous Integration systems are recommended to run `make tapfiles` (or if -they can read junittest output instead of TAP `make junitfiles`) followed by -`make -s test-results` to display the results of the tests. If multiple CPUs are -available the `-j` flag can be used to run tests in parallel, giving the maximum -jobs that should be allowed so `make -j4 tapfiles` for a system with 4 CPUs say. -Running many more jobs than you have CPUs is likely to be slower and is not -recommended. - -### Calc Engine Fixes and Enhancements - -The code that implements bit operations for Calc expressions has been reworked -to better handle some CPU architectures and compilers. As part of this work a -new operator has been added: `>>>` performs a logical right-shift, inserting -zero bits into the most significant bits (the operator `>>` is an arithmetic -right-shift which copies the sign bit as it shifts the value rightwards). - -### IOC logClient Changes - -The IOC's error logging system has been updated significantly to fix a number -of issues including: - - - Only send errlog messages to iocLogClient listeners - - Try to minimize lost messages while the log server is down: - + Detect disconnects sooner - + Don't discard the buffer on disconnect - + Flush the buffer immediately after a server reconnects - -### epicsThread: Main thread defaults to allow blocking I/O - -VxWorks IOCs (and potentially RTEMS IOCs running GeSys) have had problems with -garbled error messages from dbStaticLib routines for some time — messages -printed before `iocInit` were being queued through the errlog thread instead of -being output immediately. This has been fixed by initializing the main thread -with its `OkToBlock` flag set instead of cleared. IOCs running on other -operating systems that use iocsh to execute the startup script previously had -that set anyway in iocsh so were not affected, but this change might cause other -programs that don't use iocsh to change their behavior slightly if they use -`errlogPrintf()`, `epicsPrintf()` or `errPrintf()`. - -### catools: Handle data type changes in camonitor - -The camonitor program didn't properly cope if subscribed to a channel whose data -type changed when its IOC was rebooted without restarting the camonitor program. -This has now been fixed. - -### More Record Reference Documentation - -The remaining record types have had their reference pages moved from the Wiki, -and some new reference pages have been written to cover the analog array and -long string input and output record types plus the printf record type, none of -which were previously documented. The wiki reference pages covering the fields -common to all, input, and output record types have also been added, thanks to -Rolf Keitel. The POD conversion scripts have also been improved and they now -properly support linking to subsections in a different document, although the -POD changes to add the cross-links that appeared in the original wiki pages -still needs to be done in most cases. - -### Fix build issues with newer MinGW versions - -The `clock_gettime()` routine is no longer used under MinGW since newer versions -don't provide it any more. - -### Fix race for port in RSRV when multiple IOCs start simultaneously - -If multiple IOCs were started at the same time, by systemd say, they could race -to obtain the Channel Access TCP port number 5064. This issue has been fixed. - ------ - -## Changes made between 3.15.6 and 3.15.7 - -### GNU Readline detection on Linux - -Most Linux architectures should now configure themselves automatically to use -the GNU Readline library if its main header file can be found in the expected -place, and not try to use Readline if the header file isn't present. For older -Linux architectures where libncurses or libcurses must also be linked with, the -manual configuration of the `COMMANDLINE_LIBRARY` variable in the appropriate -`configure/os/CONFIG_SITE.Common.` file will still be necessary. - -### Replace `EPICS_TIMEZONE` with `EPICS_TZ` - -The `EPICS_TIMEZONE` environment parameter provided time-zone information for -the IOC's locale in the old ANSI format expected by VxWorks for its `TIMEZONE` -environment variable, and can also used by RTEMS to set its `TZ` environment -variable. However the `TIMEZONE` value has to be updated every year since it -contains the exact dates of the daylight-savings time changes. The Posix TZ -format that RTEMS uses contains rules that for calculating those dates, thus its -value would only need updating if the rules (or the locale) are changed. - -This release contains changes that replace the `EPICS_TIMEZONE` environment -parameter with one called `EPICS_TZ` and a routine for VxWorks that calculates -the `TIMEZONE` environment variable from the current `TZ` value. This routine -will be run once at start-up, when the EPICS clock has synchronized to its NTP -server. The calculations it contains were worked out and donated to EPICS by -Larry Hoff in 2009; it is unforunate that it has taken 10 years for them to be -integrated into Base. - -The default value for the `EPICS_TZ` environment parameter is set in the Base -`configure/CONFIG_SITE_ENV` file, which contains example settings for most EPICS -sites that use VxWorks, and a link to a page describing the Posix TZ format for -any locations that I missed. - -If a VxWorks IOC runs continuously without being rebooted from December 31st to -the start of daylight savings time the following year, its `TIMEZONE` value will -be wrong as it was calculated for the previous year. This only affects times -that are converted to a string on the IOC however and is easily fixed; just run -the command `tz2timezone()` on the VxWorks shell and the calculation will be -redone for the current year. IOCs that get rebooted at least once before the -start of summer time will not need this to be done. - -### Added new decimation channel filter - -A new server-side filter has been added to the IOC for reducing the number -and frequency of monitor updates from a channel by a client-specified factor. -The filter's behaviour is quite simplistic, it passes the first monitor event it -sees to the client and then drops the next N-1 events before passing another -event. For example to sample a 60Hz channel at 1Hz, a 10Hz channel every 6 -seconds, or a 1Hz channel once every minute: - -``` - Hal$ camonitor 'test:channel.{"dec":{"n":60}}' - ... -``` - -More information is included in the filters documentation, which can be found -in the `html/filters.html` document that is generated during the build. - -### Imported Record Reference Documentation from Wiki - -The remaining record types that had 3.14 reference documentation in the EPICS -Wiki have had that documentation converted and imported into their DBD files. -The preferred form for future updates to the record type descriptions is now an -emailed patch file, a Pull Request through GitHub, or a Merge Request through -Launchpad. Note that in some cases the behavior of a record type in a 7.0.x -release may differ from that of the same record type in a 3.15 release, although -this would be unusual, so it may be important to indicate the branch that your -changes apply to. - -**NOTE:** *These documentation changes have modified the order of the fields in -some record definitions. As a result this release is not compatible with record -or device support binaries that were compiled against earlier releases.* - -### `make test-results` for Windows - -The make target `test-results` should now work properly on Windows. Some Perl -installations used versions of `prove.bat` that would only display the results of -up to 3 tests or didn't return an error status in the event of tests failing. The -build system now calls its own perl script to summarize the results instead of -passing a list of TAP filenames to `prove`. - -### Add option to avoid CALLBACK conflict - -If a macro `EPICS_NO_CALLBACK` is defined, then callback.h will no longer -(re)define CALLBACK. The name `CALLBACK` is used by the WIN32 API, and -redefinition in callback.h cause errors if some windows headers are later -included. - -Code which defines `EPICS_NO_CALLBACK`, but still wishes to use callbacks, -should use the alternate name `epicsCallback` introduced in 3.15.6, 3.16.2, and -7.0.2. It is also possible, though not encouraged, to use `struct callbackPvt` -which has been present since the callback API was introduced. - -### Cleaning up with Multiple CA contexts in a Process - -Bruno Martins reported a problem with the CA client library at shutdown in a -process that uses multiple CA client contexts. The first context that triggers -the CA client exit handler prevents any others from being able to clean up -because it resets the ID of an internal epicsThreadPrivate variable which is -shared by all clients. This action has been removed from the client library, -which makes cleanup of clients like this possible. - -### Perl CA bindings fixed for macOS Mojave - -Apple removed some Perl header files from macOS Mojave that were available -in their SDK, requiring a change to the include paths used when compiling the -CA bindings. The new version should build on new and older macOS versions, and -these changes may also help other targets that have an incomplete installation -of Perl (the build will continue after printing a warning that the Perl CA -bindings could not be built). - -### Routine `epicsTempName()` removed from libCom - -This routine was a simple wrapper around the C89 function `tmpnam()` -which is now seen as unsafe and causes warning messages to be generated by -most modern compilers. The two internal uses of this function have been -modified to call `epicsTempFile()` instead. We were unable to find any -published code that used this function, so it was removed immediately instead -of being deprecated. - -### DBD Parsing of Record Types - -The Perl DBD file parser has been made slightly more liberal; the order in -which DBD files must be parsed is now more flexible, so that a record type -definition can now be parsed after a device support that referred to that -record type. A warning message will be displayed when the device support is -seen, but the subsequent loading of the record type will be accepted without -triggering an error. See -[Launchpad bug 1801145](https://bugs.launchpad.net/epics-base/+bug/1801145). - -### menuScan and several record types documented with POD - -The EPICS Wiki pages describing a number of standard record types has been -converted into the Perl POD documentation format and added to the DBD files, -so at build-time an HTML version of these documents is generated and installed -into the htmls directory. Thanks to Tony Pietryla. - -### CA client tools learned `-V` option - -This displays the version numbers of EPICS Base and the CA protocol. - ------ - -## Changes made between 3.15.5 and 3.15.6 - -### Unsetting environment variables - -The new command `epicsEnvUnset varname` can be used to -unset an environment variable. - -### Warning indicators in msi (and macLib) output - -The libCom macro expansion library has been modified so that when the -`SUPPRESS_WARNINGS` flag is set it will no longer include any `,undefined` -or `,recursive` indicators in its output when undefined or recursive -macros are encountered. These indicators were harmless when the output was fed -into an IOC along with a definition for the macro, but when the `msi` -tool was used to generate other kinds of files they caused problems. If the -`msi -V` flag is used the markers will still be present in the output -whenever the appropriate condition is seen. - -### Improvements to msi - -In addition to fixing its response to discovering parsing errors in its -substitution input file (reported as Launchpad -[bug 1503661](https://bugs.launchpad.net/epics-base/+bug/1503661)) -so it now deletes the incomplete output file, the msi program has been cleaned -up a little bit internally. - -### All array records now post monitors on their array-length fields - -The waveform record has been posting monitors on its NORD field since Base -3.15.0.1; we finally got around to doing the equivalent in all the other -built-in record types, which even required modifying device support in some -cases. This fixes -[Launchpad bug 1730727](https://bugs.launchpad.net/epics-base/+bug/1730727). - -### HOWTO: Converting Wiki Record Reference to POD - -Some documentation has been added to the `dbdToHtml.pl` script -explaining how Perl POD (Plain Old Documentation) markup can be added to -`.dbd` files to generate HTML documentation for the record types. To see -these instructions, run `perl bin//dbdToHtml.pl -H` -or `perldoc bin//dbdToHtml.pl`. - -### Fix problem with numeric soft events - -Changing from numeric to named soft events introduced an incompatibility -when a numeric event 1-255 is converted from a DOUBLE, e.g. from a calc record. -The `post_event()` API is not marked deprecated any more. - -Also `scanpel` has been modified to accept a glob pattern for -event name filtering and to show events with no connected records as well. - -### Add `osiSockOptMcastLoop_t` and osiSockTest - -Added a new OS-independent typedef for multicast socket options, and a test -file to check their correct operation. - -### Support for `CONFIG_SITE.local` in Base - -This feature is mostly meant for use by developers; configuration -settings that would normally appear in `base/configure/CONFIG_SITE` can now -be put in a locally created `base/configure/CONFIG_SITE.local` file instead -of having go modify or replace the original. A new `.gitignore` pattern -tells git to ignore all `configure/*.local` files. - -### Fix broken `EPICS_IOC_LOG_FILE_LIMIT=0` setting - -The Application Developers' Guide says this is allowed and disables the -limit on the log-file, but it hasn't actually worked for some time (if ever). -Note that the iocLogServer will be removed from newer Base release sometime -soon as its functionality can be implemented by other dedicated log servers -such as logstash or syslog-ng. - -Fixes [lp:1786858](https://bugs.launchpad.net/bugs/1786858) -and part of [lp:1786966](https://bugs.launchpad.net/bugs/1786966). - -### Cleanup of startup directory - -The files in the startup directory have not been maintained in recent years -and have grown crufty (technical term). This release includes the following -updates to these files: - - - The Perl `EpicsHostArch.pl` script has been rewritten, and support - for a few previously missing host architectures has been added to it. - - The `EpicsHostArch.pl` script has also been moved into the standard - `src/tools` directory, from where it will be installed into - `lib/perl`. In this new location it is no longer executable, so it must - be run by the `perl` executable. - - The build system has been adjusted to look for `EpicsHostArch.pl` in - both places if the `EPICS_HOST_ARCH` environment variable has not been - set at build-time. - - Sites that used the original Perl script to set `EPICS_HOST_ARCH` as part of - their standard environment will need to adjust their scripts when they - upgrade to this release. - - The `EpicsHostArch` shell script has been replaced with a wrapper - routine that calls the Perl `EpicsHostArch.pl` script. Sites that rely on - this script to set `EPICS_HOST_ARCH` should consider switching to the - Perl script instead. - - The `Site.cshrc` and `Site.profile` files have been renamed to - `unix.csh` and `unix.sh`, respectively. - - The existing `win32.bat` file has been cleaned up and a new - `windows.bat` file added for 64-bit targets. The contents of these files - should be seen as examples, don't uncomment or install parts for software - that you don't explicitly know that you need. - -### Recent Apple XCode Build Issues - -The latest version of XCode will not compile calls to `system()` or -`clock_settime()` for iOS targets. There were several places in Base -where these were being compiled, although there were probably never called. The -code has now been modified to permit iOS builds to complete again. - -### Prevent illegal alarm severities - -A check has been added to `recGblResetAlarms()` that prevents records -from getting an alarm severity higher than `INVALID_ALARM`. It is still possible -for a field like HSV to get set to a value that is not a legal alarm severity, -but the core IOC code should never copy such a value into a record's SEVR or -ACKS fields. With this fix the record's alarm severity will be limited to -`INVALID_ALARM`. - -### Fixes for Launchpad bugs - -The following launchpad bugs have fixes included: - - - [lp: 1786320](https://bugs.launchpad.net/epics-base/+bug/1786320), dbCa - subscribes twice to ENUM - - [lp: 541221](https://bugs.launchpad.net/epics-base/+bug/541221), - `assert (pca->pgetNative)` failed in ../dbCa.c - - [lp: 1747091](https://bugs.launchpad.net/epics-base/+bug/1747091), - epicsTimeGetEvent() / generalTime bug - - [lp: 1743076](https://bugs.launchpad.net/epics-base/+bug/1743076), Segfault - in `ca_attach_context()` during exits - - [lp: 1751380](https://bugs.launchpad.net/epics-base/+bug/1751380), Deadlock - in `ca_clear_subscription()` - - [lp: 1597809](https://bugs.launchpad.net/epics-base/+bug/1597809), Setting - NAME field in DB file may break IOC - - [lp: 1770292](https://bugs.launchpad.net/epics-base/+bug/1770292), - `get_alarm_double()` inconsistent across record types - - [lp: 1771298](https://bugs.launchpad.net/epics-base/+bug/1771298), - Conversion of NaN to integer relies on undefined behavior - -### Updated VxWorks Timezone settings - -Removed the settings for 2017; fixed the hour of the change for MET. - -### Fixed camonitor server side relative timestamps bug - -Initialize the first time-stamp from the first monitor, not the client-side -current time in this configuration. - -### Build changes for MSVC - -Windows builds using Visual Studio 2015 and later now use the `-FS` -compiler option to allow parallel builds to work properly. - -We now give the `-FC` option to tell the compiler to print absolute -paths for source files in diagnostic messages. - -### Extend maximum Posix epicsEventWaitWithTimeout() delay - -The Posix implementation of epicsEventWaitWithTimeout() was limiting the -timeout delay to at most 60 minutes (3600.0 seconds). This has been changed to -10 years; significantly longer maximum delays cause problems on systems where -`time_t` is still a signed 32-bit integer so cannot represent absolute -time-stamps after 2038-01-19. Our assumption is that such 32-bit systems will -have been retired before the year 2028, but some additional tests have been -added to the epicsTimeTest program to detect and fail if this assumption is -violated. - -### New test-related make targets - -This release adds several new make targets intended for use by developers -and Continuous Integration systems which simplify the task of running the -built-in self-test programs and viewing the results. Since these targets are -intended for limited use they can have requirements for the build host which -go beyond the standard minimum set needed to build and run Base. - -#### `test-results` - Summarize test results - -The new make target `test-results` will run the self-tests if -necessary to generate a TAP file for each test, then summarizes the TAP output -files in each test directory in turn, displaying the details of any failures. -This step uses the program `prove` which comes with Perl, but also needs -`cat` to be provided in the default search path so will not work on most -Windows systems. - -#### `junitfiles` - Convert test results to JUnit XML Format - -The new make target `junitfiles` will run the self-tests if necessary -and then convert the TAP output files into the more commonly-supported JUnit -XML format. The program that performs this conversion needs the Perl module -`XML::Generator` to have been installed. - -#### `clean-tests` - Delete test result files - -The new make target `clean-tests` removes any test result files from -previous test runs. It cleans both TAP and JUnit XML files. - -### Fix DNS related crash on exit - -The attempt to fix DNS related delays for short lived CLI programs (eg. caget) -in [lp:1527636](https://bugs.launchpad.net/epics-base/+bug/1527636) introduced a -bug which cased these short lived clients to crash on exit. This bug should now -be fixed. - -### Server bind issue on Windows - -When a National Instruments network variables CA server is already running on -a Windows system and an IOC or PCAS server is started, the IOC's attempt to -bind a TCP socket to the CA server port number fails, but Windows returns a -different error status value than the IOC is expecting in that circumstance -(because the National Instruments code requests exclusive use of that port, -unlike the EPICS code) so the IOC fails to start properly. The relevent EPICS -bind() checks have now been updated so the IOC will request that a dynamic port -number be allocated for this TCP socket instead when this happens. - -### Checking Periodic Scan Rates - -Code has been added to the IOC startup to better protect it against bad -periodic scan rates, including against locales where `.` is not -accepted as a decimal separator character. If the scan period in a menuScan -choice string cannot be parsed, the associated periodic scan thread will no -longer be started by the IOC and a warning message will be displayed at iocInit -time. The `scanppl` command will also flag the faulty menuScan value. - ------ - -## Changes made between 3.15.4 and 3.15.5 - -### dbStatic Library Speedup and Cleanup - -Loading of database files has been optimized to avoid over-proportionally -long loading times for large databases. As a part of this, the alphabetical -ordering of records instances (within a record type) has been dropped. In the -unexpected case that applications were relying on the alphabetic order, setting -`dbRecordsAbcSorted = 1` before loading the databases will retain the -old behavior. - -The routine `dbRenameRecord()` has been removed, as it was intended -to be used by database configuration tools linked against a host side version -of the dbStatic library that is not being built anymore. - -### Launchpad Bug-fixes - -In addition to the more detailed change descriptions below, the following -Launchpad bugs have also been fixed in this release: - - - [lp:1440186](https://bugs.launchpad.net/epics-base/+bug/1440186) Crash due - to a too small buffer being provided in `dbContextReadNotifyCache()` - - [lp:1479316](https://bugs.launchpad.net/epics-base/+bug/1479316) Some data - races found using Helgrind - - [lp:1495833](https://bugs.launchpad.net/epics-base/+bug/1495833) biRecord - prompt groups are nonsensical - - [lp:1606848](https://bugs.launchpad.net/epics-base/+bug/1606848) WSAIoctl - `SIO_GET_INTERFACE_LIST` failed in Windows - -### Whole-Program Optimization for MS Visual Studio Targets - -When using the Microsoft compilers a new build system variable is provided that -controls whether whole program optimization is used or not. For static builds -using Visual Studio 2010 this optimization must be disabled. This is controlled -in the files `configure/os/CONFIG_SITE.Common.windows-x64-static` and -`configure/os/CONFIG_SITE.Common.win32-x86-static` by setting the variable -`OPT_WHOLE_PROGRAM=NO` to override the default value `YES` that would otherwise -be used. - -Note that enabling this optimization slows down the build process. It is not -possible to selectively disable this optimization, when building a particular -module say; Microsoft's linker will restart itself automatically with the -`-LTCG` flag set and display a warning if it is asked to link any object -files that were compiled with the `-GL` flag. - -### Add dynamic (variable length) array support to PCAS - -Dynamic array sizing support was added to the IOC server (RSRV) in the -Base-3.14.12 release, but has not until now been supported in the Portable -Channel Access Server (PCAS). Channel Access server applications using the -PCAS may not need to be modified at all; if they already push monitors with -different gdd array lengths, those variable sizes will be forwarded to any CA -clients who have requested variable length updates. The example CAS server -application has been modified to demonstrate this feature. - -In implementing the above, the gdd method `gdd::put(const gdd *)` now -copies the full-sized array from the source gdd if the destination gdd is of -type array, has no allocated memory and a boundary size of 0. - -### Additional epicsTime conversion - -The EPICS timestamp library (epicsTime) inside libCom's OSI layer has -been extended by routines that convert from `struct tm` to the EPICS -internal `epicsTime` type, assuming UTC - i.e. without going through -the timezone mechanism. This solves issues with converting from the structured -type to the EPICS timestamp at driver level from multiple threads at a high -repetition rate, where the timezone mechanism was blocking on file access. - -### MinGW Cross-builds from Linux - -The build configuration files that allow cross-building of the 32-bit -win32-x86-mingw cross-target have been adjusted to default to building shared -libraries (DLLs) as this is now supported by recent MinGW compilers. The 64-bit -windows-x64-mingw cross-target was already being built that way by default. The -configuration options to tell the minGW cross-compiler to link programs with -static versions of the compiler support libraries have now been moved into the -`CONFIG_SITE.linux-x86.` files. - -### General Time updates - -The `iocInit` code now performs a sanity check of the current time -returned by the generalTime subsystem and will print a warning if the wall-clock -time returned has not been initialized yet. This is just a warning message; when -a time provider does synchonize the IOC will subsequently pick up and use the -correct time. This check code also primes the registered event system provider -if there is one so the `epicsTimeGetEventInt()` routine will work on IOCs -that ask for event time within an interrupt service routine. - -The osiClockTime provider's synchronization thread (which is only used on -some embedded targets) will now poll the other time providers at 1Hz until the -first time it manages to get a successful timestamp, after which it will poll -for updates every 60 seconds as before. - -The routine `generalTimeGetExceptPriority()` was designed for use by -backup (lower priority) time providers like the osiClockTime provider which do -not have their own absolute time reference and rely on other providers for an -absolute time source. This routine no longer implements the ratchet mechanism -that prevented the time it returned from going backwards. If the backup clock's -tick-timer runs fast the synchronization of the backup time provider would never -allow it to be corrected backwards when the ratchet was in place. The regular -`epicsTimeGetCurrent()` API still uses the ratchet mechanism, so this -change will not cause the IOC to see time going backwards. - -### Microsoft Visual Studio builds - -The build configuration files for builds using the Microsoft compilers have been -updated, although there should be no noticable difference at most sites. One -extra compiler warning is now being suppressed for C++ code, `C4344: behavior -change: use of explicit template arguments results in ...` which is gratuitous -and was appearing frequently in builds of the EPICS V4 modules. - -Cross-builds of the windows-x64 target from a win32-x86 host have been -removed as they don't actually work within the context of a single `make` -run. Significant changes to the build configuration files would be necessary for -these kinds of cross-builds to work properly, which could be done if someone -needs them (email Andrew Johnson before working on this, and see -[this stack-overflow answer](http://stackoverflow.com/questions/5807647/how-do-you-compile-32-bit-and-64-bit-applications-at-the-same-time-in-visual-stu) for a starting point). - -### Bazaar keywords such as 'Revision-Id' removed - -In preparation for moving to git in place of the Bazaar revision control -system we have removed all the keywords from the Base source code. - -### Linux systemd service file for CA Repeater - -Building this version of Base on a Linux system creates a systemd service -file suitable for starting the Channel Access Repeater under systemd. The file -will be installed into the target bin directory, from where it can be copied -into the appropriate systemd location and modified as necessary. Installation -instructions are included as comments in the file. - ------ - -## Changes made between 3.15.3 and 3.15.4 - -### New string input device support "getenv" - -A new "getenv" device support for both the stringin and lsi (long string -input) record types can be used to read the value of an environment variable -from the IOC at runtime. See base/db/softIocExit.db for sample usage. - -### Build rules and `DELAY_INSTALL_LIBS` - -A new order-only prerequisite build rule has been added to ensure that -library files (and DLL stubs on Windows) get installed before linking any -executables, which resolves parallel build problems on high-powered CPUs. There -are some (rare) cases though where a Makefile has to build an executable and run -it to be able to compile code for a library built by the same Makefile. With -this new build rule GNUmake will complain about a circular dependency and the -build will probably fail in those cases. To avoid this problem the failing -Makefile should set `DELAY_INSTALL_LIBS = YES` before including the -`$(TOP)/configure/RULES` file, disabling the new build rule. - -### IOC environment variables and build parameters - -The IOC now sets a number of environment variables at startup that provide the -version of EPICS Base it was built against (`EPICS_VERSION_...`) and its build -architecture (ARCH). In some cases this allows a single iocBoot/ioc directory to -be used to run the same IOC on several different architectures without any -changes. - -There are also 3 new environment parameters (`EPICS_BUILD_...`) available that -C/C++ code can use to find out the target architecture, OS class and compiler -class it was built with. These may be useful when writing interfaces to other -languages. - -### New implementation of `promptgroup`/`gui_group` field property - -The mechanism behind the `promptgroup()` field property inside a record type -definition has been changed. Instead of using a fixed set of choices, -the static database access library now collects the used gui group names -while parsing DBD information. Group names should start with a two-digit number -plus space-dash-space to allow proper sorting of groups. - -The include file `guigroup.h` that defined the fixed set of choices -has been deprecated. Instead, use the conversion functions between index number -and group string that have been added to dbStaticLib. - -When a DBD file containing record-type descriptions is expanded, any -old-style `GUI_xxx` group names will be replaced by a new-style -string for use by the IOC. This permits an older record type to be used with -the 3.15.4 release, although eventually record types should be converted by -hand with better group names used. - -### CA server configuration changes - -RSRV now honors `EPICS_CAS_INTF_ADDR_LIST` and binds only to the provided list -of network interfaces. Name searches (UDP and TCP) on other network interfaces -are ignored. For example on a computer with interfaces 10.5.1.1/24, 10.5.2.1/24, -and 10.5.3.1/24, setting `EPICS_CAS_INTF_ADDR_LIST='10.5.1.1 10.5.2.1'` will -accept traffic on the .1.1 and .2.1, but ignore from .3.1 - -RSRV now honors `EPICS_CAS_IGNORE_ADDR_LIST` and ignores UDP messages received -from addresses in this list. - -Previously, CA servers (RSRV and PCAS) would build the beacon address list using -`EPICS_CA_ADDR_LIST` if `EPICS_CAS_BEACON_ADDR_LIST` was no set. This is no -longer done. Sites depending on this should set both environment variables to -the same value. - -### IPv4 multicast for name search and beacons - -libca, RSRV, and PCAS may now use IPv4 multicasting for UDP traffic (name search -and beacons). This is disabled by default. To enable multicast address(s) must -be listed in `EPICS_CA_ADDR_LIST` for clients and `EPICS_CAS_INTF_ADDR_LIST` for -servers (IOCs should set both). For example: - - EPICS_CAS_INTF_ADDR_LIST='224.0.2.9' EPICS_CA_ADDR_LIST=224.0.2.9 - -Please note that no IPv4 multicast address is officially assigned for Channel -Access by IANA. The example 224.0.2.9 is taken from the AD-HOC Block I range. - -### Moved `mlockall()` into its own epicsThread routine - -Since EPICS Base 3.15.0.2 on Posix OSs the initialization of the epicsThread -subsystem has called `mlockall()` when the OS supports it and thread -priority scheduling is enabled. Doing so has caused problems in third-party -applications that call the CA client library, so the functionality has been -moved to a separate routine `epicsThreadRealtimeLock()` which will be -called by the IOC at iocInit (unless disabled by setting the global variable -`dbThreadRealtimeLock` to zero). - -### Added dbQuietMacroWarnings control - -When loading database files, macros get expanded even on comment lines. If a -comment contains an undefined macro, the load still continues but an error -message gets printed. For this release the error message has been changed to a -warning, but even this warning can be made less verbose by setting this new -variable to a non-zero value before loading the file, like this: - -``` - var dbQuietMacroWarnings 1 iocsh - dbQuietMacroWarnings=1 VxWorks -``` - -This was [Launchpad bug -541119](https://bugs.launchpad.net/bugs/541119). - ------ - -## Changes from the 3.14 branch between 3.15.3 and 3.15.4 - -### NTP Time Provider adjusts to OS tick rate changes - -Dirk Zimoch provided code that allows the NTP Time provider (used on VxWorks -and RTEMS only) to adapt to changes in the OS clock tick rate after the provider -has been initialized. Note that changing the tick rate after iocInit() is not -advisable, and that other software might still misbehave if initialized before -an OS tick rate change. This change was back-ported from the 3.15 branch. - -### Making IOC `ca_get` operations atomic - -When a CA client gets data from an IOC record using a compound data type such -as `DBR_TIME_DOUBLE` the value field is fetched from the database in a -separate call than the other metadata, without keeping the record locked. This -allows some other thread such as a periodic scan thread a chance to interrupt -the get operation and process the record in between. CA monitors have always -been atomic as long as the value data isn't a string or an array, but this race -condition in the CA get path has now been fixed so the record will stay locked -between the two fetch operations. - -This fixes -[Launchpad bug 1581212](https://bugs.launchpad.net/epics-base/+bug/1581212), -thanks to Till Strauman and Dehong Zhang. - -### New `CONFIG_SITE` variable for running self-tests - -The 'make runtests' and 'make tapfiles' build targets normally only run the -self-tests for the main `EPICS_HOST_ARCH` architecture. If the host is -able to execute self-test programs for other target architectures that are being -built by the host, such as when building a `-debug` version of the host -architecture for example, the names of those other architectures can be added to -the new `CROSS_COMPILER_RUNTEST_ARCHS` variable in either the -`configure/CONFIG_SITE` file or in an appropriate -`configure/os/CONFIG_SITE..Common` file to have the test -programs for those targets be run as well. - -### Additional RELEASE file checks - -An additional check has been added at build-time for the contents of the -`configure/RELEASE` file(s), which will mostly only affect users of the Debian -EPICS packages published by NSLS-2. Support modules may share an install path, -but all such modules must be listed adjacent to each other in any `RELEASE` -files that point to them. For example the following will fail the new checks: - -``` - AUTOSAVE = /usr/lib/epics - ASYN = /home/mdavidsaver/asyn - EPICS_BASE = /usr/lib/epics -``` - -giving the compile-time error - -``` - This application's RELEASE file(s) define - EPICS_BASE = /usr/lib/epics - after but not adjacent to - AUTOSAVE = /usr/lib/epics - Module definitions that share paths must be grouped together. - Either remove a definition, or move it to a line immediately - above or below the other(s). - Any non-module definitions belong in configure/CONFIG_SITE. -``` - -In many cases such as the one above the order of the `AUTOSAVE` and -`ASYN` lines can be swapped to let the checks pass, but if the -`AUTOSAVE` module depended on `ASYN` and hence had to appear -before it in the list this error indicates that `AUTOSAVE` should also be -built in its own private area; a shared copy would likely be incompatible with -the version of `ASYN` built in the home directory. - -### String field buffer overflows - -Two buffer overflow bugs that can crash the IOC have been fixed, caused by -initializing a string field with a value larger than the field size -([Launchpad bug 1563191](https://bugs.launchpad.net/bugs/1563191)). - -### Fixed stack corruption bug in epicsThread C++ API - -The C++ interface to the epicsThread API could corrupt the stack on thread -exit in some rare circumstances, usually at program exit. This bug has been -fixed ([Launchpad bug 1558206](https://bugs.launchpad.net/bugs/1558206)). - -### RTEMS NTP Support Issue - -On RTEMS the NTP Time Provider could in some circumstances get out of sync -with the server because the `osdNTPGet()` code wasn't clearing its input socket -before sending out a new request. This -([Launchpad bug 1549908](https://bugs.launchpad.net/bugs/1549908)) -has now been fixed. - -### CALC engine bitwise operator fixes - -The bitwise operators in the CALC engine have been modified to work properly -with values that have bit 31 (0x80000000) set. This modification involved -back-porting some earlier changes from the 3.15 branch, and fixes -[Launchpad bug 1514520](https://code.launchpad.net/bugs/1514520). - -### Fix `ipAddrToAsciiAsync()`: Don't try to join the daemon thread - -On process exit, don't try to stop the worker thread that makes DNS lookups -asynchronous. Previously this would wait for any lookups still in progress, -delaying the exit unnecessarily. This was most obvious with catools (eg. -cainfo). -[lp:1527636](https://bugs.launchpad.net/bugs/1527636) - -### Fix `epicsTime_localtime()` on Windows - -Simpler versions of the `epicsTime_gmtime()` and `epicsTime_localtime()` -routines have been included in the Windows implementations, and a new test -program added. The original versions do not report DST status properly. Fixes -[Launchpad bug 1528284](https://bugs.launchpad.net/bugs/1528284). diff --git a/documentation/ReleaseChecklist.html b/documentation/ReleaseChecklist.html deleted file mode 100644 index ac65fef3ba..0000000000 --- a/documentation/ReleaseChecklist.html +++ /dev/null @@ -1,425 +0,0 @@ - - - - - - EPICS Release Procedures & Checklist - - - - -

EPICS Base Release Procedures & Checklist

- -

This document describes the procedures and provides a checklist of tasks -that should be performed when creating production releases of EPICS Base.

- -

The Release Process

- -

We used to have one written down here, but we weren't following it very -closely so now the decision to make a new release is taken during the Core -Developers bi-weekly meetings in an informal manner. The steps detailed below -were written to remind Andrew (or anyone else who does the release) about -everything that has to be done since it's so easy to miss steps.

- -

Roles

- -

The following roles are used below:

- -
-
Release Manager ()
-
Responsible for managing and tagging the release
-
Platform Developers (informal)
-
Responsible for individual operating system platforms
-
Application Developers
-
Responsible for support modules that depend on EPICS Base.
-
APS Website Editor (Andrew Johnson)
-
Responsible for the APS EPICS website
-
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
CheckWhoDescription
Preparing for a release
 Release ManagerEmail all developers about the upcoming release and ask for a list - of remaining tasks that must be finished.
 All developersCheck the bug tracker for any outstanding items and handle - appropriately. All bugs that have been fixed should have been marked - as Fix Committed.
 Release ManagerSet the Feature Freeze date, by which time all Git commits for - enhancements and new functionality should have been completed. After - this date, commits should only be made to fix problems that show up - during testing.
 Release Manager
- & all developers
Ensure that documentation will be updated before the release date: -
    -
  • Release Notes
  • -
  • Other documents
  • -
-
 Release ManagerReview and update this checklist for the upcoming release.
 Release ManagerCreate a release milestone on Launchpad. If a target release date is - known set "Date Targeted" to the expected release date.
Testing
 Platform DevelopersRun the built-in test programs on all available host platforms using -
- make -s runtests -
 Platform DevelopersRun the CA client side regression tests on all available host - platforms.
 Platform DevelopersCheck that all makeBaseApp templates build and run properly, all - xxxApp and xxxBoot types and any internal options, e.g. - setting STATIC_BUILD=YES or using a different - INSTALL_LOCATION in configure/CONFIG_SITE.
 Platform DevelopersBuild the SNL Sequencer against this version of Base, and check that - the makeBaseApp example builds and runs correctly with it.
 Application DevelopersBuild external applications against this version of Base on all - available platforms and test as appropriate. Application code changes - may be necessary where the EPICS Base APIs have been modified.
 Release ManagerCheck that documentation has been updated: -
    -
  • Release Notes
  • -
  • Other documents
  • -
-
- Release Approval
Release ManagerObtain a positive Ok to release from developers.
Creating the final release version
Release Manager -

For each external submodule in turn (assuming it has not been - tagged yet):

-
    -
  1. git grep UNRELEASED and insert the module version to any - doxygen annotations that have a @since UNRELEASED comment. - Commit (don't push yet).
  2. -
  3. Check that the module's Release Notes have been updated to cover - all changes; add items as necessary, and set the module version - number and release date if appropriate. Convert to HTML and view in - a browser to check the formatting: -
    - cd base-7.0/modules/<module>/documentation
    - pandoc -f gfm -t html -o RELEASE_NOTES.html RELEASE_NOTES.md -
    - Commit changes (don't push).
  4. - -
  5. Edit the module's release version file - configure/CONFIG_module_VERSION and its top-level - Doxyfile; set the DEVELOPMENT_FLAG value to 0 and - remove -dev from the PROJECT_NUMBER string. - Commit changes (don't push).
  6. - -
  7. Tag the module: -
    - git tag -m 'ANJ: Tag for EPICS 7.0.7' <module-version> -
    -
  8. - -
  9. Update the git submodule on the Base-7.0 branch to the - newly-tagged version, but don't commit yet: -
    - cd base-7.0/modules
    - git add <module>
    - git submodule status --cached -
    -
  10. - -
  11. Edit the module's release version file - configure/CONFIG_module_VERSION and its top-level - Doxyfile; increment the MAINTENANCE_VERSION, set - the DEVELOPMENT_FLAG value to 1, and update the - PROJECT_NUMBER string, appending -dev to the new - module version number. Commit changes.
  12. - -
  13. Push commits and the new tag to the submodule's GitHub repository: -
    - cd base-7.0/modules/<module>
    - git push --follow-tags upstream master -
    -
  14. - -
-

After all submodules complete commit the submodule updates - which were added for each submodule in step 4 above to the 7.0 branch - (don't push). After committing, make sure that the output from - git submodule status --cached only shows the appropriate - version tags in the right-most parenthesized column with no - -n-gxxxxxxx suffix.

-
Release Manager -

git grep UNRELEASED and insert the release version to any - doxygen annotations that have a @since UNRELEASED comment. - Commit (don't push).

-

Edit the main EPICS Base version file and the built-in module version - files:

-
    -
  • configure/CONFIG_BASE_VERSION
  • -
  • configure/CONFIG_LIBCOM_VERSION
  • -
  • configure/CONFIG_CA_VERSION
  • -
  • configure/CONFIG_DATABASE_VERSION
  • -
-

Version numbers should be set according to the level of changes made - since the last release. Note that the MAINTENANCE_VERSION or - PATCH_LEVEL value should have been incremented after the - previous release tag was applied. Set all DEVELOPMENT_FLAG - values to 0 and EPICS_DEV_SNAPSHOT to the empty string.

-

Edit the headings in the Release Notes to show the appropriate - version number and remove the warning about this being an unreleased - version of EPICS.

-

Commit these changes (don't push).

-
Release ManagerTag the epics-base module in Git: -
- cd base-7.0
- git tag -m 'ANJ: Tagged for release' R7.0.7 -
-

Don't push anything to the Launchpad repository - yet.

-
Release ManagerEdit the main EPICS Base version file and the built-in module version - files: -
    -
  • configure/CONFIG_BASE_VERSION
  • -
  • configure/CONFIG_LIBCOM_VERSION
  • -
  • configure/CONFIG_CA_VERSION
  • -
  • configure/CONFIG_DATABASE_VERSION
  • -
-

Version numbers should be set for the next expected patch/maintenance - release by incrementing the MAINTENANCE_VERSION or PATCH_LEVEL value - in each file. Set all DEVELOPMENT_FLAG values to 1 and - EPICS_DEV_SNAPSHOT to "-DEV".

-

Set up the headings in the Release Notes for the next release - version number and restore the warning about this being an unreleased - version of EPICS.

-

Commit these changes (don't push).

-
Release ManagerExport the tagged version into a tarfile. The make-tar.sh - script generates a gzipped tarfile directly from the tag, excluding the - files and directories that are only used for continuous integration: -
- cd base-7.0
- ./.tools/make-tar.sh R7.0.7 ../base-7.0.7.tar.gz base-7.0.7/ -
- Create a GPG signature file of the tarfile as follows: -
- cd ..
- gpg --armor --sign --detach-sig base-7.0.7.tar.gz -
-
Release ManagerTest the tar file by extracting its contents and building it on at - least one supported platform. When this succeeds the commits and new git - tag can be pushed to the Launchpad repository: -
- git push --follow-tags upstream 7.0 -
-
Publish to epics.anl.gov
Website EditorCopy the tarfile and its signature to the Base download area of the - website.
Website EditorUpdate the website subdirectory that holds the release - documentation, and copy in the files from the base/documentation - directory of the tarfile.
Website EditorUpdate the webpage for the new release with links to the release - documents and tar file.
Website EditorAdd the new release tar file to the website Base download index - page.
Website EditorLink to the release webpage from other relevent areas of the - website - update front page and sidebars.
Website EditorAdd an entry to the website News page, linking to the new version - webpage.
Publish to epics-controls
Website EditorUpload the tar file and its .asc signature file to the - epics-controls web-server. -
- scp base-7.0.7.tar.gz base-7.0.7.tar.gz.asc epics-controls:download/base
-
-
Website EditorFollow instructions on - - Add a page for a new release to create a new release webpage (not - required for a patch release, just edit the existing page). Update the - TablePress "Point Releases" table and add the new download, and adjust - the XYZ Html Snippet for the series download. -
Publish to Launchpad
Release ManagerGo to the Launchpad milestone for this release. Click the Create - release button and add the release date. Put a URL for the release page - in the Release notes box, and click the Create release button. Upload - the tar file and its .asc signature file to the new Launchpad - release page.
Release ManagerFind all Launchpad bug reports with the status Fix Committed which - have been fixed in this release and mark them Fix Released.
Make Announcement
Release ManagerAnnounce the release on the tech-talk mailing list.
-
- - diff --git a/documentation/ReleaseChecklist.md b/documentation/ReleaseChecklist.md new file mode 100644 index 0000000000..7fefba00b6 --- /dev/null +++ b/documentation/ReleaseChecklist.md @@ -0,0 +1,390 @@ +# EPICS Base Release Procedures & Checklist + +This document describes the procedures and provides a checklist of tasks +that should be performed when creating production releases of EPICS +Base. + +## The Release Process + +The decision to make a new release is taken during the +Core Developers bi-weekly meetings in an informal manner. The steps +detailed below were written to remind Andrew (or whoever does +the release) exactly what has to be done, since it's so easy to +miss steps. + +### Roles + +The following roles are used below: + +**Release Manager** +Responsible for managing and tagging the release + +**Core Developers** +Responsible for maintaining the EPICS software + +**Website Editors** +Responsible for the EPICS websites + + ++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
WhoDescription
Preparing for a release
Release ManagerNotify core developers about the upcoming release and ask about any +remaining tasks that must be finished.
All developersCheck the bug tracker for any outstanding items and handle +appropriately.
Release ManagerSet a Feature Freeze date, by which time all Git branches for +enhancements and new functionality should have been merged. After this +date, commits and merges should only be made to fix problems that show +up during testing.
Release Manager
+& all developers
Request that documentation be updated and information about new +features be added before the release date: +
Release ManagerReview and update this checklist for the upcoming release. Update +the release version number in the tags and messages below.
Testing
Platform DevelopersRun the internal test programs on all appropriate platforms.
Platform DevelopersCheck that all makeBaseApp templates build and run properly, all +xxxApp and xxxBoot types and any internal options, +e.g. setting STATIC_BUILD=YES or using a different +INSTALL_LOCATION in configure/CONFIG_SITE.
Release ManagerCheck that documentation has been updated: +
Release Approval
Core DevelopersReach a consensus that the software is ready to release.
Creating the final release version
Release Manager

For each external +submodule to be tagged

+
    +
  1. cd base-7.0/modules/<module>; git grep UNRELEASED +and insert the submodule's version number into any doxygen annotations +that have a @since UNRELEASED comment. Commit (don't push +yet).
  2. +
  3. Check that the submodule's Release Notes have been updated to cover +all changes; add missing items as necessary, and set the module version +number and release date if appropriate.
    +Commit the changes to the submodule's Notes file (don't push).
  4. +
  5. Copy the new submodule version number and Release Notes entries into +a new file named +module-release +in the base-7.0/documentation/new-notes directory.
  6. +
  7. Edit the module's release version file +configure/CONFIG_module_VERSION +and the Doxyfiles in the top-level and/or documentation +directories. In these, set DEVELOPMENT_FLAG=0 and remove +-dev from the PROJECT_NUMBER string. Commit +these changes (don't push):

    +
    + git commit -m 'Final commit for <submodule-version>' +
  8. +
  9. Tag the submodule:

    +
    + git tag -m 'ANJ: Tag for EPICS 7.0.10' <submodule-version> +
  10. +
  11. Generate documentation for modules with +release_notes.dox files. Prepare to update the github-pages +website as follows:

    +
    + cd base-7.0/modules/<module>/documentation
    +make commit
    +git push --force upstream gh-pages +
    +

    Q: Delay this git push until later?

  12. +
  13. Update the Git submodule on the Base-7.0 branch to the +newly-tagged version, check the module's status matches the tag:

    +
    + cd base-7.0/modules
    +git add <module>
    +git submodule status --cached +
    +

    Don't commit the submodule updates yet.

  14. +
  15. Edit the module's release version file +configure/CONFIG_module_VERSION +and its top-level Doxyfile; increment the +MAINTENANCE_VERSION, set the DEVELOPMENT_FLAG +value to 1, and update the PROJECT_NUMBER string, appending +-dev to the new module version number. Commit changes.
  16. +
  17. Push commits and the new tag to the submodule's GitHub repository +(assumed to be the upstream remote):

    +
    + cd base-7.0/modules/<module>
    +git push --follow-tags upstream master +
  18. +
Release Manager

After all submodules +have been updated:

+
    +
  1. Commit the submodule updates which were added for each submodule +in step 7 above to the 7.0 branch (don't push):

    +
    + cd base-7.0/modules
    +git commit -m "Update git submodules for release" +
  2. +
  3. Make sure that the output from +git submodule status --cached only shows the appropriate +version tags in the right-most parenthesized column with no +-n-gxxxxxxx +suffix.
  4. +
  5. Add and commit the new Release Note entry files that were created +for each submodule in step 3 above (don't push):

    +
    + cd base-7.0/documentation
    +git add new-notes
    +git commit -m "Add submodule release note entries" +
  6. +
Release Manager

In the main epics-base +repository

+
    +
  1. cd base-7.0; git grep UNRELEASED and insert the release +version to any doxygen annotations that have a +@since UNRELEASED comment. Commit (don't push).
  2. +
  3. Edit the main EPICS Base version file and the built-in module +version files: +
      +
    • configure/CONFIG_BASE_VERSION
    • +
    • configure/CONFIG_LIBCOM_VERSION
    • +
    • configure/CONFIG_CA_VERSION
    • +
    • configure/CONFIG_DATABASE_VERSION
    • +
  4. +
  5. Version numbers should be set according to the level of changes made +since the last release. Note that the MAINTENANCE_VERSION +or PATCH_LEVEL value will have been incremented immediately +after the previous release tag was applied, so don't double-increment +them. Set all DEVELOPMENT_FLAG values to 0 and set the +EPICS_DEV_SNAPSHOT to an empty string (no quotes).
  6. +
  7. Commit the above changes (don't push):

    +
    + cd base-7.0
    +git add configure/CONFIG_*_VERSION
    +git commit -m "Set core version numbers for release" +
  8. +
  9. When EPICS_DEV_SNAPSHOT is empty because a release +is being created, the documentation/Makefile supports a +build target release for creating a new release notes file +documentation/RELEASE-version.md +from the Markdown files in the documentation/new-notes +directory. When run, it copies the notes entries from all the +new-notes/*.md files, then deletes the files and prepares a +Git commit to apply those changes permanently to the repository.

    +

    Run these commands to generate the RELEASE-7.0.10.md +file and remove the individual release note entry files:

    +
    + cd base-7.0/documentation
    +make release +
  10. +
  11. The make release command add some changes into the +Git index but didn't commit them. These commands let you check what was +done and commit the result (don't push yet!):

    +
    + git status
    +git diff --staged
    +git commit -m "Generate RELEASE-7.0.10.md notes file" +
    +

    To undo those Git actions and confirm that happened, run these:

    +
    + make unrelease
    +git status +
  12. +
Release ManagerTag the epics-base module in Git: +
+ cd base-7.0
+git tag -m 'ANJ: Tagged for release' R7.0.10 +
+

Don't push to GitHub yet.

Release Manager

After tagging the release

+Edit the main EPICS Base version file and the built-in module version +files: +
    +
  • configure/CONFIG_BASE_VERSION
  • +
  • configure/CONFIG_LIBCOM_VERSION
  • +
  • configure/CONFIG_CA_VERSION
  • +
  • configure/CONFIG_DATABASE_VERSION
  • +
+

Version numbers should be set for the next expected patch/maintenance +release by incrementing the MAINTENANCE_VERSION or PATCH_LEVEL value in +each file. Set all DEVELOPMENT_FLAG values to 1 and +EPICS_DEV_SNAPSHOT to "-DEV".

+

Commit these changes (don't push).

Release ManagerExport the tagged version into a tarfile. The +make-tar.sh script generates a gzipped tarfile directly +from the tag, excluding the files and directories that are only used for +continuous integration: +
+ cd base-7.0
+./.tools/make-tar.sh R7.0.10 ../base-7.0.10.tar.gz base-7.0.10/ +
+Create a GPG signature file of the tarfile as follows: +
+ cd ..
+gpg --armor --sign --detach-sig base-7.0.10.tar.gz +
Release ManagerTest the tar file by extracting its contents and building it on at +least one supported platform. If this succeeds the commits and new git +tag can be pushed to the GitHub repository's 7.0 branch (assumed to be +the upstream remote): +
+ git push --follow-tags upstream 7.0 +
Publish to epics.anl.gov
Website EditorCopy the tarfile and its signature to the Base download area of the +website.
Website EditorAdd the new release tar file to the website Base download index +page.
Website EditorCreate or update the website subdirectory that holds the release +documentation, and copy in the files to be published with this release +version.
Website EditorUpdate the webpage for the new release with links to the release +documents and tar file.
Website EditorLink to the release webpage from other relevant areas of the website +- update front page and sidebars.
Website EditorAdd an entry to the website News page, linking to the new version +webpage.
Publish to epics-controls.org
Website EditorUpload the tar file and its .asc signature file to the +epics-controls web-server. +
+ scp base-7.0.10.tar.gz base-7.0.10.tar.gz.asc epics-controls:download/base
+
Website Editor

Follow instructions on + +Add a page for a new release +to create a new release webpage (not +required for a patch release, just edit the existing page). Update the +TablePress "Point Releases" table and add the new download, and adjust +the Html Snippet for the series download.

+

Not covered in those instructions: Go to Posts, find a previous +release and use "Duplicate Post", then edit the result and publish it. +This generates the News item.

Publish to GitHub
Release ManagerGo to the GitHub + +Create +release from tag R7.0.10 page. Upload the tar file and its +.asc signature file to the new GitHub release page, or just +drag-n-drop them into the page. Copy/paste the text from the previous +release and edit. Submit.
Release ManagerWe used to close out bug reports in Launchpad at release-time, this +would be the time to do that if we have an equivalent on GitHub.
Make Announcement
Release ManagerAnnounce the release on the tech-talk mailing list.
diff --git a/documentation/ca-API.rst b/documentation/ca-API.rst new file mode 100644 index 0000000000..0f24288d3e --- /dev/null +++ b/documentation/ca-API.rst @@ -0,0 +1,7 @@ +Channel Access C/C++ APIs +========================= + +.. toctree:: + :maxdepth: 1 + :caption: CA Header Files + diff --git a/documentation/ca-cli.md b/documentation/ca-cli.md new file mode 100644 index 0000000000..fada7b09ee --- /dev/null +++ b/documentation/ca-cli.md @@ -0,0 +1,22 @@ +# Channel Access command-line interface + +``` {toctree} +:titlesonly: +:caption: Tools + +caget +cainfo +camonitor +caput +``` + +``` {toctree} +:titlesonly: +:caption: Utilities + +acctst +catime +casw +caEventRate +ca_test +``` diff --git a/documentation/commit-gh.sh b/documentation/commit-gh.sh deleted file mode 100755 index d630165d28..0000000000 --- a/documentation/commit-gh.sh +++ /dev/null @@ -1,45 +0,0 @@ -#!/bin/sh -set -e -x -# Usage: commit-gh -# -# Creates a commit containing only the files in the sub-directory provided as an argument -# -# Does not disturb the working copy or index - -prefix="$1" -shift - -# Commit to this branch -BRANCH=refs/heads/gh-pages - -# Use the main branch description as the gh-pages commit message -MSG=`git describe --tags --always` - -# Scratch space -TDIR=`mktemp -d -p $PWD` - -# Automatic cleanup of scratch space -trap 'rm -rf $TDIR' INT TERM QUIT EXIT - -export GIT_INDEX_FILE="$TDIR/index" - -# Add listed files to a new (empty) index -git update-index --add "$@" - -# Write the index into the repo, get tree hash -TREE=`git write-tree --prefix="$prefix"` - -echo "TREE $TREE" -git cat-file -p $TREE - -# Create a commit with our new tree -# Reference current branch head as parent (if any) -CMT=`git commit-tree -m "$MSG" $TREE` - -echo "COMMIT $CMT" -git cat-file -p $CMT - -# Update the branch with the new commit tree hash -git update-ref $BRANCH $CMT - -echo "Done" diff --git a/documentation/conf.py b/documentation/conf.py new file mode 100644 index 0000000000..bce2ff85e3 --- /dev/null +++ b/documentation/conf.py @@ -0,0 +1,130 @@ +# Configuration file for the Sphinx documentation builder. +# +# This file only contains a selection of the most common options. For a full +# list see the documentation: +# http://www.sphinx-doc.org/en/master/config + +# -- Path setup -------------------------------------------------------------- + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. +# +import os +import sys + +sys.path.insert(0, os.path.abspath(".")) + +# -- Project information ----------------------------------------------------- + +project = "EPICS Base Documentation" +copyright = "EPICS Controls" +author = "The EPICS Collaboration" + +# -- General configuration --------------------------------------------------- + +# Add any Sphinx extension module names here, as strings. They can be +# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom +# ones. + +extensions = [ + "hoverxref.extension", + "breathe", + "sphinx.ext.mathjax", + "sphinx.ext.ifconfig", + "sphinx.ext.graphviz", + "sphinx_copybutton", + "sphinx.ext.intersphinx", + 'myst_parser', +] + +# Setup the breathe extension +breathe_projects = {"epics-base": "xml"} + +breathe_default_project = "epics-base" + +# Tell sphinx what the primary language being documented is. +primary_domain = "cpp" + +# Tell sphinx what the pygments highlight language should be. +highlight_language = "cpp" + +# Add any paths that contain templates here, relative to this directory. +# templates_path = () + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +# This pattern also affects html_static_path and html_extra_path. +exclude_patterns = ["_build", "Thumbs.db", ".DS_Store", "O.*", "venv"] + +# Intersphinx links to subprojects +intersphinx_mapping = { + "epics": ("https://docs.epics-controls.org/en/latest/", None), +} +intersphinx_disabled_reftypes = ["*"] +hoverxref_role_types = { + "hoverxref": "tooltip", + "ref": "modal", + "confval": "tooltip", + "mod": "modal", + "class": "modal", + "obj": "tooltip", +} + +hoverxref_intersphinx_types = { + "readthedocs": "modal", + "sphinx": "tooltip", +} + +hoverxref_domains = [ + "py", +] + +# Enabled Markdown extensions. +# See here for what they do: +# https://myst-parser.readthedocs.io/en/latest/syntax/optional.html +myst_enable_extensions = [ + "amsmath", + "colon_fence", + "deflist", + "dollarmath", + "fieldlist", + "html_image", + "replacements", + "smartquotes", + "strikethrough", + "tasklist", +] + +# Allows auto-generated header anchors: +# https://myst-parser.readthedocs.io/en/latest/syntax/optional.html#auto-generated-header-anchors +myst_heading_anchors = 4 + + +# -- Options for HTML output ------------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +# +html_theme = "sphinx_rtd_theme" + + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +# html_static_path = ['_static'] + +# html_css_files = [ +# 'css/custom.css', +# ] + +master_doc = "index" + +# html_theme_options = { +# 'logo_only': True, +# } +# html_logo = "images/EPICS_white_logo_v02.png" + +# html_extra_path = ['../html'] + +# Breathe directives diff --git a/documentation/database-API.rst b/documentation/database-API.rst new file mode 100644 index 0000000000..a7e230d535 --- /dev/null +++ b/documentation/database-API.rst @@ -0,0 +1,7 @@ +IOC Database C/C++ APIs +======================= + +.. toctree:: + :maxdepth: 1 + :caption: IOC Header Files + diff --git a/documentation/index.rst b/documentation/index.rst new file mode 100644 index 0000000000..a84446ba74 --- /dev/null +++ b/documentation/index.rst @@ -0,0 +1,39 @@ +EPICS Base Documentation +======================== + + +.. toctree:: + :hidden: + + EPICS Documentation Index + +.. toctree:: + :maxdepth: 1 + :caption: General Information + + README + RELEASE_NOTES + ACF-Language + +.. toctree:: + :maxdepth: 2 + :caption: IOC Component Reference + + ComponentReference + +.. toctree:: + :titlesonly: + :caption: Command Line Reference + + ca-cli + msi + +.. toctree:: + :maxdepth: 1 + :caption: C/C++ Headers + + libcom-api + ca-api + database-api + record-api + menu-api diff --git a/documentation/libcom-API.rst b/documentation/libcom-API.rst new file mode 100644 index 0000000000..5d1305f419 --- /dev/null +++ b/documentation/libcom-API.rst @@ -0,0 +1,7 @@ +Common Library C/C++ APIs +========================= + +.. toctree:: + :maxdepth: 1 + :caption: LibCom Header Files + diff --git a/documentation/mainpage.dox b/documentation/mainpage.dox deleted file mode 100644 index ed3d688efe..0000000000 --- a/documentation/mainpage.dox +++ /dev/null @@ -1,15 +0,0 @@ -/** -@mainpage EPICS Base - -Documentation index - -@li @ref releasenotes -@li @ref install -@li EPICS Component Reference Manual -@li Field Modifiers and Channel Filters -@li Extensible IOC Database Links -@li Channel Access Reference Manual -@li msi: Macro Substitution and Include Tool -@li Perl 5 Interface to Channel Access - -*/ diff --git a/documentation/make-notes.pl b/documentation/make-notes.pl new file mode 100644 index 0000000000..018bd27c40 --- /dev/null +++ b/documentation/make-notes.pl @@ -0,0 +1,146 @@ +#!/usr/bin/env perl +#************************************************************************* +# Copyright (c) 2025 UChicago Argonne LLC, as Operator of Argonne +# National Laboratory. +# SPDX-License-Identifier: EPICS +# EPICS BASE is distributed subject to a Software License Agreement found +# in file LICENSE that is included with this distribution. +#************************************************************************* + +# Tool to combine release note entries + +use strict; +use warnings; + +use File::Basename; +use Getopt::Std; +use version; + +my $tool = basename($0); + +our ($opt_d, $opt_h, $opt_o, $opt_D, $opt_V); + +sub HELP_MESSAGE { + print STDERR "Usage: $tool -o outfile.md -V 7.0.10 [-d new-notes] [-D] " . + "RELEASE-*.md\n"; + exit 2; +} + +HELP_MESSAGE() + if !getopts('hd:o:DV:') || $opt_h; + +die "$tool: Release version '-V' option required\n" + unless defined $opt_V; +die "$tool: Version string '$opt_V' not legal\n" + unless $opt_V =~ m/^ (0 | [1-9][0-9]*) (\. [0-9]{1,3}){0,3} $/x; +die "$tool: Output filename '-o' required\n" + unless defined $opt_o; +die "$tool: Output filename '-o $opt_o' must end with '.md'\n" + unless $opt_o =~ m/\.md $/x; +die "$tool: New notes directory '-d $opt_d' doesn't exist\n" + if defined $opt_d && !-d $opt_d; + +open my $out, '>:encoding(UTF-8)', $opt_o or + die "$tool: Can't create $opt_o: $!\n"; + +$SIG{__DIE__} = sub { + die @_ if $^S; # Ignore eval deaths + close $out; + unlink $opt_o; +}; + +my $REL_VERS = $opt_V; +$REL_VERS .= '-DEV' if $opt_D; + +my @notes; +if ($opt_d) { + # Directory handle for scanning the new-notes directory + opendir my $dh, $opt_d or + die "$tool: Can't open '-d' directory: $!\n"; + + foreach my $fn (sort grep !m/^ \. \.? $/x, readdir $dh) { + next if $fn eq 'README.txt'; + die "$tool: Not markdown? File '$fn' lacks '.md' extension\n" + unless $fn =~ m/\.md$/; + local $/; + my $file = "$opt_d/$fn"; + push @notes, do { + open my $fh, '<:encoding(UTF-8)', $file or + die "$tool: Can't open file $file: $!\n"; + <$fh>; + }; + } + close $dh; +} + +sub relVers { + $_ = shift; + m/RELEASE-([0-9.]+)\.md/; + return $1; +} + +# Reverse sort of the RELEASE-.md filenames +my @OLD_REL_FILES = sort { + version->parse(relVers($b)) <=> version->parse(relVers($a)); +} @ARGV; + +print $out <<"__REL_INTRO__"; +# Release Notes + +This document describes the changes that were included in the release of EPICS +noted below. Release entries are now provided in a separate document for each +version in the EPICS 7 series, but all are combined into a single page for +publishing on the EPICS website. Separate release documents are also included +from the older Base 3.15 and 3.16 series. + +The external PVA submodules continue to maintain their own release notes files +as before, but the entries describing changes in those submodules since version +7.0.5 have been copied into the associated EPICS Release Notes files; they will +also be manually added to new EPICS Release Notes published in the future. + + +## EPICS Release $REL_VERS + +__REL_INTRO__ + +print $out <<"__NEW_INTRO__" if $opt_D && scalar @notes; +__This version of EPICS has not been released yet.__ +__When the changes described below get released, the version number used may be +different to the one given above.__ + +The changes below have been merged into EPICS since the last published release. + +__NEW_INTRO__ + +# Include the text from all the new entries +print $out map { "$_\n" } @notes; + +# Add myst include directives to incorporate older release files +foreach my $rfile (@OLD_REL_FILES) { + my $ver = relVers($rfile); + if ($ver =~ m/^7\./) { + print $out <<"__OLD_RELEASE_7__"; +----- + +## EPICS Release $ver + +```{include} ../$rfile +:start-after: EPICS Release $ver +``` + +__OLD_RELEASE_7__ + } + elsif ($ver =~ m/^3\.1[56]/) { + print $out <<"__OLD_RELEASE_3__"; +----- + +```{include} ../$rfile +:heading-offset: 1 +``` + +__OLD_RELEASE_3__ + } +} + +close $out; + diff --git a/documentation/menu-API.rst b/documentation/menu-API.rst new file mode 100644 index 0000000000..e5a3b56b51 --- /dev/null +++ b/documentation/menu-API.rst @@ -0,0 +1,7 @@ +C/C++ Headers from Menus +======================== + +.. toctree:: + :maxdepth: 1 + :caption: Generated Menu Header Files + diff --git a/documentation/new-notes/README.txt b/documentation/new-notes/README.txt new file mode 100644 index 0000000000..f866e0ddc4 --- /dev/null +++ b/documentation/new-notes/README.txt @@ -0,0 +1,55 @@ +documentation/new-notes/README.txt +================================== + +The documentation/new-notes directory is for new release note entries that +describe changes merged into EPICS Base since the previous release. +Files here must be written in Markdown (see below) and have the extension +'.md' at the end of their filename. +This README.txt file is the only other file that should appear here. + + +Generating RELEASE_NOTES.md +--------------------------- + +Running 'make' inside the Base documentation directory now generates a +file RELEASE_NOTES.md and installs it into the top-level doc directory. + +The file starts with a level-1 Markdown header and some introductory text. +If any new-notes files are present a level-2 header is added with a Release +version number and a -DEV suffix, followed by some notes explaining their +unreleased status. The new-notes filenames are lexically sorted and their +contents added in order, separated by an extra newline character. + +Finally a series of myst Markdown directives are added which will include all +the older RELEASE-.md files present in the documentation directory +into the published HTML version, sorted in version order with the newest first. + + +Writing a Release Notes entry +----------------------------- + +Add a new file to the new-notes directory for your entry. If this is for a +GitHub pull request to the epics-base project please use the name 'PR-nnn.md' +where nnn is the number of the pull request. If you haven't created the pull +request yet you can use the number from a related GitHub issue, or use some +other name, then rename and push it after the PR has been created. + +The file *must* start with a level-3 Markdown title for the entry, like this: + + ### Conflict-free release note entries for GitHub pull requests + + GitHub [PR #628](https://github.com/epics-base/epics-base/pull/628) + + * The three '#' characters of the title start in the left-most column. + * The title should provide a short summary, and not end in a period. + * A link to the GitHub pull-request may follow if desired as shown above + (followed by a blank line to separate it from the next paragraph), or a + link to the PR may be integrated into the text that follows. + * Use blank lines between paragraphs of text, and code-blocks for examples. + * I recommend using [semantic line-breaks](https://sembr.org/) in Markdown + files, it makes editing easier and reduces the number of lines that change + in most commits. This README.txt file isn't formatted as Markdown. + +Release note entries are not intended to provide full documentation of major +features. For small features or changes though, they may provide all the +information needed to understand and use the feature. diff --git a/documentation/record-API.rst b/documentation/record-API.rst new file mode 100644 index 0000000000..880fa94aac --- /dev/null +++ b/documentation/record-API.rst @@ -0,0 +1,7 @@ +C/C++ Headers from Record Types +=============================== + +.. toctree:: + :maxdepth: 1 + :caption: Generated Record Header Files + diff --git a/documentation/requirements.txt b/documentation/requirements.txt new file mode 100644 index 0000000000..f1d6f0bdb0 --- /dev/null +++ b/documentation/requirements.txt @@ -0,0 +1,6 @@ +sphinx==7.2.6 +myst-parser==2.0.0 +breathe==4.35.0 +sphinx_copybutton==0.5.2 +sphinx-hoverxref==1.3.0 +sphinx-rtd-theme==2.0.0 diff --git a/modules/Makefile b/modules/Makefile index ae221dead4..8ad33f0c0d 100644 --- a/modules/Makefile +++ b/modules/Makefile @@ -34,9 +34,6 @@ pvDatabase_DEPEND_DIRS = pvAccess SUBMODULES += pva2pva pva2pva_DEPEND_DIRS = pvAccess -SUBMODULES += example -example_DEPEND_DIRS = pva2pva pvaClient - # Allow sites to add extra submodules -include Makefile.local diff --git a/modules/ca/src/client/CAref.html b/modules/ca/src/client/CAref.html index 7b6ff143fc..9b5c95bb3d 100644 --- a/modules/ca/src/client/CAref.html +++ b/modules/ca/src/client/CAref.html @@ -103,7 +103,7 @@

Troubleshooting

  • Client and Server Broadcast Addresses Don't Match
  • -
  • Client Isnt Configured to Use the Server's +
  • Client Isn't Configured to Use the Server's Port
  • Unicast Addresses in the EPICS_CA_ADDR_LIST Does not Reliably Contact Servers Sharing the Same UDP Port on the Same @@ -759,7 +759,7 @@

    Configuring the Maximum Array Size

    Starting with version R3.14 the environment variable EPICS_CA_MAX_ARRAY_BYTES determines the size of the largest array that may pass through CA. Prior to this version only arrays smaller than 16k bytes could be -transfered. The CA libraries maintains a free list of 16384 byte network +transferred. The CA libraries maintains a free list of 16384 byte network buffers that are used for ordinary communication. If EPICS_CA_MAX_ARRAY_BYTES is larger than 16384 then a second free list of larger data buffers is established and used only after a client send its first large array request.

    @@ -1085,7 +1085,7 @@

    Description

    If a value is specified it is written to the PV. Next, the current value of the PV is converted to each of the many external data type that can be -specified at the CA client library interface, and each of these is formated and +specified at the CA client library interface, and each of these is formatted and then output to the console.


    @@ -1631,7 +1631,7 @@

    Description

    The -s option allows to specify an interest level for calling Channel Access' internal report function ca_client_status(), that prints lots of -internal informations on stdout, including environment settings, used CA ports +internal information on stdout, including environment settings, used CA ports etc.

    @@ -1969,7 +1969,7 @@

    Put Requests Just Prior to Process

    ENOBUFS Messages

    -

    Many Berkley UNIX derived Internet Protocol (IP) kernels use a memory +

    Many Berkeley UNIX derived Internet Protocol (IP) kernels use a memory management scheme with a fixed sized low level memory allocation quantum called an "mbuf". Messages about "ENOBUFS" are an indication that your IP kernel is running low on mbuf buffers. An IP kernel mbuf starvation situation may lead to diff --git a/modules/ca/src/client/Makefile b/modules/ca/src/client/Makefile index 08394ea54b..52600fc345 100644 --- a/modules/ca/src/client/Makefile +++ b/modules/ca/src/client/Makefile @@ -12,6 +12,11 @@ TOP = ../../../.. include $(TOP)/configure/CONFIG HTMLS += CAref.html +DOCS += acctst.md +DOCS += catime.md +DOCS += casw.md +DOCS += caEventRate.md +DOCS += ca_test.md # # includes to install from this subproject @@ -118,7 +123,6 @@ EXPAND_VARS = INSTALL_BIN=$(FINAL_LOCATION)/bin/$(T_A) SRC_DIRS += $(CURDIR)/test PROD_HOST += ca_test ca_test_SRCS = ca_test_main.c ca_test.c -ca_test_LIBS = ca Com ca_test_SYS_LIBS_WIN32 = ws2_32 advapi32 user32 OBJS_vxWorks += ca_test diff --git a/modules/ca/src/client/access.cpp b/modules/ca/src/client/access.cpp index 58cc4bca5f..6b4ef323d7 100644 --- a/modules/ca/src/client/access.cpp +++ b/modules/ca/src/client/access.cpp @@ -65,7 +65,7 @@ const char * ca_message_text [] "Sorry, that feature is planned but not supported at this time", "The supplied string is unusually large", "The request was ignored because the specified channel is disconnected", -"The data type specifed is invalid", +"The data type specified is invalid", "Remote Channel not found", "Unable to locate all user specified channels", @@ -94,7 +94,7 @@ const char * ca_message_text [] "The supplied string is empty", "Unable to spawn the CA repeater thread- auto reconnect will fail", "No channel id match for search reply- search reply ignored", -"Reseting dead connection- will try to reconnect", +"Resetting dead connection- will try to reconnect", "Server (IOC) has fallen behind or is not responding- still waiting", "No internet interface with broadcast available", @@ -381,11 +381,11 @@ int epicsStdCall ca_clear_channel ( chid pChan ) } else { // - // we will definately stall out here if all of the + // we will definitely stall out here if all of the // following are true // // o user creates non-preemptive mode client library context - // o user doesnt periodically call a ca function + // o user doesn't periodically call a ca function // o user calls this function from an auxiliary thread // CallbackGuard cbGuard ( cac.cbMutex ); diff --git a/modules/ca/src/client/acctst.c b/modules/ca/src/client/acctst.c index 34298680cf..cf7c23908d 100644 --- a/modules/ca/src/client/acctst.c +++ b/modules/ca/src/client/acctst.c @@ -844,7 +844,7 @@ void verifyBlockInPendIO ( chid chan, unsigned interestLevel ) } } else if ( resp != -100 ) { - printf ( "CA didnt block for get to return?\n" ); + printf ( "CA didn't block for get to return?\n" ); } req = 1; @@ -1739,7 +1739,7 @@ void arrayEventExceptionNotify ( struct event_handler_args args ) if ( args.status == ECA_NORMAL ) { printf ( "arrayEventExceptionNotify: expected " - "exception but didnt receive one against \"%s\" \n", + "exception but didn't receive one against \"%s\" \n", ca_name ( args.chid ) ); } else { @@ -2575,7 +2575,8 @@ void monitorUpdateTest ( chid chan, unsigned interestLevel ) SEVCHK ( ca_get ( DBR_FLOAT, chan, &temp ), NULL ); SEVCHK ( ca_pend_io ( timeoutToPendIO ), NULL ); - /* printf ( "flow control bypassed %u events\n", flowCtrlCount ); */ + if (0) + printf ( "flow control bypassed %u events\n", flowCtrlCount ); showProgressEnd ( interestLevel ); } @@ -3232,7 +3233,7 @@ void verifyDisconnect ( /* * if its a local channel and will never disconnect - * then skip the portions of this test that cant be + * then skip the portions of this test that can't be * completed. */ if ( ca_get_ioc_connection_count () == 0 ) { @@ -3306,7 +3307,7 @@ void verifyContextRundownFlush ( const char * pName, unsigned interestLevel ) status = ca_create_channel ( pName, 0, 0, 0, & chan ); /* - * currently in-memory channels cant be used with this test + * currently in-memory channels can't be used with this test * !!!! FIX ME, FIX ME, FIX ME, FIX ME !!!! */ if ( status != ECA_UNAVAILINSERV ) { @@ -3334,7 +3335,7 @@ void verifyContextRundownFlush ( const char * pName, unsigned interestLevel ) status = ca_create_channel ( pName, 0, 0, 0, & chan ); SEVCHK ( status, NULL ); /* - * currently in-memory channels cant be used with this test + * currently in-memory channels can't be used with this test * !!!! FIX ME, FIX ME, FIX ME, FIX ME !!!! */ if ( status != ECA_UNAVAILINSERV ) { @@ -3378,7 +3379,7 @@ void verifyContextRundownChanStillExist ( for ( i = 0; i < NELEMENTS ( chan ); i++ ) { status = ca_create_channel ( pName, 0, 0, 0, & chan[i] ); /* - * currently in-memory channels cant be used with this test + * currently in-memory channels can't be used with this test * !!!! FIX ME, FIX ME, FIX ME, FIX ME !!!! */ if ( status == ECA_UNAVAILINSERV ) { @@ -3395,7 +3396,7 @@ void verifyContextRundownChanStillExist ( showProgressEnd ( interestLevel ); } -int acctst ( const char * pName, unsigned interestLevel, unsigned channelCount, +void acctst ( const char * pName, unsigned interestLevel, unsigned channelCount, unsigned repetitionCount, enum ca_preemptive_callback_select select ) { chid chan; @@ -3548,8 +3549,6 @@ int acctst ( const char * pName, unsigned interestLevel, unsigned channelCount, printf ( "\nTest Complete\n" ); epicsExit ( EXIT_SUCCESS ); - - return 0; } diff --git a/modules/ca/src/client/acctst.md b/modules/ca/src/client/acctst.md new file mode 100644 index 0000000000..036b440bc7 --- /dev/null +++ b/modules/ca/src/client/acctst.md @@ -0,0 +1,18 @@ +# acctst + + acctst [progress logging level] [channel duplication count] + [test repetition count] [enable preemptive callback] + +## Description + +Channel Access Client Library regression test. + +The PV used with the test must be native type `DBR_DOUBLE` or `DBR_FLOAT`, +and modified only by acctst while the test is running. Therefore, +periodically scanned hardware attached analog input records do not work +well. Test failure is indicated if the program stops prior to printing +"test complete". If unspecified the progress logging level is zero, +and no messages are printed while the test is progressing. If +unspecified, the channel duplication count is 20000. If unspecified, the +test repetition count is once only. If unspecified, preemptive callback +is disabled. diff --git a/modules/ca/src/client/bhe.cpp b/modules/ca/src/client/bhe.cpp index 3808f061ca..450db69278 100644 --- a/modules/ca/src/client/bhe.cpp +++ b/modules/ca/src/client/bhe.cpp @@ -192,7 +192,7 @@ bool bhe::updatePeriod ( this->beaconAnomalyNotify ( guard ); /* - * this is the 2nd beacon seen. We cant tell about + * this is the 2nd beacon seen. We can't tell about * the change in period at this point so we just * initialize the average period and return. */ diff --git a/modules/ca/src/client/caDiagnostics.h b/modules/ca/src/client/caDiagnostics.h index 5bf1755bc3..019913e1c9 100644 --- a/modules/ca/src/client/caDiagnostics.h +++ b/modules/ca/src/client/caDiagnostics.h @@ -20,7 +20,8 @@ extern "C" { enum appendNumberFlag {appendNumber, dontAppendNumber}; int catime ( const char *channelName, unsigned channelCount, enum appendNumberFlag appNF ); -int acctst ( const char *pname, unsigned logggingInterestLevel, +EPICS_NORETURN +void acctst ( const char *pname, unsigned logggingInterestLevel, unsigned channelCount, unsigned repetitionCount, enum ca_preemptive_callback_select select ); diff --git a/modules/ca/src/client/caEventRate.md b/modules/ca/src/client/caEventRate.md new file mode 100644 index 0000000000..c12d906814 --- /dev/null +++ b/modules/ca/src/client/caEventRate.md @@ -0,0 +1,10 @@ +# caEventRate + + caEventRate [subscription count] + +## Description + +Connect to the specified PV, subscribe for monitor updates the specified +number of times (default once), and periodically log the current sampled +event rate, average event rate, and the standard deviation of the event +rate in Hertz to standard out. diff --git a/modules/ca/src/client/caProto.h b/modules/ca/src/client/caProto.h index 57b2801777..188e098f28 100644 --- a/modules/ca/src/client/caProto.h +++ b/modules/ca/src/client/caProto.h @@ -49,7 +49,7 @@ /* * These port numbers are only used if the CA repeater and - * CA server port numbers cant be obtained from the EPICS + * CA server port numbers can't be obtained from the EPICS * environment variables "EPICS_CA_REPEATER_PORT" and * "EPICS_CA_SERVER_PORT" */ diff --git a/modules/ca/src/client/ca_client_context.cpp b/modules/ca/src/client/ca_client_context.cpp index 1e830fd080..1507fceec6 100644 --- a/modules/ca/src/client/ca_client_context.cpp +++ b/modules/ca/src/client/ca_client_context.cpp @@ -775,11 +775,11 @@ LIBCA_API int epicsStdCall ca_clear_subscription ( evid pMon ) } else { // - // we will definately stall out here if all of the + // we will definitely stall out here if all of the // following are true // // o user creates non-preemptive mode client library context - // o user doesnt periodically call a ca function + // o user doesn't periodically call a ca function // o user calls this function from an auxiliary thread // CallbackGuard cbGuard ( cac.cbMutex ); diff --git a/modules/ca/src/client/ca_test.md b/modules/ca/src/client/ca_test.md new file mode 100644 index 0000000000..e61ef644e6 --- /dev/null +++ b/modules/ca/src/client/ca_test.md @@ -0,0 +1,10 @@ +# ca_test + + ca_test [value to be written] + +## Description + +If a value is specified it is written to the PV. Next, the current value +of the PV is converted to each of the many external data type that can +be specified at the CA client library interface, and each of these is +formatted and then output to the console. diff --git a/modules/ca/src/client/cac.cpp b/modules/ca/src/client/cac.cpp index f8d1d08000..fd641ad214 100644 --- a/modules/ca/src/client/cac.cpp +++ b/modules/ca/src/client/cac.cpp @@ -25,6 +25,7 @@ #include #include // vxWorks 6.0 requires this include +#include "epicsStdio.h" #include "dbDefs.h" #include "epicsGuard.h" #include "epicsVersion.h" @@ -1008,7 +1009,7 @@ bool cac::defaultExcep ( char buf[512]; char hostName[64]; iiu.getHostName ( guard, hostName, sizeof ( hostName ) ); - sprintf ( buf, "host=%s ctx=%.400s", hostName, pCtx ); + epicsSnprintf( buf, sizeof(buf), "host=%s ctx=%.400s", hostName, pCtx ); this->notify.exception ( guard, status, buf, 0, 0u ); return true; } @@ -1312,7 +1313,7 @@ void cac::pvMultiplyDefinedNotify ( msgForMultiplyDefinedPV & mfmdpv, const char * pChannelName, const char * pAcc, const char * pRej ) { char buf[256]; - sprintf ( buf, "Channel: \"%.64s\", Connecting to: %.64s, Ignored: %.64s", + epicsSnprintf( buf, sizeof(buf), "Channel: \"%.64s\", Connecting to: %.64s, Ignored: %.64s", pChannelName, pAcc, pRej ); { callbackManager mgr ( this->notify, this->cbMutex ); diff --git a/modules/ca/src/client/cac.h b/modules/ca/src/client/cac.h index 79dcbaa9be..8fcb7c563b 100644 --- a/modules/ca/src/client/cac.h +++ b/modules/ca/src/client/cac.h @@ -203,6 +203,7 @@ class cac : void destroyIIU ( tcpiiu & iiu ); const char * pLocalHostName (); + const resTable < tcpiiu, caServerID > & getServerTable(); private: epicsSingleton < localHostName > :: reference _refLocalHostName; @@ -424,4 +425,10 @@ inline double cac :: return this->connTMO; } +inline const resTable < tcpiiu, caServerID > & cac :: + getServerTable() +{ + return this->serverTable; +} + #endif // ifndef INC_cac_H diff --git a/modules/ca/src/client/cacChannel.cpp b/modules/ca/src/client/cacChannel.cpp index 33345967ca..a76d6e9574 100644 --- a/modules/ca/src/client/cacChannel.cpp +++ b/modules/ca/src/client/cacChannel.cpp @@ -129,6 +129,13 @@ unsigned cacChannel::getHostName ( return 0u; } +unsigned cacChannel::getHostMinorProtocol ( + epicsGuard < epicsMutex > &) const throw () +{ + epicsThreadOnce ( & cacChannelIdOnce, cacChannelSetup, 0); + return 0u; +} + // the default is to assume that it is a locally hosted channel const char * cacChannel::pHostName ( epicsGuard < epicsMutex > & ) const throw () diff --git a/modules/ca/src/client/cacIO.h b/modules/ca/src/client/cacIO.h index 760d8dd72a..777155047e 100644 --- a/modules/ca/src/client/cacIO.h +++ b/modules/ca/src/client/cacIO.h @@ -246,7 +246,8 @@ class LIBCA_API cacChannel { // !! deprecated, avoid use !! virtual const char * pHostName ( epicsGuard < epicsMutex > & guard ) const throw (); - + virtual unsigned getHostMinorProtocol ( + epicsGuard < epicsMutex > &) const throw () ; // exceptions class badString {}; class badType {}; diff --git a/modules/ca/src/client/cadef.h b/modules/ca/src/client/cadef.h index 12861c3845..a112021c31 100644 --- a/modules/ca/src/client/cadef.h +++ b/modules/ca/src/client/cadef.h @@ -7,6 +7,7 @@ * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ + /* * * L O S A L A M O S @@ -21,6 +22,11 @@ * */ +/** \file cadef.h + * + * \brief Channel Access library. + */ + #ifndef INC_cadef_H #define INC_cadef_H @@ -43,29 +49,34 @@ extern "C" { #endif +/** \brief Channel identifier. */ typedef struct oldChannelNotify *chid; +/** \brief Channel identifier. */ typedef chid chanId; /* for when the structures field name is "chid" */ typedef long chtype; typedef struct oldSubscription *evid; typedef double ca_real; -/* arguments passed to user connection handlers */ +/** \brief Arguments passed to user connection handlers. */ struct connection_handler_args { - chanId chid; /* channel id */ - long op; /* one of CA_OP_CONN_UP or CA_OP_CONN_DOWN */ + chanId chid; /**< channel id */ + long op; /**< one of ::CA_OP_CONN_UP or ::CA_OP_CONN_DOWN */ }; typedef void caCh (struct connection_handler_args args); +/** \brief Access rights. + */ typedef struct ca_access_rights { - unsigned read_access:1; - unsigned write_access:1; + unsigned read_access:1; /**< Read access. */ + unsigned write_access:1; /**< Write access. */ } caar; -/* arguments passed to user access rights handlers */ +/** \brief Arguments passed to user access rights handlers + */ struct access_rights_handler_args { - chanId chid; /* channel id */ - caar ar; /* new access rights state */ + chanId chid; /**< Channel id. */ + caar ar; /**< New access rights state. */ }; typedef void caArh (struct access_rights_handler_args args); @@ -73,8 +84,7 @@ typedef void caArh (struct access_rights_handler_args args); /* The conversion routine to call for each type */ #define VALID_TYPE(TYPE) (((unsigned short)TYPE)<=LAST_BUFFER_TYPE) -/* - * Arguments passed to event handlers and get/put call back handlers. +/** \brief Arguments passed to event handlers and get/put call back handlers. * * The status field below is the CA ECA_XXX status of the requested * operation which is saved from when the operation was attempted in the @@ -83,32 +93,46 @@ typedef void caArh (struct access_rights_handler_args args); * and the requested operation can not be assumed to be successful. */ typedef struct event_handler_args { - void *usr; /* user argument supplied with request */ - chanId chid; /* channel id */ - long type; /* the type of the item returned */ - long count; /* the element count of the item returned */ - const void *dbr; /* a pointer to the item returned */ - int status; /* ECA_XXX status of the requested op from the server */ + void *usr; /**< User argument supplied with request. */ + chanId chid; /**< Channel id. */ + long type; /**< The type of the item returned. */ + long count; /**< The element count of the item returned. */ + const void *dbr; /**< A pointer to the item returned. */ + int status; /**< `ECA_XXXX` status of the requested op from the server. */ } evargs; typedef void caEventCallBackFunc (struct event_handler_args); +/** \brief A built-in subscription update callback handler + * for debugging purposes that prints diagnostics to standard out. + * + * # Examples + * + * ```c + * void ca_test_event (); + * status = ca_create_subscription ( type, chid, ca_test_event, NULL, NULL ); + * SEVCHK ( status, .... ); + * ``` + * + * \sa ca_create_subscription() + */ LIBCA_API void epicsStdCall ca_test_event ( struct event_handler_args ); -/* arguments passed to user exception handlers */ +/** \brief Arguments passed to user exception handlers. + */ struct exception_handler_args { - void *usr; /* user argument supplied when installed */ - chanId chid; /* channel id (may be nill) */ - long type; /* type requested */ - long count; /* count requested */ - void *addr; /* user's address to write results of CA_OP_GET */ - long stat; /* channel access ECA_XXXX status code */ - long op; /* CA_OP_GET, CA_OP_PUT, ..., CA_OP_OTHER */ - const char *ctx; /* a character string containing context info */ - const char *pFile; /* source file name (may be NULL) */ - unsigned lineNo; /* source file line number (may be zero) */ + void *usr; /**< User argument supplied when installed */ + chanId chid; /**< Channel id (may be null) */ + long type; /**< Type requested */ + long count; /**< Count requested */ + void *addr; /**< User's address to write results of ::CA_OP_GET */ + long stat; /**< Channel access ECA_XXXX status code */ + long op; /**< ::CA_OP_GET, ::CA_OP_PUT, ..., ::CA_OP_OTHER */ + const char *ctx; /**< A character string containing context info */ + const char *pFile; /**< Source file name (may be NULL) */ + unsigned lineNo; /**< Source file line number (may be zero) */ }; typedef unsigned CA_SYNC_GID; @@ -116,24 +140,61 @@ typedef unsigned CA_SYNC_GID; /* * External OP codes for CA operations */ + +/** + * \name Channel Access operations + * @{ + */ + +/** \brief GET Channel Access operation. */ #define CA_OP_GET 0 +/** \brief PUT Channel Access operation. */ #define CA_OP_PUT 1 +/** \brief CREATE_CHANNEL Channel Access operation. */ #define CA_OP_CREATE_CHANNEL 2 +/** \brief ADD_EVENT Channel Access operation. */ #define CA_OP_ADD_EVENT 3 +/** \brief CLEAR_EVENT Channel Access operation. */ #define CA_OP_CLEAR_EVENT 4 +/** \brief Other Channel Access operation. */ #define CA_OP_OTHER 5 -/* - * used with connection_handler_args +/** \brief Connection up Channel Access operation. + * + * Used with connection_handler_args */ #define CA_OP_CONN_UP 6 + +/** \brief Connection down Channel Access operation. + * + * Used with connection_handler_args + */ #define CA_OP_CONN_DOWN 7 -/* deprecated */ +/** \brief SEARCH Channel Access operation. + * + * \deprecated + */ #define CA_OP_SEARCH 2 -/* - * provides efficient test and display of channel access errors +/** @} */ + +/** \brief Provides efficient test and display of channel access errors. + * + * SEVCHK() is a macro envelope around ca_signal() which only calls + * ca_signal() if the supplied error code indicates an unsuccessful + * operation. SEVCHK() is the recommended error handler for simple applications + * which do not wish to write code testing the status returned from each + * channel access call. + * + * # Examples + * + * ```c + * status = ca_context_create (...); + * SEVCHK ( status, "Unable to create a CA client context" ); + * ``` + * + * \sa ca_signal(). */ #define SEVCHK(CA_ERROR_CODE, MESSAGE_STRING) \ { \ @@ -146,25 +207,85 @@ typedef unsigned CA_SYNC_GID; __LINE__); \ } +/** \brief The channel's native type when disconnected. */ +#define TYPENOTCONN (-1) -#define TYPENOTCONN (-1) /* the channel's native type when disconnected */ +/** \brief Return the native type in the server of the process variable. + * + * \param[in] chan Channel identifier. + * \returns The data type code will be a member of the set of `DBF_XXXX` in `db_access.h`. + * The constant TYPENOTCONN is returned if the channel is disconnected. + */ LIBCA_API short epicsStdCall ca_field_type (chid chan); + +/** \brief Return the maximum array element count in the server for the specified IO channel. + * + * \param[in] chan Channel identifier. + * \returns The maximum array element count in the server. + * An element count of zero is returned if the channel is disconnected. + */ LIBCA_API unsigned long epicsStdCall ca_element_count (chid chan); + +/** \brief Return the name provided when the supplied channel id was created. + * + * \param[in] chan Channel identifier. + * \returns The channel name. + * The string returned is valid as long as the channel specified exists. + */ LIBCA_API const char * epicsStdCall ca_name (chid chan); + +/** \brief Set a user private void pointer variable retained + * with each channel for use at the users discretion. + * + * \param[in] chan Channel identifier. + * \param[in] puser user private void pointer + */ LIBCA_API void epicsStdCall ca_set_puser (chid chan, void *puser); + +/** \brief Return a user private void pointer variable + * retained with each channel for use at the users discretion. + * + * \param[in] chan Channel identifier. + * \returns User private pointer + */ LIBCA_API void * epicsStdCall ca_puser (chid chan); + +/** \brief Returns whether the client currently has read access + * to the specified channel. + * + * \param[in] chan Channel identifier. + * \returns Boolean true if the client currently has read access to the specified channel + * and boolean false otherwise. + */ LIBCA_API unsigned epicsStdCall ca_read_access (chid chan); + +/** \brief Returns whether the client currently has write access + * to the specified channel. + * + * \param[in] chan Channel identifier. + * \returns Boolean true if the client currently has write access to the specified channel + * and boolean false otherwise + */ LIBCA_API unsigned epicsStdCall ca_write_access (chid chan); -/* - * cs_ - `channel state' +/** \brief Channel state + */ +enum channel_state { + /// valid chid, IOC not found or unavailable + cs_never_conn, + /// valid chid, IOC was found, but unavailable (previously connected to server) + cs_prev_conn, + /// valid chid, IOC was found, still available + cs_conn, + /// channel deleted by user + cs_closed +}; + +/** \brief Returns an enumerated type indicating the current state of the specified IO channel. * - * cs_never_conn valid chid, IOC not found - * cs_prev_conn valid chid, IOC was found, but unavailable - * cs_conn valid chid, IOC was found, still available - * cs_closed channel deleted by user + * \param[in] chan Channel identifier. + * \returns the connection state. */ -enum channel_state {cs_never_conn, cs_prev_conn, cs_conn, cs_closed}; LIBCA_API enum channel_state epicsStdCall ca_state (chid chan); /************************************************************************/ @@ -172,19 +293,121 @@ LIBCA_API enum channel_state epicsStdCall ca_state (chid chan); /* */ /* Must be called once before calling any of the other routines */ /************************************************************************/ + +/** \brief Initialize a Channel Access task. + * + * \deprecated Use ca_context_create() instead. + */ LIBCA_API int epicsStdCall ca_task_initialize (void); + enum ca_preemptive_callback_select { ca_disable_preemptive_callback, ca_enable_preemptive_callback }; + +/** \brief Create a Channel Access context. + * + * This function, or ca_attach_context(), should be called once from each + * thread prior to making any of the other Channel Access calls. If one of the + * above is not called before making other CA calls then a non-preemptive + * context is created by default, and future attempts to create a preemptive + * context for the current threads will fail. + * + * If ::ca_disable_preemptive_callback is specified then additional threads are + * *not* allowed to join the CA context using ca_attach_context() because + * allowing other threads to join implies that CA callbacks will be called + * preemptively from more than one thread. + * + * \param[in] select + * \parblock + * This argument specifies if preemptive invocation of callback + * functions is allowed. If so your callback functions might be called + * when the thread that calls this routine is not executing in the CA + * client library. There are two implications to consider. + * + * First, if preemptive callback mode is enabled the developer must + * provide mutual exclusion protection for his data structures. In this + * mode it's possible for two threads to touch the application's data + * structures at once: this might be the initializing thread (the + * thread that called ca_context_create()) and also a private thread + * created by the CA client library for the purpose of receiving + * network messages and calling callbacks. It might be prudent for + * developers who are unfamiliar with mutual exclusion locking in a + * multi-threaded environment to specify + * ::ca_disable_preemptive_callback. + * + * Second, if preemptive callback mode is enabled the application is no + * longer burdened with the necessity of periodically polling the CA + * client library in order that it might take care of its background + * activities. If ca_enable_preemptive_callback is specified then CA + * client background activities, such as connection management, will + * proceed even if the thread that calls this routine is not executing + * in the CA client library. Furthermore, in preemptive callback mode + * callbacks might be called with less latency because the library is + * not required to wait until the initializing thread (the thread that + * called ca_context_create()) is executing within the CA client library. + * \endparblock + * + * \returns ::ECA_NORMAL - Normal successful completion + * \returns ::ECA_ALLOCMEM - Failed, unable to allocate space in pool + * \returns ::ECA_NOTTHREADED - Current thread is already a member of a non-preemptive callback CA context (possibly created implicitly) + * + * \sa ca_context_destroy() + */ LIBCA_API int epicsStdCall ca_context_create (enum ca_preemptive_callback_select select); -LIBCA_API void epicsStdCall ca_detach_context (); + +/** \brief Detach from any CA context currently attached to the calling thread. + * + * This does *not* cleanup or shutdown any currently attached CA context (for + * that use ca_context_destroy()). + * + * \sa ca_current_context(), ca_attach_context(), ca_context_create(), ca_context_destroy() + */ +LIBCA_API void epicsStdCall ca_detach_context (); /************************************************************************/ /* Remove CA facility from your task */ /* */ /* Normally called automatically at task exit */ /************************************************************************/ + +/** \brief Exit a Channel Access task. + * + * \deprecated Use ca_context_destroy() instead. + */ LIBCA_API int epicsStdCall ca_task_exit (void); + +/** \brief Destroy a Channel Access context. + * + * Shut down the calling thread's channel access client context and free + * any resources allocated. Detach the calling thread from any CA client + * context. + * + * Any user-created threads that have attached themselves to the CA context + * must stop using it prior to its being destroyed. A program running in an + * IOC context must delete all of its channels prior to calling + * ca_context_destroy() to avoid a crash. + * + * A CA client application that calls epicsExit() *must* install an EPICS + * exit handler that calls ca_context_destroy() only *after* first + * calling ca_context_create(). This will guarantee that the EPICS exit + * handlers get called in the correct order. + * + * On many OS that execute programs in a process based environment the + * resources used by the client library such as sockets and allocated + * memory are automatically released by the system when the process exits + * and ca_context_destroy() hasn't been called, but on light weight + * systems such as vxWorks or RTEMS no cleanup occurs unless the + * application calls ca_context_destroy(). + * + * \note This operation blocks until any user callbacks for any channel + * created in the current context have run to completion. If callbacks take + * a lock (mutex) then it is the user's responsibility to ensure that this + * lock is not held when ca_clear_context() is called, otherwise a + * deadlock may ensue. (See also ca_clear_channel() and + * ca_clear_subscription().) + * + * \sa ca_context_create() + */ LIBCA_API void epicsStdCall ca_context_destroy (void); typedef unsigned capri; @@ -196,17 +419,102 @@ typedef unsigned capri; #define CA_PRIORITY_ARCHIVE 20 #define CA_PRIORITY_OPI 0 -/* - * ca_create_channel () - * - * pChanName R channel name string - * pConnStateCallback R address of connection state change - * callback function - * pUserPrivate R placed in the channel's user private field - * o can be fetched later by ca_puser(CHID) - * o passed as void * arg to *pConnectCallback above - * priority R priority level in the server 0 - 100 - * pChanID RW channel id written here +/** \brief Create a Channel Access channel. + * + * This function creates a CA channel. The CA client library will attempt + * to establish and maintain a virtual circuit between the caller's + * application and a named process variable in a CA server. Each call to + * ca_create_channel() allocates resources in the CA client library and + * potentially also a CA server. The function ca_clear_channel() is used + * to release these resources. If successful, the routine writes a channel + * identifier into the user's variable of type "chid". This identifier + * can be used with any channel access call that operates on a channel. + * + * The circuit may be initially connected or disconnected depending on the + * state of the network and the location of the channel. A channel will + * only enter a connected state after the server's address is determined, + * and only if channel access successfully establishes a virtual circuit + * through the network to the server. Channel access routines that send a + * request to a server will return ::ECA_DISCONNCHID if the channel is + * currently disconnected. + * + * There are two ways to obtain asynchronous notification when a channel + * enters a connected state. + * + * - The first and simplest method requires that you call ca_pend_io(), + * and wait for successful completion, prior to using a channel that + * was created specifying a null connection callback function pointer. + * - The second method requires that you register a connection handler by + * supplying a valid connection callback function pointer. This + * connection handler is called whenever the connection state of the + * channel changes. If you have installed a connection handler then + * ca_pend_io() will *not* block waiting for the channel to enter a + * connected state. + * + * The function ca_state(CHID) can be used to test the connection state of + * a channel. Valid connections may be isolated from invalid ones with this + * function if ca_pend_io() times out. + * + * Due to the inherently transient nature of network connections the order + * of connection callbacks relative to the order that ca_create_channel() + * calls are made by the application can't be guaranteed, and application + * programs may need to be prepared for a connected channel to enter a + * disconnected state at any time. + * + * # Example + * + * See `caExample.c` in the example application created by `makeBaseApp.pl`. + * + * \param[in] pChanName A nil terminated process variable name string. + * \parblock + * EPICS process control * function block database variable names are of the + * form `.`. If the field name and the period + * separator are omitted then the `VAL` field is implicit. For example + * `RFHV01` and `RFHV01.VAL` reference the same EPICS process control function + * block database variable. + * \endparblock + * + * \param[in] pConnStateCallback address of connection state change callback function + * \parblock + * Optional pointer to the user's callback function to be run when the + * connection state changes. Casual users of channel access may decide + * to set this field to null or 0 if they do not need to have a + * callback function run in response to each connection state change + * event. + * + * A connection_handler_args structure is passed *by value* to the user's + * connection callback function. The `op` field will be set by the CA + * client library to ::CA_OP_CONN_UP when the channel connects, and to + * ::CA_OP_CONN_DOWN when the channel disconnects. See ca_puser() + * if the `pUserPrivate` argument is required in your callback handler. + * \endparblock + * + * \param[in] pUserPrivate The value of this void pointer argument is retained in storage + * associated with the specified channel. + * Casual users of channel access may wish to set this field to null or 0. + * Can be fetched later by ca_puser(CHID). + * passed as `void *` arg to `*pConnStateCallback` above + * + * \param[in] priority The priority level for dispatch within the server or + * network with 0 specifying the lowest dispatch priority and 99 the highest. + * This parameter currently does not impact dispatch priorities within the + * client, but this might change in the future. The abstract priority range + * specified is mapped into an operating system specific range of priorities + * within the server. This parameter is ignored if the server is running on a + * network or operating system that does not have native support for + * prioritized delivery or execution respectively. Specifying many different + * priorities within the same program can increase resource consumption in the + * client and the server because an independent virtual circuit, and associated + * data structures, is created for each priority that is used on a particular + * server. + * + * \param[inout] pChanID The user supplied channel identifier storage is overwritten + * with a channel identifier if this routine is successful. + * + * \returns ::ECA_NORMAL - Normal successful completion + * \returns ::ECA_BADTYPE - Invalid DBR_XXXX type + * \returns ::ECA_STRTOBIG - Unusually large string + * \returns ::ECA_ALLOCMEM - Unable to allocate memory */ LIBCA_API int epicsStdCall ca_create_channel ( @@ -229,38 +537,112 @@ LIBCA_API int epicsStdCall ca_change_connection_event caCh * pfunc ); -/* - * ca_replace_access_rights_event () +/** \brief Install or replace the access rights state change callback handler + * for the specified channel. * - * chan R channel identifier - * pfunc R address of access rights call-back function + * The callback handler is called in the following situations. + * + * - whenever CA connects the channel immediately before the channel's + * connection handler is called + * - whenever CA disconnects the channel immediately after the channel's + * disconnect callback is called + * - once immediately after installation if the channel is connected. + * - whenever the access rights state of a connected channel changes + * + * When a channel is created no access rights handler is installed. + * + * \param[in] chan Channel identifier. + * \param[in] pfunc Pointer to a user supplied callback function. + * A null pointer uninstalls the current handler. + * An access_rights_handler_args structure is passed *by value* to the supplied callback handler. + * + * \returns ::ECA_NORMAL - Normal successful completion */ LIBCA_API int epicsStdCall ca_replace_access_rights_event ( chid chan, caArh *pfunc ); -/* - * ca_add_exception_event () - * - * replace the default exception handler - * - * pfunc R address of exception call-back function - * pArg R copy of this pointer passed to exception - * call-back function - */ typedef void caExceptionHandler (struct exception_handler_args); + +/** \brief Replace the currently installed CA context global exception handler callback. + * + * When an error occurs in the server asynchronous to the clients thread then + * information about this type of error is passed from the server to the client + * in an exception message. When the client receives this exception message an + * exception handler callback is called. The default exception handler prints a + * diagnostic message on the client's standard out and terminates execution if + * the error condition is severe. + * + * Note that certain fields in struct exception_handler_args are not + * applicable in the context of some error messages. For instance, a failed get + * will supply the address in the client task where the returned value was + * requested to be written. For other failed operations the value of the addr + * field should not be used. + * + * # Example + * + * ```c + * void ca_exception_handler ( + * struct exception_handler_args args) + * { + * char buf[512]; + * char *pName; + * + * if ( args.chid ) { + * pName = ca_name ( args.chid ); + * } + * else { + * pName = "?"; + * } + * sprintf ( buf, + * "%s - with request chan=%s op=%d data type=%s count=%d", + * args.ctx, pName, args.op, dbr_type_to_text ( args.type ), args.count ); + * ca_signal ( args.stat, buf ); + * } + * ca_add_exception_event ( ca_exception_handler , 0 ); + * ``` + * + * \param[in] pfunc Pointer to a user callback function to be executed when exceptions occur. + * Passing a null value causes the default exception handler to be reinstalled. + * An exception_handler_args structure is passed by value to the user's callback function. + * Currently, the `op` field can be one of ::CA_OP_GET, ::CA_OP_PUT, + * ::CA_OP_CREATE_CHANNEL, ::CA_OP_ADD_EVENT, ::CA_OP_CLEAR_EVENT, + * or ::CA_OP_OTHER. + * + * \param[in] pArg Pointer sized variable retained and passed back to user function above. + * + * \returns ::ECA_NORMAL - Normal successful completion + */ LIBCA_API int epicsStdCall ca_add_exception_event ( caExceptionHandler *pfunc, void *pArg ); -/* - * ca_clear_channel() - * - deallocate resources reserved for a channel +/** \brief Shutdown and reclaim resources associated with a channel created by ca_create_channel(). + * + * All remote operation requests such as the above are accumulated + * (buffered) and not forwarded to the IOC until one of ca_flush_io(), + * ca_pend_io(), ca_pend_event(), or ca_sg_block() are called. This + * allows several requests to be efficiently sent over the network in one + * message. + * + * Clearing a channel does not cause its disconnect handler to be called, + * but clearing a channel does shutdown and reclaim any channel state + * change event subscriptions (monitors) registered with the channel. * - * chanId R channel ID + * Note: This operation blocks until any user callbacks for this channel + * have run to completion. If callbacks take a lock (mutex) then it is the + * user's responsibility to ensure that this lock is not held when + * ca_clear_channel() is called, otherwise a deadlock may ensue. + * + * \sa ca_clear_subscription() + * + * \param[in] chanId Identifies the channel to delete. + * + * \returns ::ECA_NORMAL - Normal successful completion + * \returns ::ECA_BADCHID - Corrupted CHID */ LIBCA_API int epicsStdCall ca_clear_channel ( @@ -293,22 +675,108 @@ ca_array_put(DBR_STRING, 1u, chan, (const dbr_string_t *) (pValue)) #define ca_rput(chan,pValue) \ ca_array_put(DBR_FLOAT, 1u, chan, (const dbr_float_t *) pValue) -/* - * ca_put() +/** \brief Write a scalar value to a process variable. + * + * See ca_array_put(). * - * type R data type from db_access.h - * chan R channel identifier - * pValue R new channel value copied from this location + * \param[in] type The external type of the supplied value to be written. + * Conversion will occur if this does not match the native type. Specify one + * from the set of `DBR_XXXX` in `db_access.h` + * + * \param[in] chan Channel identifier. + * + * \param[in] pValue Pointer to a value or array of values provided by the application + * to be written to the channel. + * + * \returns ::ECA_NORMAL - Normal successful completion + * \returns ::ECA_BADCHID - Corrupted CHID + * \returns ::ECA_BADTYPE - Invalid DBR_XXXX type + * \returns ::ECA_BADCOUNT - Requested count larger than native element count + * \returns ::ECA_STRTOBIG - Unusually large string supplied + * \returns ::ECA_NOWTACCESS - Write access denied + * \returns ::ECA_ALLOCMEM - Unable to allocate memory + * \returns ::ECA_DISCONN - Channel is disconnected */ #define ca_put(type, chan, pValue) ca_array_put (type, 1u, chan, pValue) -/* - * ca_array_put() +/** \brief Write a scalar or array value to a process variable. * - * type R data type from db_access.h - * count R array element count - * chan R channel identifier - * pValue R new channel value copied from this location + * When ca_put() or ca_array_put() are invoked the client will receive + * no response unless the request can not be fulfilled in the server. If + * unsuccessful an exception handler is run on the client side. + * + * When ca_put_callback() or ca_array_put_callback() are invoked the + * user supplied asynchronous callback is called only after the initiated + * write operation, and all actions resulting from the initiating write + * operation, complete. The arguments to the user supplied callback function + * are declared in the structure event_handler_args and include the pointer + * sized user argument supplied when ca_array_put_callback() is called. + * + * If unsuccessful the callback function is invoked indicating failure + * status. + * + * If the channel disconnects before a put callback request can be + * completed, then the client's callback function is called with failure + * status, but this does not guarantee that the server did not receive and + * process the request before the disconnect. If a connection is lost and + * then resumed outstanding ca put requests are not automatically reissued + * following reconnect. + * + * All of these functions return ::ECA_DISCONN if the channel is currently + * disconnected. + * + * All put requests are accumulated (buffered) and not forwarded to the IOC + * until one of ca_flush_io(), ca_pend_io(), ca_pend_event(), or + * ca_sg_block() are called. This allows several requests to be + * efficiently combined into one message. + * + * # Description (IOC Database Specific) + * + * A CA put request causes the record to process if the record's SCAN + * field is set to passive, and the field being written has its process + * passive attribute set to true. If such a record is already processing + * when a put request is initiated the specified field is written + * immediately, and the record is scheduled to process again as soon as it + * finishes processing. Earlier instances of multiple put requests + * initiated while the record is being processing may be discarded, but the + * last put request initiated is always written and processed. + * + * A CA put *callback* request causes the record to process if the + * record's SCAN field is set to passive, and the field being written has + * its process passive attribute set to true. For such a record, the + * user's put callback function is not called until after the record, and + * any records that the record links to, finish processing. If such a + * record is already processing when a put *callback* request is initiated + * the put *callback* request is postponed until the record, and any + * records it links to, finish processing. + * + * If the record's SCAN field is not set to passive, or the field being + * written has its process passive attribute set to false then the CA put + * or CA put *callback* request cause the specified field to be immediately + * written, but they do not cause the record to be processed. + * + * \sa ca_flush_io(), ca_pend_event(), ca_sg_array_put() + * + * \param[in] type The external type of the supplied value to be written. + * Conversion will occur if this does not match the native type. Specify one + * from the set of `DBR_XXXX` in `db_access.h` + * + * \param[in] count Element count to be written to the specified channel. + * This must match the array pointed to by pValue. + * + * \param[in] chanId Channel identifier. + * + * \param[in] pValue Pointer to a value or array of values provided by the application + * to be written to the channel. + * + * \returns ::ECA_NORMAL - Normal successful completion + * \returns ::ECA_BADCHID - Corrupted CHID + * \returns ::ECA_BADTYPE - Invalid DBR_XXXX type + * \returns ::ECA_BADCOUNT - Requested count larger than native element count + * \returns ::ECA_STRTOBIG - Unusually large string supplied + * \returns ::ECA_NOWTACCESS - Write access denied + * \returns ::ECA_ALLOCMEM - Unable to allocate memory + * \returns ::ECA_DISCONN - Channel is disconnected */ LIBCA_API int epicsStdCall ca_array_put ( @@ -318,22 +786,36 @@ LIBCA_API int epicsStdCall ca_array_put const void * pValue ); -/* - * ca_array_put_callback() +/** \brief Asynchronously write a scalar or array value to a process variable. * - * This routine functions identically to the original ca put request - * with the addition of a callback to the user supplied function - * after recod processing completes in the IOC. The arguments - * to the user supplied callback function are declared in - * the structure event_handler_args and include the pointer - * sized user argument supplied when ca_array_put_callback() is called. + * See ca_array_put(). * - * type R data type from db_access.h - * count R array element count - * chan R channel identifier - * pValue R new channel value copied from this location - * pFunc R pointer to call-back function - * pArg R copy of this pointer passed to pFunc + * \param[in] type The external type of the supplied value to be written. + * Conversion will occur if this does not match the native type. Specify one + * from the set of `DBR_XXXX` in `db_access.h` + * + * \param[in] count Element count to be written to the specified channel. + * This must match the array pointed to by pValue. + * + * \param[in] chanId Channel identifier. + * + * \param[in] pValue Pointer to a value or array of values provided by the application + * to be written to the channel. + * + * \param[in] pFunc Pointer to a user supplied callback function to be run + * when the requested operation completes. + * + * \param[in] pArg Pointer sized variable retained + * and then passed back to user supplied function above. + * + * \returns ::ECA_NORMAL - Normal successful completion + * \returns ::ECA_BADCHID - Corrupted CHID + * \returns ::ECA_BADTYPE - Invalid DBR_XXXX type + * \returns ::ECA_BADCOUNT - Requested count larger than native element count + * \returns ::ECA_STRTOBIG - Unusually large string supplied + * \returns ::ECA_NOWTACCESS - Write access denied + * \returns ::ECA_ALLOCMEM - Unable to allocate memory + * \returns ::ECA_DISCONN - Channel is disconnected */ LIBCA_API int epicsStdCall ca_array_put_callback ( @@ -345,6 +827,34 @@ LIBCA_API int epicsStdCall ca_array_put_callback void * pArg ); +/** \brief Asynchronously write a scalar value to a process variable. + * + * See ca_array_put(). + * + * \param[in] type The external type of the supplied value to be written. + * Conversion will occur if this does not match the native type. Specify one + * from the set of `DBR_XXXX` in `db_access.h` + * + * \param[in] chan Channel identifier. + * + * \param[in] pValue Pointer to a value or array of values provided by the application + * to be written to the channel. + * + * \param[in] pFunc Pointer to a user supplied callback function to be run + * when the requested operation completes. + * + * \param[in] pArg Pointer sized variable retained + * and then passed back to user supplied function above. + * + * \returns ::ECA_NORMAL - Normal successful completion + * \returns ::ECA_BADCHID - Corrupted CHID + * \returns ::ECA_BADTYPE - Invalid DBR_XXXX type + * \returns ::ECA_BADCOUNT - Requested count larger than native element count + * \returns ::ECA_STRTOBIG - Unusually large string supplied + * \returns ::ECA_NOWTACCESS - Write access denied + * \returns ::ECA_ALLOCMEM - Unable to allocate memory + * \returns ::ECA_DISCONN - Channel is disconnected + */ #define ca_put_callback(type, chan, pValue, pFunc, pArg) \ ca_array_put_callback(type, 1u, chan, pValue, pFunc, pArg) @@ -375,22 +885,88 @@ ca_array_get(DBR_STRING, 1u, chan, (dbr_string_t *)(pValue)) #define ca_rget(chan, pValue) \ ca_array_get(DBR_FLOAT, 1u, chan, (dbr_float_t *)(pValue)) -/* - * ca_rget() +/** \brief Read a scalar value from a process variable. * - * type R data type from db_access.h - * chan R channel identifier - * pValue W channel value copied to this location + * See ca_array_get(). + * + * \param[in] type The external type of the user variable to return the value into. + * Conversion will occur if this does not match the native type. + * Specify one from the set of DBR_XXXX in db_access.h + * + * \param[in] chan Channel identifier. + * + * \param[out] pValue Pointer to an application supplied buffer + * where the current value of the channel is to be written. + * + * \returns ::ECA_NORMAL - Normal successful completion + * \returns ::ECA_BADTYPE - Invalid DBR_XXXX type + * \returns ::ECA_BADCHID - Corrupted CHID + * \returns ::ECA_BADCOUNT - Requested count larger than native element count + * \returns ::ECA_GETFAIL - A local database get failed + * \returns ::ECA_NORDACCESS - Read access denied + * \returns ::ECA_ALLOCMEM - Unable to allocate memory + * \returns ::ECA_DISCONN - Channel is disconnected */ #define ca_get(type, chan, pValue) ca_array_get(type, 1u, chan, pValue) -/* - * ca_array_get() +/** \brief Read a scalar or array value from a process variable. * - * type R data type from db_access.h - * count R array element count - * chan R channel identifier - * pValue W channel value copied to this location + * When ca_get() or ca_array_get() are invoked the returned channel + * value can't be assumed to be stable in the application supplied buffer + * until after ECA_NORMAL is returned from ca_pend_io(). If a connection + * is lost outstanding ca get requests are not automatically reissued + * following reconnect. + * + * When ca_get_callback() or ca_array_get_callback() are invoked a + * value is read from the channel and then the user's callback is invoked + * with a pointer to the retrieved value. Note that ca_pend_io() will not + * block for the delivery of values requested by ca_get_callback(). If + * the channel disconnects before a ca_get_callback() request can be + * completed, then the client's callback function is called with failure + * status. + * + * All of these functions return ::ECA_DISCONN if the channel is currently + * disconnected. + * + * All get requests are accumulated (buffered) and not forwarded to the IOC + * until one of ca_flush_io(), ca_pend_io(), ca_pend_event(), or + * ca_sg_block() are called. This allows several requests to be + * efficiently sent over the network in one message. + * + * ## Description (IOC Database Specific) + * + * A CA get or CA get callback request causes the record's field to be + * read immediately independent of whether the record is currently being + * processed or not. There is currently no mechanism in place to cause a + * record to be processed when a CA get request is initiated. + * + * # Example + * + * See `caExample.c` in the example application created by `makeBaseApp.pl`. + * + * \sa ca_pend_io(), ca_pend_event(), ca_sg_array_get() + * + * \param[in] type The external type of the user variable to return the value into. + * Conversion will occur if this does not match the native type. + * Specify one from the set of DBR_XXXX in db_access.h + * + * \param[in] count Element count to be read from the specified channel. + * Must match the array pointed to by pValue. + * For ca_array_get_callback() a count of zero means use the current element count from the server. + * + * \param[in] chanId Channel identifier. + * + * \param[out] pValue Pointer to an application supplied buffer + * where the current value of the channel is to be written. + * + * \returns ::ECA_NORMAL - Normal successful completion + * \returns ::ECA_BADTYPE - Invalid DBR_XXXX type + * \returns ::ECA_BADCHID - Corrupted CHID + * \returns ::ECA_BADCOUNT - Requested count larger than native element count + * \returns ::ECA_GETFAIL - A local database get failed + * \returns ::ECA_NORDACCESS - Read access denied + * \returns ::ECA_ALLOCMEM - Unable to allocate memory + * \returns ::ECA_DISCONN - Channel is disconnected */ LIBCA_API int epicsStdCall ca_array_get ( @@ -431,25 +1007,62 @@ ca_array_get_callback (DBR_STRING, 1u, chan, pFunc, pArg) #define ca_rget_callback(chan, pFunc, pArg)\ ca_array_get_callback (DBR_FLOAT, 1u, chan, pFunc, pArg) -/* - * ca_get_callback() +/** \brief Asynchronously read a scalar value from a process variable. * - * type R data type from db_access.h - * chan R channel identifier - * pFunc R pointer to call-back function - * pArg R copy of this pointer passed to pFunc + * See ca_array_get(). + * + * \param[in] type The external type of the user variable to return the value into. + * Conversion will occur if this does not match the native type. + * Specify one from the set of DBR_XXXX in db_access.h + * + * \param[in] chan Channel identifier. + * + * \param[in] pFunc Pointer to a user supplied callback function to be run + * when the requested operation completes. + * + * \param[in] pArg Pointer sized variable retained + * and then passed back to user supplied callback function above. + * + * \returns ::ECA_NORMAL - Normal successful completion + * \returns ::ECA_BADTYPE - Invalid DBR_XXXX type + * \returns ::ECA_BADCHID - Corrupted CHID + * \returns ::ECA_BADCOUNT - Requested count larger than native element count + * \returns ::ECA_GETFAIL - A local database get failed + * \returns ::ECA_NORDACCESS - Read access denied + * \returns ::ECA_ALLOCMEM - Unable to allocate memory + * \returns ::ECA_DISCONN - Channel is disconnected */ #define ca_get_callback(type, chan, pFunc, pArg)\ ca_array_get_callback (type, 1u, chan, pFunc, pArg) -/* - * ca_array_get_callback() +/** \brief Asynchronously read a scalar or array value from a process variable. * - * type R data type from db_access.h - * count R array element count - * chan R channel identifier - * pFunc R pointer to call-back function - * pArg R copy of this pointer passed to pFunc + * See ca_array_get(). + * + * \param[in] type The external type of the user variable to return the value into. + * Conversion will occur if this does not match the native type. + * Specify one from the set of DBR_XXXX in db_access.h + * + * \param[in] count Element count to be read from the specified channel. + * Must match the array pointed to by pValue. + * For ca_array_get_callback() a count of zero means use the current element count from the server. + * + * \param[in] chanId Channel identifier. + * + * \param[in] pFunc Pointer to a user supplied callback function to be run + * when the requested operation completes. + * + * \param[in] pArg Pointer sized variable retained + * and then passed back to user supplied callback function above. + * + * \returns ::ECA_NORMAL - Normal successful completion + * \returns ::ECA_BADTYPE - Invalid DBR_XXXX type + * \returns ::ECA_BADCHID - Corrupted CHID + * \returns ::ECA_BADCOUNT - Requested count larger than native element count + * \returns ::ECA_GETFAIL - A local database get failed + * \returns ::ECA_NORDACCESS - Read access denied + * \returns ::ECA_ALLOCMEM - Unable to allocate memory + * \returns ::ECA_DISCONN - Channel is disconnected */ LIBCA_API int epicsStdCall ca_array_get_callback ( @@ -470,16 +1083,97 @@ LIBCA_API int epicsStdCall ca_array_get_callback /* */ /************************************************************************/ -/* - * ca_create_subscription () - * - * type R data type from db_access.h - * count R array element count - * chan R channel identifier - * mask R event mask - one of {DBE_VALUE, DBE_ALARM, DBE_LOG} - * pFunc R pointer to call-back function - * pArg R copy of this pointer passed to pFunc - * pEventID W event id written at specified address +/** \brief Register a state change subscription and specify a callback function + * to be invoked whenever the process variable undergoes significant state + * changes. + * + * A significant change can be a change in the process variable's + * value, alarm status, or alarm severity. In the process control function + * block database the deadband field determines the magnitude of a + * significant change for the process variable's value. Each call to this + * function consumes resources in the client library and potentially a CA + * server until one of ca_clear_channel() or ca_clear_subscription() is + * called. + * + * Subscriptions may be installed or canceled against both connected and + * disconnected channels. The specified pFunc is called once immediately + * after the subscription is installed with the process variable's current + * state if the process variable is connected. Otherwise, the specified + * pFunc is called immediately after establishing a connection (or + * reconnection) with the process variable. The specified pFunc is + * called immediately with the process variable's current state from + * within ca_create_subscription() if the client and the process variable + * share the same address space. + * + * If a subscription is installed on a channel in a disconnected state then + * the requested count will be set to the native maximum element count of + * the channel if the requested count is larger. + * + * All subscription requests such as the above are accumulated (buffered) + * and not forwarded to the IOC until one of ca_flush_io(), + * ca_pend_io(), ca_pend_event(), or ca_sg_block() are called. This + * allows several requests to be efficiently sent over the network in one + * message. + * + * If at any time after subscribing, read access to the specified process + * variable is lost, then the callback will be invoked immediately + * indicating that read access was lost via the status argument. When read + * access is restored normal event processing will resume starting always + * with at least one update indicating the current state of the channel. + * + * A better name for this function might have been ca_subscribe(). + * + * # Example + * + * See `caMonitor.c` in the example application created by `makeBaseApp.pl`. + * + * \sa ca_pend_event(), ca_flush_io() + * + * \param[in] type The type of value presented to the callback function. + * Conversion will occur if it does not match native type. + * Specify one from the set of `DBR_XXXX` in `db_access.h` + * + * \param[in] count The element count to be read from the specified channel. + * A count of zero means use the current element count from the server, + * effectively resulting in a variable size array subscription. + * + * \param[in] chanId Channel identifier. + * + * \param[in] mask + * \parblock + * A mask with bits set for each of the event trigger types requested. + * The event trigger mask must be a *bitwise or* of one or more of the + * following constants. + * + * - ::DBE_VALUE - Trigger events when the channel value exceeds the + * monitor dead band + * - ::DBE_ARCHIVE (or ::DBE_LOG) - Trigger events when the channel value + * exceeds the archival dead band + * - ::DBE_ALARM - Trigger events when the channel alarm state changes + * - ::DBE_PROPERTY - Trigger events when a channel property changes. + * + * For functions above that do not include a trigger specification, + * events will be triggered when there are significant changes in the + * channel's value or when there are changes in the channel's alarm + * state. This is the same as `DBE_VALUE | DBE_ALARM`. + * \endparblock + * + * \param[in] pFunc Pointer to a user supplied callback function to be invoked + * with each subscription update. + * + * \param[in] pArg Pointer sized variable retained and passed back + * to user callback function + * + * \param[out] pEventID This is a pointer to user supplied event id which is overwritten + * if successful. + * This event id can later be used to clear a specific event. + * This option may be omitted by passing a null pointer. + * + * \returns ::ECA_NORMAL - Normal successful completion + * \returns ::ECA_BADCHID - Corrupted CHID + * \returns ::ECA_BADTYPE - Invalid DBR_XXXX type + * \returns ::ECA_ALLOCMEM - Unable to allocate memory + * \returns ::ECA_ADDFAIL - A local database event add failed */ LIBCA_API int epicsStdCall ca_create_subscription ( @@ -497,10 +1191,26 @@ LIBCA_API int epicsStdCall ca_create_subscription /* whenever significant changes occur to a channel */ /* */ /************************************************************************/ -/* - * ca_clear_subscription() + +/** \brief Cancel a subscription. + * + * All cancel-subscription requests such as the above are accumulated + * (buffered) and not forwarded to the server until one of ca_flush_io(), + * ca_pend_io(), ca_pend_event(), or ca_sg_block() are called. This + * allows several requests to be efficiently sent together in one message. + * + * \note This operation blocks until any user callbacks for this channel + * have run to completion. If callbacks take a lock (mutex) then it is the + * user's responsibility to ensure that this lock is not held when + * ca_clear_subscription() is called, otherwise a deadlock may ensue. + * (See also ca_clear_channel().) + * + * \sa ca_create_subscription() * - * eventID R event id + * \param[in] eventID Event id returned by ca_create_subscription(). + * + * \returns ::ECA_NORMAL - Normal successful completion + * \returns ::ECA_BADCHID - Corrupted CHID */ LIBCA_API int epicsStdCall ca_clear_subscription ( @@ -556,48 +1266,142 @@ LIBCA_API chid epicsStdCall ca_evid_to_chid ( evid id ); /* without processing. */ /************************************************************************/ -/* - * ca_pend_event() +/** \brief Flush the send buffer + * and process CA background activity for `timeout` seconds. + * + * The ca_pend_event() function will *not* return before the specified + * timeout expires and all unfinished channel access labor has been + * processed, and unlike ca_pend_io() returning from the + * function does *not* indicate anything about the status of pending IO + * requests. + * + * Both ca_pend_event() and ca_poll() return ::ECA_TIMEOUT when + * successful. This behavior probably isn\'t intuitive, but it is preserved + * to insure backwards compatibility. + * + * See also "Thread Safety and Preemptive Callback to User Code" + * from the Channel Access reference manual. * - * timeout R wait for this delay in seconds + * \sa ca_poll() + * + * \param[in] timeout The duration to block in this routine in seconds. + * A timeout of zero seconds blocks forever. + * + * \returns ::ECA_TIMEOUT - The operation timed out + * \returns ::ECA_EVDISALLOW - Function inappropriate for use within a callback handler */ LIBCA_API int epicsStdCall ca_pend_event (ca_real timeout); -#define ca_poll() ca_pend_event(1e-12) -/* - * ca_pend_io() +/** \brief Flush the send buffer + * and process any outstanding CA background activity. + * + * See ca_pend_event(). * - * timeout R wait for this delay in seconds but return early - * if all get requests (or search requests with null - * connection handler pointer have completed) + * \returns ::ECA_TIMEOUT - The operation timed out + * \returns ::ECA_EVDISALLOW - Function inappropriate for use within a callback * handler + */ +#define ca_poll() ca_pend_event(1e-12) + +/** \brief Flushes the send buffer and then blocks + * until outstanding ca_get() requests complete, + * and until channels created specifying null connection handler function pointers + * connect for the first time. + * + * - If ::ECA_NORMAL is returned then it can be safely assumed that all + * outstanding ca_get() requests have completed + * successfully and channels created specifying null connection handler + * function pointers have connected for the first time. + * - If ::ECA_TIMEOUT is returned then it must be assumed for all previous + * ca_get() requests and properly qualified first time + * channel connects have failed. + * + * If ::ECA_TIMEOUT is returned then get requests may be reissued followed by + * a subsequent call to ca_pend_io(). Specifically, the function will + * block only for outstanding ca_get() requests issued, and + * also any channels created specifying a null connection handler function + * pointer, after the last call to ca_pend_io() or ca client context + * creation whichever is later. Note that + * ca_create_channel() requests generally should + * not be reissued for the same process variable unless + * ca_clear_channel() is called first. + * + * If no ca_get() or connection state change events are + * outstanding then ca_pend_io() will flush the send buffer and return + * immediately *without processing any outstanding channel access + * background activities*. + * + * The delay specified to ca_pend_io() should take into account worst + * case network delays such as Ethernet collision exponential back off + * until retransmission delays which can be quite long on overloaded + * networks. + * + * Unlike ca_pend_event(), this routine will not + * process CA's background activities if none of the selected IO requests + * are pending. + * + * \sa ca_get(), ca_create_channel(), ca_test_io() + * + * \param[in] timeout Specifies the time out interval. + * A `timeout` interval of zero specifies forever. + * + * \returns ::ECA_NORMAL - Normal successful completion + * \returns ::ECA_TIMEOUT - Selected IO requests didn't complete before specified timeout + * \returns ::ECA_EVDISALLOW - Function inappropriate for use within an event handler */ LIBCA_API int epicsStdCall ca_pend_io (ca_real timeout); /* calls ca_pend_io() if early is true otherwise ca_pend_event() is called */ LIBCA_API int epicsStdCall ca_pend (ca_real timeout, int early); -/* - * ca_test_io() +/** \brief Test to see if all ca_get() requests are complete + * and channels created specifying a null connection callback function pointer are connected. + * + * It will report the status of outstanding ca_get() requests issued, and + * channels created specifying a null connection callback function pointer, + * after the last call to ca_pend_io() or CA context initialization whichever + * is later. * - * returns TRUE when get requests (or search requests with null - * connection handler pointer) are outstanding + * \sa ca_pend_io() + * + * \returns ::ECA_IODONE - All IO operations completed + * \returns ::ECA_IOINPROGRESS - IO operations still in progress */ LIBCA_API int epicsStdCall ca_test_io (void); /************************************************************************/ /* Send out all outstanding messages in the send queue */ /************************************************************************/ -/* - * ca_flush_io() + +/** \brief Flush outstanding IO requests to the server. + * + * This routine might be useful to users who need to flush requests prior to + * performing client side labor in parallel with labor performed in the server. + * + * Outstanding requests are also sent whenever the buffer which holds them + * becomes full. + * + * \returns ::ECA_NORMAL - Normal successful completion */ LIBCA_API int epicsStdCall ca_flush_io (void); - -/* - * ca_signal() +/** \brief Provide the error message character string associated with the supplied channel access error code + * and the supplied error context to diagnostics. * - * errorCode R status returned from channel access function - * pCtxStr R context string included with error print out + * If the error code indicates an unsuccessful operation a stack dump is + * printed, if this capability is available on the local operating system, and + * execution is terminated. + * + * \note SEVCHK() is the recommended error handler for simple applications + * which do not wish to write code testing the status returned from each + * channel access call. + * + * If the application only wishes to print the message associated with an error + * code or test the severity of an error there are also functions provided for + * this purpose. + * + * \param[in] errorCode The status returned from a channel access function. + * \param[in] pCtxStr A null terminated character string to supply + * as error context to diagnostics. */ LIBCA_API void epicsStdCall ca_signal ( @@ -632,37 +1436,97 @@ LIBCA_API void epicsStdCall ca_signal_with_file_and_lineno LIBCA_API void epicsStdCall ca_signal_formated (long ca_status, const char *pfilenm, int lineno, const char *pFormat, ...); -/* - * ca_host_name_function() +/** \brief Return a character string which contains the name of the host + * to which a channel is currently connected. * - * channel R channel identifier + * \warning This function is _not_ thread safe. + * See ca_get_host_name() for a thread safe version. * - * !!!! this function is _not_ thread safe !!!! + * \param[in] channel Channel identifier. + * \returns The process variable server's host name. + * If the channel is disconnected the string `` is returned. */ LIBCA_API const char * epicsStdCall ca_host_name (chid channel); -/* thread safe version */ + +/** \brief Return a character string which contains the name of the host + * to which a channel is currently connected. + * + * \param[in] pChan Channel identifier. + * \param[out] pBuf Where to write the process variable server's host name. + * If the channel is disconnected the string `` is returned. + * \param[out] bufLength The size of the pBuf buffer. + * \returns The effective size of the written host name. + */ LIBCA_API unsigned epicsStdCall ca_get_host_name ( chid pChan, char *pBuf, unsigned bufLength ); -/* - * CA_ADD_FD_REGISTRATION +/** \brief Return the minor protocol version number used by the host to + * which a channel is currently connected. + * + * \param[in] pChan channel identifier + * \returns The minor protocol version number. + * If the channel is disconnected CA_UKN_MINOR_VERSION is returned. + */ +LIBCA_API unsigned epicsStdCall ca_host_minor_protocol (chid pChan); + +#define HAS_CA_HOST_MINOR_PROTOCOL + +/** \brief Call their function with their argument whenever + * a new fd is added or removed. * - * call their function with their argument whenever - * a new fd is added or removed - * (for use with a manager of the select system call under UNIX) + * For use with a manager of the select system call under UNIX. * * if (opened) then fd was created * if (!opened) then fd was deleted - * */ typedef void CAFDHANDLER (void *parg, int fd, int opened); -/* - * ca_add_fd_registration() - * - * pHandler R pointer to function which is to be called - * when an fd is created or deleted - * pArg R argument passed to above function +/** \brief For use with the services provided by a file descriptor manager + * (IO multiplexor) such as "fdmgr.c". + * + * A file descriptor manager is often needed when two file descriptor IO + * intensive libraries such as the EPICS channel access client library and the + * X window system client library must coexist in the same UNIX process. This + * function allows an application code to be notified whenever the CA client + * library places a new file descriptor into service and whenever the CA client + * library removes a file descriptor from service. Specifying pHandler=NULL + * disables file descriptor registration (this is the default). + * + * # Example + * + * ```c + * int s; + * static struct myStruct aStruct; + * + * void fdReg ( struct myStruct *pStruct, int fd, int opened ) + * { + * if ( opened ) printf ( "fd %d was opened\n", fd ); + * else printf ( "fd %d was closed\n", fd ); + * } + * s = ca_add_fd_registration ( fdReg, & aStruct ); + * SEVCHK ( s, NULL ); + * ``` + * + * # Comments + * + * When using this function it is advisable to call it only once prior to + * calling any other CA function, or once just after creating the CA context + * (if you create the context explicitly). Use of this interface can improve + * latency slightly in applications that use non preemptive callback mode at + * the expense of some additional runtime overhead when compared to the + * alternative which is just polling ca_pend_event() periodically. It would + * probably not be appropriate to use this function with preemptive callback + * mode. Starting with R3.14 this function is implemented in a special backward + * compatibility mode. if ca_add_fd_registration() is called, a single pseudo + * UDP fd is created which CA pokes whenever something significant happens. Xt + * and others can watch this fd so that backwards compatibility is preserved, + * and so that they will not need to use preemptive callback mode but they will + * nevertheless get the lowest latency response to the arrival of CA messages. + * + * \param[in] pHandler Pointer to a user supplied C function returning null with the above arguments. + * \param[in] pArg User supplied pointer sized variable passed to the above function. + * + * \returns ::ECA_NORMAL - Normal successful completion */ LIBCA_API int epicsStdCall ca_add_fd_registration ( @@ -681,64 +1545,174 @@ LIBCA_API int epicsStdCall ca_add_fd_registration * block for IO completion within any one of the groups as needed. */ -/* - * ca_sg_create() +/** \brief Create a synchronous group and return an identifier for it. + * + * A synchronous group can be used to guarantee that a set of channel access + * requests have completed. Once a synchronous group has been created then + * channel access get and put requests may be issued within it using + * ca_sg_array_get() and ca_sg_array_put() respectively. The routines + * ca_sg_block() and ca_sg_test() can be used to block for and test for + * completion respectively. The routine ca_sg_reset() is used to discard + * knowledge of old requests which have timed out and in all likelihood will + * never be satisfied. + * + * Any number of asynchronous groups can have application requested operations + * outstanding within them at any given time. + * + * # Examples * - * create a sync group + * ```c + * CA_SYNC_GID gid; + * status = ca_sg_create ( &gid ); + * SEVCHK ( status, Sync group create failed ); + * ``` * - * pgid W pointer to sync group id that will be written + * \sa ca_sg_delete(), ca_sg_block(), ca_sg_test(), ca_sg_reset(), + * ca_sg_array_put(), ca_sg_array_get() + * + * \param[out] pgid Pointer to a user supplied CA_SYNC_GID + * that will be written. + * + * \returns ::ECA_NORMAL - Normal successful completion + * \returns ::ECA_ALLOCMEM - Failed, unable to allocate memory */ LIBCA_API int epicsStdCall ca_sg_create (CA_SYNC_GID * pgid); -/* - * ca_sg_delete() +/** \brief Deletes a synchronous group. * - * delete a sync group + * # Examples * - * gid R sync group id + * ```c + * CA_SYNC_GID gid; + * status = ca_sg_delete ( gid ); + * SEVCHK ( status, Sync group delete failed ); + * ``` + * + * \sa ca_sg_create() + * + * \param[in] gid Identifier of the synchronous group to be deleted. + * + * \returns ::ECA_NORMAL - Normal successful completion + * \returns ::ECA_BADSYNCGRP - Invalid synchronous group */ LIBCA_API int epicsStdCall ca_sg_delete (const CA_SYNC_GID gid); -/* - * ca_sg_block() - * - * block for IO performed within a sync group to complete - * - * gid R sync group id - * timeout R wait for this duration prior to timing out - * and returning ECA_TIMEOUT +/** \brief Flushes the send buffer + * and then waits until outstanding requests complete + * or the specified time out expires. + * + * At this time outstanding requests include calls to ca_sg_array_get() and + * calls to ca_sg_array_put(). If ECA_TIMEOUT is returned then failure must be + * assumed for all outstanding queries. Operations can be reissued followed by + * another ca_sg_block(). This routine will only block on outstanding queries + * issued after the last call to ca_sg_block(), ca_sg_reset(), or + * ca_sg_create() whichever occurs later in time. If no queries are outstanding + * then ca_sg_block() will return immediately without processing any pending + * channel access activities. + * + * Values written into your program's variables by a channel access synchronous + * group request should not be referenced by your program until ECA_NORMAL has + * been received from ca_sg_block(). This routine will process pending channel + * access background activity while it is waiting. + * + * # Examples + * + * ```c + * CA_SYNC_GID gid; + * status = ca_sg_block(gid, 0.0); + * SEVCHK(status, Sync group block failed); + * ``` + * + * \sa ca_sg_test(), ca_sg_reset() + * + * \param[in] gid Identifier of the synchronous group. + * \param[in] timeout The duration to block in this routine in seconds. + * A timeout of zero seconds blocks forever. + * + * \returns ::ECA_NORMAL - Normal successful completion + * \returns ::ECA_TIMEOUT - The operation timed out + * \returns ::ECA_EVDISALLOW - Function inappropriate for use within an event handler + * \returns ::ECA_BADSYNCGRP - Invalid synchronous group */ LIBCA_API int epicsStdCall ca_sg_block (const CA_SYNC_GID gid, ca_real timeout); -/* - * ca_sg_test() +/** \brief Test to see if all requests made within a synchronous group have completed. * - * test for sync group IO operations in progress + * # Examples * - * gid R sync group id + * ```c + * CA_SYNC_GID gid; + * status = ca_sg_test ( gid ); + * ``` * - * returns one of ECA_BADSYNCGRP, ECA_IOINPROGRESS, ECA_IODONE + * \param[in] gid Identifier of the synchronous group. + * + * \returns ::ECA_IODONE - IO operations completed + * \returns ::ECA_IOINPROGRESS - Some IO operations still in progress + * \returns ::ECA_BADSYNCGRP - Invalid synchronous group */ LIBCA_API int epicsStdCall ca_sg_test (const CA_SYNC_GID gid); -/* - * ca_sg_reset +/** \brief Reset the number of outstanding requests within the specified synchronous group to zero. * - * gid R sync group id + * So that ca_sg_test() will return ::ECA_IODONE and ca_sg_block() will not + * block unless additional subsequent requests are made. + * + * # Examples + * + * ```c + * CA_SYNC_GID gid; + * status = ca_sg_reset(gid); + * ``` + * + * \param[in] gid Identifier of the synchronous group. + * + * \returns ::ECA_NORMAL - Normal successful completion + * \returns ::ECA_BADSYNCGRP - Invalid synchronous group */ LIBCA_API int epicsStdCall ca_sg_reset(const CA_SYNC_GID gid); -/* - * ca_sg_array_get() + +/** \brief Read an array of values from a channel + * and increment the outstanding request count of a synchronous group. * - * initiate a get within a sync group - * (essentially a ca_array_get() with a sync group specified) + * The ca_sg_get() and ca_sg_array_get() functionality is implemented using + * ca_array_get_callback(). * - * gid R sync group id - * type R data type from db_access.h - * count R array element count - * chan R channel identifier - * pValue W channel value copied to this location + * The values written into your program's variables by ca_sg_get() or + * ca_sg_array_get() should not be referenced by your program until + * ::ECA_NORMAL has been received from ca_sg_block(), or until ca_sg_test() + * returns ::ECA_IODONE. + * + * All remote operation requests such as the above are accumulated (buffered) + * and not forwarded to the server until one of ca_flush_io(), ca_pend_io(), + * ca_pend_event(), or ca_sg_block() are called. This allows several requests + * to be efficiently sent in one message. + * + * If a connection is lost and then resumed outstanding gets are not reissued. + * + * \sa ca_sg_get(), ca_pend_io(), ca_flush_io(), ca_get_callback() + * + * \param[in] gid Identifier of the synchronous group. + * + * \param[in] type External type of returned value. + * Conversion will occur if this does not match native type. + * Specify one from the set of `DBR_XXXX` in `db_access.h`. + * + * \param[in] count Element count to be read from the specified channel. + * It must match the array pointed to by `pValue`. + * + * \param[in] chan Channel identifier. + * + * \param[out] pValue Pointer to application supplied buffer + * that is to contain the value or array of values to be returned. + * + * \returns ::ECA_NORMAL - Normal successful completion + * \returns ::ECA_BADSYNCGRP - Invalid synchronous group + * \returns ::ECA_BADCHID - Corrupted CHID + * \returns ::ECA_BADCOUNT - Requested count larger than native element count + * \returns ::ECA_BADTYPE - Invalid DBR_XXXX type + * \returns ::ECA_GETFAIL - A local database get failed */ LIBCA_API int epicsStdCall ca_sg_array_get ( @@ -749,20 +1723,68 @@ LIBCA_API int epicsStdCall ca_sg_array_get void *pValue ); +/** \brief Read a value from a channel + * and increment the outstanding request count of a synchronous group. + * + * See ca_sg_array_get() + * + * \param[in] gid Identifier of the synchronous group. + * + * \param[in] type External type of returned value. + * Conversion will occur if this does not match native type. + * Specify one from the set of `DBR_XXXX` in `db_access.h`. + * + * \param[in] chan Channel identifier. + * + * \param[out] pValue Pointer to application supplied buffer + * that is to contain the value or array of values to be returned. + * + * \returns ::ECA_NORMAL - Normal successful completion + * \returns ::ECA_BADSYNCGRP - Invalid synchronous group + * \returns ::ECA_BADCHID - Corrupted CHID + * \returns ::ECA_BADCOUNT - Requested count larger than native element count + * \returns ::ECA_BADTYPE - Invalid DBR_XXXX type + * \returns ::ECA_GETFAIL - A local database get failed + */ #define ca_sg_get(gid, type, chan, pValue) \ ca_sg_array_get (gid, type, 1u, chan, pValue) -/* - * ca_sg_array_put() +/** \brief Write an array of values to a channel + * and increment the outstanding request count of a synchronous group. * - * initiate a put within a sync group - * (essentially a ca_array_put() with a sync group specified) + * The ca_sg_put() and ca_sg_array_put() functionality is implemented using + * ca_array_put_callback(). * - * gid R sync group id - * type R data type from db_access.h - * count R array element count - * chan R channel identifier - * pValue R new channel value copied from this location + * All remote operation requests such as the above are accumulated (buffered) + * and not forwarded to the server until one of ca_flush_io(), ca_pend_io(), + * ca_pend_event(), or ca_sg_block() are called. This allows several requests + * to be efficiently sent in one message. + * + * If a connection is lost and then resumed outstanding puts are not reissued. + * + * \sa ca_sg_get(), ca_flush_io() + * + * \param[in] gid Identifier of the synchronous group. + * + * \param[in] type The type of supplied value. + * Conversion will occur if it does not match the native type. + * Specify one from the set of `DBR_XXXX` in `db_access.h`. + * + * \param[in] count Element count to be written to the specified channel. + * Must match the array pointed to by `pValue`. + * + * \param[in] chan Channel identifier. + * + * \param[out] pValue A pointer to an application supplied buffer + * containing the value or array of values returned. + * + * \returns ::ECA_NORMAL - Normal successful completion + * \returns ::ECA_BADSYNCGRP - Invalid synchronous group + * \returns ::ECA_BADCHID - Corrupted CHID + * \returns ::ECA_BADTYPE - Invalid DBR_XXXX type + * \returns ::ECA_BADCOUNT - Requested count larger than native element count + * \returns ::ECA_STRTOBIG - Unusually large string supplied + * \returns ::ECA_PUTFAIL - A local database put failed */ LIBCA_API int epicsStdCall ca_sg_array_put ( @@ -773,6 +1795,30 @@ LIBCA_API int epicsStdCall ca_sg_array_put const void *pValue ); +/** \brief Write a value to a channel + * and increment the outstanding request count of a synchronous group. + * + * See ca_sg_array_get(). + * + * \param[in] gid Identifier of the synchronous group. + * + * \param[in] type The type of supplied value. + * Conversion will occur if it does not match the native type. + * Specify one from the set of `DBR_XXXX` in `db_access.h`. + * + * \param[in] chan Channel identifier. + * + * \param[out] pValue A pointer to an application supplied buffer + * containing the value or array of values returned. + * + * \returns ::ECA_NORMAL - Normal successful completion + * \returns ::ECA_BADSYNCGRP - Invalid synchronous group + * \returns ::ECA_BADCHID - Corrupted CHID + * \returns ::ECA_BADTYPE - Invalid DBR_XXXX type + * \returns ::ECA_BADCOUNT - Requested count larger than native element count + * \returns ::ECA_STRTOBIG - Unusually large string supplied + * \returns ::ECA_PUTFAIL - A local database put failed + */ #define ca_sg_put(gid, type, chan, pValue) \ ca_sg_array_put (gid, type, 1u, chan, pValue) @@ -785,9 +1831,15 @@ ca_sg_array_put (gid, type, 1u, chan, pValue) */ LIBCA_API int epicsStdCall ca_sg_stat (CA_SYNC_GID gid); +/** \brief Dumps the specified dbr data type to standard out. + * + * \param[in] type The data type + * (from the `DBR_XXXX` set described in `db_access.h`). + * \param[in] count The array element count + * \param[in] pbuffer A pointer to data of the specified count and number. + */ LIBCA_API void epicsStdCall ca_dump_dbr (chtype type, unsigned count, const void * pbuffer); - /* * ca_v42_ok() * @@ -814,12 +1866,32 @@ LIBCA_API const char * epicsStdCall ca_version (void); * text output goes * * use two ifdef's for trad C compatibility - * - * ca_printf_func R pointer to new function called when - * CA prints an error message */ #ifndef CA_DONT_INCLUDE_STDARGH typedef int caPrintfFunc (const char *pformat, va_list args); + +/** \brief Replace the default handler for formatted diagnostic message output. + * + * The default handler uses fprintf to send messages to 'stderr'. + * + * \param[in] ca_printf_func A pointer to a user supplied callback handler + * to be invoked when CA prints diagnostic messages. + * Installing a null pointer will cause the default callback handler to be reinstalled. + * + * # Examples + * + * ```c + * int my_printf ( char *pformat, va_list args ) { + * int status; + * status = vfprintf( stderr, pformat, args); + * return status; + * } + * status = ca_replace_printf_handler ( my_printf ); + * SEVCHK ( status, "failed to install my printf handler" ); + * ``` + * + * \returns ::ECA_NORMAL - Normal successful completion + */ LIBCA_API int epicsStdCall ca_replace_printf_handler ( caPrintfFunc *ca_printf_func ); @@ -836,16 +1908,57 @@ LIBCA_API unsigned epicsStdCall ca_search_attempts (chid chan); LIBCA_API double epicsStdCall ca_beacon_period (chid chan); LIBCA_API double epicsStdCall ca_receive_watchdog_delay (chid chan); -/* - * used when an auxiliary thread needs to join a CA client context started +/** \brief Returns a pointer to the current thread's CA context. + * + * If none then null is returned. + * + * Used when an auxiliary thread needs to join a CA client context started * by another thread + * + * \sa ca_attach_context(), ca_detach_context(), ca_context_create(), ca_context_destroy() */ LIBCA_API struct ca_client_context * epicsStdCall ca_current_context (); -LIBCA_API int epicsStdCall ca_attach_context ( struct ca_client_context * context ); +/** \brief The calling thread becomes a member of the specified CA context. + * + * If ca_disable_preemptive_callback is specified when ca_context_create() is + * called (or if ca_task_initialize() is called) then additional threads are + * *not* allowed to join the CA context because allowing other threads to join + * implies that CA callbacks will be called preemptively from more than one + * thread. + * + * \sa ca_current_context(), ca_detach_context(), ca_context_create(), ca_context_destroy() + * + * \param[in] context A pointer to the CA context to join with. + * + * \returns ::ECA_NORMAL - Normal successful completion + * \returns ::ECA_NOTTHREADED - Context is not preemptive so cannot be joined + * \returns ::ECA_ISATTACHED - Thread already attached to a CA context + */ +LIBCA_API int epicsStdCall ca_attach_context ( struct ca_client_context * context ); +/** \brief Prints information about the client context. + * + * Including, at higher interest levels, status for each channel. Lacking a CA + * context pointer, ca_client_status() prints information about the calling + * threads CA context. + * + * \param[in] level The interest level. + * Increasing level produces increasing detail. + */ LIBCA_API int epicsStdCall ca_client_status ( unsigned level ); -LIBCA_API int epicsStdCall ca_context_status ( struct ca_client_context *, unsigned level ); + +/** \brief Prints information about the client context. + * + * Including, at higher interest levels, status for each channel. Lacking a CA + * context pointer, ca_client_status() prints information about the calling + * threads CA context. + * + * \param[in] context A pointer to the CA context to examine. + * \param[in] level The interest level. + * Increasing level produces increasing detail. + */ +LIBCA_API int epicsStdCall ca_context_status ( struct ca_client_context *context, unsigned level ); /* * deprecated @@ -857,15 +1970,39 @@ ca_build_and_connect(NAME, XXXXX, ZZZZZZ, CHIDPTR, YYYYY, 0, 0) LIBCA_API int epicsStdCall ca_build_and_connect ( const char *pChanName, chtype, unsigned long, chid * pChanID, void *, caCh * pFunc, void * pArg ); + +/** \brief Search for a channel over Channel Access. + * + * \deprecated Use ca_create_channel() instead. + */ #define ca_search(pChanName, pChanID)\ ca_search_and_connect (pChanName, pChanID, 0, 0) + +/** \brief Search and connect to a channel over Channel Access. + * + * \deprecated Use ca_create_channel() instead. + */ LIBCA_API int epicsStdCall ca_search_and_connect ( const char * pChanName, chid * pChanID, caCh *pFunc, void * pArg ); + LIBCA_API int epicsStdCall ca_channel_status (epicsThreadId tid); + +/** \brief Cancel a subscription. + * + * \deprecated Use ca_clear_subscription() instead. + */ LIBCA_API int epicsStdCall ca_clear_event ( evid eventID ); + +/** \brief Register a state change subscription and specify a callback function + * to be invoked whenever the process variable undergoes significant state + * changes. + * + * \deprecated Use ca_create_subscription() instead. + */ #define ca_add_event(type,chan,pFunc,pArg,pEventID)\ ca_add_array_event(type,1u,chan,pFunc,pArg,0.0,0.0,0.0,pEventID) + #define ca_add_delta_event(TYPE,CHID,ENTRY,ARG,DELTA,EVID)\ ca_add_array_event(TYPE,1,CHID,ENTRY,ARG,DELTA,DELTA,0.0,EVID) #define ca_add_general_event(TYPE,CHID,ENTRY,ARG,P_DELTA,N_DELTA,TO,EVID)\ diff --git a/modules/ca/src/client/caerr.h b/modules/ca/src/client/caerr.h index bc6f654d15..bd37881db4 100644 --- a/modules/ca/src/client/caerr.h +++ b/modules/ca/src/client/caerr.h @@ -19,6 +19,10 @@ * */ +/** \file caerr.h + * + * \brief Channel access error definitions. + */ #ifndef INC_caerr_H #define INC_caerr_H @@ -75,72 +79,139 @@ * servers on earlier releases that communicate with current clients * might still generate exceptions with these error constants */ + +/// Normal successful completion #define ECA_NORMAL DEFMSG(CA_K_SUCCESS, 0) /* success */ +/// Maximum simultaneous IOC connections exceeded #define ECA_MAXIOC DEFMSG(CA_K_ERROR, 1) /* defunct */ +/// Unknown internet host #define ECA_UKNHOST DEFMSG(CA_K_ERROR, 2) /* defunct */ +/// Unknown internet service #define ECA_UKNSERV DEFMSG(CA_K_ERROR, 3) /* defunct */ +/// Unable to allocate a new socket #define ECA_SOCK DEFMSG(CA_K_ERROR, 4) /* defunct */ +/// Unable to connect to internet host or service #define ECA_CONN DEFMSG(CA_K_WARNING, 5) /* defunct */ +/// Unable to allocate additional dynamic memory #define ECA_ALLOCMEM DEFMSG(CA_K_WARNING, 6) +/// Unknown IO channel #define ECA_UKNCHAN DEFMSG(CA_K_WARNING, 7) /* defunct */ +/// ECA_UKNFIELD - Record field specified inappropriate for channel specified #define ECA_UKNFIELD DEFMSG(CA_K_WARNING, 8) /* defunct */ +/// The requested data transfer is greater than available memory or EPICS_CA_MAX_ARRAY_BYTES #define ECA_TOLARGE DEFMSG(CA_K_WARNING, 9) +/// User specified timeout on IO operation expired #define ECA_TIMEOUT DEFMSG(CA_K_WARNING, 10) +/// Sorry, that feature is planned but not supported at this time #define ECA_NOSUPPORT DEFMSG(CA_K_WARNING, 11) /* defunct */ +/// The supplied string is unusually large #define ECA_STRTOBIG DEFMSG(CA_K_WARNING, 12) /* defunct */ +/// The request was ignored because the specified channel is disconnected #define ECA_DISCONNCHID DEFMSG(CA_K_ERROR, 13) /* defunct */ +/// The data type specified is invalid #define ECA_BADTYPE DEFMSG(CA_K_ERROR, 14) +/// Remote Channel not found #define ECA_CHIDNOTFND DEFMSG(CA_K_INFO, 15) /* defunct */ +/// Unable to locate all user specified channels #define ECA_CHIDRETRY DEFMSG(CA_K_INFO, 16) /* defunct */ +/// Channel Access Internal Failure #define ECA_INTERNAL DEFMSG(CA_K_FATAL, 17) +/// The requested local DB operation failed #define ECA_DBLCLFAIL DEFMSG(CA_K_WARNING, 18) /* defunct */ +/// Channel read request failed #define ECA_GETFAIL DEFMSG(CA_K_WARNING, 19) +/// Channel write request failed #define ECA_PUTFAIL DEFMSG(CA_K_WARNING, 20) +/// Channel subscription request failed #define ECA_ADDFAIL DEFMSG(CA_K_WARNING, 21) /* defunct */ +/// Invalid element count requested #define ECA_BADCOUNT DEFMSG(CA_K_WARNING, 22) +/// Invalid string #define ECA_BADSTR DEFMSG(CA_K_ERROR, 23) +/// Virtual circuit disconnect #define ECA_DISCONN DEFMSG(CA_K_WARNING, 24) +/// Identical process variable names on multiple servers #define ECA_DBLCHNL DEFMSG(CA_K_WARNING, 25) +/// Request inappropriate within subscription (monitor) update callback #define ECA_EVDISALLOW DEFMSG(CA_K_ERROR, 26) +/// Database value get for that channel failed during channel search #define ECA_BUILDGET DEFMSG(CA_K_WARNING, 27) /* defunct */ +/// Unable to initialize without the vxWorks VX_FP_TASK task option set #define ECA_NEEDSFP DEFMSG(CA_K_WARNING, 28) /* defunct */ +/// Event queue overflow has prevented first pass event after event add #define ECA_OVEVFAIL DEFMSG(CA_K_WARNING, 29) /* defunct */ +/// Bad event subscription (monitor) identifier #define ECA_BADMONID DEFMSG(CA_K_ERROR, 30) +/// Remote channel has new network address #define ECA_NEWADDR DEFMSG(CA_K_WARNING, 31) /* defunct */ +/// New or resumed network connection #define ECA_NEWCONN DEFMSG(CA_K_INFO, 32) /* defunct */ +/// Specified task isn't a member of a CA context #define ECA_NOCACTX DEFMSG(CA_K_WARNING, 33) /* defunct */ +/// Attempt to use defunct CA feature failed #define ECA_DEFUNCT DEFMSG(CA_K_FATAL, 34) /* defunct */ +/// The supplied string is empty #define ECA_EMPTYSTR DEFMSG(CA_K_WARNING, 35) /* defunct */ +/// Unable to spawn the CA repeater thread- auto reconnect will fail #define ECA_NOREPEATER DEFMSG(CA_K_WARNING, 36) /* defunct */ +/// No channel id match for search reply- search reply ignored #define ECA_NOCHANMSG DEFMSG(CA_K_WARNING, 37) /* defunct */ +/// Resetting dead connection- will try to reconnect #define ECA_DLCKREST DEFMSG(CA_K_WARNING, 38) /* defunct */ +/// Server (IOC) has fallen behind or is not responding- still waiting #define ECA_SERVBEHIND DEFMSG(CA_K_WARNING, 39) /* defunct */ +/// No internet interface with broadcast available #define ECA_NOCAST DEFMSG(CA_K_WARNING, 40) /* defunct */ +/// Invalid event selection mask #define ECA_BADMASK DEFMSG(CA_K_ERROR, 41) +/// IO operations have completed #define ECA_IODONE DEFMSG(CA_K_INFO, 42) +/// IO operations are in progress #define ECA_IOINPROGRESS DEFMSG(CA_K_INFO, 43) +/// Invalid synchronous group identifier #define ECA_BADSYNCGRP DEFMSG(CA_K_ERROR, 44) +/// Put callback timed out #define ECA_PUTCBINPROG DEFMSG(CA_K_ERROR, 45) +/// Read access denied #define ECA_NORDACCESS DEFMSG(CA_K_WARNING, 46) +/// Write access denied #define ECA_NOWTACCESS DEFMSG(CA_K_WARNING, 47) +/// Requested feature is no longer supported #define ECA_ANACHRONISM DEFMSG(CA_K_ERROR, 48) +/// Empty PV search address list #define ECA_NOSEARCHADDR DEFMSG(CA_K_WARNING, 49) +/// No reasonable data conversion between client and server types #define ECA_NOCONVERT DEFMSG(CA_K_WARNING, 50) +/// Invalid channel identifier #define ECA_BADCHID DEFMSG(CA_K_ERROR, 51) +/// Invalid function pointer #define ECA_BADFUNCPTR DEFMSG(CA_K_ERROR, 52) +/// Thread is already attached to a client context #define ECA_ISATTACHED DEFMSG(CA_K_WARNING, 53) +/// Not supported by attached service #define ECA_UNAVAILINSERV DEFMSG(CA_K_WARNING, 54) +/// User destroyed channel #define ECA_CHANDESTROY DEFMSG(CA_K_WARNING, 55) +/// Invalid channel priority #define ECA_BADPRIORITY DEFMSG(CA_K_ERROR, 56) +/// Preemptive callback not enabled - additional threads may not join context #define ECA_NOTTHREADED DEFMSG(CA_K_ERROR, 57) +/// Client's protocol revision does not support transfers exceeding 16k bytes #define ECA_16KARRAYCLIENT DEFMSG(CA_K_WARNING, 58) +/// Virtual circuit connection sequence aborted #define ECA_CONNSEQTMO DEFMSG(CA_K_WARNING, 59) +/// Virtual circuit unresponsive #define ECA_UNRESPTMO DEFMSG(CA_K_WARNING, 60) #ifdef __cplusplus extern "C" { #endif +/** \brief Return a message character string corresponding to a user specified CA status code. + * + * \param[in] ca_status A CA status code. + * \returns The corresponding error message string. + */ LIBCA_API const char * epicsStdCall ca_message(long ca_status); LIBCA_API extern const char * ca_message_text []; diff --git a/modules/ca/src/client/caeventmask.h b/modules/ca/src/client/caeventmask.h index 949f2743b4..adf298ea9d 100644 --- a/modules/ca/src/client/caeventmask.h +++ b/modules/ca/src/client/caeventmask.h @@ -8,37 +8,43 @@ * in file LICENSE that is included with this distribution. \*************************************************************************/ +/** \file caeventmask.h + * + * \brief Event selections + */ + #ifndef INCLcaeventmaskh #define INCLcaeventmaskh /* - event selections (If any more than 8 of these are needed then update the select field in the event_block struct in db_event.c from unsigned char to unsigned short) + */ - - DBE_VALUE - Trigger an event when a significant change in the channel's value - occurs. Relies on the monitor deadband field under DCT. - - DBE_ARCHIVE (DBE_LOG) - Trigger an event when an archive significant change in the channel's - value occurs. Relies on the archiver monitor deadband field under DCT. - - DBE_ALARM - Trigger an event when the alarm state changes - - DBE_PROPERTY - Trigger an event when a property change (control limit, graphical - limit, status string, enum string ...) occurs. - -*/ - +/** \brief Trigger an event when a significant change in the channel's value occurs. + * + * Relies on the monitor deadband field under DCT. + */ #define DBE_VALUE (1<<0) + +/** \brief Trigger an event when an archive significant change in the channel's value occurs. + * + * Relies on the archiver monitor deadband field under DCT. + */ #define DBE_ARCHIVE (1<<1) + +/** \brief Same as ::DBE_ARCHIVE. + */ #define DBE_LOG DBE_ARCHIVE + +/** \brief Trigger an event when the alarm state changes. + */ #define DBE_ALARM (1<<2) + +/** \brief Trigger an event when a property change occurs. + * (control limit, graphical limit, status string, enum string ...) + */ #define DBE_PROPERTY (1<<3) #endif diff --git a/modules/ca/src/client/casw.md b/modules/ca/src/client/casw.md new file mode 100644 index 0000000000..dab63c1c88 --- /dev/null +++ b/modules/ca/src/client/casw.md @@ -0,0 +1,26 @@ +# casw + + casw [-i ] + +## Description + +CA server "beacon anomaly" logging. + +CA server beacon anomalies occur when a new server joins the network, a +server is rebooted, network connectivity to a server is reestablished, +or if a server's CPU exits a CPU load saturated state. + +CA clients with unresolved channels reset their search request +scheduling timers whenever they see a beacon anomaly. + +This program can be used to detect situations where there are too many +beacon anomalies. IP routing configuration problems may result in false +beacon anomalies that might cause CA clients to use unnecessary +additional network bandwidth and server CPU load when searching for +unresolved channels. + +If there are no new CA servers appearing on the network, and network +connectivity remains constant, then casw should print no messages at +all. At higher interest levels the program prints a message for every +beacon that is received, and anomalous entries are flagged with a star. + diff --git a/modules/ca/src/client/catime.md b/modules/ca/src/client/catime.md new file mode 100644 index 0000000000..aa82736cb8 --- /dev/null +++ b/modules/ca/src/client/catime.md @@ -0,0 +1,15 @@ +# catime + + catime [channel count] [append number to pv name if true] + +## Description + +Channel Access Client Library performance test. + +If unspecified, the channel count is 10000. If the "append number to pv +name if true" argument is specified and it is greater than zero then +the channel names in the test are numbered as follows. + + 000000, 000001, ... nnnnnn + + diff --git a/modules/ca/src/client/comQueSend.cpp b/modules/ca/src/client/comQueSend.cpp index c0c535f42f..9d63fd908b 100644 --- a/modules/ca/src/client/comQueSend.cpp +++ b/modules/ca/src/client/comQueSend.cpp @@ -24,14 +24,14 @@ // // Requirements: -// 1) Allow sufficent headroom so that users will be able to perform +// 1) Allow sufficient headroom so that users will be able to perform // a reasonable amount of IO within CA callbacks without experiencing // a push/pull deadlock. If a potential push/pull deadlock situation // occurs then detect and avoid it and provide diagnostic to the user // via special status. // 2) Return status to the user when there is insufficient memory to // queue a complete message. -// 3) return status to the user when a message cant be flushed because +// 3) return status to the user when a message can't be flushed because // a connection dropped. // 4) Do not allocate too much memory in exception situations (such as // after a circuit disconnect). diff --git a/modules/ca/src/client/convert.cpp b/modules/ca/src/client/convert.cpp index 361b60222d..e2dfae030b 100644 --- a/modules/ca/src/client/convert.cpp +++ b/modules/ca/src/client/convert.cpp @@ -17,7 +17,7 @@ * * 1) All routines in this file have an encode argument which * determines if we are converting from the standard format to - * the local format or vise versa. To date only float and double data + * the local format or vice versa. To date only float and double data * types must be converted differently depending on the encode * argument - joh * diff --git a/modules/ca/src/client/db_access.h b/modules/ca/src/client/db_access.h index 3ab92fbc0b..0fd9c56915 100644 --- a/modules/ca/src/client/db_access.h +++ b/modules/ca/src/client/db_access.h @@ -516,13 +516,40 @@ struct dbr_ctrl_double{ dbr_double_t value; /* current value */ }; +/** \brief Returns the size in bytes for a `DBR_XXXX` type with `COUNT` elements. + * + * If the DBR type is a structure then the value field is the last field in the + * structure. If `COUNT` is greater than one then `COUNT-1` elements are + * appended to the end of the structure so that they can be addressed as an + * array through a pointer to the value field. + * + * \sa dbr_size, dbr_value_size + * + * \param[in] TYPE The data type. + * \param[in] COUNT The element count. + * \returns The size in bytes of the specified type + * with the specified number of elements. + */ #define dbr_size_n(TYPE,COUNT)\ ((unsigned)((COUNT)<0?dbr_size[TYPE]:dbr_size[TYPE]+((COUNT)-1)*dbr_value_size[TYPE])) -/* size for each type - array indexed by the DBR_ type code */ +/** \brief Size in bytes for each `DBR_XXXX` type. + * + * Array indexed by the `DBR_XXXX` type code. + * + * \sa dbr_size_n() + */ LIBCA_API extern const unsigned short dbr_size[]; -/* size for each type's value - array indexed by the DBR_ type code */ +/** \brief Size in bytes for each type's value. + * + * Array indexed by the `DBR_XXXX` type code. + * + * If the type is a structure the size of the value field is returned otherwise + * the size of the type is returned. + * + * \sa dbr_size_n() + */ LIBCA_API extern const unsigned short dbr_value_size[]; #ifndef db_accessHFORdb_accessC @@ -676,7 +703,7 @@ union db_access_val{ (type)%(LAST_TYPE+1) == DBR_DOUBLE) #define dbf_type_to_text(type) \ - ( ((type) >= -1 && (type) < dbf_text_dim-2) ? \ + ( ((type+1) >= 0 && (type) < dbf_text_dim-2) ? \ dbf_text[type+1] : dbf_text_invalid ) #define dbf_text_to_type(text, type) \ @@ -685,6 +712,14 @@ union db_access_val{ break; \ } +/** \brief Returns a constant null terminated string + * corresponding to the specified dbr type. + * + * \param[in] type The data type code. + * A member of the set of `DBR_XXXX` in `db_access.h`. + * + * \returns The const string corresponding to the `DBR_XXXX` type. + */ #define dbr_type_to_text(type) \ ( ((type) >= 0 && (type) < dbr_text_dim) ? \ dbr_text[(type)] : dbr_text_invalid ) diff --git a/modules/ca/src/client/iocinf.cpp b/modules/ca/src/client/iocinf.cpp index c760a0d349..8bd42f1080 100644 --- a/modules/ca/src/client/iocinf.cpp +++ b/modules/ca/src/client/iocinf.cpp @@ -21,6 +21,9 @@ #define epicsAssertAuthor "Jeff Hill johill@lanl.gov" +#include +#include + #include #include #include @@ -28,6 +31,7 @@ #include "envDefs.h" #include "epicsAssert.h" +#include "epicsString.h" #include "epicsStdioRedirect.h" #include "errlog.h" #include "osiWireFormat.h" @@ -35,39 +39,6 @@ #include "addrList.h" #include "iocinf.h" -/* - * getToken() - */ -static char *getToken ( const char **ppString, char *pBuf, unsigned bufSIze ) -{ - bool tokenFound = false; - const char *pToken; - unsigned i; - - pToken = *ppString; - while ( isspace (*pToken) && *pToken ){ - pToken++; - } - - for ( i=0u; iname); - fprintf ( stderr, "\tBad internet address or host name: '%s'\n", pToken); - continue; - } + try { + std::vector scratch(pStr, pStr+strlen(pStr)+1); // copy chars and trailing nil - if ( ignoreNonDefaultPort && ntohs ( addr.sin_port ) != port ) { - continue; - } + char *save = NULL; + for(const char *pToken = epicsStrtok_r(&scratch[0], " \t\n\r", &save); + pToken; + pToken = epicsStrtok_r(NULL, " \t\n\r", &save)) + { + if(!pToken[0]) { + continue; + } - pNewNode = (osiSockAddrNode *) calloc (1, sizeof(*pNewNode)); - if (pNewNode==NULL) { - fprintf ( stderr, "addAddrToChannelAccessAddressList(): no memory available for configuration\n"); - break; - } + status = aToIPAddr ( pToken, port, &addr ); + if (status<0) { + fprintf ( stderr, "%s: Parsing '%s'\n", __FILE__, pEnv->name); + fprintf ( stderr, "\tBad internet address or host name: '%s'\n", pToken); + continue; + } + + if ( ignoreNonDefaultPort && ntohs ( addr.sin_port ) != port ) { + continue; + } - pNewNode->addr.ia = addr; + pNewNode = (osiSockAddrNode *) calloc (1, sizeof(*pNewNode)); + if (pNewNode==NULL) { + fprintf ( stderr, "addAddrToChannelAccessAddressList(): no memory available for configuration\n"); + break; + } + + pNewNode->addr.ia = addr; - /* - * LOCK applied externally - */ - ellAdd (pList, &pNewNode->node); - ret = 0; /* success if anything is added to the list */ + /* + * LOCK applied externally + */ + ellAdd (pList, &pNewNode->node); + ret = 0; /* success if anything is added to the list */ + } + } catch(std::exception&) { // only bad_alloc currently possible + ret = -1; } return ret; diff --git a/modules/ca/src/client/nciu.cpp b/modules/ca/src/client/nciu.cpp index a862cb228d..347c5c031f 100644 --- a/modules/ca/src/client/nciu.cpp +++ b/modules/ca/src/client/nciu.cpp @@ -154,7 +154,7 @@ void nciu::connect ( unsigned nativeType, // the callback lock is also taken when a channel // disconnects to prevent a race condition with the // code below - ie we hold the callback lock here - // so a channel cant be destroyed out from under us. + // so a channel can't be destroyed out from under us. this->notify().connectNotify ( guard ); } @@ -410,6 +410,13 @@ const char * nciu::pHostName ( return this->piiu->pHostName ( guard ); } +unsigned nciu::getHostMinorProtocol ( + epicsGuard < epicsMutex > & guard) const throw () +{ + return this->piiu->getHostMinorProtocol ( + guard ); +} + bool nciu::ca_v42_ok ( epicsGuard < epicsMutex > & guard ) const { @@ -618,7 +625,7 @@ unsigned channelNode::getSearchTimerIndex ( } else { throw std::runtime_error ( - "channel was expected to be in a search timer, but wasnt" );; + "channel was expected to be in a search timer, but wasn't" );; } return index; } diff --git a/modules/ca/src/client/nciu.h b/modules/ca/src/client/nciu.h index d909aa9173..7e94fe1b85 100644 --- a/modules/ca/src/client/nciu.h +++ b/modules/ca/src/client/nciu.h @@ -183,6 +183,8 @@ class nciu : unsigned getHostName ( epicsGuard < epicsMutex > &, char * pBuf, unsigned bufLen ) const throw (); + unsigned getHostMinorProtocol ( + epicsGuard < epicsMutex > &) const throw (); void writeException ( epicsGuard < epicsMutex > &, epicsGuard < epicsMutex > &, int status, const char *pContext, unsigned type, arrayElementCount count ); diff --git a/modules/ca/src/client/netiiu.cpp b/modules/ca/src/client/netiiu.cpp index c73732af6b..87ad704af3 100644 --- a/modules/ca/src/client/netiiu.cpp +++ b/modules/ca/src/client/netiiu.cpp @@ -116,6 +116,12 @@ const char * netiiu::pHostName ( return pHostNameNetIIU; } +unsigned netiiu::getHostMinorProtocol ( + epicsGuard < epicsMutex > & ) const throw () +{ + return CA_UKN_MINOR_VERSION; +} + osiSockAddr netiiu::getNetworkAddress ( epicsGuard < epicsMutex > & ) const { @@ -161,7 +167,7 @@ void netiiu::uninstallChanDueToSuccessfulSearchResponse ( epicsGuard < epicsMutex > &, nciu &, const epicsTime & ) { throw std::runtime_error ( - "search response occured when not attached to udpiiu?" ); + "search response occurred when not attached to udpiiu?" ); } bool netiiu::searchMsg ( diff --git a/modules/ca/src/client/netiiu.h b/modules/ca/src/client/netiiu.h index 298337bd37..d21ed84288 100644 --- a/modules/ca/src/client/netiiu.h +++ b/modules/ca/src/client/netiiu.h @@ -43,6 +43,8 @@ class netiiu { unsigned bufLength ) const throw () = 0; virtual const char * pHostName ( epicsGuard < epicsMutex > & ) const throw () = 0; + virtual unsigned getHostMinorProtocol ( + epicsGuard < epicsMutex > & ) const throw (); virtual bool ca_v41_ok ( epicsGuard < epicsMutex > & ) const = 0; virtual bool ca_v42_ok ( diff --git a/modules/ca/src/client/oldAccess.h b/modules/ca/src/client/oldAccess.h index 2c39d27688..ec2df38cae 100644 --- a/modules/ca/src/client/oldAccess.h +++ b/modules/ca/src/client/oldAccess.h @@ -64,6 +64,8 @@ struct oldChannelNotify : private cacChannelNotify { chid pChan, char * pBuf, unsigned bufLength ); friend const char * epicsStdCall ca_host_name ( chid pChan ); + friend unsigned epicsStdCall ca_host_minor_protocol ( + chid pChan ); friend const char * epicsStdCall ca_name ( chid pChan ); friend void epicsStdCall ca_set_puser ( @@ -595,11 +597,11 @@ void ca_client_context :: whenThereIsAnExceptionDestroySyncGroupIO ( epicsGuardRelease < epicsMutex > guardRelease ( guard ); { // - // we will definately stall out here if all of the + // we will definitely stall out here if all of the // following are true // // o user creates non-preemptive mode client library context - // o user doesnt periodically call a ca function + // o user doesn't periodically call a ca function // o user calls this function from an auxiliary thread // CallbackGuard cbGuard ( this->cbMutex ); diff --git a/modules/ca/src/client/oldChannelNotify.cpp b/modules/ca/src/client/oldChannelNotify.cpp index 71688c94cd..0a35128270 100644 --- a/modules/ca/src/client/oldChannelNotify.cpp +++ b/modules/ca/src/client/oldChannelNotify.cpp @@ -193,6 +193,16 @@ const char * epicsStdCall ca_host_name ( return pChan->io.pHostName ( guard ); } +/* + * ca_host_minorProtocol () + */ +unsigned epicsStdCall ca_host_minor_protocol ( + chid pChan ) +{ + epicsGuard < epicsMutex > guard ( pChan->cacCtx.mutexRef () ); + return pChan->io.getHostMinorProtocol( guard ); +} + /* * ca_set_puser () */ diff --git a/modules/ca/src/client/searchTimer.cpp b/modules/ca/src/client/searchTimer.cpp index 60b1536392..61dbda2177 100644 --- a/modules/ca/src/client/searchTimer.cpp +++ b/modules/ca/src/client/searchTimer.cpp @@ -353,7 +353,7 @@ void searchTimer::uninstallChanDueToSuccessfulSearchResponse ( // when we get 100% success immediately // send another search request // - debugPrintf ( ( "All requests succesful, set timer delay to zero\n" ) ); + debugPrintf ( ( "All requests successful, set timer delay to zero\n" ) ); this->timer.start ( *this, currentTime ); } } diff --git a/modules/ca/src/client/syncgrp.cpp b/modules/ca/src/client/syncgrp.cpp index 4f79348fda..3967eacb82 100644 --- a/modules/ca/src/client/syncgrp.cpp +++ b/modules/ca/src/client/syncgrp.cpp @@ -79,11 +79,11 @@ extern "C" int epicsStdCall ca_sg_delete ( const CA_SYNC_GID gid ) } else { // - // we will definately stall out here if all of the + // we will definitely stall out here if all of the // following are true // // o user creates non-preemptive mode client library context - // o user doesnt periodically call a ca function + // o user doesn't periodically call a ca function // o user calls this function from an auxiliary thread // CallbackGuard cbGuard ( pcac->cbMutex ); @@ -103,11 +103,11 @@ void sync_group_reset ( ca_client_context & client, CASG & sg ) } else { // - // we will definately stall out here if all of the + // we will definitely stall out here if all of the // following are true // // o user creates non-preemptive mode client library context - // o user doesnt periodically call a ca function + // o user doesn't periodically call a ca function // o user calls this function from an auxiliary thread // CallbackGuard cbGuard ( client.cbMutex ); @@ -216,11 +216,11 @@ extern "C" int epicsStdCall ca_sg_test ( const CA_SYNC_GID gid ) } else { // - // we will definately stall out here if all of the + // we will definitely stall out here if all of the // following are true // // o user creates non-preemptive mode client library context - // o user doesnt periodically call a ca function + // o user doesn't periodically call a ca function // o user calls this function from an auxiliary thread // CallbackGuard cbGuard ( pcac->cbMutex ); diff --git a/modules/ca/src/client/tcpRecvWatchdog.cpp b/modules/ca/src/client/tcpRecvWatchdog.cpp index 367e09237c..e8e8f93c0a 100644 --- a/modules/ca/src/client/tcpRecvWatchdog.cpp +++ b/modules/ca/src/client/tcpRecvWatchdog.cpp @@ -97,7 +97,7 @@ void tcpRecvWatchdog::beaconArrivalNotify ( guard.assertIdenticalMutex ( this->mutex ); if ( ! ( this->shuttingDown || this->beaconAnomaly || this->probeResponsePending ) ) { this->timer.start ( *this, this->period ); - debugPrintf ( ("saw a normal beacon - reseting circuit receive watchdog\n") ); + debugPrintf ( ("saw a normal beacon - resetting circuit receive watchdog\n") ); } } @@ -124,7 +124,7 @@ void tcpRecvWatchdog::messageArrivalNotify ( if ( ! ( this->shuttingDown || this->probeResponsePending ) ) { this->beaconAnomaly = false; this->timer.start ( *this, this->period ); - debugPrintf ( ("received a message - reseting circuit recv watchdog\n") ); + debugPrintf ( ("received a message - resetting circuit recv watchdog\n") ); } } @@ -182,7 +182,7 @@ void tcpRecvWatchdog::sendBacklogProgressNotify ( // receive at least one message from the server. if ( this->probeResponsePending && ! this->shuttingDown ) { this->timer.start ( *this, CA_ECHO_TIMEOUT ); - debugPrintf ( ("saw heavy send backlog - reseting circuit recv watchdog\n") ); + debugPrintf ( ("saw heavy send backlog - resetting circuit recv watchdog\n") ); } } diff --git a/modules/ca/src/client/tcpiiu.cpp b/modules/ca/src/client/tcpiiu.cpp index 30f6fad174..511582c7f8 100644 --- a/modules/ca/src/client/tcpiiu.cpp +++ b/modules/ca/src/client/tcpiiu.cpp @@ -1804,6 +1804,13 @@ const char * tcpiiu::pHostName ( return this->hostNameCacheInstance.pointer (); } +unsigned tcpiiu::getHostMinorProtocol ( + epicsGuard < epicsMutex > & guard) const throw () +{ + guard.assertIdenticalMutex ( this->mutex ); + return this->minorProtocolVersion; +} + void tcpiiu::disconnectAllChannels ( epicsGuard < epicsMutex > & cbGuard, epicsGuard < epicsMutex > & guard, @@ -1817,7 +1824,7 @@ void tcpiiu::disconnectAllChannels ( } while ( nciu * pChan = this->createRespPend.get () ) { - // we don't yet know the server's id so we cant + // we don't yet know the server's id so we can't // send a channel delete request and will instead // trust that the server can do the proper cleanup // when the circuit disconnects @@ -1883,7 +1890,7 @@ void tcpiiu::unlinkAllChannels ( while ( nciu * pChan = this->createRespPend.get () ) { pChan->channelNode::listMember = channelNode::cs_none; - // we don't yet know the server's id so we cant + // we don't yet know the server's id so we can't // send a channel delete request and will instead // trust that the server can do the proper cleanup // when the circuit disconnects diff --git a/modules/ca/src/client/udpiiu.cpp b/modules/ca/src/client/udpiiu.cpp index c6ffb6def1..3e981450fe 100644 --- a/modules/ca/src/client/udpiiu.cpp +++ b/modules/ca/src/client/udpiiu.cpp @@ -83,7 +83,7 @@ double getMaxPeriod() } } else { - epicsPrintf ( "EPICS \"%s\" wasnt a real number\n", + epicsPrintf ( "EPICS \"%s\" wasn't a real number\n", EPICS_CA_MAX_SEARCH_PERIOD.name ); epicsPrintf ( "Setting \"%s\" = %f seconds\n", EPICS_CA_MAX_SEARCH_PERIOD.name, maxPeriod ); @@ -549,7 +549,7 @@ void epicsStdCall caRepeaterRegistrationMessage ( char sockErrBuf[64]; epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) ); - fprintf ( stderr, "error sending registration message to CA repeater daemon was \"%s\"\n", + fprintf ( stderr, ERL_ERROR " sending registration message to CA repeater daemon was \"%s\"\n", sockErrBuf ); } } @@ -813,13 +813,13 @@ bool udpiiu::exceptionRespAction ( if ( msg.m_postsize > sizeof ( caHdr ) ){ errlogPrintf ( - "error condition \"%s\" detected by %s with context \"%s\" at %s\n", + ERL_ERROR " condition \"%s\" detected by %s with context \"%s\" at %s\n", ca_message ( msg.m_available ), name, reinterpret_cast ( &reqMsg + 1 ), date ); } else{ errlogPrintf ( - "error condition \"%s\" detected by %s at %s\n", + ERL_ERROR " condition \"%s\" detected by %s at %s\n", ca_message ( msg.m_available ), name, date ); } @@ -1342,6 +1342,12 @@ const char * udpiiu::pHostName ( return netiiu::pHostName ( cacGuard ); } +unsigned udpiiu::getHostMinorProtocol ( + epicsGuard < epicsMutex > & cacGuard ) const throw () +{ + return netiiu::getHostMinorProtocol ( cacGuard ); +} + bool udpiiu::ca_v42_ok ( epicsGuard < epicsMutex > & cacGuard ) const { diff --git a/modules/ca/src/client/udpiiu.h b/modules/ca/src/client/udpiiu.h index aba79be5dd..abc3fa7576 100644 --- a/modules/ca/src/client/udpiiu.h +++ b/modules/ca/src/client/udpiiu.h @@ -239,7 +239,9 @@ class udpiiu : unsigned bufLength ) const throw (); const char * pHostName ( epicsGuard < epicsMutex > & ) const throw (); - bool ca_v41_ok ( + unsigned getHostMinorProtocol ( + epicsGuard < epicsMutex > & ) const throw (); + bool ca_v41_ok ( epicsGuard < epicsMutex > & ) const; bool ca_v42_ok ( epicsGuard < epicsMutex > & ) const; diff --git a/modules/ca/src/client/virtualCircuit.h b/modules/ca/src/client/virtualCircuit.h index 42af12f5f1..0f43ea95e7 100644 --- a/modules/ca/src/client/virtualCircuit.h +++ b/modules/ca/src/client/virtualCircuit.h @@ -168,6 +168,8 @@ class tcpiiu : unsigned getHostName ( epicsGuard < epicsMutex > &, char *pBuf, unsigned bufLength ) const throw (); + unsigned getHostMinorProtocol ( + epicsGuard < epicsMutex > &) const throw (); bool alive ( epicsGuard < epicsMutex > & ) const; bool connecting ( diff --git a/modules/ca/src/perl/Cap5.xs b/modules/ca/src/perl/Cap5.xs index 5da8948088..9ba55320bf 100644 --- a/modules/ca/src/perl/Cap5.xs +++ b/modules/ca/src/perl/Cap5.xs @@ -64,7 +64,7 @@ const char * get_error_msg(int status) { "ECA_NOSUPPORT - Sorry, that feature is planned but not supported at this time", "ECA_STRTOBIG - The supplied string is unusually large", "ECA_DISCONNCHID - The request was ignored because the specified channel is disconnected", - "ECA_BADTYPE - The data type specifed is invalid", + "ECA_BADTYPE - The data type specified is invalid", "ECA_CHIDNOTFND - Remote Channel not found", "ECA_CHIDRETRY - Unable to locate all user specified channels", "ECA_INTERNAL - Channel Access Internal Failure", @@ -83,12 +83,12 @@ const char * get_error_msg(int status) { "ECA_BADMONID - Bad event subscription (monitor) identifier", "ECA_NEWADDR - Remote channel has new network address", "ECA_NEWCONN - New or resumed network connection", - "ECA_NOCACTX - Specified task isnt a member of a CA context", + "ECA_NOCACTX - Specified task isn't a member of a CA context", "ECA_DEFUNCT - Attempt to use defunct CA feature failed", "ECA_EMPTYSTR - The supplied string is empty", "ECA_NOREPEATER - Unable to spawn the CA repeater thread- auto reconnect will fail", "ECA_NOCHANMSG - No channel id match for search reply- search reply ignored", - "ECA_DLCKREST - Reseting dead connection- will try to reconnect", + "ECA_DLCKREST - Resetting dead connection- will try to reconnect", "ECA_SERVBEHIND - Server (IOC) has fallen behind or is not responding- still waiting", "ECA_NOCAST - No internet interface with broadcast available", "ECA_BADMASK - Invalid event selection mask", @@ -886,7 +886,7 @@ void CA_get_callback(SV *ca_ref, SV *sub, ...) { while (items > i && SvOK(ST(i))) { if (SvIOK(ST(i))) { - /* Interger => Count arg, zero means current size */ + /* Integer => Count arg, zero means current size */ count = SvIV(ST(i)); if (count < 0 || count > ca_element_count(pch->chan)) { croak_msg = "Requested array size is out of range"; @@ -962,7 +962,7 @@ SV * CA_create_subscription(SV *ca_ref, const char *mask_str, SV *sub, ...) { while (items > i && SvOK(ST(i))) { if (SvIOK(ST(i))) { - /* Interger => Count arg, zero means current size */ + /* Integer => Count arg, zero means current size */ count = SvIV(ST(i)); if (count < 0 || count > ca_element_count(pch->chan)) { croak_msg = "Requested array size is out of range"; diff --git a/modules/ca/src/perl/Makefile b/modules/ca/src/perl/Makefile index 05500ad054..77130d5b57 100644 --- a/modules/ca/src/perl/Makefile +++ b/modules/ca/src/perl/Makefile @@ -23,7 +23,7 @@ ifdef T_A # Special settings for Darwin: ifeq ($(OS_CLASS),Darwin) # Use hdepends command (not GNU compiler flags) - # to generate header file dependancies for Darwin. + # to generate header file dependencies for Darwin. # Darwin has multiple -arch compiler flags. HDEPENDS_METHOD = MKMF @@ -38,7 +38,7 @@ ifeq ($(wildcard $(PERL_h)),) endif endif -ifneq (inc,$(strip $(MAKECMDGOALS))) +ifneq ($(filter-out inc,$(strip $(MAKECMDGOALS))),) ifeq ($(T_A),$(EPICS_HOST_ARCH)) # No cross-builds (wrong Perl!) ifeq ($(strip $(XSUBPP)),) $(warning Perl's xsubpp program was not found.) diff --git a/modules/ca/src/template/top/caClientApp/Makefile b/modules/ca/src/template/top/caClientApp/Makefile index 47011375cf..98c52fd037 100644 --- a/modules/ca/src/template/top/caClientApp/Makefile +++ b/modules/ca/src/template/top/caClientApp/Makefile @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2003 Argonne National Laboratory +# +# SPDX-License-Identifier: EPICS + TOP=.. include $(TOP)/configure/CONFIG diff --git a/modules/ca/src/template/top/caClientApp/caExample.c b/modules/ca/src/template/top/caClientApp/caExample.c index 5ab2308274..63afbe880f 100644 --- a/modules/ca/src/template/top/caClientApp/caExample.c +++ b/modules/ca/src/template/top/caClientApp/caExample.c @@ -1,3 +1,7 @@ +/* SPDX-FileCopyrightText: 1998 Argonne National Laboratory */ + +/* SPDX-License-Identifier: EPICS */ + /*caExample.c*/ #include #include diff --git a/modules/ca/src/template/top/caClientApp/caMonitor.c b/modules/ca/src/template/top/caClientApp/caMonitor.c index 5adc90e7fc..8e19b99cba 100644 --- a/modules/ca/src/template/top/caClientApp/caMonitor.c +++ b/modules/ca/src/template/top/caClientApp/caMonitor.c @@ -1,3 +1,7 @@ +/* SPDX-FileCopyrightText: 2000 Argonne National Laboratory */ + +/* SPDX-License-Identifier: EPICS */ + /*caMonitor.c*/ /* This example accepts the name of a file containing a list of pvs to monitor. diff --git a/modules/ca/src/template/top/caPerlApp/Makefile b/modules/ca/src/template/top/caPerlApp/Makefile index 34439cbfd5..5932f000ea 100644 --- a/modules/ca/src/template/top/caPerlApp/Makefile +++ b/modules/ca/src/template/top/caPerlApp/Makefile @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2018 Argonne National Laboratory +# +# SPDX-License-Identifier: EPICS + TOP=.. include $(TOP)/configure/CONFIG diff --git a/modules/ca/src/template/top/caPerlApp/caget.pl b/modules/ca/src/template/top/caPerlApp/caget.pl index 0d9af37a13..2538628fb7 100644 --- a/modules/ca/src/template/top/caPerlApp/caget.pl +++ b/modules/ca/src/template/top/caPerlApp/caget.pl @@ -1,5 +1,9 @@ #!/usr/bin/env perl +# SPDX-FileCopyrightText: 2008 Argonne National Laboratory +# +# SPDX-License-Identifier: EPICS + use strict; # This construct sets @INC to search lib/perl of all RELEASE entries diff --git a/modules/ca/src/template/top/caPerlApp/cainfo.pl b/modules/ca/src/template/top/caPerlApp/cainfo.pl index 3e38e8bafc..71550c364d 100644 --- a/modules/ca/src/template/top/caPerlApp/cainfo.pl +++ b/modules/ca/src/template/top/caPerlApp/cainfo.pl @@ -1,5 +1,9 @@ #!/usr/bin/env perl +# SPDX-FileCopyrightText: 2008 Argonne National Laboratory +# +# SPDX-License-Identifier: EPICS + use strict; # This construct sets @INC to search lib/perl of all RELEASE entries diff --git a/modules/ca/src/template/top/caPerlApp/camonitor.pl b/modules/ca/src/template/top/caPerlApp/camonitor.pl index 3a61f2d6ca..483be29a2e 100644 --- a/modules/ca/src/template/top/caPerlApp/camonitor.pl +++ b/modules/ca/src/template/top/caPerlApp/camonitor.pl @@ -1,5 +1,9 @@ #!/usr/bin/env perl +# SPDX-FileCopyrightText: 2008 Argonne National Laboratory +# +# SPDX-License-Identifier: EPICS + use strict; # This construct sets @INC to search lib/perl of all RELEASE entries diff --git a/modules/ca/src/template/top/caPerlApp/caput.pl b/modules/ca/src/template/top/caPerlApp/caput.pl index 64ff9cbda2..0ea5724126 100644 --- a/modules/ca/src/template/top/caPerlApp/caput.pl +++ b/modules/ca/src/template/top/caPerlApp/caput.pl @@ -1,5 +1,9 @@ #!/usr/bin/env perl +# SPDX-FileCopyrightText: 2008 Argonne National Laboratory +# +# SPDX-License-Identifier: EPICS + use strict; # This construct sets @INC to search lib/perl of all RELEASE entries @@ -163,7 +167,7 @@ sub HELP_MESSAGE { " -w : Wait time, specifies CA timeout, default is $opt_w second\n", " -c: Use put_callback to wait for completion\n", "Format options:\n", - " -t: Terse mode - print only sucessfully written value, without name\n", + " -t: Terse mode - print only successfully written value, without name\n", " -l: Long mode \"name timestamp value stat sevr\" (read PVs as DBR_TIME_xxx)\n", " -S: Put string as an array of char (long string)\n", "Enum format:\n", diff --git a/modules/ca/src/tools/Makefile b/modules/ca/src/tools/Makefile index 0157865f61..8715bda7d1 100644 --- a/modules/ca/src/tools/Makefile +++ b/modules/ca/src/tools/Makefile @@ -24,4 +24,9 @@ cainfo_SRCS = cainfo.c PROD_LIBS = ca Com +DOCS += caget.md +DOCS += cainfo.md +DOCS += camonitor.md +DOCS += caput.md + include $(TOP)/configure/RULES diff --git a/modules/ca/src/tools/caget.c b/modules/ca/src/tools/caget.c index d24fcd066d..0472cddad4 100644 --- a/modules/ca/src/tools/caget.c +++ b/modules/ca/src/tools/caget.c @@ -343,6 +343,7 @@ static int caget (pv *pvs, int nPvs, RequestT request, OutputT format, default : break; } + free(pvs[n].value); } return 0; } @@ -388,7 +389,7 @@ int main (int argc, char *argv[]) int digits = 0; /* getopt() no. of float digits */ int nPvs; /* Number of PVs */ - pv* pvs; /* Array of PV structures */ + pv* pvs = NULL; /* Array of PV structures */ LINE_BUFFER(stdout); /* Configure stdout buffering */ @@ -556,6 +557,7 @@ int main (int argc, char *argv[]) result = caget(pvs, nPvs, request, format, type, count); /* Shut down Channel Access */ + free(pvs); ca_context_destroy(); return result; diff --git a/modules/ca/src/tools/caget.md b/modules/ca/src/tools/caget.md new file mode 100644 index 0000000000..e90665e7a1 --- /dev/null +++ b/modules/ca/src/tools/caget.md @@ -0,0 +1,172 @@ +# caget + +``` {program} caget +``` + + caget [options] ... + +## Description + +Get and print value for PV(s). + +The values for one or multiple PVs are read and printed to stdout. The +`DBR_...` format in which the data is read, the output format, and a +number of details of how integer and float values are represented can be +controlled using command line options. + +When getting multiple PVs, their order on the command line is retained +in the output. + +## Options + +::: {option} -h +Print usage information +::: + +### Channel access options + +::: {option} -w +Wait time, specifies longer CA timeout, default is 1.0 second. +::: + +::: {option} -c +Asynchronous get (use ca_get_callback instead of ca_get). +::: + +::: {option} -p +CA priority (0--99, default 0=lowest) +::: + +### Format and data type options + +Default output format is `name value` + +::: {option} -t +Terse mode --- print only value, without name. +::: + +::: {option} -a +Wide mode `name timestamp value stat sevr` (read PVs as `DBR_TIME_xxx`) +::: + +::: {option} -d +Request specific dbr type; use string (`DBR_` prefix may be omitted) +or number of one of the following types: + +:STRING: 0 +:INT: 1 +:SHORT: 1 +:FLOAT: 2 +:ENUM: 3 +:CHAR: 4 +:LONG: 5 +:DOUBLE: 6 +:STS_STRING: 7 +:STS_INT: 8 +:STS_SHORT: 8 +:STS_FLOAT: 9 +:STS_ENUM: 10 +:STS_CHAR: 11 +:STS_LONG: 12 +:STS_DOUBLE: 13 +:TIME_STRING: 14 +:TIME_INT: 15 +:TIME_SHORT: 15 +:TIME_FLOAT: 16 +:TIME_ENUM: 17 +:TIME_CHAR: 18 +:TIME_LONG: 19 +:TIME_DOUBLE: 20 +:GR_STRING: 21 +:GR_INT: 22 +:GR_SHORT: 22 +:GR_FLOAT: 23 +:GR_ENUM: 24 +:GR_CHAR: 25 +:GR_LONG: 26 +:GR_DOUBLE: 27 +:CTRL_STRING: 28 +:CTRL_INT: 29 +:CTRL_SHORT: 29 +:CTRL_FLOAT: 30 +:CTRL_ENUM: 31 +:CTRL_CHAR: 32 +:CTRL_LONG: 33 +:CTRL_DOUBLE: 34 +:STSACK_STRING: 37 +:CLASS_NAME: 38 +::: + +### Enum format + +::: {option} -n +Print `DBF_ENUM` value as number (default is enum string) +::: + +### Arrays + +Value format: Print number of requested values, then list of values. + +By default: print all values. + +::: {option} -# +Print first \ elements of an array. +::: + +::: {option} -S +Print array of char as a string (long string). +::: + +### Floating point type format + +By default: use `%g` format. + +::: {option} -e +Use `%e` format, with a precision of \ digits. +::: + +::: {option} -f +Use `%f` format, with a precision of \ digits. +::: + +::: {option} -g +Use `%g` format, with a precision of \ digits. +::: + +::: {option} -s +Get value as string (honors server-side precision). +::: + +::: {option} -lx +Round to long integer and print as hex number. +::: + +::: {option} -lo +Round to long integer and print as octal number. +::: + +::: {option} -lb +Round to long integer and print as binary number. +::: + +### Integer number format + +By default: print as decimal number. + +::: {option} -0x +Print as hex number. +::: + +::: {option} -0o +Print as octal number. +::: + +::: {option} -0b +Print as binary number. +::: + +### Alternate output field separator + +::: {option} -F +Use \ as an alternate output field separator. +::: diff --git a/modules/ca/src/tools/cainfo.md b/modules/ca/src/tools/cainfo.md new file mode 100644 index 0000000000..a48801f600 --- /dev/null +++ b/modules/ca/src/tools/cainfo.md @@ -0,0 +1,40 @@ +# cainfo + +``` {program} cainfo +``` + + cainfo [options] ... + +## Description + +Get and print channel and connection information for PV(s). + +All available Channel Access related information about PV(s) is printed +to stdout. + +The {option}`-s` option allows to specify an interest level for calling Channel +Access' internal report function `ca_client_status()`, that prints lots +of internal information on stdout, including environment settings, used +CA ports etc. + +## Options + +::: {option} -h +Print usage information +::: + +### CA options + +::: {option} -w +Wait time, specifies longer CA timeout, default is 1.0 second. +::: + +::: {option} -s +Call ca_client_status with the specified interest level +::: + +::: {option} -p +CA priority (0--99, default 0=lowest) +::: + + diff --git a/modules/ca/src/tools/camonitor.md b/modules/ca/src/tools/camonitor.md new file mode 100644 index 0000000000..e058b61829 --- /dev/null +++ b/modules/ca/src/tools/camonitor.md @@ -0,0 +1,122 @@ +# camonitor + +``` {program} camonitor +``` + + camonitor [options] ... + +## Description + +Subscribe to and print value updates for PV(s). + +## Options + +::: {option} -h +Print usage information +::: + +### CA options + +::: {option} -w +Wait time, specifies longer CA timeout, default is 1.0 second. +::: + +::: {option} -m +Specify CA event mask to use. + +\ is any combination of `v` (value), `a` (alarm), `l` (log/archive), +`p` (property). + +Default event mask is `va` +::: + +::: {option} -p +CA priority (0--99, default 0=lowest) +::: + +### Timestamps + +Default: Print absolute timestamps (as reported by CA server) + +::: {option} -t +Specify timestamp source(s) and type, with \ containing: + +`s` = CA server (remote) timestamps\ +`c` = CA client (local) timestamps (shown in `()`s)\ +`n` = no timestamps\ +`r` = relative timestamps (time elapsed since start of program)\ +`i` = incremental timestamps (time elapsed since last update)\ +`I` = incremental timestamps (time since last update, by channel)\ + +`r`, `i` or `I` require `s` or `c` to select the time source. +::: + +### Enum format + +::: {option} -n +Print `DBF_ENUM` values as number (default is enum string) +::: + +### Arrays + +Array values: Print number of elements, then list of values. + +By default: request and print all elements (dynamic arrays supported). + +::: {option} -# +Request and print first \ elements of an array. +::: + +::: {option} -S +Print array of char as a string (long string). +::: + +### Floating point format + +By default: use `%g` format. + +::: {option} -e +Use `%e` format, with a precision of \ digits. +::: + +::: {option} -f +Use `%f` format, with a precision of \ digits. +::: + +::: {option} -g +Use `%g` format, with a precision of \ digits. +::: + +::: {option} -s +Get value as string (honors server-side precision). +::: + +::: {option} -lx +Round to long integer and print as hex number. +::: + +::: {option} -lo +Round to long integer and print as octal number. +::: + +::: {option} -lb +Round to long integer and print as binary number. +::: + +### Integer number format + +By default: print as decimal number. + +::: {option} -0x +Print as hex number. +::: + +::: {option} -0o +Print as octal number. +::: + +::: {option} -0b +Print as binary number. +::: + + diff --git a/modules/ca/src/tools/caput.c b/modules/ca/src/tools/caput.c index 3f368469c5..fe5374a55d 100644 --- a/modules/ca/src/tools/caput.c +++ b/modules/ca/src/tools/caput.c @@ -35,6 +35,7 @@ #include #include +#include #include #include #include @@ -67,7 +68,7 @@ void usage (void) " -c: Asynchronous put (use ca_put_callback and wait for completion)\n" " -p : CA priority (0-%u, default 0=lowest)\n" "Format options:\n" - " -t: Terse mode - print only sucessfully written value, without name\n" + " -t: Terse mode - print only successfully written value, without name\n" " -l: Long mode \"name timestamp value stat sevr\" (read PVs as DBR_TIME_xxx)\n" "Enum format:\n" " Default: Auto - try value as ENUM string, then as index number\n" @@ -549,7 +550,7 @@ int main (int argc, char *argv[]) result = ca_array_put (dbrType, count, pvs[0].chid, pbuf); } if (result != ECA_NORMAL) { - fprintf(stderr, "Error from put operation: %s\n", ca_message(result)); + fprintf(stderr, ERL_ERROR " from put operation: %s\n", ca_message(result)); free(sbuf); free(dbuf); free(ebuf); return 1; } @@ -570,7 +571,7 @@ int main (int argc, char *argv[]) } if (result != ECA_NORMAL) { - fprintf(stderr, "Error occured writing data: %s\n", ca_message(result)); + fprintf(stderr, ERL_ERROR " occurred writing data: %s\n", ca_message(result)); free(sbuf); free(dbuf); free(ebuf); return 1; } diff --git a/modules/ca/src/tools/caput.md b/modules/ca/src/tools/caput.md new file mode 100644 index 0000000000..447c463db7 --- /dev/null +++ b/modules/ca/src/tools/caput.md @@ -0,0 +1,85 @@ +# caput + +``` {program} caput +``` + + caput [options] ... + caput -a [options] ... + +## Description + +Put value to a PV. + +The specified value is written to the PV (as a string). The PV's value is read +before and after the write operation and printed as "Old" and "New" values on +stdout. + +There are two variants to the arguments for this command. For the scalar +variant without the {option}`-a` flag, all the value arguments provided after +the PV name are concatenated with a single space character between them, and +the resulting string (up to 40 characters long unless the {option}`-S` flag is +given) is written to the specified PV. + +The array variant with the {option}`-a` flag writes an array of string values +to the specified PV. The numeric argument giving the number of array elements +is actually ignored, the array length to be written is actually controlled by +the number of values provided on the command line. + +## Options + +::: {option} -h +Print usage information +::: + +### CA options + +::: {option} -w +Wait time, specifies longer CA timeout, default is 1.0 second. +::: + +::: {option} -c +Asynchronous get (use ca_get_callback instead of ca_get). +::: + +::: {option} -p +CA priority (0--99, default 0=lowest) +::: + +### Format options + +::: {option} -t +Terse mode --- print only successfully written value, without name. +::: + +::: {option} -l +Long mode `name timestamp value stat sevr` (read PVs as `DBR_TIME_xxx`) +::: + +### Enum Format + +By default: Auto --- try value as ENUM string, then as index number + +::: {option} -n +Force interpretation of values as numbers +::: + +::: {option} -s +Force interpretation of values as strings +::: + +### Arrays + +By default: put scalar + +Value format: all value arguments concatenated with spaces + +::: {option} -S +Put string as an array of chars (long string) +::: + +::: {option} -a +Put array + +Value format: number of values, then list of values +::: + diff --git a/modules/database/src/ioc/Makefile b/modules/database/src/ioc/Makefile index cf33453557..15894cd0d9 100644 --- a/modules/database/src/ioc/Makefile +++ b/modules/database/src/ioc/Makefile @@ -16,7 +16,7 @@ USR_CPPFLAGS += -DUSE_TYPED_RSET -DUSE_TYPED_DSET # Shared library ABI version. SHRLIB_VERSION = $(EPICS_DATABASE_MAJOR_VERSION).$(EPICS_DATABASE_MINOR_VERSION).$(EPICS_DATABASE_MAINTENANCE_VERSION) -API_HEADER = dbCoreAPI.h +# dbCoreAPI.h generated earlier during libcom/ dbCore_API = dbCore LIBRARY_IOC += dbCore diff --git a/modules/database/src/ioc/as/Makefile b/modules/database/src/ioc/as/Makefile index bef8637e59..25237c26c5 100644 --- a/modules/database/src/ioc/as/Makefile +++ b/modules/database/src/ioc/as/Makefile @@ -23,4 +23,4 @@ dbCore_SRCS += asIocRegister.c PROD_HOST += ascheck ascheck_SRCS = ascheck.c -ascheck_LIBS = dbCore ca Com +ascheck_LIBS = dbCore ca diff --git a/modules/database/src/ioc/as/asCa.c b/modules/database/src/ioc/as/asCa.c index a250a0f7a6..a62d867691 100644 --- a/modules/database/src/ioc/as/asCa.c +++ b/modules/database/src/ioc/as/asCa.c @@ -225,6 +225,11 @@ static void asCaTask(void) if(asCaDebug) printf("asCaTask has cleared all channels\n"); epicsEventSignal(asCaTaskWait); } + + /* ATM never reached, just a placeholder */ + cantProceed("Unreachable. Perpetual thread."); + + taskwdRemove(0); } void asCaStart(void) diff --git a/modules/database/src/ioc/as/asIocRegister.c b/modules/database/src/ioc/as/asIocRegister.c index dcc2a6bdda..d7885ed3a9 100644 --- a/modules/database/src/ioc/as/asIocRegister.c +++ b/modules/database/src/ioc/as/asIocRegister.c @@ -33,7 +33,7 @@ static const iocshArg asSetSubstitutionsArg0 = { "substitutions",iocshArgString} static const iocshArg * const asSetSubstitutionsArgs[] = {&asSetSubstitutionsArg0}; static const iocshFuncDef asSetSubstitutionsFuncDef = {"asSetSubstitutions",1,asSetSubstitutionsArgs, - "Set subtitutions used when reading ACF file.\n" + "Set substitutions used when reading ACF file.\n" "No immediate effect. Run asInit to (re)load.\n" "Example: asSetSubstitutions var1=5,var2=hello\n"}; static void asSetSubstitutionsCallFunc(const iocshArgBuf *args) @@ -90,7 +90,7 @@ static const iocshFuncDef asprulesFuncDef = { "asprules",1,asprulesArgs, "List rules of an Access Security Group.\n" "If no Group is specified then list the rules for all groups\n" - "Example: asprules mygroup" + "Example: asprules mygroup\n" }; static void asprulesCallFunc(const iocshArgBuf *args) { diff --git a/modules/database/src/ioc/as/ascheck.c b/modules/database/src/ioc/as/ascheck.c index cbbeac48d2..5b344efc8a 100644 --- a/modules/database/src/ioc/as/ascheck.c +++ b/modules/database/src/ioc/as/ascheck.c @@ -53,5 +53,6 @@ int main(int argc,char **argv) status = -1; } errlogFlush(); + asFreeAll((ASBASE*)pasbase); return status; } diff --git a/modules/database/src/ioc/bpt/Makefile b/modules/database/src/ioc/bpt/Makefile index a383d0864a..78886a3b9b 100644 --- a/modules/database/src/ioc/bpt/Makefile +++ b/modules/database/src/ioc/bpt/Makefile @@ -20,7 +20,7 @@ BPT_DBD += bptTypeJdegF.dbd BPT_DBD += bptTypeKdegC.dbd BPT_DBD += bptTypeKdegF.dbd -ifneq (inc,$(strip $(MAKECMDGOALS))) +ifneq ($(filter-out inc,$(strip $(MAKECMDGOALS))),) DBD += $(BPT_DBD) endif @@ -28,5 +28,5 @@ PROD_HOST += makeBpt makeBpt_SRCS = makeBpt +DOCS += menuConvert.md HTMLS += menuConvert.html - diff --git a/modules/database/src/ioc/bpt/makeBpt.c b/modules/database/src/ioc/bpt/makeBpt.c index 23bcb31434..dd6e8d6f4f 100644 --- a/modules/database/src/ioc/bpt/makeBpt.c +++ b/modules/database/src/ioc/bpt/makeBpt.c @@ -21,6 +21,7 @@ #include #include "dbDefs.h" +#include "errlog.h" #include "ellLib.h" #include "cvtTable.h" @@ -125,12 +126,12 @@ int main(int argc, char **argv) } inFile = fopen(argv[1],"r"); if(!inFile) { - fprintf(stderr,"Error opening %s\n",argv[1]); + fprintf(stderr,ERL_ERROR " opening %s\n",argv[1]); exit(-1); } outFile = fopen(outFilename,"w"); if(!outFile) { - fprintf(stderr,"Error opening %s\n",outFilename); + fprintf(stderr,ERL_ERROR " opening %s\n",outFilename); exit(-1); } while(fgets(inbuf,MAX_LINE_SIZE,inFile)) { @@ -223,6 +224,8 @@ int main(int argc, char **argv) fprintf(outFile,"}\n"); fclose(inFile); fclose(outFile); + free(outFilename); + free(pname); return(0); } @@ -357,7 +360,7 @@ static int create_break( struct brkCreateInfo *pbci, brkInt *pabrkInt, if (inc < 1) inc = 1; valid = TRUE; - /* keep trying intervals until cant do better */ + /* keep trying intervals until can't do better */ expanding = TRUE; /* originally we are trying larger and larger * intervals */ while (valid) { diff --git a/modules/database/src/ioc/db/Makefile b/modules/database/src/ioc/db/Makefile index ebb78e229f..a17e45f18b 100644 --- a/modules/database/src/ioc/db/Makefile +++ b/modules/database/src/ioc/db/Makefile @@ -61,6 +61,12 @@ DBDINC += menuScan DBDINC += dbCommon dbMenusPod = $(notdir $(wildcard ../db/menu*.dbd.pod)) + +DOCS += $(patsubst %.dbd.pod,%.md,$(dbMenusPod)) +DOCS += dbCommonRecord.md +DOCS += dbCommonInput.md +DOCS += dbCommonOutput.md + HTMLS += $(patsubst %.dbd.pod,%.html,$(dbMenusPod)) HTMLS += dbCommonRecord.html HTMLS += dbCommonInput.html diff --git a/modules/database/src/ioc/db/callback.c b/modules/database/src/ioc/db/callback.c index 556da37b96..cf6a7b467d 100644 --- a/modules/database/src/ioc/db/callback.c +++ b/modules/database/src/ioc/db/callback.c @@ -100,6 +100,10 @@ static int priorityValue[NUM_CALLBACK_PRIORITIES] = {0, 1, 2}; int callbackSetQueueSize(int size) { + if (size<=0) { + fprintf(stderr, "Queue size must be positive\n"); + return -1; + } if (epicsAtomicGetIntT(&cbState)!=cbInit) { fprintf(stderr, "Callback system already initialized\n"); return -1; @@ -344,6 +348,10 @@ int callbackRequest(epicsCallback *pcallback) epicsInterruptContextMessage("callbackRequest: " ERL_ERROR " pcallback was NULL\n"); return S_db_notInit; } + if (!pcallback->callback) { + epicsInterruptContextMessage("callbackRequest: " ERL_ERROR " pcallback->callback was NULL\n"); + return S_db_notInit; + } priority = pcallback->priority; if (priority < 0 || priority >= NUM_CALLBACK_PRIORITIES) { epicsInterruptContextMessage("callbackRequest: " ERL_ERROR " Bad priority\n"); diff --git a/modules/database/src/ioc/db/callback.h b/modules/database/src/ioc/db/callback.h index ac600fd01c..bbfb687137 100644 --- a/modules/database/src/ioc/db/callback.h +++ b/modules/database/src/ioc/db/callback.h @@ -20,6 +20,10 @@ #include "dbCoreAPI.h" +/** @file callback.h + * @brief Process database deferred execution utility + */ + #ifdef __cplusplus extern "C" { #endif @@ -38,53 +42,176 @@ extern "C" { #define priorityMedium 1 #define priorityHigh 2 +/** Handle for delayed work. + * + * @pre Must be zero initialized prior to first use. + * + * @since 3.15.6 epicsCallback typedef added. CALLBACK typedef deprecated. + */ typedef struct callbackPvt { + /** Callback function */ void (*callback)(struct callbackPvt*); + /** One of priorityLow, priorityMedium, or priorityHigh */ int priority; - void *user; /*for use by callback user*/ - void *timer; /*for use by callback itself*/ + /** for use by callback API user*/ + void *user; + /** Must be zero initialized. Used by callback internals. */ + void *timer; }epicsCallback; #if !defined(EPICS_NO_CALLBACK) +/** Deprecated alias for epicsCallback + * + * Name conflicts with definition from windows.h. + * Portable applications should prefer epicsCallback + * and define the EPICS_NO_CALLBACK pre-processor macro to hide this typedef. + * + * @since 3.15.6 Deprecated in favor of epicsCallback typedef + */ typedef epicsCallback CALLBACK; #endif typedef void (*CALLBACKFUNC)(struct callbackPvt*); - +/** See callbackQueueStatus() */ typedef struct callbackQueueStats { + /** Maxiumum depth of queues */ int size; + /** Current number of elements on each queue */ int numUsed[NUM_CALLBACK_PRIORITIES]; + /** Maximum numUsed seen so far (from init or reset) */ int maxUsed[NUM_CALLBACK_PRIORITIES]; + /** Number of overflow events */ int numOverflow[NUM_CALLBACK_PRIORITIES]; } callbackQueueStats; +/** Assigns callbackPvt::callback */ #define callbackSetCallback(PFUN, PCALLBACK) \ ( (PCALLBACK)->callback = (PFUN) ) +/** Assigns callbackPvt::priority */ #define callbackSetPriority(PRIORITY, PCALLBACK) \ ( (PCALLBACK)->priority = (PRIORITY) ) +/** Assigns callbackPvt::priority */ #define callbackGetPriority(PRIORITY, PCALLBACK) \ ( (PRIORITY) = (PCALLBACK)->priority ) +/** Assigns callbackPvt::user */ #define callbackSetUser(USER, PCALLBACK) \ ( (PCALLBACK)->user = (void *) (USER) ) +/** Read and return callbackPvt::user */ #define callbackGetUser(USER, PCALLBACK) \ ( (USER) = (PCALLBACK)->user ) DBCORE_API void callbackInit(void); DBCORE_API void callbackStop(void); DBCORE_API void callbackCleanup(void); +/** Queue immediate callback request. + * + * Each epicsCallback may be queued multiple times. + * epicsCallback object must not be modified while queued, + * and must remain valid while queued or executing. + * + * @param pCallback Caller expected to initialize or zero all members before first call. + * @return Zero on success. + * Errors if callback members not initialized correctly, or if queue is full. + */ DBCORE_API int callbackRequest(epicsCallback *pCallback); +/** Setup callback to process a record + * @pre Callback must be zero initialized. + * + * @param pcallback Callback to initialize. + * @param Priority priorityLow, priorityMedium, or priorityHigh + * @param pRec A record pointer (dbCommon or specific recordType) + */ DBCORE_API void callbackSetProcess( epicsCallback *pcallback, int Priority, void *pRec); +/** (Re)Initialize callback object and queue + * + * Shorthand for callbackSetProcess() followed by callbackRequest() + * + * @pre Callback object must be zero initialized before first call. + */ DBCORE_API int callbackRequestProcessCallback( epicsCallback *pCallback,int Priority, void *pRec); +/** Queue delayed callback request + * + * Each epicsCallback has a single timer. + * Repeated calls before expiration will cancel and reschedule timer. + * epicsCallback object must not be modified while queued, + * and must remain valid while queued or executing. + * + * epicsCallback::timer must be zeroed before the first call, + * and left unmodified for subsequent calls. + * Each epicsCallback is allocated a timer on first call. + * There is no way to free this allocation. + * Reuse of epicsCallback is strongly recommended. + * + * @param pCallback Callback object. + * Caller expected to initialize or zero all members prior to first call. + * @param seconds Relative to call time. Expected to be >= 0. + * @return Zero on success. + * Errors if callback members not initialized correctly, or if queue is full. + */ DBCORE_API void callbackRequestDelayed( epicsCallback *pCallback,double seconds); +/** Cancel delayed callback. + * + * Usage not recommended. Caller can not distinguish between successful + * cancellation, or expiration. In the later case the callback may still be + * queued or executing. + * + * @param pcallback Callback object previously passed to callbackRequestDelayed() + * + * @post Timer is cancelled. However, callback may be queued or executing. + */ DBCORE_API void callbackCancelDelayed(epicsCallback *pcallback); +/** (Re)Initialize callback object and queue + * + * Shorthand for callbackSetProcess() followed by callbackRequestDelayed() + * + * @pre Callback object must be zero initialized before first call. + */ DBCORE_API void callbackRequestProcessCallbackDelayed( epicsCallback *pCallback, int Priority, void *pRec, double seconds); +/** Set callback queue depth + * + * @param size A positive integer + * @return -1 if too late to change depth + * + * @pre Must be called before iocInit() + */ DBCORE_API int callbackSetQueueSize(int size); +/** Query configuration and statistics from callback system + * @param reset If non-zero, reset maxUsed after reading. + * @param result NULL, or location for results + * @return -2 if result is NULL. reset happens anyway. + * + * @since 7.0.2. Also present in 3.16.2 + */ DBCORE_API int callbackQueueStatus(const int reset, callbackQueueStats *result); DBCORE_API void callbackQueueShow(const int reset); +/** Setup multiple worker threads for specified priority + * + * By default, only one thread is run for each priority (3 in total). + * + * Calling with count==0 will take the count from the callbackParallelThreadsDefault + * global variable (default default is 2). + * Calling with count>0 sets the number of worker threads directly. + * Calling with count<0 computes the count based on the number of CPU cores on the host. + * eg. Passing -2 on an 8 core system will start 6 worker threads. + * In all cases, at least one worker thread will always run. + * + * A special prio name of "*" will modify all priorities. + * Otherwise, only the named priority is modified. + * + * @param count If zero, reset to default (callbackParallelThreadsDefault global/iocsh variable). + * If positive, exact number of worker threads to create. + * If negative, number of worker threads less than core count. + * @param prio Priority name. eg. "*", "LOW", "MEDIUM" or "HIGH". + * @return zero on success, non-zero if called after iocInit() or with invalid arguments. + * + * @pre Must be called before iocInit() + * + * @since 3.15.0.2 + */ DBCORE_API int callbackParallelThreads(int count, const char *prio); #ifdef __cplusplus diff --git a/modules/database/src/ioc/db/dbAccess.c b/modules/database/src/ioc/db/dbAccess.c index 7f6a6b6f74..4b90dc3cb3 100644 --- a/modules/database/src/ioc/db/dbAccess.c +++ b/modules/database/src/ioc/db/dbAccess.c @@ -112,7 +112,7 @@ void dbSpcAsRegisterCallback(SPC_ASCALLBACK func) long dbPutSpecial(DBADDR *paddr,int pass) { - long int (*pspecial)()=NULL; + long int (*pspecial)(struct dbAddr *, int)=NULL; rset *prset; dbCommon *precord = paddr->precord; long status=0; @@ -145,63 +145,66 @@ long dbPutSpecial(DBADDR *paddr,int pass) } static void get_enum_strs(DBADDR *paddr, char **ppbuffer, - rset *prset,long *options) + rset *prset, long *options) { - short field_type=paddr->field_type; - dbFldDes *pdbFldDes = paddr->pfldDes; - dbMenu *pdbMenu; - dbDeviceMenu *pdbDeviceMenu; - char **papChoice; - unsigned long no_str; - char *ptemp; - struct dbr_enumStrs *pdbr_enumStrs=(struct dbr_enumStrs*)(*ppbuffer); - unsigned int i; - - memset(pdbr_enumStrs,'\0',dbr_enumStrs_size); - switch(field_type) { - case DBF_ENUM: - if( prset && prset->get_enum_strs ) { - (*prset->get_enum_strs)(paddr,pdbr_enumStrs); - } else { - *options = (*options)^DBR_ENUM_STRS;/*Turn off option*/ - } - break; - case DBF_MENU: - pdbMenu = (dbMenu *)pdbFldDes->ftPvt; - no_str = pdbMenu->nChoice; - papChoice= pdbMenu->papChoiceValue; - goto choice_common; - case DBF_DEVICE: - pdbDeviceMenu = (dbDeviceMenu *)pdbFldDes->ftPvt; - if(!pdbDeviceMenu) { - *options = (*options)^DBR_ENUM_STRS;/*Turn off option*/ - break; - } - no_str = pdbDeviceMenu->nChoice; - papChoice = pdbDeviceMenu->papChoice; - goto choice_common; -choice_common: - i = sizeof(pdbr_enumStrs->strs)/ - sizeof(pdbr_enumStrs->strs[0]); - if(ino_str = no_str; - ptemp = &(pdbr_enumStrs->strs[0][0]); - for (i=0; istrs[0])); - *(ptemp+sizeof(pdbr_enumStrs->strs[0])-1) = 0; - } - ptemp += sizeof(pdbr_enumStrs->strs[0]); - } - break; - default: - *options = (*options)^DBR_ENUM_STRS;/*Turn off option*/ - break; + struct dbr_enumStrs *penum=(struct dbr_enumStrs*)(*ppbuffer); + /* advance output buffer on success or failure for next option */ + *ppbuffer = dbr_enumStrs_size + (char*)penum; + + memset(penum, 0, dbr_enumStrs_size); + + /* from this point + * on success, return early + * on failure, jump or fall through to clear *options bit. + */ + + if(paddr->field_type == DBF_ENUM) { + if( prset && prset->get_enum_strs ) { + (*prset->get_enum_strs)(paddr,penum); + return; + } + + } else if(paddr->field_type == DBF_MENU || paddr->field_type == DBF_DEVICE) { + char **ppchoices; + epicsUInt32 i, nchoices = 0; + + if(paddr->field_type == DBF_MENU) { + dbMenu *pmenu = paddr->pfldDes->ftPvt; + nchoices = pmenu->nChoice; + ppchoices= pmenu->papChoiceValue; + + } else if(paddr->field_type == DBF_DEVICE) { + dbDeviceMenu *pdevs = paddr->pfldDes->ftPvt; + if(!pdevs) + goto nostrs; + + nchoices = pdevs->nChoice; + ppchoices = pdevs->papChoice; + } + + if(nchoices > NELEMENTS(penum->strs)) + nchoices = NELEMENTS(penum->strs); /* available > capacity, truncated list */ + + penum->no_str = nchoices; + + for(i=0; istrs[i], ppchoices[i], + sizeof(penum->strs[i])); + /* strs[i][] allowed to omit trailing nil */ + } else { + penum->strs[i][0] = '\0'; + } } - *ppbuffer = ((char *)*ppbuffer) + dbr_enumStrs_size; return; + + } else { + /* other DBF_* fall through to error */ + } + +nostrs: + /* indicate option data not available. distinct from no_str==0 */ + *options = (*options)^DBR_ENUM_STRS; } static void get_graphics(DBADDR *paddr, char **ppbuffer, @@ -798,18 +801,13 @@ int dbLoadRecords(const char* file, const char* subs) return -1; } status = dbReadDatabase(&pdbbase, file, 0, subs); - switch(status) - { - case 0: + if(status==0) { if(dbLoadRecordsHook) dbLoadRecordsHook(file, subs); - break; - case -2: - errlogPrintf("dbLoadRecords: failed to load '%s'\n" - " Records cannot be loaded after iocInit!\n", file); - break; - default: - errlogPrintf("dbLoadRecords: failed to load '%s'\n", file); + } else { + fprintf(stderr, ERL_ERROR ": Failed to load '%s'\n", file); + if(status==-2) + fprintf(stderr, " Records cannot be loaded after iocInit!\n"); } return status; } @@ -1097,7 +1095,7 @@ static long dbPutFieldLink(DBADDR *paddr, return S_db_badDbrtype; } - status = dbParseLink(pstring, pfldDes->field_type, &link_info); + status = dbParseLink(pstring, pfldDes->field_type, &link_info, precord->name, pfldDes->name); if (status) return status; @@ -1132,7 +1130,7 @@ static long dbPutFieldLink(DBADDR *paddr, } if (dbCanSetLink(plink, &link_info, new_devsup)) { - /* link type mis-match prevents assignment */ + /* link type mismatch prevents assignment */ status = S_dbLib_badField; goto unlock; } @@ -1327,6 +1325,7 @@ long dbPut(DBADDR *paddr, short dbrType, long status = 0; dbFldDes *pfldDes; int isValueField; + int propertyUpdate = paddr->pfldDes->prop && precord->mlis.count; if (special == SPC_ATTRIBUTE) return S_db_noMod; @@ -1371,12 +1370,31 @@ long dbPut(DBADDR *paddr, short dbrType, if (nRequest < 1) { recGblSetSevr(precord, LINK_ALARM, INVALID_ALARM); } else { - status = dbFastPutConvertRoutine[dbrType][field_type](pbuffer, - paddr->pfield, paddr); + if (propertyUpdate && paddr->field_size <= MAX_STRING_SIZE) { + char propBuffer[MAX_STRING_SIZE]; + status = dbFastPutConvertRoutine[dbrType][field_type](pbuffer, + &propBuffer, paddr); + if (!status) { + if (memcmp(paddr->pfield, &propBuffer, paddr->field_size) != 0) { + memcpy(paddr->pfield, &propBuffer, paddr->field_size); + } else { + /* suppress DBE_PROPERTY event if property did not change */ + propertyUpdate = 0; + } + } + } else { + status = dbFastPutConvertRoutine[dbrType][field_type](pbuffer, + paddr->pfield, paddr); + } nRequest = 1; } } + /* Post property updates before second dbPutSpecial */ + /* which may post DBE_VALUE and/or DBE_LOG events */ + if (propertyUpdate && !status) + db_post_events(precord, NULL, DBE_PROPERTY); + /* Always do special processing if needed */ if (special) { long status2 = dbPutSpecial(paddr, 1); @@ -1393,12 +1411,6 @@ long dbPut(DBADDR *paddr, short dbrType, if (precord->mlis.count && !(isValueField && pfldDes->process_passive)) db_post_events(precord, pfieldsave, DBE_VALUE | DBE_LOG); - /* If this field is a property (metadata) field, - * then post a property change event (even if the field - * didn't change). - */ - if (precord->mlis.count && pfldDes->prop) - db_post_events(precord, NULL, DBE_PROPERTY); done: paddr->pfield = pfieldsave; return status; diff --git a/modules/database/src/ioc/db/dbCa.c b/modules/database/src/ioc/db/dbCa.c index 57cca404fe..40c1e3fd83 100644 --- a/modules/database/src/ioc/db/dbCa.c +++ b/modules/database/src/ioc/db/dbCa.c @@ -115,13 +115,12 @@ static int dbca_chan_count; * During link modification or IOC shutdown the pca->plink pointer (guarded by caLink.lock) * is used as a flag to indicate that a link is no longer active. * - * References to the struct caLink are owned by the dbCaTask, and any scanOnceCallback() - * which is in progress. + * References to the struct caLink are owned by the dbCaTask. * - * The libca and scanOnceCallback callbacks take no action if pca->plink==NULL. + * The libca callbacks take no action if pca->plink==NULL. * * dbCaPutLinkCallback causes an additional complication because - * when dbCaRemoveLink is called the callback may not have occured. + * when dbCaRemoveLink is called the callback may not have occurred. * If putComplete sees plink==0 it will not call the user's code. * If pca->putCallback is non-zero, dbCaTask will call the * user's callback AFTER it has called ca_clear_channel. @@ -225,6 +224,7 @@ void testdbCaWaitForEvent(DBLINK *plink, unsigned long cnt, enum testEvent event assert(plink->type==CA_LINK); pca = (caLink *)plink->value.pv_link.pvt; + caLinkInc(pca); epicsMutexMustLock(pca->lock); assert(!pca->monitor && !pca->connect && !pca->userPvt); @@ -239,6 +239,8 @@ void testdbCaWaitForEvent(DBLINK *plink, unsigned long cnt, enum testEvent event dbScanUnlock(plink->precord); epicsEventMustWait(evt); + /* ensure worker has finished executing */ + dbCaSync(); dbScanLock(plink->precord); epicsMutexMustLock(pca->lock); @@ -250,6 +252,7 @@ void testdbCaWaitForEvent(DBLINK *plink, unsigned long cnt, enum testEvent event epicsEventDestroy(evt); epicsMutexUnlock(pca->lock); + caLinkDec(pca); dbScanUnlock(plink->precord); } @@ -287,16 +290,15 @@ void dbCaSync(void) epicsEventMustWait(wake); /* Worker holds workListLock when calling epicsEventMustTrigger() - * we cycle through workListLock to ensure worker call to + * we hold workListLock to ensure worker call to * epicsEventMustTrigger() returns before we destroy the event. */ epicsMutexMustLock(workListLock); - epicsMutexUnlock(workListLock); - assert(templink.refcount==1); epicsMutexDestroy(templink.lock); epicsEventDestroy(wake); + epicsMutexUnlock(workListLock); } void dbCaCallbackProcess(void *userPvt) @@ -785,38 +787,6 @@ static long doLocked(struct link *plink, dbLinkUserCallback rtn, void *priv) return status; } -static void scanComplete(void *raw, dbCommon *prec) -{ - caLink *pca = raw; - epicsMutexMustLock(pca->lock); - if(!pca->plink) { - /* IOC shutdown or link re-targeted. Do nothing. */ - } else if(pca->scanningOnce==0) { - errlogPrintf("dbCa.c complete callback w/ scanningOnce==0\n"); - } else if(--pca->scanningOnce){ - /* another scan is queued */ - if(scanOnceCallback(prec, scanComplete, raw)) { - errlogPrintf("dbCa.c failed to re-queue scanOnce\n"); - } else - caLinkInc(pca); - } - epicsMutexUnlock(pca->lock); - caLinkDec(pca); -} - -/* must be called with pca->lock held */ -static void scanLinkOnce(dbCommon *prec, caLink *pca) { - if(pca->scanningOnce==0) { - if(scanOnceCallback(prec, scanComplete, pca)) { - errlogPrintf("dbCa.c failed to queue scanOnce\n"); - } else - caLinkInc(pca); - } - if(pca->scanningOnce<5) - pca->scanningOnce++; - /* else too many scans queued */ -} - static lset dbCa_lset = { 0, 1, /* not Constant, Volatile */ NULL, dbCaRemoveLink, @@ -853,7 +823,9 @@ static void connectionCallback(struct connection_handler_args arg) if (precord && ((ppv_link->pvlMask & pvlOptCP) || ((ppv_link->pvlMask & pvlOptCPP) && precord->scan == 0))) - scanLinkOnce(precord, pca); + { + link_action |= CA_DBPROCESS; + } goto done; } pca->hasReadAccess = ca_read_access(arg.chid); @@ -985,7 +957,9 @@ static void eventCallback(struct event_handler_args arg) if ((ppv_link->pvlMask & pvlOptCP) || ((ppv_link->pvlMask & pvlOptCPP) && precord->scan == 0)) - scanLinkOnce(precord, pca); + { + addAction(pca, CA_DBPROCESS); + } } done: epicsMutexUnlock(pca->lock); @@ -1058,7 +1032,9 @@ static void accessRightsCallback(struct access_rights_handler_args arg) if (precord && ((ppv_link->pvlMask & pvlOptCP) || ((ppv_link->pvlMask & pvlOptCPP) && precord->scan == 0))) - scanLinkOnce(precord, pca); + { + addAction(pca, CA_DBPROCESS); + } done: epicsMutexUnlock(pca->lock); } @@ -1270,6 +1246,15 @@ static void dbCaTask(void *arg) printLinks(pca); } } + if (link_action & CA_DBPROCESS) { + dbCommon *prec; + epicsMutexMustLock(pca->lock); + prec = pca->plink->precord; + epicsMutexUnlock(pca->lock); + dbScanLock(prec); + db_process(prec); + dbScanUnlock(prec); + } } SEVCHK(ca_flush_io(), "dbCaTask"); } diff --git a/modules/database/src/ioc/db/dbCaPvt.h b/modules/database/src/ioc/db/dbCaPvt.h index 6c1d902ca7..1ee0745e04 100644 --- a/modules/database/src/ioc/db/dbCaPvt.h +++ b/modules/database/src/ioc/db/dbCaPvt.h @@ -31,6 +31,7 @@ #define CA_MONITOR_STRING 0x20 #define CA_GET_ATTRIBUTES 0x40 #define CA_SYNC 0x1000 +#define CA_DBPROCESS 0x2000 /* write type */ #define CA_PUT 0x1 #define CA_PUT_CALLBACK 0x2 diff --git a/modules/database/src/ioc/db/dbChannel.c b/modules/database/src/ioc/db/dbChannel.c index fd682c93ed..ad05315801 100644 --- a/modules/database/src/ioc/db/dbChannel.c +++ b/modules/database/src/ioc/db/dbChannel.c @@ -635,9 +635,21 @@ long dbChannelGetField(dbChannel *chan, short dbrType, void *pbuffer, { dbCommon *precord = chan->addr.precord; long status = 0; + unsigned char local_fl = 0; dbScanLock(precord); + if (!pfl && (ellCount(&chan->pre_chain) || ellCount(&chan->post_chain))) { + pfl = db_create_read_log(chan); + if (pfl) { + local_fl = 1; + pfl = dbChannelRunPreChain(chan, pfl); + pfl = dbChannelRunPostChain(chan, pfl); + } + } status = dbChannelGet(chan, dbrType, pbuffer, options, nRequest, pfl); + if (local_fl) { + db_delete_field_log(pfl); + } dbScanUnlock(precord); return status; } diff --git a/modules/database/src/ioc/db/dbChannel.h b/modules/database/src/ioc/db/dbChannel.h index d4cb9e9bc3..74c2a445be 100644 --- a/modules/database/src/ioc/db/dbChannel.h +++ b/modules/database/src/ioc/db/dbChannel.h @@ -346,7 +346,7 @@ DBCORE_API void dbChannelInit(void); /** \brief Cleanup the dbChannel subsystem. */ DBCORE_API void dbChannelExit(void); -/** \brief Test the given PV name for existance. +/** \brief Test the given PV name for existence. * * This routine looks up the given record and field name, but does not check * whether any field modifiers given after the field name are correct. @@ -511,6 +511,10 @@ DBCORE_API long dbChannelGet(dbChannel *chan, short type, * \param[in,out] nRequest Pointer to the element count. * \param[in] pfl Pointer to a db_field_log or NULL. * \returns 0, or an error status value. + * + * \since 7.0.10 If pfl is NULL and chan has filters, db_create_read_log() will be called + * internally to create a temporary db_field_log which is passed to dbChannelGet() + * then deallocated. */ DBCORE_API long dbChannelGetField(dbChannel *chan, short type, void *pbuffer, long *options, long *nRequest, void *pfl); diff --git a/modules/database/src/ioc/db/dbChannelIO.cpp b/modules/database/src/ioc/db/dbChannelIO.cpp index 449717e730..87a0044ee8 100644 --- a/modules/database/src/ioc/db/dbChannelIO.cpp +++ b/modules/database/src/ioc/db/dbChannelIO.cpp @@ -14,6 +14,8 @@ * 505 665 1831 */ +#define USE_TYPED_DBEVENT + #include #include @@ -165,7 +167,7 @@ unsigned long dbChannelIO::nativeElementCount ( { guard.assertIdenticalMutex ( this->mutex ); long elements = dbChannelElements ( this->dbch ); - if ( elements >= 0u ) { + if ( elements >= 0 ) { return static_cast < unsigned long > ( elements ); } return 0u; diff --git a/modules/database/src/ioc/db/dbCommon.dbd.pod b/modules/database/src/ioc/db/dbCommon.dbd.pod index 5366d477a2..a3a52589b6 100644 --- a/modules/database/src/ioc/db/dbCommon.dbd.pod +++ b/modules/database/src/ioc/db/dbCommon.dbd.pod @@ -7,7 +7,7 @@ # in file LICENSE that is included with this distribution. #************************************************************************* -=head3 Operator Display Parameters +=head2 Operator Display Parameters The B field contains the record name which must be unique within an EPICS Channel Access name space. The name is supplied by the application @@ -43,7 +43,7 @@ record's purpose. Maximum length is 40 characters. size(29) } -=head3 Scan Fields +=head2 Scan Fields These fields contain information related to how and when a record processes. A few records have unique fields that also affect how they process. These @@ -244,7 +244,7 @@ The B field is for internal use by the scanning system. interest(3) } -=head3 Alarm Fields +=head2 Alarm Fields Alarm fields indicate the status and severity of record alarms, or determine how and when alarms are triggered. Of course, many records have alarm-related @@ -285,7 +285,11 @@ Inf (Infinite) value. UDF defaults to TRUE but can be set in a database file. Record and device support routines which write to the VAL field are generally responsible for setting and clearing UDF. -=fields STAT, SEVR, AMSG, NSTA, NSEV, NAMSG, ACKS, ACKT, UDF +The B field specifies the alarm severity that the record will be set to +whenever its value is undefined (i.e., the UDF field is 1). This includes the +initial severity of the record being undefined after the IOC boots. + +=fields STAT, SEVR, AMSG, NSTA, NSEV, NAMSG, ACKS, ACKT, UDF, UDFS =cut @@ -387,7 +391,7 @@ responsible for setting and clearing UDF. extra("struct scan_element *spvt") } -=head3 Device Fields +=head2 Device Fields The B field contains the address of the Record Support Entry Table. See the Application Developers Guide for details on usage. @@ -441,7 +445,7 @@ The B field is is for private use of the device support modules. menu(menuPriority) } -=head3 Debugging Fields +=head2 Debugging Fields The B field can be used to trace record processing. When this field is non-zero and the record is processed, a trace message will be be printed for @@ -456,7 +460,7 @@ database processing can be supported using this. =fields TPRO, BKPT -=head3 Miscellaneous Fields +=head2 Miscellaneous Fields The B string field sets the name of the access security group used for this record. If left empty, the record is placed in group C. @@ -517,7 +521,7 @@ hardware. =item * -Positive values (normally between 1-255) get the time of the last occurance of +Positive values (normally between 1-255) get the time of the last occurrence of the numbered generalTime event. =back diff --git a/modules/database/src/ioc/db/dbCommonInput.dbd.pod b/modules/database/src/ioc/db/dbCommonInput.dbd.pod index 5ce9136b1f..877c93c364 100644 --- a/modules/database/src/ioc/db/dbCommonInput.dbd.pod +++ b/modules/database/src/ioc/db/dbCommonInput.dbd.pod @@ -11,7 +11,7 @@ These fields usually have the same meaning whenever they are used. See also L and L. -=head3 Input and Value Fields +=head2 Input and Value Fields The B field specifies an input link. It is used by the device support routines to obtain input. For soft analog records it can be a constant, a @@ -30,7 +30,7 @@ reads values directly into VAL, bypassing this field. The B field contains the record's final value, after any needed conversions have been performed. -=head3 Device Input +=head2 Device Input A device input routine normally returns one of the following values to its associated record support routine: @@ -65,7 +65,7 @@ to the engineering units value. =back -=head3 Device Support for Soft Records +=head2 Device Support for Soft Records In most cases, two soft output device support modules are provided: Soft Channel and Raw Soft Channel. Both allow INP to be a constant, a database link, or a @@ -83,7 +83,7 @@ If a value was returned by the link the UDF field is set to FALSE. The device support read routine normally returns the status from C. -=head3 Input Simulation Fields +=head2 Input Simulation Fields The B field controls simulation mode. By setting this field to YES or RAW, the record can be switched into @@ -121,7 +121,7 @@ The B field specifies the SCAN mechanism to be used in simulation mode. This is specifically useful for 'I/O Intr' scanned records, which would otherwise never be scanned in simulation mode. -=head3 Simulation Mode for Input Records +=head2 Simulation Mode for Input Records An input record can be switched into simulation mode of operation by setting the value of SIMM to YES or RAW. diff --git a/modules/database/src/ioc/db/dbCommonOutput.dbd.pod b/modules/database/src/ioc/db/dbCommonOutput.dbd.pod index cfea659c02..fda758172a 100644 --- a/modules/database/src/ioc/db/dbCommonOutput.dbd.pod +++ b/modules/database/src/ioc/db/dbCommonOutput.dbd.pod @@ -11,7 +11,7 @@ These fields usually have the same meaning whenever they are used. See also L and L. -=head3 Output and Value Fields +=head2 Output and Value Fields The B field specifies an output link. It is used by the device support routines to decide where to send output. For soft records, it can be a @@ -38,7 +38,7 @@ The B field contains - whenever possible - the actual read back value obtained from the hardware itself or from the associated device driver. -=head3 Device Support for Soft Records +=head2 Device Support for Soft Records Normally two soft output device support modules are provided, Soft Channel and and Raw Soft Channel. Both write a value through the output link OUT. @@ -50,7 +50,7 @@ The device support write routine normally calls C which writes a value through the OUT link, and returns the status from that call. -=head3 Input and Mode Select Fields +=head2 Input and Mode Select Fields The B field is a link from which the desired output value can be fetched. DOL can be a constant, a database link, or a channel access link. If DOL is a @@ -63,7 +63,7 @@ value C. By setting this field a record can be switched between supervisory and closed loop mode of operation. While in closed loop mode, the VAL field cannot be set via dbPuts. -=head3 Output Mode Selection +=head2 Output Mode Selection The fields DOL and OMSL are used to allow the output record to be part of a closed loop control algorithm. OMSL is meaningful only if DOL refers to a @@ -76,7 +76,7 @@ types with an OIF field and OIF is Full, VAL is set equal to the value obtained from the location referenced by DOL; if OIF is Incremental VAL is incremented by the value obtained from DOL. -=head3 Invalid Output Action Fields +=head2 Invalid Output Action Fields The B field specifies the output action for the case that the record is put into an INVALID alarm severity. IVOA can be one of the following actions: @@ -102,7 +102,7 @@ in engineering units. If a new severity has been set to INVALID and IVOA is C, then VAL is set to IVOV and converted to RVAL before device support is called. -=head3 Invalid Alarm Output Action +=head2 Invalid Alarm Output Action Whenever an output record is put into INVALID alarm severity, IVOA specifies an action to take. The record support process routine for each output record @@ -142,7 +142,7 @@ If IVOA not one of the above, an error message is generated. =back -=head3 Output Simulation Fields +=head2 Output Simulation Fields The B field controls simulation mode. It has either the value YES or NO. By setting this field to YES, the record can be switched into simulation mode @@ -171,7 +171,7 @@ The B field specifies the SCAN mechanism to be used in simulation mode. This is specifically useful for 'I/O Intr' scanned records, which would otherwise never be scanned in simulation mode. -=head3 Simulation Mode for Output Records +=head2 Simulation Mode for Output Records An output record can be switched into simulation mode of operation by setting the value of SIMM to YES. During simulation, the record will be put into alarm diff --git a/modules/database/src/ioc/db/dbCommonPvt.h b/modules/database/src/ioc/db/dbCommonPvt.h index eda8b18a71..4d41902a2a 100644 --- a/modules/database/src/ioc/db/dbCommonPvt.h +++ b/modules/database/src/ioc/db/dbCommonPvt.h @@ -15,13 +15,21 @@ typedef struct dbCommonPvt { /* Thread which is currently processing this record */ struct epicsThreadOSD* procThread; - struct dbCommon common; + /* actually followed by: + * struct dbCommon common; + */ } dbCommonPvt; static EPICS_ALWAYS_INLINE dbCommonPvt* dbRec2Pvt(struct dbCommon *prec) { - return CONTAINER(prec, dbCommonPvt, common); + return (dbCommonPvt*)((char*)prec - sizeof(dbCommonPvt)); +} + +static EPICS_ALWAYS_INLINE +dbCommon* dbPvt2Rec(struct dbCommonPvt *pvt) +{ + return (dbCommon*)&pvt[1]; } #endif // DBCOMMONPVT_H diff --git a/modules/database/src/ioc/db/dbCommonRecord.dbd.pod b/modules/database/src/ioc/db/dbCommonRecord.dbd.pod index fcbea4200b..5a825fba5f 100644 --- a/modules/database/src/ioc/db/dbCommonRecord.dbd.pod +++ b/modules/database/src/ioc/db/dbCommonRecord.dbd.pod @@ -8,8 +8,8 @@ This section contains a description of the fields that are common to all record types. These fields are defined in dbCommon.dbd. -See also L and L. +See also L and +L. =recordtype dbCommon diff --git a/modules/database/src/ioc/db/dbConstLink.c b/modules/database/src/ioc/db/dbConstLink.c index 46ffd92b23..2bc3057621 100644 --- a/modules/database/src/ioc/db/dbConstLink.c +++ b/modules/database/src/ioc/db/dbConstLink.c @@ -94,7 +94,7 @@ static long cvt_st_UInt32(const char *from, void *pfield, const dbAddr *paddr) status = epicsParseFloat64(from, &dval, &end); if (!status && dval >=0 && - dval <= ULONG_MAX) + dval <= UINT_MAX) *to = dval; } return status; diff --git a/modules/database/src/ioc/db/dbContext.cpp b/modules/database/src/ioc/db/dbContext.cpp index dc30876025..c38ca663c6 100644 --- a/modules/database/src/ioc/db/dbContext.cpp +++ b/modules/database/src/ioc/db/dbContext.cpp @@ -13,6 +13,8 @@ * 505 665 1831 */ +#define USE_TYPED_DBEVENT + #include #include "epicsMutex.h" diff --git a/modules/database/src/ioc/db/dbContextReadNotifyCache.cpp b/modules/database/src/ioc/db/dbContextReadNotifyCache.cpp index 1c5e0d0f9b..c11ca3ab43 100644 --- a/modules/database/src/ioc/db/dbContextReadNotifyCache.cpp +++ b/modules/database/src/ioc/db/dbContextReadNotifyCache.cpp @@ -12,6 +12,8 @@ * Author Jeff Hill */ +#define USE_TYPED_DBEVENT + #include #include "epicsMutex.h" diff --git a/modules/database/src/ioc/db/dbConvert.c b/modules/database/src/ioc/db/dbConvert.c index 84f36ed28d..2b17358c4d 100644 --- a/modules/database/src/ioc/db/dbConvert.c +++ b/modules/database/src/ioc/db/dbConvert.c @@ -171,7 +171,7 @@ static long getStringChar(const dbAddr *paddr, *pdst++ = 0; else { char *end; - long status = epicsParseInt8(psrc, pdst++, 10, &end); + long status = epicsParseInt8(psrc, pdst++, dbConvertBase, &end); if (status) return status; @@ -195,7 +195,7 @@ static long getStringUchar(const dbAddr *paddr, *pdst++ = 0; else { char *end; - long status = epicsParseUInt8(psrc, pdst++, 10, &end); + long status = epicsParseUInt8(psrc, pdst++, dbConvertBase, &end); if (status) return status; @@ -219,7 +219,7 @@ static long getStringShort(const dbAddr *paddr, *pdst++ = 0; else { char *end; - long status = epicsParseInt16(psrc, pdst++, 10, &end); + long status = epicsParseInt16(psrc, pdst++, dbConvertBase, &end); if (status) return status; @@ -243,7 +243,7 @@ static long getStringUshort(const dbAddr *paddr, *pdst++ = 0; else { char *end; - long status = epicsParseUInt16(psrc, pdst++, 10, &end); + long status = epicsParseUInt16(psrc, pdst++, dbConvertBase, &end); if (status) return status; @@ -267,7 +267,7 @@ static long getStringLong(const dbAddr *paddr, *pdst++ = 0; else { char *end; - long status = epicsParseInt32(psrc, pdst++, 10, &end); + long status = epicsParseInt32(psrc, pdst++, dbConvertBase, &end); if (status) return status; @@ -291,7 +291,7 @@ static long getStringUlong(const dbAddr *paddr, *pdst++ = 0; else { char *end; - long status = epicsParseUInt32(psrc, pdst, 10, &end); + long status = epicsParseUInt32(psrc, pdst, dbConvertBase, &end); if (status == S_stdlib_noConversion || (!status && (*end == '.' || *end == 'e' || *end == 'E'))) { @@ -302,7 +302,7 @@ static long getStringUlong(const dbAddr *paddr, epicsFloat64 dval; status = epicsParseFloat64(psrc, &dval, &end); - if (!status && 0 <= dval && dval <= ULONG_MAX) + if (!status && 0 <= dval && dval <= UINT_MAX) *pdst = dval; } if (status) @@ -328,7 +328,7 @@ static long getStringInt64(const dbAddr *paddr, *pdst++ = 0; else { char *end; - long status = epicsParseInt64(psrc, pdst++, 10, &end); + long status = epicsParseInt64(psrc, pdst++, dbConvertBase, &end); if (status) return status; @@ -946,7 +946,7 @@ static long putStringChar(dbAddr *paddr, while (nRequest--) { char *end; - long status = epicsParseInt8(psrc, pdst++, 10, &end); + long status = epicsParseInt8(psrc, pdst++, dbConvertBase, &end); if (status) return status; @@ -965,7 +965,7 @@ static long putStringUchar(dbAddr *paddr, while (nRequest--) { char *end; - long status = epicsParseUInt8(psrc, pdst++, 10, &end); + long status = epicsParseUInt8(psrc, pdst++, dbConvertBase, &end); if (status) return status; @@ -984,7 +984,7 @@ static long putStringShort(dbAddr *paddr, while (nRequest--) { char *end; - long status = epicsParseInt16(psrc, pdst++, 10, &end); + long status = epicsParseInt16(psrc, pdst++, dbConvertBase, &end); if (status) return status; @@ -1003,7 +1003,7 @@ static long putStringUshort(dbAddr *paddr, while (nRequest--) { char *end; - long status = epicsParseUInt16(psrc, pdst++, 10, &end); + long status = epicsParseUInt16(psrc, pdst++, dbConvertBase, &end); if (status) return status; @@ -1022,7 +1022,7 @@ static long putStringLong(dbAddr *paddr, while (nRequest--) { char *end; - long status = epicsParseInt32(psrc, pdst++, 10, &end); + long status = epicsParseInt32(psrc, pdst++, dbConvertBase, &end); if (status) return status; @@ -1041,7 +1041,7 @@ static long putStringUlong(dbAddr *paddr, while (nRequest--) { char *end; - long status = epicsParseUInt32(psrc, pdst, 10, &end); + long status = epicsParseUInt32(psrc, pdst, dbConvertBase, &end); if (status == S_stdlib_noConversion || (!status && (*end == '.' || *end == 'e' || *end == 'E'))) { @@ -1052,7 +1052,7 @@ static long putStringUlong(dbAddr *paddr, epicsFloat64 dval; status = epicsParseFloat64(psrc, &dval, &end); - if (!status && 0 <= dval && dval <= ULONG_MAX) + if (!status && 0 <= dval && dval <= UINT_MAX) *pdst = dval; } if (status) @@ -1074,7 +1074,7 @@ static long putStringInt64(dbAddr *paddr, while (nRequest--) { char *end; - long status = epicsParseInt64(psrc, pdst++, 10, &end); + long status = epicsParseInt64(psrc, pdst++, dbConvertBase, &end); if (status) return status; @@ -1178,7 +1178,7 @@ static long putStringEnum(dbAddr *paddr, epicsEnum16 val; char *end; - status = epicsParseUInt16(pfrom, &val, 10, &end); + status = epicsParseUInt16(pfrom, &val, dbConvertBase, &end); if (!status && val < enumStrs.no_str) { *pfield = val; return 0; @@ -1219,7 +1219,7 @@ static long putStringMenu(dbAddr *paddr, } } - if (!epicsParseUInt16(pfrom, &val, 10, NULL) + if (!epicsParseUInt16(pfrom, &val, dbConvertBase, NULL) && val < nChoice) { *pfield = val; return 0; @@ -1258,7 +1258,7 @@ static long putStringDevice(dbAddr *paddr, } } - if (!epicsParseUInt16(pfrom, &val, 10, NULL) && val < nChoice) { + if (!epicsParseUInt16(pfrom, &val, dbConvertBase, NULL) && val < nChoice) { *pfield = val; return 0; } diff --git a/modules/database/src/ioc/db/dbConvertFast.h b/modules/database/src/ioc/db/dbConvertFast.h index 7ab630b91f..470cedbdd9 100644 --- a/modules/database/src/ioc/db/dbConvertFast.h +++ b/modules/database/src/ioc/db/dbConvertFast.h @@ -7,20 +7,38 @@ * EPICS Base is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ -/* dbConvertFast.h */ +/** @file dbConvertFast.h + * @brief Data conversion for scalar values + * + * The typedef FASTCONVERTFUNC is defined in link.h as: + * @code + * long convert(const void *from, void *to, const struct dbAddr *paddr); + * @endcode + * + * The arrays declared here provide pointers to the fast conversion + * routine where the first array index is the data type for the first + * "from" pointer arg, and the second array index is the data type for + * the second "to" pointer arg. The array index values are a subset of + * the DBF_ enum values defined in dbFldTypes.h + */ #ifndef INCdbConvertFasth #define INCdbConvertFasth #include "dbFldTypes.h" #include "dbCoreAPI.h" +#include "link.h" #ifdef __cplusplus extern "C" { #endif -DBCORE_API extern long (*dbFastGetConvertRoutine[DBF_DEVICE+1][DBR_ENUM+1])(); -DBCORE_API extern long (*dbFastPutConvertRoutine[DBR_ENUM+1][DBF_DEVICE+1])(); +/** Function pointers for get conversions */ +DBCORE_API extern const FASTCONVERTFUNC + dbFastGetConvertRoutine[DBF_DEVICE+1][DBR_ENUM+1]; +/** Function pointers for put conversions */ +DBCORE_API extern const FASTCONVERTFUNC + dbFastPutConvertRoutine[DBR_ENUM+1][DBF_DEVICE+1]; #ifdef __cplusplus } diff --git a/modules/database/src/ioc/db/dbConvertJSON.c b/modules/database/src/ioc/db/dbConvertJSON.c index 95f944e2b2..ce3f5bbed5 100644 --- a/modules/database/src/ioc/db/dbConvertJSON.c +++ b/modules/database/src/ioc/db/dbConvertJSON.c @@ -19,8 +19,6 @@ #include "dbConvertFast.h" #include "dbConvertJSON.h" -typedef long (*FASTCONVERT)(); - typedef struct parseContext { int depth; short dbrType; @@ -42,7 +40,7 @@ static int dbcj_boolean(void *ctx, int val) { static int dbcj_integer(void *ctx, long long num) { parseContext *parser = (parseContext *) ctx; epicsInt64 val64 = num; - FASTCONVERT conv = dbFastPutConvertRoutine[DBF_INT64][parser->dbrType]; + FASTCONVERTFUNC conv = dbFastPutConvertRoutine[DBF_INT64][parser->dbrType]; if (parser->elems > 0) { conv(&val64, parser->pdest, NULL); @@ -54,7 +52,7 @@ static int dbcj_integer(void *ctx, long long num) { static int dbcj_double(void *ctx, double num) { parseContext *parser = (parseContext *) ctx; - FASTCONVERT conv = dbFastPutConvertRoutine[DBF_DOUBLE][parser->dbrType]; + FASTCONVERTFUNC conv = dbFastPutConvertRoutine[DBF_DOUBLE][parser->dbrType]; if (parser->elems > 0) { conv(&num, parser->pdest, NULL); diff --git a/modules/database/src/ioc/db/dbDbLink.c b/modules/database/src/ioc/db/dbDbLink.c index 77d9266307..4962210893 100644 --- a/modules/database/src/ioc/db/dbDbLink.c +++ b/modules/database/src/ioc/db/dbDbLink.c @@ -226,25 +226,48 @@ static long dbDbGetValue(struct link *plink, short dbrType, void *pbuffer, } if (!status && precord != dbChannelRecord(chan)) - recGblInheritSevr(plink->value.pv_link.pvlMask & pvlOptMsMode, + recGblInheritSevrMsg(plink->value.pv_link.pvlMask & pvlOptMsMode, plink->precord, - dbChannelRecord(chan)->stat, dbChannelRecord(chan)->sevr); + dbChannelRecord(chan)->stat, dbChannelRecord(chan)->sevr, + dbChannelRecord(chan)->amsg); return status; } -static long dbDbGetControlLimits(const struct link *plink, double *low, - double *high) +/* Some records get options (precsision, units, ...) for some fields + * from an input link. We need to catch the case that this link + * points back to the same field or we will end in an infinite recursion. +*/ +static long dbDbGetOptionLoopSafe(const struct link *plink, short dbrType, + void *pbuffer, long option) { + /* We need to cast away const to set the flags. + That's ok because we know that plink is never actually readonly. + And we reset everything to its original state. + */ + struct link *mutable_plink = (struct link *)plink; + long status = S_dbLib_badLink; dbChannel *chan = linkChannel(plink); DBADDR *paddr = &chan->addr; + long number_elements = 0; + + dbScanLock(paddr->precord); + if (!(mutable_plink->flags & DBLINK_FLAG_VISITED)) { + mutable_plink->flags |= DBLINK_FLAG_VISITED; + status = dbGet(paddr, dbrType, pbuffer, &option, &number_elements, NULL); + mutable_plink->flags &= ~DBLINK_FLAG_VISITED; + } + dbScanUnlock(paddr->precord); + return status; +} + +static long dbDbGetControlLimits(const struct link *plink, double *low, + double *high) +{ struct buffer { DBRctrlDouble double value; } buffer; - long options = DBR_CTRL_DOUBLE; - long number_elements = 0; - long status = dbGet(paddr, DBR_DOUBLE, &buffer, &options, &number_elements, - NULL); + long status = dbDbGetOptionLoopSafe(plink, DBR_DOUBLE, &buffer, DBR_CTRL_DOUBLE); if (status) return status; @@ -257,16 +280,11 @@ static long dbDbGetControlLimits(const struct link *plink, double *low, static long dbDbGetGraphicLimits(const struct link *plink, double *low, double *high) { - dbChannel *chan = linkChannel(plink); - DBADDR *paddr = &chan->addr; struct buffer { DBRgrDouble double value; } buffer; - long options = DBR_GR_DOUBLE; - long number_elements = 0; - long status = dbGet(paddr, DBR_DOUBLE, &buffer, &options, &number_elements, - NULL); + long status = dbDbGetOptionLoopSafe(plink, DBR_DOUBLE, &buffer, DBR_GR_DOUBLE); if (status) return status; @@ -279,16 +297,11 @@ static long dbDbGetGraphicLimits(const struct link *plink, double *low, static long dbDbGetAlarmLimits(const struct link *plink, double *lolo, double *low, double *high, double *hihi) { - dbChannel *chan = linkChannel(plink); - DBADDR *paddr = &chan->addr; struct buffer { DBRalDouble double value; } buffer; - long options = DBR_AL_DOUBLE; - long number_elements = 0; - long status = dbGet(paddr, DBR_DOUBLE, &buffer, &options, &number_elements, - 0); + long status = dbDbGetOptionLoopSafe(plink, DBR_DOUBLE, &buffer, DBR_AL_DOUBLE); if (status) return status; @@ -302,16 +315,11 @@ static long dbDbGetAlarmLimits(const struct link *plink, double *lolo, static long dbDbGetPrecision(const struct link *plink, short *precision) { - dbChannel *chan = linkChannel(plink); - DBADDR *paddr = &chan->addr; struct buffer { DBRprecision double value; } buffer; - long options = DBR_PRECISION; - long number_elements = 0; - long status = dbGet(paddr, DBR_DOUBLE, &buffer, &options, &number_elements, - 0); + long status = dbDbGetOptionLoopSafe(plink, DBR_DOUBLE, &buffer, DBR_PRECISION); if (status) return status; @@ -322,16 +330,11 @@ static long dbDbGetPrecision(const struct link *plink, short *precision) static long dbDbGetUnits(const struct link *plink, char *units, int unitsSize) { - dbChannel *chan = linkChannel(plink); - DBADDR *paddr = &chan->addr; struct buffer { DBRunits double value; } buffer; - long options = DBR_UNITS; - long number_elements = 0; - long status = dbGet(paddr, DBR_DOUBLE, &buffer, &options, &number_elements, - 0); + long status = dbDbGetOptionLoopSafe(plink, DBR_DOUBLE, &buffer, DBR_UNITS); if (status) return status; @@ -376,8 +379,8 @@ static long dbDbPutValue(struct link *plink, short dbrType, dbCommon *pdest = dbChannelRecord(chan); long status = dbPut(paddr, dbrType, pbuffer, nRequest); - recGblInheritSevr(ppv_link->pvlMask & pvlOptMsMode, pdest, psrce->nsta, - psrce->nsev); + recGblInheritSevrMsg(ppv_link->pvlMask & pvlOptMsMode, pdest, psrce->nsta, + psrce->nsev, psrce->namsg); if (status) return status; @@ -440,6 +443,17 @@ static long processTarget(dbCommon *psrc, dbCommon *pdst) epicsUInt8 pact = psrc->pact; epicsThreadId self = epicsThreadGetIdSelf(); +#ifdef LOCKSET_DEBUG + { + lockSet *ls = dbLockGetRef(psrc->lset); + assert(ls->owner == self); + dbLockDecRef(ls); + ls = dbLockGetRef(pdst->lset); + assert(ls->owner == self); + dbLockDecRef(ls); + } +#endif + psrc->pact = TRUE; if (psrc->ppn) diff --git a/modules/database/src/ioc/db/dbEvent.c b/modules/database/src/ioc/db/dbEvent.c index 38ec9e701f..a60f2572bc 100644 --- a/modules/database/src/ioc/db/dbEvent.c +++ b/modules/database/src/ioc/db/dbEvent.c @@ -19,6 +19,8 @@ */ #define EPICS_PRIVATE_API +#define USE_TYPED_DBEVENT + #include #include #include @@ -82,9 +84,10 @@ struct event_que { struct event_user { struct event_que firstque; /* the first event que */ + ELLLIST waiters; /* event_waiter::node */ + epicsMutexId lock; epicsEventId ppendsem; /* Wait while empty */ - epicsEventId pflush_sem; /* wait for flush */ epicsEventId pexitsem; /* wait for event task to join */ EXTRALABORFUNC *extralabor_sub;/* off load to event task */ @@ -97,10 +100,15 @@ struct event_user { unsigned char extra_labor; /* if set call extra labor func */ unsigned char flowCtrlMode; /* replace existing monitor */ unsigned char extraLaborBusy; - void (*init_func)(); - epicsThreadId init_func_arg; + void (*init_func)(void *); + void *init_func_arg; }; +typedef struct { + ELLNODE node; /* event_user::waiters */ + epicsEventId wake; +} event_waiter; + /* * Reliable intertask communication requires copying the current value of the * channel for later queuing so 3 stepper motor steps of 10 each do not turn @@ -306,9 +314,6 @@ dbEventCtx db_init_events (void) evUser->ppendsem = epicsEventCreate(epicsEventEmpty); if (!evUser->ppendsem) goto fail; - evUser->pflush_sem = epicsEventCreate(epicsEventEmpty); - if (!evUser->pflush_sem) - goto fail; evUser->lock = epicsMutexCreate(); if (!evUser->lock) goto fail; @@ -326,8 +331,6 @@ dbEventCtx db_init_events (void) epicsMutexDestroy (evUser->firstque.writelock); if(evUser->ppendsem) epicsEventDestroy (evUser->ppendsem); - if(evUser->pflush_sem) - epicsEventDestroy (evUser->pflush_sem); if(evUser->pexitsem) epicsEventDestroy (evUser->pexitsem); freeListFree(dbevEventUserFreeList,evUser); @@ -391,7 +394,6 @@ void db_close_events (dbEventCtx ctx) epicsEventDestroy(evUser->pexitsem); epicsEventDestroy(evUser->ppendsem); - epicsEventDestroy(evUser->pflush_sem); epicsMutexDestroy(evUser->lock); epicsMutexUnlock (stopSync); @@ -555,6 +557,40 @@ static void event_remove ( struct event_que *ev_que, pevent->npend--; } +/* synchronize with worker thread. + * + * On return, any previously pending events or extra labor have been handled. + * + * caller must lock evUser->lock + */ +static +void db_sync_event (struct event_user * const evUser) +{ + /* grab current cycle counter, then wait for it to change */ + epicsUInt32 curSeq = evUser->pflush_seq; + event_waiter wait; + wait.wake = epicsEventCreate(epicsEventEmpty); /* failure allowed */ + + ellAdd(&evUser->waiters, &wait.node); + do { + epicsMutexUnlock( evUser->lock ); + /* ensure worker will cycle at least once */ + epicsEventMustTrigger(evUser->ppendsem); + + if(wait.wake) { + epicsEventMustWait(wait.wake); + } else { + epicsThreadSleep(0.01); /* ick. but better than cantProceed() */ + } + + epicsMutexMustLock ( evUser->lock ); + } while(curSeq == evUser->pflush_seq); + ellDelete(&evUser->waiters, &wait.node); + /* destroy under lock to ensure epicsEventMustTrigger() has returned */ + if(wait.wake) + epicsEventDestroy(wait.wake); +} + /* * DB_CANCEL_EVENT() * @@ -592,24 +628,9 @@ void db_cancel_event (dbEventSubscription event) UNLOCKEVQUE (que); if(sync) { - /* wait for worker to cycle */ - struct event_user *evUser = que->evUser; - epicsUInt32 curSeq; - epicsMutexMustLock ( evUser->lock ); - /* grab current cycle counter, then wait for it to change */ - curSeq = evUser->pflush_seq; - do { - epicsMutexUnlock( evUser->lock ); - epicsEventMustWait(evUser->pflush_sem); - /* The complexity needed to track the # of waiters does not seem - * worth it for the relatively rare situation of concurrent cancel. - * So uncondtionally re-trigger. This will result in one spurious - * wakeup for each cancellation. - */ - epicsEventTrigger(evUser->pflush_sem); - epicsMutexMustLock ( evUser->lock ); - } while(curSeq == evUser->pflush_seq); - epicsMutexUnlock( evUser->lock ); + epicsMutexMustLock ( que->evUser->lock ); + db_sync_event(que->evUser); + epicsMutexUnlock( que->evUser->lock ); } } @@ -623,10 +644,10 @@ void db_flush_extra_labor_event (dbEventCtx ctx) struct event_user * const evUser = (struct event_user *) ctx; epicsMutexMustLock ( evUser->lock ); - while ( evUser->extraLaborBusy ) { - epicsMutexUnlock ( evUser->lock ); - epicsThreadSleep(0.1); - epicsMutexMustLock ( evUser->lock ); + if ( evUser->extraLaborBusy || (evUser->extra_labor && evUser->extralabor_sub) ) { + db_sync_event(evUser); + // At this point, original labor completed. + // Do not wait for any additional labor queued afterwards. } epicsMutexUnlock ( evUser->lock ); } @@ -1008,7 +1029,6 @@ static void event_task (void *pParm) do { void (*pExtraLaborSub) (void *); void *pExtraLaborArg; - char wake; epicsEventMustWait(evUser->ppendsem); /* @@ -1016,9 +1036,7 @@ static void event_task (void *pParm) * labor to this task */ epicsMutexMustLock ( evUser->lock ); - evUser->extraLaborBusy = TRUE; if ( evUser->extra_labor && evUser->extralabor_sub ) { - evUser->extra_labor = FALSE; pExtraLaborSub = evUser->extralabor_sub; pExtraLaborArg = evUser->extralabor_arg; } @@ -1026,12 +1044,14 @@ static void event_task (void *pParm) pExtraLaborSub = NULL; pExtraLaborArg = NULL; } + evUser->extra_labor = FALSE; if ( pExtraLaborSub ) { + evUser->extraLaborBusy = TRUE; epicsMutexUnlock ( evUser->lock ); (*pExtraLaborSub)(pExtraLaborArg); epicsMutexMustLock ( evUser->lock ); + evUser->extraLaborBusy = FALSE; } - evUser->extraLaborBusy = FALSE; for ( ev_que = &evUser->firstque; ev_que; ev_que = ev_que->nextque ) { /* unlock during iteration is safe as event_que will not be free'd */ @@ -1042,11 +1062,18 @@ static void event_task (void *pParm) pendexit = evUser->pendexit; evUser->pflush_seq++; + if(ellCount(&evUser->waiters)) { + /* hold lock throughout to avoid race between event trigger and destroy */ + ELLNODE *cur; + for(cur = ellFirst(&evUser->waiters); cur; cur = ellNext(cur)) { + event_waiter *w = CONTAINER(cur, event_waiter, node); + if(w->wake) + epicsEventMustTrigger(w->wake); + } + } epicsMutexUnlock ( evUser->lock ); - epicsEventSignal(evUser->pflush_sem); - } while( ! pendexit ); epicsMutexDestroy(evUser->firstque.writelock); diff --git a/modules/database/src/ioc/db/dbEvent.h b/modules/database/src/ioc/db/dbEvent.h index 277d6c07ec..32e54265b3 100644 --- a/modules/database/src/ioc/db/dbEvent.h +++ b/modules/database/src/ioc/db/dbEvent.h @@ -32,6 +32,15 @@ struct dbChannel; struct db_field_log; struct evSubscrip; +#ifdef USE_TYPED_DBEVENT +struct dbEventContext; // use dbEventCtx +typedef struct evSubscrip* dbEventSubscription; +typedef struct dbEventContext* dbEventCtx; +#else +typedef void * dbEventSubscription; +typedef void * dbEventCtx; +#endif + DBCORE_API int db_event_list ( const char *name, unsigned level); DBCORE_API int dbel ( @@ -39,8 +48,6 @@ DBCORE_API int dbel ( DBCORE_API int db_post_events ( void *pRecord, void *pField, unsigned caEventMask ); -typedef void * dbEventCtx; - typedef void EXTRALABORFUNC (void *extralabor_arg); DBCORE_API dbEventCtx db_init_events (void); DBCORE_API int db_start_events ( @@ -63,7 +70,6 @@ DBCORE_API void db_init_event_freelists (void); typedef void EVENTFUNC (void *user_arg, struct dbChannel *chan, int eventsRemaining, struct db_field_log *pfl); -typedef void * dbEventSubscription; DBCORE_API dbEventSubscription db_add_event ( dbEventCtx ctx, struct dbChannel *chan, EVENTFUNC *user_sub, void *user_arg, unsigned select); diff --git a/modules/database/src/ioc/db/dbFastLinkConv.c b/modules/database/src/ioc/db/dbFastLinkConv.c index 39cddce5f5..890c6732fd 100644 --- a/modules/database/src/ioc/db/dbFastLinkConv.c +++ b/modules/database/src/ioc/db/dbFastLinkConv.c @@ -71,11 +71,10 @@ */ /* Convert String to String */ -static long cvt_st_st( - char *from, - char *to, - const dbAddr *paddr) +static long cvt_st_st(const void *f, void *t, const dbAddr *paddr) { + const char *from = (const char *) f; + char *to = (char *) t; size_t size; if (paddr && paddr->field_size < MAX_STRING_SIZE) { @@ -89,86 +88,80 @@ static long cvt_st_st( } /* Convert String to Char */ -static long cvt_st_c( - char *from, - epicsInt8 *to, - const dbAddr *paddr) +static long cvt_st_c(const void *f, void *t, const dbAddr *paddr) { + const char *from = (const char *) f; + epicsInt8 *to = (epicsInt8 *) t; char *end; if (*from == 0) { *to = 0; return 0; } - return epicsParseInt8(from, to, 10, &end); + return epicsParseInt8(from, to, dbConvertBase, &end); } /* Convert String to Unsigned Char */ -static long cvt_st_uc( - char *from, - epicsUInt8 *to, - const dbAddr *paddr) +static long cvt_st_uc(const void *f, void *t, const dbAddr *paddr) { + const char *from = (const char *) f; + epicsUInt8 *to = (epicsUInt8 *) t; char *end; if (*from == 0) { *to = 0; return 0; } - return epicsParseUInt8(from, to, 10, &end); + return epicsParseUInt8(from, to, dbConvertBase, &end); } /* Convert String to Short */ -static long cvt_st_s( - char *from, - epicsInt16 *to, - const dbAddr *paddr) +static long cvt_st_s(const void *f, void *t, const dbAddr *paddr) { + const char *from = (const char *) f; + epicsInt16 *to = (epicsInt16 *) t; char *end; if (*from == 0) { *to = 0; return 0; } - return epicsParseInt16(from, to, 10, &end); + return epicsParseInt16(from, to, dbConvertBase, &end); } /* Convert String to Unsigned Short */ -static long cvt_st_us( - char *from, - epicsUInt16 *to, - const dbAddr *paddr) +static long cvt_st_us(const void *f, void *t, const dbAddr *paddr) { + const char *from = (const char *) f; + epicsUInt16 *to = (epicsUInt16 *) t; char *end; if (*from == 0) { *to = 0; return 0; } - return epicsParseUInt16(from, to, 10, &end); + return epicsParseUInt16(from, to, dbConvertBase, &end); } /* Convert String to Long */ -static long cvt_st_l( - char *from, - epicsInt32 *to, - const dbAddr *paddr) +static long cvt_st_l(const void *f, void *t, const dbAddr *paddr) { + const char *from = (const char *) f; + epicsInt32 *to = (epicsInt32 *) t; char *end; if (*from == 0) { *to = 0; return 0; } - return epicsParseInt32(from, to, 10, &end); + return epicsParseInt32(from, to, dbConvertBase, &end); } /* Convert String to Unsigned Long */ -static long cvt_st_ul( - char *from, - epicsUInt32 *to, - const dbAddr *paddr) +static long cvt_st_ul(const void *f, void *t, const dbAddr *paddr) { + const char *from = (const char *) f; + epicsUInt32 *to = (epicsUInt32 *) t; char *end; long status; @@ -176,7 +169,7 @@ static long cvt_st_ul( *to = 0; return 0; } - status = epicsParseUInt32(from, to, 10, &end); + status = epicsParseUInt32(from, to, dbConvertBase, &end); if (status == S_stdlib_noConversion || (!status && (*end == '.' || *end == 'e' || *end == 'E'))) { /* @@ -188,33 +181,31 @@ static long cvt_st_ul( status = epicsParseFloat64(from, &dval, &end); if (!status && dval >=0 && - dval <= ULONG_MAX) + dval <= UINT_MAX) *to = dval; } return status; } /* Convert String to Int64 */ -static long cvt_st_q( - char *from, - epicsInt64 *to, - const dbAddr *paddr) +static long cvt_st_q(const void *f, void *t, const dbAddr *paddr) { + const char *from = (const char *) f; + epicsInt64 *to = (epicsInt64 *) t; char *end; if (*from == 0) { *to = 0; return 0; } - return epicsParseInt64(from, to, 10, &end); + return epicsParseInt64(from, to, dbConvertBase, &end); } /* Convert String to UInt64 */ -static long cvt_st_uq( - char *from, - epicsUInt64 *to, - const dbAddr *paddr) +static long cvt_st_uq(const void *f, void *t, const dbAddr *paddr) { + const char *from = (const char *) f; + epicsUInt64 *to = (epicsUInt64 *) t; char *end; if (*from == 0) { @@ -225,41 +216,38 @@ static long cvt_st_uq( } /* Convert String to Float */ -static long cvt_st_f( - char *from, - epicsFloat32 *to, - const dbAddr *paddr) +static long cvt_st_f(const void *f, void *t, const dbAddr *paddr) { + const char *from = (const char *) f; + epicsFloat32 *to = (epicsFloat32 *) t; char *end; - if (*from == 0) { - *to = 0; - return 0; - } - return epicsParseFloat32(from, to, &end); + if (*from == 0) { + *to = 0; + return 0; + } + return epicsParseFloat32(from, to, &end); } /* Convert String to Double */ -static long cvt_st_d( - char *from, - epicsFloat64 *to, - const dbAddr *paddr) +static long cvt_st_d(const void *f, void *t, const dbAddr *paddr) { + const char *from = (const char *) f; + epicsFloat64 *to = (epicsFloat64 *) t; char *end; - if (*from == 0) { - *to = 0.0; - return 0; - } - return epicsParseFloat64(from, to, &end); + if (*from == 0) { + *to = 0.0; + return 0; + } + return epicsParseFloat64(from, to, &end); } /* Convert String to Enumerated */ -static long cvt_st_e( - char *from, - epicsEnum16 *to, - const dbAddr *paddr) +static long cvt_st_e(const void *f, void *t, const dbAddr *paddr) { + const char *from = (const char *) f; + epicsEnum16 *to = (epicsEnum16 *) t; rset *prset = dbGetRset(paddr); long status = S_db_noRSET; struct dbr_enumStrs enumStrs; @@ -281,7 +269,7 @@ static long cvt_st_e( if (!status) { epicsEnum16 val; - status = epicsParseUInt16(from, &val, 10, NULL); + status = epicsParseUInt16(from, &val, dbConvertBase, NULL); if (!status && val < enumStrs.no_str) { *to = val; return 0; @@ -294,11 +282,10 @@ static long cvt_st_e( } /* Convert String to Menu */ -static long cvt_st_menu( - char *from, - epicsEnum16 *to, - const dbAddr *paddr) +static long cvt_st_menu(const void *f, void *t, const dbAddr *paddr) { + const char *from = (const char *) f; + epicsEnum16 *to = (epicsEnum16 *) t; dbFldDes *pdbFldDes = paddr->pfldDes; dbMenu *pdbMenu; char **pchoices; @@ -319,21 +306,20 @@ static long cvt_st_menu( } } - if (!epicsParseUInt16(from, &val, 10, NULL) && val < nChoice) { + if (!epicsParseUInt16(from, &val, dbConvertBase, NULL) && val < nChoice) { *to = val; return 0; } } recGblDbaddrError(S_db_badChoice, paddr, "dbFastLinkConv(cvt_st_menu)"); - return(S_db_badChoice); + return S_db_badChoice; } /* Convert String to Device */ -static long cvt_st_device( - char *from, - epicsEnum16 *to, - const dbAddr *paddr) +static long cvt_st_device(const void *f, void *t, const dbAddr *paddr) { + const char *from = (const char *) f; + epicsEnum16 *to = (epicsEnum16 *) t; dbFldDes *pdbFldDes = paddr->pfldDes; dbDeviceMenu *pdbDeviceMenu = pdbFldDes->ftPvt; char **pchoices, *pchoice; @@ -353,7 +339,7 @@ static long cvt_st_device( } } - if (!epicsParseUInt16(from, &val, 10, NULL) && val < nChoice) { + if (!epicsParseUInt16(from, &val, dbConvertBase, NULL) && val < nChoice) { *to = val; return 0; } @@ -363,979 +349,1234 @@ static long cvt_st_device( } /* Convert Char to String */ -static long cvt_c_st( - epicsInt8 *from, - char *to, - const dbAddr *paddr) -{ cvtCharToString(*from, to); return(0); } +static long cvt_c_st(const void *f, void *t, const dbAddr *paddr) +{ + const epicsInt8 *from = (const epicsInt8 *) f; + char *to = (char *) t; + cvtCharToString(*from, to); + return 0; +} /* Convert Char to Char */ -static long cvt_c_c( - epicsInt8 *from, - epicsInt8 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_c_c(const void *f, void *t, const dbAddr *paddr) +{ + const epicsInt8 *from = (const epicsInt8 *) f; + epicsInt8 *to = (epicsInt8 *) t; + *to=*from; + return 0; +} /* Convert Char to Unsigned Char */ -static long cvt_c_uc( - epicsInt8 *from, - epicsUInt8 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_c_uc(const void *f, void *t, const dbAddr *paddr) +{ + const epicsInt8 *from = (const epicsInt8 *) f; + epicsUInt8 *to = (epicsUInt8 *) t; + *to=*from; + return 0; +} /* Convert Char to Short */ -static long cvt_c_s( - epicsInt8 *from, - epicsInt16 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_c_s(const void *f, void *t, const dbAddr *paddr) +{ + const epicsInt8 *from = (const epicsInt8 *) f; + epicsInt16 *to = (epicsInt16 *) t; + *to=*from; + return 0; +} /* Convert Char to Unsigned Short */ -static long cvt_c_us( - epicsInt8 *from, - epicsUInt16 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_c_us(const void *f, void *t, const dbAddr *paddr) +{ + const epicsInt8 *from = (const epicsInt8 *) f; + epicsUInt16 *to = (epicsUInt16 *) t; + *to=*from; + return 0; +} /* Convert Char to Long */ -static long cvt_c_l( - epicsInt8 *from, - epicsInt32 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_c_l(const void *f, void *t, const dbAddr *paddr) +{ + const epicsInt8 *from = (const epicsInt8 *) f; + epicsInt32 *to = (epicsInt32 *) t; + *to=*from; + return 0; +} /* Convert Char to Unsigned Long */ -static long cvt_c_ul( - epicsInt8 *from, - epicsUInt32 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_c_ul(const void *f, void *t, const dbAddr *paddr) +{ + const epicsInt8 *from = (const epicsInt8 *) f; + epicsUInt32 *to = (epicsUInt32 *) t; + *to=*from; + return 0; +} /* Convert Char to Int64 */ -static long cvt_c_q( - epicsInt8 *from, - epicsInt64 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_c_q(const void *f, void *t, const dbAddr *paddr) +{ + const epicsInt8 *from = (const epicsInt8 *) f; + epicsInt64 *to = (epicsInt64 *) t; + *to=*from; + return 0; +} /* Convert Char to UInt64 */ -static long cvt_c_uq( - epicsInt8 *from, - epicsUInt64 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_c_uq(const void *f, void *t, const dbAddr *paddr) +{ + const epicsInt8 *from = (const epicsInt8 *) f; + epicsUInt64 *to = (epicsUInt64 *) t; + *to=*from; + return 0; +} /* Convert Char to Float */ -static long cvt_c_f( - epicsInt8 *from, - epicsFloat32 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_c_f(const void *f, void *t, const dbAddr *paddr) +{ + const epicsInt8 *from = (const epicsInt8 *) f; + epicsFloat32 *to = (epicsFloat32 *) t; + *to=*from; + return 0; +} /* Convert Char to Double */ -static long cvt_c_d( - epicsInt8 *from, - epicsFloat64 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_c_d(const void *f, void *t, const dbAddr *paddr) +{ + const epicsInt8 *from = (const epicsInt8 *) f; + epicsFloat64 *to = (epicsFloat64 *) t; + *to=*from; + return 0; +} /* Convert Char to Enumerated */ -static long cvt_c_e( - epicsInt8 *from, - epicsEnum16 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_c_e(const void *f, void *t, const dbAddr *paddr) +{ + const epicsInt8 *from = (const epicsInt8 *) f; + epicsEnum16 *to = (epicsEnum16 *) t; + *to=*from; + return 0; +} /* Convert Unsigned Char to String */ -static long cvt_uc_st( - epicsUInt8 *from, - char *to, - const dbAddr *paddr) -{ cvtUcharToString(*from, to); return(0); } +static long cvt_uc_st(const void *f, void *t, const dbAddr *paddr) +{ + const epicsUInt8 *from = (const epicsUInt8 *) f; + char *to = (char *) t; + cvtUcharToString(*from, to); + return 0; +} /* Convert Unsigned Char to Char */ -static long cvt_uc_c( - epicsUInt8 *from, - epicsInt8 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_uc_c(const void *f, void *t, const dbAddr *paddr) +{ + const epicsUInt8 *from = (const epicsUInt8 *) f; + epicsInt8 *to = (epicsInt8 *) t; + *to=*from; + return 0; +} /* Convert Unsigned Char to Unsigned Char */ -static long cvt_uc_uc( - epicsUInt8 *from, - epicsUInt8 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_uc_uc(const void *f, void *t, const dbAddr *paddr) +{ + const epicsUInt8 *from = (const epicsUInt8 *) f; + epicsUInt8 *to = (epicsUInt8 *) t; + *to=*from; + return 0; +} /* Convert Unsigned Char to Short */ -static long cvt_uc_s( - epicsUInt8 *from, - epicsInt16 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_uc_s(const void *f, void *t, const dbAddr *paddr) +{ + const epicsUInt8 *from = (const epicsUInt8 *) f; + epicsInt16 *to = (epicsInt16 *) t; + *to=*from; + return 0; +} /* Convert Unsigned Char to Unsigned Short */ -static long cvt_uc_us( - epicsUInt8 *from, - epicsUInt16 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_uc_us(const void *f, void *t, const dbAddr *paddr) +{ + const epicsUInt8 *from = (const epicsUInt8 *) f; + epicsUInt16 *to = (epicsUInt16 *) t; + *to=*from; + return 0; +} /* Convert Unsigned Char to Long */ -static long cvt_uc_l( - epicsUInt8 *from, - epicsInt32 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_uc_l(const void *f, void *t, const dbAddr *paddr) +{ + const epicsUInt8 *from = (const epicsUInt8 *) f; + epicsInt32 *to = (epicsInt32 *) t; + *to=*from; + return 0; +} /* Convert Unsigned Char to Unsigned Long */ -static long cvt_uc_ul( - epicsUInt8 *from, - epicsUInt32 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_uc_ul(const void *f, void *t, const dbAddr *paddr) +{ + const epicsUInt8 *from = (const epicsUInt8 *) f; + epicsUInt32 *to = (epicsUInt32 *) t; + *to=*from; + return 0; +} /* Convert Unsigned Char to Int64 */ -static long cvt_uc_q( - epicsUInt8 *from, - epicsInt64 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_uc_q(const void *f, void *t, const dbAddr *paddr) +{ + const epicsUInt8 *from = (const epicsUInt8 *) f; + epicsInt64 *to = (epicsInt64 *) t; + *to=*from; + return 0; +} /* Convert Unsigned Char to UInt64 */ -static long cvt_uc_uq( - epicsUInt8 *from, - epicsUInt64 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_uc_uq(const void *f, void *t, const dbAddr *paddr) +{ + const epicsUInt8 *from = (const epicsUInt8 *) f; + epicsUInt64 *to = (epicsUInt64 *) t; + *to=*from; + return 0; +} /* Convert Unsigned Char to Float */ -static long cvt_uc_f( - epicsUInt8 *from, - epicsFloat32 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_uc_f(const void *f, void *t, const dbAddr *paddr) +{ + const epicsUInt8 *from = (const epicsUInt8 *) f; + epicsFloat32 *to = (epicsFloat32 *) t; + *to=*from; + return 0; +} /* Convert Unsigned Char to Double */ -static long cvt_uc_d( - epicsUInt8 *from, - epicsFloat64 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_uc_d(const void *f, void *t, const dbAddr *paddr) +{ + const epicsUInt8 *from = (const epicsUInt8 *) f; + epicsFloat64 *to = (epicsFloat64 *) t; + *to=*from; + return 0; +} /* Convert Unsigned Char to Enumerated */ -static long cvt_uc_e( - epicsUInt8 *from, - epicsEnum16 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_uc_e(const void *f, void *t, const dbAddr *paddr) +{ + const epicsUInt8 *from = (const epicsUInt8 *) f; + epicsEnum16 *to = (epicsEnum16 *) t; + *to=*from; + return 0; +} /* Convert Short to String */ -static long cvt_s_st( - epicsInt16 *from, - char *to, - const dbAddr *paddr) -{ cvtShortToString(*from, to); return(0); } +static long cvt_s_st(const void *f, void *t, const dbAddr *paddr) +{ + const epicsInt16 *from = (const epicsInt16 *) f; + char *to = (char *) t; + cvtShortToString(*from, to); + return 0; +} /* Convert Short to Char */ -static long cvt_s_c( - epicsInt16 *from, - epicsInt8 *to, - const dbAddr *paddr) - { *to=(epicsInt8)*from; return(0); } +static long cvt_s_c(const void *f, void *t, const dbAddr *paddr) +{ + const epicsInt16 *from = (const epicsInt16 *) f; + epicsInt8 *to = (epicsInt8 *) t; + *to=(epicsInt8)*from; + return 0; +} /* Convert Short to Unsigned Char */ -static long cvt_s_uc( - epicsInt16 *from, - epicsUInt8 *to, - const dbAddr *paddr) - { *to=(epicsUInt8)*from; return(0); } +static long cvt_s_uc(const void *f, void *t, const dbAddr *paddr) +{ + const epicsInt16 *from = (const epicsInt16 *) f; + epicsUInt8 *to = (epicsUInt8 *) t; + *to=(epicsUInt8)*from; + return 0; +} /* Convert Short to Short */ -static long cvt_s_s( - epicsInt16 *from, - epicsInt16 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_s_s(const void *f, void *t, const dbAddr *paddr) +{ + const epicsInt16 *from = (const epicsInt16 *) f; + epicsInt16 *to = (epicsInt16 *) t; + *to=*from; + return 0; +} /* Convert Short to Unsigned Short */ -static long cvt_s_us( - epicsInt16 *from, - epicsUInt16 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_s_us(const void *f, void *t, const dbAddr *paddr) +{ + const epicsInt16 *from = (const epicsInt16 *) f; + epicsUInt16 *to = (epicsUInt16 *) t; + *to=*from; + return 0; +} /* Convert Short to Long */ -static long cvt_s_l( - epicsInt16 *from, - epicsInt32 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_s_l(const void *f, void *t, const dbAddr *paddr) +{ + const epicsInt16 *from = (const epicsInt16 *) f; + epicsInt32 *to = (epicsInt32 *) t; + *to=*from; + return 0; +} /* Convert Short to Unsigned Long */ -static long cvt_s_ul( - epicsInt16 *from, - epicsUInt32 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_s_ul(const void *f, void *t, const dbAddr *paddr) +{ + const epicsInt16 *from = (const epicsInt16 *) f; + epicsUInt32 *to = (epicsUInt32 *) t; + *to=*from; + return 0; +} /* Convert Short to Int64 */ -static long cvt_s_q( - epicsInt16 *from, - epicsInt64 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_s_q(const void *f, void *t, const dbAddr *paddr) +{ + const epicsInt16 *from = (const epicsInt16 *) f; + epicsInt64 *to = (epicsInt64 *) t; + *to=*from; + return 0; +} /* Convert Short to UInt64 */ -static long cvt_s_uq( - epicsInt16 *from, - epicsUInt64 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_s_uq(const void *f, void *t, const dbAddr *paddr) +{ + const epicsInt16 *from = (const epicsInt16 *) f; + epicsUInt64 *to = (epicsUInt64 *) t; + *to=*from; + return 0; +} /* Convert Short to Float */ -static long cvt_s_f( - epicsInt16 *from, - epicsFloat32 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_s_f(const void *f, void *t, const dbAddr *paddr) +{ + const epicsInt16 *from = (const epicsInt16 *) f; + epicsFloat32 *to = (epicsFloat32 *) t; + *to=*from; + return 0; +} /* Convert Short to Double */ -static long cvt_s_d( - epicsInt16 *from, - epicsFloat64 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_s_d(const void *f, void *t, const dbAddr *paddr) +{ + const epicsInt16 *from = (const epicsInt16 *) f; + epicsFloat64 *to = (epicsFloat64 *) t; + *to=*from; + return 0; +} /* Convert Short to Enumerated */ -static long cvt_s_e( - epicsInt16 *from, - epicsEnum16 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_s_e(const void *f, void *t, const dbAddr *paddr) +{ + const epicsInt16 *from = (const epicsInt16 *) f; + epicsEnum16 *to = (epicsEnum16 *) t; + *to=*from; + return 0; +} /* Convert Unsigned Short to String */ -static long cvt_us_st( - epicsUInt16 *from, - char *to, - const dbAddr *paddr) -{ cvtUshortToString(*from, to); return(0); } +static long cvt_us_st(const void *f, void *t, const dbAddr *paddr) +{ + const epicsUInt16 *from = (const epicsUInt16 *) f; + char *to = (char *) t; + cvtUshortToString(*from, to); + return 0; +} /* Convert Unsigned Short to Char */ -static long cvt_us_c( - epicsUInt16 *from, - epicsInt8 *to, - const dbAddr *paddr) - { *to=(epicsInt8)*from; return(0); } +static long cvt_us_c(const void *f, void *t, const dbAddr *paddr) +{ + const epicsUInt16 *from = (const epicsUInt16 *) f; + epicsInt8 *to = (epicsInt8 *) t; + *to=(epicsInt8)*from; + return 0; +} /* Convert Unsigned Short to Unsigned Char */ -static long cvt_us_uc( - epicsUInt16 *from, - epicsUInt8 *to, - const dbAddr *paddr) - { *to=(epicsUInt8)*from; return(0); } +static long cvt_us_uc(const void *f, void *t, const dbAddr *paddr) +{ + const epicsUInt16 *from = (const epicsUInt16 *) f; + epicsUInt8 *to = (epicsUInt8 *) t; + *to=(epicsUInt8)*from; + return 0; +} /* Convert Unsigned Short to Short */ -static long cvt_us_s( - epicsUInt16 *from, - epicsInt16 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_us_s(const void *f, void *t, const dbAddr *paddr) +{ + const epicsUInt16 *from = (const epicsUInt16 *) f; + epicsInt16 *to = (epicsInt16 *) t; + *to=*from; + return 0; +} /* Convert Unsigned Short to Unsigned Short */ -static long cvt_us_us( - epicsUInt16 *from, - epicsUInt16 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_us_us(const void *f, void *t, const dbAddr *paddr) +{ + const epicsUInt16 *from = (const epicsUInt16 *) f; + epicsUInt16 *to = (epicsUInt16 *) t; + *to=*from; + return 0; +} /* Convert Unsigned Short to Long */ -static long cvt_us_l( - epicsUInt16 *from, - epicsInt32 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_us_l(const void *f, void *t, const dbAddr *paddr) +{ + const epicsUInt16 *from = (const epicsUInt16 *) f; + epicsInt32 *to = (epicsInt32 *) t; + *to=*from; + return 0; +} /* Convert Unsigned Short to Unsigned Long */ -static long cvt_us_ul( - epicsUInt16 *from, - epicsUInt32 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_us_ul(const void *f, void *t, const dbAddr *paddr) +{ + const epicsUInt16 *from = (const epicsUInt16 *) f; + epicsUInt32 *to = (epicsUInt32 *) t; + *to=*from; + return 0; +} /* Convert Unsigned Short to Int64 */ -static long cvt_us_q( - epicsUInt16 *from, - epicsInt64 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_us_q(const void *f, void *t, const dbAddr *paddr) +{ + const epicsUInt16 *from = (const epicsUInt16 *) f; + epicsInt64 *to = (epicsInt64 *) t; + *to=*from; + return 0; +} /* Convert Unsigned Short to UInt64 */ -static long cvt_us_uq( - epicsUInt16 *from, - epicsUInt64 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_us_uq(const void *f, void *t, const dbAddr *paddr) +{ + const epicsUInt16 *from = (const epicsUInt16 *) f; + epicsUInt64 *to = (epicsUInt64 *) t; + *to=*from; + return 0; +} /* Convert Unsigned Short to Float */ -static long cvt_us_f( - epicsUInt16 *from, - epicsFloat32 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_us_f(const void *f, void *t, const dbAddr *paddr) +{ + const epicsUInt16 *from = (const epicsUInt16 *) f; + epicsFloat32 *to = (epicsFloat32 *) t; + *to=*from; + return 0; +} /* Convert Unsigned Short to Double */ -static long cvt_us_d( - epicsUInt16 *from, - epicsFloat64 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_us_d(const void *f, void *t, const dbAddr *paddr) +{ + const epicsUInt16 *from = (const epicsUInt16 *) f; + epicsFloat64 *to = (epicsFloat64 *) t; + *to=*from; + return 0; +} /* Convert Unsigned Short to Enumerated */ -static long cvt_us_e( - epicsUInt16 *from, - epicsUInt16 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_us_e(const void *f, void *t, const dbAddr *paddr) +{ + const epicsUInt16 *from = (const epicsUInt16 *) f; + epicsUInt16 *to = (epicsUInt16 *) t; + *to=*from; + return 0; +} /* Convert Long to String */ -static long cvt_l_st( - epicsInt32 *from, - char *to, - const dbAddr *paddr) -{ cvtLongToString(*from, to); return(0); } +static long cvt_l_st(const void *f, void *t, const dbAddr *paddr) +{ + const epicsInt32 *from = (const epicsInt32 *) f; + char *to = (char *) t; + cvtLongToString(*from, to); + return 0; +} /* Convert Long to Char */ -static long cvt_l_c( - epicsInt32 *from, - epicsInt8 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_l_c(const void *f, void *t, const dbAddr *paddr) +{ + const epicsInt32 *from = (const epicsInt32 *) f; + epicsInt8 *to = (epicsInt8 *) t; + *to=*from; + return 0; +} /* Convert Long to Unsigned Char */ -static long cvt_l_uc( - epicsInt32 *from, - epicsUInt8 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_l_uc(const void *f, void *t, const dbAddr *paddr) +{ + const epicsInt32 *from = (const epicsInt32 *) f; + epicsUInt8 *to = (epicsUInt8 *) t; + *to=*from; + return 0; +} /* Convert Long to Short */ -static long cvt_l_s( - epicsInt32 *from, - epicsInt16 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_l_s(const void *f, void *t, const dbAddr *paddr) +{ + const epicsInt32 *from = (const epicsInt32 *) f; + epicsInt16 *to = (epicsInt16 *) t; + *to=*from; + return 0; +} /* Convert Long to Unsigned Short */ -static long cvt_l_us( - epicsInt32 *from, - epicsUInt16 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_l_us(const void *f, void *t, const dbAddr *paddr) +{ + const epicsInt32 *from = (const epicsInt32 *) f; + epicsUInt16 *to = (epicsUInt16 *) t; + *to=*from; + return 0; +} /* Convert Long to Long */ -static long cvt_l_l( - epicsInt32 *from, - epicsInt32 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_l_l(const void *f, void *t, const dbAddr *paddr) +{ + const epicsInt32 *from = (const epicsInt32 *) f; + epicsInt32 *to = (epicsInt32 *) t; + *to=*from; + return 0; +} /* Convert Long to Unsigned Long */ -static long cvt_l_ul( - epicsInt32 *from, - epicsUInt32 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_l_ul(const void *f, void *t, const dbAddr *paddr) +{ + const epicsInt32 *from = (const epicsInt32 *) f; + epicsUInt32 *to = (epicsUInt32 *) t; + *to=*from; + return 0; +} /* Convert Long to Int64 */ -static long cvt_l_q( - epicsInt32 *from, - epicsInt64 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_l_q(const void *f, void *t, const dbAddr *paddr) +{ + const epicsInt32 *from = (const epicsInt32 *) f; + epicsInt64 *to = (epicsInt64 *) t; + *to=*from; + return 0; +} /* Convert Long to UInt64 */ -static long cvt_l_uq( - epicsInt32 *from, - epicsUInt64 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_l_uq(const void *f, void *t, const dbAddr *paddr) +{ + const epicsInt32 *from = (const epicsInt32 *) f; + epicsUInt64 *to = (epicsUInt64 *) t; + *to=*from; + return 0; +} /* Convert Long to Float */ -static long cvt_l_f( - epicsInt32 *from, - epicsFloat32 *to, - const dbAddr *paddr) - { *to=(epicsFloat32)*from; return(0); } +static long cvt_l_f(const void *f, void *t, const dbAddr *paddr) +{ + const epicsInt32 *from = (const epicsInt32 *) f; + epicsFloat32 *to = (epicsFloat32 *) t; + *to=(epicsFloat32)*from; + return 0; +} /* Convert Long to Double */ -static long cvt_l_d( - epicsInt32 *from, - epicsFloat64 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_l_d(const void *f, void *t, const dbAddr *paddr) +{ + const epicsInt32 *from = (const epicsInt32 *) f; + epicsFloat64 *to = (epicsFloat64 *) t; + *to=*from; + return 0; +} /* Convert Long to Enumerated */ -static long cvt_l_e( - epicsInt32 *from, - epicsEnum16 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_l_e(const void *f, void *t, const dbAddr *paddr) +{ + const epicsInt32 *from = (const epicsInt32 *) f; + epicsEnum16 *to = (epicsEnum16 *) t; + *to=*from; + return 0; +} /* Convert Unsigned Long to String */ -static long cvt_ul_st( - epicsUInt32 *from, - char *to, - const dbAddr *paddr) -{ cvtUlongToString(*from, to); return(0); } +static long cvt_ul_st(const void *f, void *t, const dbAddr *paddr) +{ + const epicsUInt32 *from = (const epicsUInt32 *) f; + char *to = (char *) t; + cvtUlongToString(*from, to); + return 0; +} /* Convert Unsigned Long to Char */ -static long cvt_ul_c( - epicsUInt32 *from, - epicsInt8 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_ul_c(const void *f, void *t, const dbAddr *paddr) +{ + const epicsUInt32 *from = (const epicsUInt32 *) f; + epicsInt8 *to = (epicsInt8 *) t; + *to=*from; + return 0; +} /* Convert Unsigned Long to Unsigned Char */ -static long cvt_ul_uc( - epicsUInt32 *from, - epicsUInt8 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_ul_uc(const void *f, void *t, const dbAddr *paddr) +{ + const epicsUInt32 *from = (const epicsUInt32 *) f; + epicsUInt8 *to = (epicsUInt8 *) t; + *to=*from; + return 0; +} /* Convert Unsigned Long to Short */ -static long cvt_ul_s( - epicsUInt32 *from, - epicsInt16 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_ul_s(const void *f, void *t, const dbAddr *paddr) +{ + const epicsUInt32 *from = (const epicsUInt32 *) f; + epicsInt16 *to = (epicsInt16 *) t; + *to=*from; + return 0; +} /* Convert Unsigned Long to Unsigned Short */ -static long cvt_ul_us( - epicsUInt32 *from, - epicsUInt16 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_ul_us(const void *f, void *t, const dbAddr *paddr) +{ + const epicsUInt32 *from = (const epicsUInt32 *) f; + epicsUInt16 *to = (epicsUInt16 *) t; + *to=*from; + return 0; +} /* Convert Unsigned Long to Long */ -static long cvt_ul_l( - epicsUInt32 *from, - epicsInt32 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_ul_l(const void *f, void *t, const dbAddr *paddr) +{ + const epicsUInt32 *from = (const epicsUInt32 *) f; + epicsInt32 *to = (epicsInt32 *) t; + *to=*from; + return 0; +} /* Convert Unsigned Long to Unsigned Long */ -static long cvt_ul_ul( - epicsUInt32 *from, - epicsUInt32 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_ul_ul(const void *f, void *t, const dbAddr *paddr) +{ + const epicsUInt32 *from = (const epicsUInt32 *) f; + epicsUInt32 *to = (epicsUInt32 *) t; + *to=*from; + return 0; +} /* Convert Unsigned Long to Int64 */ -static long cvt_ul_q( - epicsUInt32 *from, - epicsInt64 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_ul_q(const void *f, void *t, const dbAddr *paddr) +{ + const epicsUInt32 *from = (const epicsUInt32 *) f; + epicsInt64 *to = (epicsInt64 *) t; + *to=*from; + return 0; +} /* Convert Unsigned Long to UInt64 */ -static long cvt_ul_uq( - epicsUInt32 *from, - epicsUInt64 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_ul_uq(const void *f, void *t, const dbAddr *paddr) +{ + const epicsUInt32 *from = (const epicsUInt32 *) f; + epicsUInt64 *to = (epicsUInt64 *) t; + *to=*from; + return 0; +} /* Convert Unsigned Long to Float */ -static long cvt_ul_f( - epicsUInt32 *from, - epicsFloat32 *to, - const dbAddr *paddr) - { *to=(epicsFloat32)*from; return(0); } +static long cvt_ul_f(const void *f, void *t, const dbAddr *paddr) +{ + const epicsUInt32 *from = (const epicsUInt32 *) f; + epicsFloat32 *to = (epicsFloat32 *) t; + *to=(epicsFloat32)*from; + return 0; +} /* Convert Unsigned Long to Double */ -static long cvt_ul_d( - epicsUInt32 *from, - epicsFloat64 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_ul_d(const void *f, void *t, const dbAddr *paddr) +{ + const epicsUInt32 *from = (const epicsUInt32 *) f; + epicsFloat64 *to = (epicsFloat64 *) t; + *to=*from; + return 0; +} /* Convert Unsigned Long to Enumerated */ -static long cvt_ul_e( - epicsUInt32 *from, - epicsEnum16 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_ul_e(const void *f, void *t, const dbAddr *paddr) +{ + const epicsUInt32 *from = (const epicsUInt32 *) f; + epicsEnum16 *to = (epicsEnum16 *) t; + *to=*from; + return 0; +} /* Convert Int64 to String */ -static long cvt_q_st( - epicsInt64 *from, - char *to, - const dbAddr *paddr) -{ cvtInt64ToString(*from, to); return(0); } +static long cvt_q_st(const void *f, void *t, const dbAddr *paddr) +{ + const epicsInt64 *from = (const epicsInt64 *) f; + char *to = (char *) t; + cvtInt64ToString(*from, to); + return 0; +} /* Convert Int64 to Char */ -static long cvt_q_c( - epicsInt64 *from, - epicsInt8 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_q_c(const void *f, void *t, const dbAddr *paddr) +{ + const epicsInt64 *from = (const epicsInt64 *) f; + epicsInt8 *to = (epicsInt8 *) t; + *to=*from; + return 0; +} /* Convert Int64 to Unsigned Char */ -static long cvt_q_uc( - epicsInt64 *from, - epicsUInt8 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_q_uc(const void *f, void *t, const dbAddr *paddr) +{ + const epicsInt64 *from = (const epicsInt64 *) f; + epicsUInt8 *to = (epicsUInt8 *) t; + *to=*from; + return 0; +} /* Convert Int64 to Short */ -static long cvt_q_s( - epicsInt64 *from, - epicsInt16 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_q_s(const void *f, void *t, const dbAddr *paddr) +{ + const epicsInt64 *from = (const epicsInt64 *) f; + epicsInt16 *to = (epicsInt16 *) t; + *to=*from; + return 0; +} /* Convert Int64 to Unsigned Short */ -static long cvt_q_us( - epicsInt64 *from, - epicsUInt16 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_q_us(const void *f, void *t, const dbAddr *paddr) +{ + const epicsInt64 *from = (const epicsInt64 *) f; + epicsUInt16 *to = (epicsUInt16 *) t; + *to=*from; + return 0; +} /* Convert Int64 to Long */ -static long cvt_q_l( - epicsInt64 *from, - epicsInt32 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_q_l(const void *f, void *t, const dbAddr *paddr) +{ + const epicsInt64 *from = (const epicsInt64 *) f; + epicsInt32 *to = (epicsInt32 *) t; + *to=*from; + return 0; +} /* Convert Int64 to Unsigned Long */ -static long cvt_q_ul( - epicsInt64 *from, - epicsUInt32 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_q_ul(const void *f, void *t, const dbAddr *paddr) +{ + const epicsInt64 *from = (const epicsInt64 *) f; + epicsUInt32 *to = (epicsUInt32 *) t; + *to=*from; + return 0; +} /* Convert Int64 to Int64 */ -static long cvt_q_q( - epicsInt64 *from, - epicsInt64 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_q_q(const void *f, void *t, const dbAddr *paddr) +{ + const epicsInt64 *from = (const epicsInt64 *) f; + epicsInt64 *to = (epicsInt64 *) t; + *to=*from; + return 0; +} /* Convert Int64 to UInt64 */ -static long cvt_q_uq( - epicsInt64 *from, - epicsUInt64 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_q_uq(const void *f, void *t, const dbAddr *paddr) +{ + const epicsInt64 *from = (const epicsInt64 *) f; + epicsUInt64 *to = (epicsUInt64 *) t; + *to=*from; + return 0; +} /* Convert Int64 to Float */ -static long cvt_q_f( - epicsInt64 *from, - epicsFloat32 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_q_f(const void *f, void *t, const dbAddr *paddr) +{ + const epicsInt64 *from = (const epicsInt64 *) f; + epicsFloat32 *to = (epicsFloat32 *) t; + *to=*from; + return 0; +} /* Convert Int64 to Double */ -static long cvt_q_d( - epicsInt64 *from, - epicsFloat64 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_q_d(const void *f, void *t, const dbAddr *paddr) +{ + const epicsInt64 *from = (const epicsInt64 *) f; + epicsFloat64 *to = (epicsFloat64 *) t; + *to=*from; + return 0; +} /* Convert Int64 to Enumerated */ -static long cvt_q_e( - epicsInt64 *from, - epicsEnum16 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_q_e(const void *f, void *t, const dbAddr *paddr) +{ + const epicsInt64 *from = (const epicsInt64 *) f; + epicsEnum16 *to = (epicsEnum16 *) t; + *to=*from; + return 0; +} /* Convert UInt64 to String */ -static long cvt_uq_st( - epicsUInt64 *from, - char *to, - const dbAddr *paddr) -{ cvtUInt64ToString(*from, to); return(0); } +static long cvt_uq_st(const void *f, void *t, const dbAddr *paddr) +{ + const epicsUInt64 *from = (const epicsUInt64 *) f; + char *to = (char *) t; + cvtUInt64ToString(*from, to); + return 0; +} /* Convert UInt64 to Char */ -static long cvt_uq_c( - epicsUInt64 *from, - epicsInt8 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_uq_c(const void *f, void *t, const dbAddr *paddr) +{ + const epicsUInt64 *from = (const epicsUInt64 *) f; + epicsInt8 *to = (epicsInt8 *) t; + *to=*from; + return 0; +} /* Convert UInt64 to Unsigned Char */ -static long cvt_uq_uc( - epicsUInt64 *from, - epicsUInt8 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_uq_uc(const void *f, void *t, const dbAddr *paddr) +{ + const epicsUInt64 *from = (const epicsUInt64 *) f; + epicsUInt8 *to = (epicsUInt8 *) t; + *to=*from; + return 0; +} /* Convert UInt64 to Short */ -static long cvt_uq_s( - epicsUInt64 *from, - epicsInt16 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_uq_s(const void *f, void *t, const dbAddr *paddr) +{ + const epicsUInt64 *from = (const epicsUInt64 *) f; + epicsInt16 *to = (epicsInt16 *) t; + *to=*from; + return 0; +} /* Convert UInt64 to Unsigned Short */ -static long cvt_uq_us( - epicsUInt64 *from, - epicsUInt16 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_uq_us(const void *f, void *t, const dbAddr *paddr) +{ + const epicsUInt64 *from = (const epicsUInt64 *) f; + epicsUInt16 *to = (epicsUInt16 *) t; + *to=*from; + return 0; +} /* Convert UInt64 to Long */ -static long cvt_uq_l( - epicsUInt64 *from, - epicsInt32 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_uq_l(const void *f, void *t, const dbAddr *paddr) +{ + const epicsUInt64 *from = (const epicsUInt64 *) f; + epicsInt32 *to = (epicsInt32 *) t; + *to=*from; + return 0; +} /* Convert UInt64 to Unsigned Long */ -static long cvt_uq_ul( - epicsUInt64 *from, - epicsUInt32 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_uq_ul(const void *f, void *t, const dbAddr *paddr) +{ + const epicsUInt64 *from = (const epicsUInt64 *) f; + epicsUInt32 *to = (epicsUInt32 *) t; + *to=*from; + return 0; +} /* Convert UInt64 to Int64 */ -static long cvt_uq_q( - epicsUInt64 *from, - epicsInt64 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_uq_q(const void *f, void *t, const dbAddr *paddr) +{ + const epicsUInt64 *from = (const epicsUInt64 *) f; + epicsInt64 *to = (epicsInt64 *) t; + *to=*from; + return 0; +} /* Convert UInt64 to UInt64 */ -static long cvt_uq_uq( - epicsUInt64 *from, - epicsUInt64 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_uq_uq(const void *f, void *t, const dbAddr *paddr) +{ + const epicsUInt64 *from = (const epicsUInt64 *) f; + epicsUInt64 *to = (epicsUInt64 *) t; + *to=*from; + return 0; +} /* Convert UInt64 to Float */ -static long cvt_uq_f( - epicsUInt64 *from, - epicsFloat32 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_uq_f(const void *f, void *t, const dbAddr *paddr) +{ + const epicsUInt64 *from = (const epicsUInt64 *) f; + epicsFloat32 *to = (epicsFloat32 *) t; + *to=*from; + return 0; +} /* Convert UInt64 to Double */ -static long cvt_uq_d( - epicsUInt64 *from, - epicsFloat64 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_uq_d(const void *f, void *t, const dbAddr *paddr) +{ + const epicsUInt64 *from = (const epicsUInt64 *) f; + epicsFloat64 *to = (epicsFloat64 *) t; + *to=*from; + return 0; +} /* Convert UInt64 to Enumerated */ -static long cvt_uq_e( - epicsUInt64 *from, - epicsEnum16 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_uq_e(const void *f, void *t, const dbAddr *paddr) +{ + const epicsUInt64 *from = (const epicsUInt64 *) f; + epicsEnum16 *to = (epicsEnum16 *) t; + *to=*from; + return 0; +} /* Convert Float to String */ -static long cvt_f_st( - epicsFloat32 *from, - char *to, - const dbAddr *paddr) - { - rset *prset = 0; - long status = 0; - long precision = 6; - - if(paddr) prset = dbGetRset(paddr); - - if (prset && prset->get_precision) - status = (*prset->get_precision)(paddr, &precision); - cvtFloatToString(*from, to, (unsigned short)precision); - return(status); +static long cvt_f_st(const void *f, void *t, const dbAddr *paddr) +{ + const epicsFloat32 *from = (const epicsFloat32 *) f; + char *to = (char *) t; + + rset *prset = 0; + long status = 0; + long precision = 6; + + if (paddr) prset = dbGetRset(paddr); + + if (prset && prset->get_precision) + status = (*prset->get_precision)(paddr, &precision); + cvtFloatToString(*from, to, (unsigned short)precision); + return status; } /* Convert Float to Char */ -static long cvt_f_c( - epicsFloat32 *from, - epicsInt8 *to, - const dbAddr *paddr) - { *to=(epicsInt8)*from; return(0); } +static long cvt_f_c(const void *f, void *t, const dbAddr *paddr) +{ + const epicsFloat32 *from = (const epicsFloat32 *) f; + epicsInt8 *to = (epicsInt8 *) t; + *to=(epicsInt8)*from; + return 0; +} /* Convert Float to Unsigned Char */ -static long cvt_f_uc( - epicsFloat32 *from, - epicsUInt8 *to, - const dbAddr *paddr) - { *to=(epicsUInt8)*from; return(0); } +static long cvt_f_uc(const void *f, void *t, const dbAddr *paddr) +{ + const epicsFloat32 *from = (const epicsFloat32 *) f; + epicsUInt8 *to = (epicsUInt8 *) t; + *to=(epicsUInt8)*from; + return 0; +} /* Convert Float to Short */ -static long cvt_f_s( - epicsFloat32 *from, - epicsInt16 *to, - const dbAddr *paddr) - { *to=(epicsInt16)*from; return(0); } +static long cvt_f_s(const void *f, void *t, const dbAddr *paddr) +{ + const epicsFloat32 *from = (const epicsFloat32 *) f; + epicsInt16 *to = (epicsInt16 *) t; + *to=(epicsInt16)*from; + return 0; +} /* Convert Float to Unsigned Short */ -static long cvt_f_us( - epicsFloat32 *from, - epicsUInt16 *to, - const dbAddr *paddr) - { *to=(epicsUInt16)*from; return(0); } +static long cvt_f_us(const void *f, void *t, const dbAddr *paddr) +{ + const epicsFloat32 *from = (const epicsFloat32 *) f; + epicsUInt16 *to = (epicsUInt16 *) t; + *to=(epicsUInt16)*from; + return 0; +} /* Convert Float to Long */ -static long cvt_f_l( - epicsFloat32 *from, - epicsInt32 *to, - const dbAddr *paddr) - { *to=(epicsInt32)*from; return(0); } +static long cvt_f_l(const void *f, void *t, const dbAddr *paddr) +{ + const epicsFloat32 *from = (const epicsFloat32 *) f; + epicsInt32 *to = (epicsInt32 *) t; + *to=(epicsInt32)*from; + return 0; +} /* Convert Float to Unsigned Long */ -static long cvt_f_ul( - epicsFloat32 *from, - epicsUInt32 *to, - const dbAddr *paddr) - { *to=(epicsUInt32)*from; return(0); } +static long cvt_f_ul(const void *f, void *t, const dbAddr *paddr) +{ + const epicsFloat32 *from = (const epicsFloat32 *) f; + epicsUInt32 *to = (epicsUInt32 *) t; + *to=(epicsUInt32)*from; + return 0; +} /* Convert Float to Int64 */ -static long cvt_f_q( - epicsFloat32 *from, - epicsInt64 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_f_q(const void *f, void *t, const dbAddr *paddr) +{ + const epicsFloat32 *from = (const epicsFloat32 *) f; + epicsInt64 *to = (epicsInt64 *) t; + *to=*from; + return 0; +} /* Convert Float to UInt64 */ -static long cvt_f_uq( - epicsFloat32 *from, - epicsUInt64 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_f_uq(const void *f, void *t, const dbAddr *paddr) +{ + const epicsFloat32 *from = (const epicsFloat32 *) f; + epicsUInt64 *to = (epicsUInt64 *) t; + *to=*from; + return 0; +} /* Convert Float to Float */ -static long cvt_f_f( - epicsFloat32 *from, - epicsFloat32 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_f_f(const void *f, void *t, const dbAddr *paddr) +{ + const epicsFloat32 *from = (const epicsFloat32 *) f; + epicsFloat32 *to = (epicsFloat32 *) t; + *to=*from; + return 0; +} /* Convert Float to Double */ -static long cvt_f_d( - epicsFloat32 *from, - epicsFloat64 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_f_d(const void *f, void *t, const dbAddr *paddr) +{ + const epicsFloat32 *from = (const epicsFloat32 *) f; + epicsFloat64 *to = (epicsFloat64 *) t; + *to=*from; + return 0; +} /* Convert Float to Enumerated */ -static long cvt_f_e( - epicsFloat32 *from, - epicsEnum16 *to, - const dbAddr *paddr) - { *to=(epicsEnum16)*from; return(0); } +static long cvt_f_e(const void *f, void *t, const dbAddr *paddr) +{ + const epicsFloat32 *from = (const epicsFloat32 *) f; + epicsEnum16 *to = (epicsEnum16 *) t; + *to=(epicsEnum16)*from; + return 0; +} /* Convert Double to String */ -static long cvt_d_st( - epicsFloat64 *from, - char *to, - const dbAddr *paddr) - { - rset *prset = 0; - long status = 0; - long precision = 6; - - if(paddr) prset = dbGetRset(paddr); - - if (prset && prset->get_precision) - status = (*prset->get_precision)(paddr, &precision); - cvtDoubleToString(*from, to, (unsigned short)precision); - return(status); +static long cvt_d_st(const void *f, void *t, const dbAddr *paddr) +{ + const epicsFloat64 *from = (const epicsFloat64 *) f; + char *to = (char *) t; + rset *prset = 0; + long status = 0; + long precision = 6; + + if (paddr) prset = dbGetRset(paddr); + + if (prset && prset->get_precision) + status = (*prset->get_precision)(paddr, &precision); + cvtDoubleToString(*from, to, (unsigned short)precision); + return status; } /* Convert Double to Char */ -static long cvt_d_c( - epicsFloat64 *from, - epicsInt8 *to, - const dbAddr *paddr) - { *to=(epicsInt8)*from; return(0); } +static long cvt_d_c(const void *f, void *t, const dbAddr *paddr) +{ + const epicsFloat64 *from = (const epicsFloat64 *) f; + epicsInt8 *to = (epicsInt8 *) t; + *to=(epicsInt8)*from; + return 0; +} /* Convert Double to Unsigned Char */ -static long cvt_d_uc( - epicsFloat64 *from, - epicsUInt8 *to, - const dbAddr *paddr) - { *to=(epicsUInt8)*from; return(0); } +static long cvt_d_uc(const void *f, void *t, const dbAddr *paddr) +{ + const epicsFloat64 *from = (const epicsFloat64 *) f; + epicsUInt8 *to = (epicsUInt8 *) t; + *to=(epicsUInt8)*from; + return 0; +} /* Convert Double to Short */ -static long cvt_d_s( - epicsFloat64 *from, - epicsInt16 *to, - const dbAddr *paddr) - { *to=(epicsInt16)*from; return(0); } +static long cvt_d_s(const void *f, void *t, const dbAddr *paddr) +{ + const epicsFloat64 *from = (const epicsFloat64 *) f; + epicsInt16 *to = (epicsInt16 *) t; + *to=(epicsInt16)*from; + return 0; +} /* Convert Double to Unsigned Short */ -static long cvt_d_us( - epicsFloat64 *from, - epicsUInt16 *to, - const dbAddr *paddr) - { *to=(epicsUInt16)*from; return(0); } +static long cvt_d_us(const void *f, void *t, const dbAddr *paddr) +{ + const epicsFloat64 *from = (const epicsFloat64 *) f; + epicsUInt16 *to = (epicsUInt16 *) t; + *to=(epicsUInt16)*from; + return 0; +} /* Convert Double to Long */ -static long cvt_d_l( - epicsFloat64 *from, - epicsInt32 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_d_l(const void *f, void *t, const dbAddr *paddr) +{ + const epicsFloat64 *from = (const epicsFloat64 *) f; + epicsInt32 *to = (epicsInt32 *) t; + *to=*from; + return 0; +} /* Convert Double to Unsigned Long */ -static long cvt_d_ul( - epicsFloat64 *from, - epicsUInt32 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_d_ul(const void *f, void *t, const dbAddr *paddr) +{ + const epicsFloat64 *from = (const epicsFloat64 *) f; + epicsUInt32 *to = (epicsUInt32 *) t; + *to=*from; + return 0; +} /* Convert Double to Int64 */ -static long cvt_d_q( - epicsFloat64 *from, - epicsInt64 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_d_q(const void *f, void *t, const dbAddr *paddr) +{ + const epicsFloat64 *from = (const epicsFloat64 *) f; + epicsInt64 *to = (epicsInt64 *) t; + *to=*from; + return 0; +} /* Convert Double to UInt64 */ -static long cvt_d_uq( - epicsFloat64 *from, - epicsUInt64 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_d_uq(const void *f, void *t, const dbAddr *paddr) +{ + const epicsFloat64 *from = (const epicsFloat64 *) f; + epicsUInt64 *to = (epicsUInt64 *) t; + *to=*from; + return 0; +} /* Convert Double to Float */ -static long cvt_d_f( - epicsFloat64 *from, - epicsFloat32 *to, - const dbAddr *paddr) -{ *to = epicsConvertDoubleToFloat(*from); return 0;} +static long cvt_d_f(const void *f, void *t, const dbAddr *paddr) +{ + const epicsFloat64 *from = (const epicsFloat64 *) f; + epicsFloat32 *to = (epicsFloat32 *) t; + *to = epicsConvertDoubleToFloat(*from); return 0;} /* Convert Double to Double */ -static long cvt_d_d( - epicsFloat64 *from, - epicsFloat64 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_d_d(const void *f, void *t, const dbAddr *paddr) +{ + const epicsFloat64 *from = (const epicsFloat64 *) f; + epicsFloat64 *to = (epicsFloat64 *) t; + *to=*from; + return 0; +} /* Convert Double to Enumerated */ -static long cvt_d_e( - epicsFloat64 *from, - epicsEnum16 *to, - const dbAddr *paddr) - { *to=(epicsEnum16)*from; return(0); } +static long cvt_d_e(const void *f, void *t, const dbAddr *paddr) +{ + const epicsFloat64 *from = (const epicsFloat64 *) f; + epicsEnum16 *to = (epicsEnum16 *) t; + *to=(epicsEnum16)*from; + return 0; +} /* Convert Enumerated to Char */ -static long cvt_e_c( - epicsEnum16 *from, - epicsInt8 *to, - const dbAddr *paddr) - { *to=(epicsInt8)*from; return(0); } +static long cvt_e_c(const void *f, void *t, const dbAddr *paddr) +{ + const epicsEnum16 *from = (const epicsEnum16 *) f; + epicsInt8 *to = (epicsInt8 *) t; + *to=(epicsInt8)*from; + return 0; +} /* Convert Enumerated to Unsigned Char */ -static long cvt_e_uc( - epicsEnum16 *from, - epicsUInt8 *to, - const dbAddr *paddr) - { *to=(epicsUInt8)*from; return(0); } +static long cvt_e_uc(const void *f, void *t, const dbAddr *paddr) +{ + const epicsEnum16 *from = (const epicsEnum16 *) f; + epicsUInt8 *to = (epicsUInt8 *) t; + *to=(epicsUInt8)*from; + return 0; +} /* Convert Enumerated to Short */ -static long cvt_e_s( - epicsEnum16 *from, - epicsInt16 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_e_s(const void *f, void *t, const dbAddr *paddr) +{ + const epicsEnum16 *from = (const epicsEnum16 *) f; + epicsInt16 *to = (epicsInt16 *) t; + *to=*from; + return 0; +} /* Convert Enumerated to Unsigned Short */ -static long cvt_e_us( - epicsEnum16 *from, - epicsUInt16 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_e_us(const void *f, void *t, const dbAddr *paddr) +{ + const epicsEnum16 *from = (const epicsEnum16 *) f; + epicsUInt16 *to = (epicsUInt16 *) t; + *to=*from; + return 0; +} /* Convert Enumerated to Long */ -static long cvt_e_l( - epicsEnum16 *from, - epicsInt32 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_e_l(const void *f, void *t, const dbAddr *paddr) +{ + const epicsEnum16 *from = (const epicsEnum16 *) f; + epicsInt32 *to = (epicsInt32 *) t; + *to=*from; + return 0; +} /* Convert Enumerated to Unsigned Long */ -static long cvt_e_ul( - epicsEnum16 *from, - epicsUInt32 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_e_ul(const void *f, void *t, const dbAddr *paddr) +{ + const epicsEnum16 *from = (const epicsEnum16 *) f; + epicsUInt32 *to = (epicsUInt32 *) t; + *to=*from; + return 0; +} /* Convert Enumerated to Int64 */ -static long cvt_e_q( - epicsEnum16 *from, - epicsInt64 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_e_q(const void *f, void *t, const dbAddr *paddr) +{ + const epicsEnum16 *from = (const epicsEnum16 *) f; + epicsInt64 *to = (epicsInt64 *) t; + *to=*from; + return 0; +} /* Convert Enumerated to UInt64 */ -static long cvt_e_uq( - epicsEnum16 *from, - epicsUInt64 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_e_uq(const void *f, void *t, const dbAddr *paddr) +{ + const epicsEnum16 *from = (const epicsEnum16 *) f; + epicsUInt64 *to = (epicsUInt64 *) t; + *to=*from; + return 0; +} /* Convert Enumerated to Float */ -static long cvt_e_f( - epicsEnum16 *from, - epicsFloat32 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_e_f(const void *f, void *t, const dbAddr *paddr) +{ + const epicsEnum16 *from = (const epicsEnum16 *) f; + epicsFloat32 *to = (epicsFloat32 *) t; + *to=*from; + return 0; +} /* Convert Enumerated to Double */ -static long cvt_e_d( - epicsEnum16 *from, - epicsFloat64 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_e_d(const void *f, void *t, const dbAddr *paddr) +{ + const epicsEnum16 *from = (const epicsEnum16 *) f; + epicsFloat64 *to = (epicsFloat64 *) t; + *to=*from; + return 0; +} /* Convert Enumerated to Enumerated */ -static long cvt_e_e( - epicsEnum16 *from, - epicsEnum16 *to, - const dbAddr *paddr) - { *to=*from; return(0); } +static long cvt_e_e(const void *f, void *t, const dbAddr *paddr) +{ + const epicsEnum16 *from = (const epicsEnum16 *) f; + epicsEnum16 *to = (epicsEnum16 *) t; + *to=*from; + return 0; +} /* Convert Choices And Enumerated Types To String ... */ /* Get Enumerated to String */ -static long cvt_e_st_get( - epicsEnum16 *from, - char *to, - const dbAddr *paddr) - { - rset *prset = 0; - long status; +static long cvt_e_st_get(const void *f, void *t, const dbAddr *paddr) +{ + char *to = (char *) t; + rset *prset = 0; + long status; - if(paddr) prset = dbGetRset(paddr); + if(paddr) prset = dbGetRset(paddr); - if (prset && prset->get_enum_str) - return (*prset->get_enum_str)(paddr, to); + if (prset && prset->get_enum_str) + return (*prset->get_enum_str)(paddr, to); - status = S_db_noRSET; - recGblRecSupError(status, paddr, "dbGetField", "get_enum_str"); + status = S_db_noRSET; + recGblRecSupError(status, paddr, "dbGetField", "get_enum_str"); - return(S_db_badDbrtype); - } + return S_db_badDbrtype; +} /* Put Enumerated to String */ -static long cvt_e_st_put( - epicsEnum16 *from, - char *to, - const dbAddr *paddr) - { cvtUshortToString(*from, to); return(0); } +static long cvt_e_st_put(const void *f, void *t, const dbAddr *paddr) +{ + const epicsEnum16 *from = (const epicsEnum16 *) f; + char *to = (char *) t; + + cvtUshortToString(*from, to); + return 0; +} /* Get Menu to String */ -static long cvt_menu_st( - epicsEnum16 *from, - char *to, - const dbAddr *paddr) +static long cvt_menu_st(const void *f, void *t, const dbAddr *paddr) { + const epicsEnum16 *from = (const epicsEnum16 *) f; + char *to = (char *) t; dbFldDes *pdbFldDes; dbMenu *pdbMenu; @@ -1358,15 +1599,14 @@ static long cvt_menu_st( /* Get Device to String */ -static long cvt_device_st( - epicsEnum16 *from, - char *to, - const dbAddr *paddr) - { - dbFldDes *pdbFldDes; - dbDeviceMenu *pdbDeviceMenu; - char **papChoice; - char *pchoice; +static long cvt_device_st(const void *f, void *t, const dbAddr *paddr) +{ + const epicsEnum16 *from = (const epicsEnum16 *) f; + char *to = (char *) t; + dbFldDes *pdbFldDes; + dbDeviceMenu *pdbDeviceMenu; + char **papChoice; + char *pchoice; if (!paddr || !(pdbFldDes = paddr->pfldDes)) { @@ -1382,11 +1622,11 @@ static long cvt_device_st( || !(papChoice= pdbDeviceMenu->papChoice) || !(pchoice=papChoice[*from])) { recGblDbaddrError(S_db_badChoice,paddr,"dbFastLinkConv(cvt_device_st)"); - return(S_db_badChoice); + return S_db_badChoice; } strncpy(to,pchoice,MAX_STRING_SIZE); - return(0); - } + return 0; +} /* * Get conversion routine lookup table @@ -1399,7 +1639,7 @@ static long cvt_device_st( * NULL implies the conversion is not supported. */ -long (*dbFastGetConvertRoutine[DBF_DEVICE+1][DBR_ENUM+1])() = { +FASTCONVERTFUNC const dbFastGetConvertRoutine[DBF_DEVICE+1][DBR_ENUM+1] = { /* Convert DBF_STRING to ... */ { cvt_st_st, cvt_st_c, cvt_st_uc, cvt_st_s, cvt_st_us, cvt_st_l, cvt_st_ul, cvt_st_q, cvt_st_uq, cvt_st_f, cvt_st_d, cvt_st_e }, @@ -1455,7 +1695,7 @@ long (*dbFastGetConvertRoutine[DBF_DEVICE+1][DBR_ENUM+1])() = { * NULL implies the conversion is not supported. */ -long (*dbFastPutConvertRoutine[DBR_ENUM+1][DBF_DEVICE+1])() = { +FASTCONVERTFUNC const dbFastPutConvertRoutine[DBR_ENUM+1][DBF_DEVICE+1] = { /* Convert DBR_STRING to ... */ { cvt_st_st, cvt_st_c, cvt_st_uc, cvt_st_s, cvt_st_us, cvt_st_l, cvt_st_ul, cvt_st_q, cvt_st_uq, cvt_st_f, cvt_st_d, cvt_st_e, cvt_st_menu, cvt_st_device}, diff --git a/modules/database/src/ioc/db/dbIocRegister.c b/modules/database/src/ioc/db/dbIocRegister.c index 83a2c91143..abdc28fa7e 100644 --- a/modules/database/src/ioc/db/dbIocRegister.c +++ b/modules/database/src/ioc/db/dbIocRegister.c @@ -75,21 +75,21 @@ static const iocshFuncDef dbbFuncDef = {"dbb",1,dbbArgs, "Set Breakpoint on a record\n" "This command spawns one breakpoint continuation task per lockset," " in which further record execution is run\n"}; -static void dbbCallFunc(const iocshArgBuf *args) { dbb(args[0].sval);} +static void dbbCallFunc(const iocshArgBuf *args) { iocshSetError(dbb(args[0].sval));} /* dbd */ static const iocshArg dbdArg0 = { "record name",iocshArgStringRecord}; static const iocshArg * const dbdArgs[1] = {&dbdArg0}; static const iocshFuncDef dbdFuncDef = {"dbd",1,dbdArgs, "Remove breakpoint from a record.\n"}; -static void dbdCallFunc(const iocshArgBuf *args) { dbd(args[0].sval);} +static void dbdCallFunc(const iocshArgBuf *args) { iocshSetError(dbd(args[0].sval));} /* dbc */ static const iocshArg dbcArg0 = { "record name",iocshArgStringRecord}; static const iocshArg * const dbcArgs[1] = {&dbcArg0}; static const iocshFuncDef dbcFuncDef = {"dbc",1,dbcArgs, "Continue processing in a lockset until next breakpoint is found.\n"}; -static void dbcCallFunc(const iocshArgBuf *args) { dbc(args[0].sval);} +static void dbcCallFunc(const iocshArgBuf *args) { iocshSetError(dbc(args[0].sval));} /* dbs */ static const iocshArg dbsArg0 = { "record name",iocshArgStringRecord}; @@ -97,12 +97,12 @@ static const iocshArg * const dbsArgs[1] = {&dbsArg0}; static const iocshFuncDef dbsFuncDef = {"dbs",1,dbsArgs, "Step through record processing within a lockset.\n" "If called without an argument, automatically steps with the last breakpoint.\n"}; -static void dbsCallFunc(const iocshArgBuf *args) { dbs(args[0].sval);} +static void dbsCallFunc(const iocshArgBuf *args) { iocshSetError(dbs(args[0].sval));} /* dbstat */ static const iocshFuncDef dbstatFuncDef = {"dbstat",0,0, "Print list of suspended records, and breakpoints set in locksets.\n"}; -static void dbstatCallFunc(const iocshArgBuf *args) { dbstat();} +static void dbstatCallFunc(const iocshArgBuf *args) { iocshSetError(dbstat());} /* dbp */ static const iocshArg dbpArg0 = { "record name",iocshArgStringRecord}; @@ -119,7 +119,9 @@ static const iocshFuncDef dbpFuncDef = { " 3 - Fields of minor interest to a System developer.\n" " 4 - Internal record fields.\n"}; static void dbpCallFunc(const iocshArgBuf *args) -{ dbp(args[0].sval,args[1].ival);} +{ + iocshSetError(dbp(args[0].sval,args[1].ival)); +} /* dbap */ static const iocshArg dbapArg0 = { "record name",iocshArgStringRecord}; @@ -127,7 +129,7 @@ static const iocshArg * const dbapArgs[1] = {&dbapArg0}; static const iocshFuncDef dbapFuncDef = {"dbap",1,dbapArgs, "Auto Print.\n" "Toggle automatic printing after processing a record that has a breakpoint.\n"}; -static void dbapCallFunc(const iocshArgBuf *args) { dbap(args[0].sval);} +static void dbapCallFunc(const iocshArgBuf *args) { iocshSetError(dbap(args[0].sval));} /* dbsr */ static const iocshArg dbsrArg0 = { "interest level",iocshArgInt}; @@ -150,7 +152,7 @@ static const iocshFuncDef dbcarFuncDef = {"dbcar",2,dbcarArgs, " 2 - Shows info. for all links.\n"}; static void dbcarCallFunc(const iocshArgBuf *args) { - dbcar(args[0].sval,args[1].ival); + iocshSetError(dbcar(args[0].sval,args[1].ival)); } /* dbjlr */ @@ -162,7 +164,7 @@ static const iocshFuncDef dbjlrFuncDef = {"dbjlr",2,dbjlrArgs, "List all JSON links in a record. If no record is specified, print for all\n"}; static void dbjlrCallFunc(const iocshArgBuf *args) { - dbjlr(args[0].sval,args[1].ival); + iocshSetError(dbjlr(args[0].sval,args[1].ival)); } /* dbel */ @@ -176,7 +178,7 @@ static const iocshFuncDef dbelFuncDef = {"dbel",2,dbelArgs, "Example: dbel aitest 2\n"}; static void dbelCallFunc(const iocshArgBuf *args) { - dbel(args[0].sval, args[1].ival); + iocshSetError(dbel(args[0].sval, args[1].ival)); } /* dba */ @@ -187,7 +189,7 @@ static const iocshFuncDef dbaFuncDef = {"dba",1,dbaArgs, "Print information in the dbAddr structure for a specific field.\n" "If no field is specified, VAL is assumed.\n\n" "Example: dba(\"aitest.HIGH\")\n"}; -static void dbaCallFunc(const iocshArgBuf *args) { dba(args[0].sval);} +static void dbaCallFunc(const iocshArgBuf *args) { iocshSetError(dba(args[0].sval));} /* dbl */ static const iocshArg dblArg0 = { "record type",iocshArgString}; @@ -197,14 +199,14 @@ static const iocshFuncDef dblFuncDef = {"dbl",2,dblArgs, "Database list.\n" "List record/field names.\n" "With no arguments, lists all record names.\n" - "If record type is given, then only the names of records maching the type are printed\n" + "If record type is given, then only the names of records matching the type are printed\n" "If a field list is given, then their values are also printed\n\n" "Example: dbl(\"\")\n" " dbl(\"ai\")\n" " dbl(\"ai\",\"HIGH LOW VAL PREC\")\n"}; static void dblCallFunc(const iocshArgBuf *args) { - dbl(args[0].sval,args[1].sval); + iocshSetError(dbl(args[0].sval,args[1].sval)); } /* dbnr */ @@ -213,7 +215,7 @@ static const iocshArg * const dbnrArgs[1] = {&dbnrArg0}; static const iocshFuncDef dbnrFuncDef = {"dbnr",1,dbnrArgs, "List number of records and aliases by type.\n" "If verbose, list all record types regardless of being instanced\n"}; -static void dbnrCallFunc(const iocshArgBuf *args) { dbnr(args[0].ival);} +static void dbnrCallFunc(const iocshArgBuf *args) { iocshSetError(dbnr(args[0].ival));} /* dbli */ static const iocshArg dbliArg0 = { "pattern",iocshArgString}; @@ -221,7 +223,7 @@ static const iocshArg * const dbliArgs[1] = {&dbliArg0}; static const iocshFuncDef dbliFuncDef = {"dbli",1,dbliArgs, "List info() tags with names matching pattern.\n\n" "Example: dbli(\"autosave*\")\n"}; -static void dbliCallFunc(const iocshArgBuf *args) { dbli(args[0].sval);} +static void dbliCallFunc(const iocshArgBuf *args) { iocshSetError(dbli(args[0].sval));} /* dbla */ static const iocshArg dblaArg0 = { "pattern",iocshArgStringRecord}; @@ -229,18 +231,25 @@ static const iocshArg * const dblaArgs[1] = {&dblaArg0}; static const iocshFuncDef dblaFuncDef = {"dbla",1,dblaArgs, "List record alias()s by alias name pattern.\n\n" "Example: dbla(\"alia*\")\n"}; -static void dblaCallFunc(const iocshArgBuf *args) { dbla(args[0].sval);} - -/* dbgrep */ -static const iocshArg dbgrepArg0 = { "pattern",iocshArgStringRecord}; -static const iocshArg * const dbgrepArgs[1] = {&dbgrepArg0}; -static const iocshFuncDef dbgrepFuncDef = {"dbgrep",1,dbgrepArgs, - "List record names matching pattern.\n" +static void dblaCallFunc(const iocshArgBuf *args) { iocshSetError(dbla(args[0].sval));} + +/* dbglob */ +static const iocshArg dbglobArg0 = { "pattern",iocshArgStringRecord}; +static const iocshArg dbglobArg1 = { "fields",iocshArgString}; +static const iocshArg * const dbglobArgs[2] = {&dbglobArg0,&dbglobArg1}; +static const iocshFuncDef dbglobFuncDef = {"dbglob",2,dbglobArgs, + "List record names matching pattern and optionally print field values. \n" "The pattern can contain any characters that are legal in record names as well as:\n" " - \"?\", which matches 0 or one characters.\n" " - \"*\", which matches 0 or more characters.\n\n" - "Example: dbgrep(\"*gpibAi*\")\n"}; -static void dbgrepCallFunc(const iocshArgBuf *args) { dbgrep(args[0].sval);} + "Example: dbglob(\"*gpibAi*\")\n" + " dbglob(\"*gpibAi*\",\"VAL DESC\")\n"}; +static void dbglobCallFunc(const iocshArgBuf *args) { iocshSetError(dbglob(args[0].sval,args[1].sval));} + +/* dbgrep; alias for dbglob, so it should have the same arguments */ +static const iocshFuncDef dbgrepFuncDef = {"dbgrep",2,dbglobArgs, + "See dbglob.\n"}; +static void dbgrepCallFunc(const iocshArgBuf *args) { iocshSetError(dbglob(args[0].sval,args[1].sval));} /* dbgf */ static const iocshArg dbgfArg0 = { "record name",iocshArgStringRecord}; @@ -250,7 +259,7 @@ static const iocshFuncDef dbgfFuncDef = {"dbgf",1,dbgfArgs, "Print current value of record field.\n" "If no field name is specified, VAL is assumed.\n\n" "Example: dbgf(\"aitest.VAL\")\n"}; -static void dbgfCallFunc(const iocshArgBuf *args) { dbgf(args[0].sval);} +static void dbgfCallFunc(const iocshArgBuf *args) { iocshSetError(dbgf(args[0].sval));} /* dbpf */ static const iocshArg dbpfArg0 = { "record name",iocshArgStringRecord}; @@ -261,7 +270,7 @@ static const iocshFuncDef dbpfFuncDef = {"dbpf",2,dbpfArgs, "Change value of record field and read it back with dbgf.\n" "If no field is specified, VAL is assumed\n"}; static void dbpfCallFunc(const iocshArgBuf *args) -{ dbpf(args[0].sval,args[1].sval);} +{ iocshSetError(dbpf(args[0].sval,args[1].sval));} /* dbpr */ static const iocshArg dbprArg0 = { "record name",iocshArgStringRecord}; @@ -279,14 +288,14 @@ static const iocshFuncDef dbprFuncDef = { "Example: dbpr aitest 3\n" }; static void dbprCallFunc(const iocshArgBuf *args) -{ dbpr(args[0].sval,args[1].ival);} +{ iocshSetError(dbpr(args[0].sval,args[1].ival));} /* dbtr */ static const iocshArg dbtrArg0 = { "record name",iocshArgStringRecord}; static const iocshArg * const dbtrArgs[1] = {&dbtrArg0}; static const iocshFuncDef dbtrFuncDef = {"dbtr",1,dbtrArgs, "Process record and then some fields.\n"}; -static void dbtrCallFunc(const iocshArgBuf *args) { dbtr(args[0].sval);} +static void dbtrCallFunc(const iocshArgBuf *args) { iocshSetError(dbtr(args[0].sval));} /* dbtgf */ static const iocshArg dbtgfArg0 = { "record name",iocshArgStringRecord}; @@ -296,7 +305,7 @@ static const iocshFuncDef dbtgfFuncDef = {"dbtgf",1,dbtgfArgs, "Get and print the specified field with all possible DBR_* types\n" "Example: dbtgf aitest\n" "Example: dbtgf aitest.VAL\n"}; -static void dbtgfCallFunc(const iocshArgBuf *args) { dbtgf(args[0].sval);} +static void dbtgfCallFunc(const iocshArgBuf *args) { iocshSetError(dbtgf(args[0].sval));} /* dbtpf */ static const iocshArg dbtpfArg0 = { "record name",iocshArgStringRecord}; @@ -308,7 +317,7 @@ static const iocshFuncDef dbtpfFuncDef = {"dbtpf",2,dbtpfArgs, "for all possible DBR_* types\n\n" "Example: dbtpf aitest 5.0\n"}; static void dbtpfCallFunc(const iocshArgBuf *args) -{ dbtpf(args[0].sval,args[1].sval);} +{ iocshSetError(dbtpf(args[0].sval,args[1].sval));} /* dbior */ static const iocshArg dbiorArg0 = { "driver name",iocshArgString}; @@ -317,7 +326,7 @@ static const iocshArg * const dbiorArgs[] = {&dbiorArg0,&dbiorArg1}; static const iocshFuncDef dbiorFuncDef = {"dbior",2,dbiorArgs, "Driver Report.\n"}; static void dbiorCallFunc(const iocshArgBuf *args) -{ dbior(args[0].sval,args[1].ival);} +{ iocshSetError(dbior(args[0].sval,args[1].ival));} /* dbhcr */ static const iocshFuncDef dbhcrFuncDef = {"dbhcr",0,0, @@ -327,7 +336,7 @@ static const iocshFuncDef dbhcrFuncDef = {"dbhcr",0,0, "Use the UNIX sort command:\n" "dbhcr > report\n" "sort report > report.sorted\n"}; -static void dbhcrCallFunc(const iocshArgBuf *args) { dbhcr();} +static void dbhcrCallFunc(const iocshArgBuf *args) { iocshSetError(dbhcr());} /* gft */ static const iocshArg gftArg0 = { "record name",iocshArgStringRecord}; @@ -336,7 +345,7 @@ static const iocshFuncDef gftFuncDef = {"gft",1,gftArgs, "Report dbChannel info and value.\n" "Example: gft aitest\n" "Example: gft aitest.VAL\n"}; -static void gftCallFunc(const iocshArgBuf *args) { gft(args[0].sval);} +static void gftCallFunc(const iocshArgBuf *args) { iocshSetError(gft(args[0].sval));} /* pft */ static const iocshArg pftArg0 = { "record name",iocshArgStringRecord}; @@ -346,7 +355,7 @@ static const iocshFuncDef pftFuncDef = {"pft",2,pftArgs, "dbChannel put value.\n" "Example: pft aitest 5.0\n"}; static void pftCallFunc(const iocshArgBuf *args) -{ pft(args[0].sval,args[1].sval);} +{ iocshSetError(pft(args[0].sval,args[1].sval));} /* dbtpn */ static const iocshArg dbtpnArg0 = { "record name",iocshArgStringRecord}; @@ -359,12 +368,12 @@ static const iocshFuncDef dbtpnFuncDef = {"dbtpn",2,dbtpnArgs, "Example: dbtpn aitest\n" "Example: dbtpn aitest 5.0\n"}; static void dbtpnCallFunc(const iocshArgBuf *args) -{ dbtpn(args[0].sval,args[1].sval);} +{ iocshSetError(dbtpn(args[0].sval,args[1].sval));} /* dbNotifyDump */ static const iocshFuncDef dbNotifyDumpFuncDef = {"dbNotifyDump",0,0, "Report status of any active async processing with completion notification.\n"}; -static void dbNotifyDumpCallFunc(const iocshArgBuf *args) { dbNotifyDump();} +static void dbNotifyDumpCallFunc(const iocshArgBuf *args) { iocshSetError(dbNotifyDump());} /* dbPutAttribute */ static const iocshArg dbPutAttrArg0 = { "record type",iocshArgString}; @@ -375,7 +384,7 @@ static const iocshArg * const dbPutAttrArgs[] = static const iocshFuncDef dbPutAttrFuncDef = {"dbPutAttribute",3,dbPutAttrArgs, "Set/Create record attribute.\n"}; static void dbPutAttrCallFunc(const iocshArgBuf *args) -{ dbPutAttribute(args[0].sval,args[1].sval,args[2].sval);} +{ iocshSetError(dbPutAttribute(args[0].sval,args[1].sval,args[2].sval));} /* tpn */ static const iocshArg tpnArg0 = { "record name",iocshArgStringRecord}; @@ -385,7 +394,7 @@ static const iocshFuncDef tpnFuncDef = {"tpn",2,tpnArgs, "Test Process Notify.\n\n" "Example: tpn aitest 5.0\n"}; static void tpnCallFunc(const iocshArgBuf *args) -{ tpn(args[0].sval,args[1].sval);} +{ iocshSetError(tpn(args[0].sval,args[1].sval));} /* dblsr */ static const iocshArg dblsrArg0 = { "record name",iocshArgStringRecord}; @@ -399,7 +408,7 @@ static const iocshFuncDef dblsrFuncDef = {"dblsr",2,dblsrArgs, " 2 - Show each record and all database links in the lock set.\n\n" "Example: dblsr aitest 2\n"}; static void dblsrCallFunc(const iocshArgBuf *args) -{ dblsr(args[0].sval,args[1].ival);} +{ iocshSetError(dblsr(args[0].sval,args[1].ival));} /* dbLockShowLocked */ static const iocshArg dbLockShowLockedArg0 = { "interest level",iocshArgInt}; @@ -412,7 +421,7 @@ static const iocshFuncDef dbLockShowLockedFuncDef = { "Example: dbLockShowLocked 0\n" }; static void dbLockShowLockedCallFunc(const iocshArgBuf *args) -{ dbLockShowLocked(args[0].ival);} +{ iocshSetError(dbLockShowLocked(args[0].ival));} /* scanOnceSetQueueSize */ static const iocshArg scanOnceSetQueueSizeArg0 = { "size",iocshArgInt}; @@ -423,7 +432,7 @@ static const iocshFuncDef scanOnceSetQueueSizeFuncDef = {"scanOnceSetQueueSize", "Must be called before iocInit().\n"}; static void scanOnceSetQueueSizeCallFunc(const iocshArgBuf *args) { - scanOnceSetQueueSize(args[0].ival); + iocshSetError(scanOnceSetQueueSize(args[0].ival)); } /* scanOnceQueueShow */ @@ -431,7 +440,7 @@ static const iocshArg scanOnceQueueShowArg0 = { "reset",iocshArgInt}; static const iocshArg * const scanOnceQueueShowArgs[1] = {&scanOnceQueueShowArg0}; static const iocshFuncDef scanOnceQueueShowFuncDef = {"scanOnceQueueShow",1,scanOnceQueueShowArgs, - "Show details and statitics of scan once queue processing.\n"}; + "Show details and statistics of scan once queue processing.\n"}; static void scanOnceQueueShowCallFunc(const iocshArgBuf *args) { scanOnceQueueShow(args[0].ival); @@ -444,7 +453,7 @@ static const iocshFuncDef scanpplFuncDef = {"scanppl",1,scanpplArgs, "Print info for records with periodic scan.\n" "If rate == 0.0, all periods are shown.\n"}; static void scanpplCallFunc(const iocshArgBuf *args) -{ scanppl(args[0].dval);} +{ iocshSetError(scanppl(args[0].dval));} /* scanpel */ static const iocshArg scanpelArg0 = { "event name",iocshArgString}; @@ -452,7 +461,7 @@ static const iocshArg * const scanpelArgs[1] = {&scanpelArg0}; static const iocshFuncDef scanpelFuncDef = {"scanpel",1,scanpelArgs, "Print info for records with SCAN = \"Event\".\n"}; static void scanpelCallFunc(const iocshArgBuf *args) -{ scanpel(args[0].sval);} +{ iocshSetError(scanpel(args[0].sval));} /* postEvent */ static const iocshArg postEventArg0 = { "event name",iocshArgString}; @@ -468,7 +477,7 @@ static void postEventCallFunc(const iocshArgBuf *args) /* scanpiol */ static const iocshFuncDef scanpiolFuncDef = {"scanpiol",0,0, "Print info for records with SCAN = \"I/O Intr\".\n"}; -static void scanpiolCallFunc(const iocshArgBuf *args) { scanpiol();} +static void scanpiolCallFunc(const iocshArgBuf *args) { iocshSetError(scanpiol());} /* callbackSetQueueSize */ static const iocshArg callbackSetQueueSizeArg0 = { "bufsize",iocshArgInt}; @@ -479,7 +488,7 @@ static const iocshFuncDef callbackSetQueueSizeFuncDef = {"callbackSetQueueSize", "Must be called before iocInit().\n"}; static void callbackSetQueueSizeCallFunc(const iocshArgBuf *args) { - callbackSetQueueSize(args[0].ival); + iocshSetError(callbackSetQueueSize(args[0].ival)); } /* callbackQueueShow */ @@ -504,7 +513,7 @@ static const iocshFuncDef callbackParallelThreadsFuncDef = {"callbackParallelThr "or one of LOW, MEDIUM, or HIGH.\n"}; static void callbackParallelThreadsCallFunc(const iocshArgBuf *args) { - callbackParallelThreads(args[0].ival, args[1].sval); + iocshSetError(callbackParallelThreads(args[0].ival, args[1].sval)); } /* dbStateCreate */ @@ -514,7 +523,8 @@ static const iocshFuncDef dbStateCreateFuncDef = {"dbStateCreate", 1, dbStateCre "Allocate new state name for \"state\" filter.\n"}; static void dbStateCreateCallFunc (const iocshArgBuf *args) { - dbStateCreate(args[0].sval); + if (!dbStateCreate(args[0].sval)) + iocshSetError(-1); } /* dbStateSet */ @@ -527,6 +537,8 @@ static void dbStateSetCallFunc (const iocshArgBuf *args) if (sid) dbStateSet(sid); + else + iocshSetError(-1); } /* dbStateClear */ @@ -539,6 +551,8 @@ static void dbStateClearCallFunc (const iocshArgBuf *args) if (sid) dbStateClear(sid); + else + iocshSetError(-1); } /* dbStateShow */ @@ -552,6 +566,8 @@ static void dbStateShowCallFunc (const iocshArgBuf *args) if (sid) dbStateShow(sid, args[1].ival); + else + iocshSetError(-1); } /* dbStateShowAll */ @@ -589,6 +605,7 @@ void dbIocRegister(void) iocshRegister(&dbnrFuncDef,dbnrCallFunc); iocshRegister(&dblaFuncDef,dblaCallFunc); iocshRegister(&dbliFuncDef,dbliCallFunc); + iocshRegister(&dbglobFuncDef,dbglobCallFunc); iocshRegister(&dbgrepFuncDef,dbgrepCallFunc); iocshRegister(&dbgfFuncDef,dbgfCallFunc); iocshRegister(&dbpfFuncDef,dbpfCallFunc); diff --git a/modules/database/src/ioc/db/dbLink.c b/modules/database/src/ioc/db/dbLink.c index f5d2864479..a2819d5530 100644 --- a/modules/database/src/ioc/db/dbLink.c +++ b/modules/database/src/ioc/db/dbLink.c @@ -133,7 +133,7 @@ void dbInitLink(struct link *plink, short dbfType) plink->value.pv_link.pvlMask |= pvlOptFWD; } else { - errlogPrintf("Forward-link uses Channel Access " + errlogPrintf(ERL_WARNING ": Forward-link uses Channel Access " "without pointing to PROC field\n" " %s.%s => %s\n", precord->name, dbLinkFieldName(plink), diff --git a/modules/database/src/ioc/db/dbLink.h b/modules/database/src/ioc/db/dbLink.h index 9b4e9a9f0a..e531335415 100644 --- a/modules/database/src/ioc/db/dbLink.h +++ b/modules/database/src/ioc/db/dbLink.h @@ -307,7 +307,7 @@ typedef struct lset { * * @param plink the link * @param dbrType data type code - * @param pbuffer where to put the value + * @param pbuffer where the data is * @param nRequest number of elements to send * @returns status value */ @@ -324,7 +324,7 @@ typedef struct lset { * * @param plink the link * @param dbrType data type code - * @param pbuffer where to put the value + * @param pbuffer where the data is * @param nRequest number of elements to send * @returns status value */ @@ -359,6 +359,8 @@ typedef struct lset { * @param plink the link * @param rtn routine to execute * @returns status value + * + * @since 3.16.1 */ long (*doLocked)(struct link *plink, dbLinkUserCallback rtn, void *priv); @@ -389,6 +391,10 @@ typedef struct lset { #define dbGetSevr(link, sevr) \ dbGetAlarm(link, NULL, sevr) +/** @brief Lookup link field name from pointer. + * Returns only field name. aka. value of ``dbFldDes::name`` + * @since 3.16.2 + */ DBCORE_API const char * dbLinkFieldName(const struct link *plink); DBCORE_API void dbInitLink(struct link *plink, short dbfType); diff --git a/modules/database/src/ioc/db/dbLock.c b/modules/database/src/ioc/db/dbLock.c index 8ddb5dfc84..b6afbd629e 100644 --- a/modules/database/src/ioc/db/dbLock.c +++ b/modules/database/src/ioc/db/dbLock.c @@ -869,7 +869,7 @@ void dbLockSetSplit(dbLocker *locker, dbCommon *pfirst, dbCommon *psecond) } } -static char *msstring[4]={"NMS","MS","MSI","MSS"}; +static const char *msstring[4]={"NMS","MS","MSI","MSS"}; long dblsr(char *recordname,int level) { @@ -918,7 +918,7 @@ long dblsr(char *recordname,int level) pdbFldDes = pdbRecordType->papFldDes[pdbRecordType->link_ind[link]]; plink = (DBLINK *)((char *)precord + pdbFldDes->offset); if(plink->type != DB_LINK) continue; - pdbAddr = (DBADDR *)(plink->value.pv_link.pvt); + pdbAddr = &((dbChannel *)(plink->value.pv_link.pvt))->addr; printf("\t%s",pdbFldDes->name); if(pdbFldDes->field_type==DBF_INLINK) { printf("\t INLINK"); diff --git a/modules/database/src/ioc/db/dbLockPvt.h b/modules/database/src/ioc/db/dbLockPvt.h index 99ab590df3..03ed0bde4e 100644 --- a/modules/database/src/ioc/db/dbLockPvt.h +++ b/modules/database/src/ioc/db/dbLockPvt.h @@ -12,6 +12,7 @@ #include "dbLock.h" #include "epicsMutex.h" #include "epicsSpin.h" +#include "epicsThread.h" /* Define to enable additional error checking */ #undef LOCKSET_DEBUG diff --git a/modules/database/src/ioc/db/dbPutNotifyBlocker.cpp b/modules/database/src/ioc/db/dbPutNotifyBlocker.cpp index fe42d094c0..62fc44612d 100644 --- a/modules/database/src/ioc/db/dbPutNotifyBlocker.cpp +++ b/modules/database/src/ioc/db/dbPutNotifyBlocker.cpp @@ -15,6 +15,8 @@ * 505 665 1831 */ +#define USE_TYPED_DBEVENT + #include #include diff --git a/modules/database/src/ioc/db/dbScan.c b/modules/database/src/ioc/db/dbScan.c index 6e8799784b..a9a16a29d2 100644 --- a/modules/database/src/ioc/db/dbScan.c +++ b/modules/database/src/ioc/db/dbScan.c @@ -155,7 +155,7 @@ void scanStop(void) { int i; - if (scanCtl == ctlExit) return; + if (scanCtl == ctlInit || scanCtl == ctlExit) return; scanCtl = ctlExit; interruptAccept = FALSE; @@ -264,7 +264,7 @@ void scanAdd(struct dbCommon *precord) } else if (scan == menuScanI_O_Intr) { ioscan_head *piosh = NULL; int prio; - DEVSUPFUN get_ioint_info; + long (*get_ioint_info)(int, struct dbCommon *, IOSCANPVT*); if (precord->dset == NULL){ recGblRecordError(-1, (void *)precord, @@ -332,7 +332,7 @@ void scanDelete(struct dbCommon *precord) } else if (scan == menuScanI_O_Intr) { ioscan_head *piosh = NULL; int prio; - DEVSUPFUN get_ioint_info; + long (*get_ioint_info)(int, struct dbCommon *, IOSCANPVT*); if (precord->dset==NULL) { recGblRecordError(-1, (void *)precord, @@ -680,7 +680,9 @@ int scanOnceCallback(struct dbCommon *precord, once_complete cb, void *usr) pushOK = epicsRingBytesPut(onceQ, (void*)&ent, sizeof(ent)); if (!pushOK) { - if (newOverflow) errlogPrintf("scanOnce: Ring buffer overflow\n"); + if (newOverflow) + errlogPrintf("%s : " ERL_WARNING " scanOnce: Ring buffer overflow\n", + precord->name); newOverflow = FALSE; epicsAtomicIncrIntT(&onceQOverruns); } else { diff --git a/modules/database/src/ioc/db/dbScan.h b/modules/database/src/ioc/db/dbScan.h index 1839ae3216..2439fd45f3 100644 --- a/modules/database/src/ioc/db/dbScan.h +++ b/modules/database/src/ioc/db/dbScan.h @@ -62,8 +62,28 @@ DBCORE_API void post_event(int event); DBCORE_API void scanAdd(struct dbCommon *); DBCORE_API void scanDelete(struct dbCommon *); DBCORE_API double scanPeriod(int scan); -DBCORE_API int scanOnce(struct dbCommon *); -DBCORE_API int scanOnceCallback(struct dbCommon *, once_complete cb, void *usr); +/** Shorthand for scanOnceCallback(prec, NULL, NULL) + */ +DBCORE_API int scanOnce(struct dbCommon *prec); +/** @brief scanOnce Request immediate record processing from another thread. + * + * Queue a request for record processing from the dedicated "Once" thread. + * Request may fail if Once queue overflows. See scanOnceSetQueueSize() + * + * @param prec Record to process + * @param cb Function called after target record dbProcess() + * Does not wait for async record completion. + * @param usr Argumentfor cb + * @return Zero on success. Non-zero if the request could not be queued. + */ +DBCORE_API int scanOnceCallback(struct dbCommon *prec, once_complete cb, void *usr); +/** @brief Set Once queue size + * + * Must be called prior to iocInit() + * + * @param size New size. May be smaller + * @return Zero on success + */ DBCORE_API int scanOnceSetQueueSize(int size); DBCORE_API int scanOnceQueueStatus(const int reset, scanOnceQueueStats *result); DBCORE_API void scanOnceQueueShow(const int reset); @@ -77,9 +97,39 @@ DBCORE_API int scanpel(const char *event_name); /*print io_event list*/ DBCORE_API int scanpiol(void); +/** @brief Initialize "I/O Intr" source + * @param ppios Pointer to scan list to be initialized + * + * Afterwards this IOSCANPVT may be assigned during a get_ioint_info() callback. + * See typed_dset::get_ioint_info() + * + * @note There is currently no way to free this allocation. + */ DBCORE_API void scanIoInit(IOSCANPVT *ppios); +/** @brief Request processing of all associated records from callback threads + * @param pios The scan list + * @pre pios must be initialized by scanIoInit() + * @return + */ DBCORE_API unsigned int scanIoRequest(IOSCANPVT pios); +/** @brief Process all records on the scan list for the specified priority. + * + * Also executes the callback set by scanIoSetComplete() + * + * @param pios The scan list + * @param prio one of priorityLow through priorityHigh (defined in callback.h). + * A value between 0 and NUM_CALLBACK_PRIORITIES-1 . + * @return Zero if the scan list was empty or 1<node)) { + if (psrv->stats && + (!name || strcmp(name, psrv->name) == 0)) { + unsigned lch = 0, lcl = 0; + + psrv->stats(&lch, &lcl); + tch += lch; + tcl += lcl; + nmatch++; + if (name) + break; /* No duplicate names in serverList */ + } + } + if (channels) *channels = tch; + if (clients) *clients = tcl; + return nmatch; +} + #define STARTSTOP(routine, method, newState) \ void routine(void) \ { \ diff --git a/modules/database/src/ioc/db/dbServer.h b/modules/database/src/ioc/db/dbServer.h index 3261660606..bef5a10ee6 100644 --- a/modules/database/src/ioc/db/dbServer.h +++ b/modules/database/src/ioc/db/dbServer.h @@ -17,9 +17,6 @@ * the dbServer interface provides allow the IOC to start, pause and stop * the servers together, and to provide status and debugging information * to the IOC user/developer through a common set of commands. - * - * @todo No API is provided yet for calling stats() methods. - * Nothing in the IOC calls dbStopServers(), not sure where it should go. */ #ifndef INC_dbServer_H @@ -59,8 +56,8 @@ typedef struct dbServer { /** @brief Get number of channels and clients currently connected. * - * @param channels NULL or pointer for returning channel count. - * @param clients NULL or pointer for returning client count. + * @param channels @c NULL or pointer for returning channel count. + * @param clients @c NULL or pointer for returning client count. */ void (* stats) (unsigned *channels, unsigned *clients); @@ -145,6 +142,30 @@ DBCORE_API void dbsr(unsigned level); */ DBCORE_API int dbServerClient(char *pBuf, size_t bufSize); +/** @brief CPP Macro indicating the dbServerStats() routine exists. + * @since 7.0.10 + */ +#define HAS_DBSERVER_STATS + +/** @brief Fetch statistics from server layers. + * + * This is an API for iocStats and similar to fetch the number of channels + * and clients connected to the registered server layers. + * If the name given is NULL the statistics returned are the totals from + * all registered server layers, otherwise just from the named server. + * @param name Server name + * @param channels NULL, or where to return the channel count + * @param clients NULL or where to return the client count + * @returns -1 if the IOC isn't running or no servers are registered, without + * writing to the statistics variables. Otherwise it writes to the statistics + * variables and returns the number of dbServer::stats() methods called, + * 0 if a named server wasn't found or doesn't have a stats() method. + * + * @since 7.0.10 + */ +DBCORE_API int dbServerStats(const char *name, unsigned *channels, + unsigned *clients); + /** @brief Initialize all registered servers. * * Calls all dbServer::init() methods. diff --git a/modules/database/src/ioc/db/dbSubscriptionIO.cpp b/modules/database/src/ioc/db/dbSubscriptionIO.cpp index 674ee1dc62..1d19266f83 100644 --- a/modules/database/src/ioc/db/dbSubscriptionIO.cpp +++ b/modules/database/src/ioc/db/dbSubscriptionIO.cpp @@ -14,6 +14,8 @@ * 505 665 1831 */ +#define USE_TYPED_DBEVENT + #include #include diff --git a/modules/database/src/ioc/db/dbTest.c b/modules/database/src/ioc/db/dbTest.c index 3c52f93e10..b81820a21d 100644 --- a/modules/database/src/ioc/db/dbTest.c +++ b/modules/database/src/ioc/db/dbTest.c @@ -100,13 +100,59 @@ long dba(const char*pname) return 0; } +/* split a space separated list of field names and return the number of + fields with each field an element of papfields. These elements + point to within the fieldnames variable which is modified + by the function. memory is allocated for *ppapfields and needs + to be freed later by the calling routine */ +static int splitFieldsList(char *fieldnames, char ***ppapfields) +{ + char *pnext = fieldnames; + int nfields = 0, maxfields = 1; + char* saveptr = NULL; + /* this may overcount real fields e.g. " VAL " hence maxfields */ + while (*pnext && (pnext = strchr(pnext, ' '))) { + maxfields++; + while (*pnext == ' ') pnext++; + } + *ppapfields = dbCalloc(maxfields, sizeof(char *)); + pnext = epicsStrtok_r(fieldnames, " ", &saveptr); + while(pnext != NULL) { + (*ppapfields)[nfields++] = pnext; + pnext = epicsStrtok_r(NULL, " ", &saveptr); + } + return nfields; +} + +static void printFieldsList(DBENTRY *pdbentry, char** papfields, int nfields) +{ + int ifield; + for (ifield = 0; ifield < nfields; ifield++) { + char *pvalue; + long status = dbFindField(pdbentry, papfields[ifield]); + if (status) { + if (!strcmp(papfields[ifield], "recordType")) { + pvalue = dbGetRecordTypeName(pdbentry); + } + else { + printf(", "); + continue; + } + } + else { + pvalue = dbGetString(pdbentry); + } + printf(", \"%s\"", (pvalue ? pvalue : "")); + } + printf("\n"); +} + long dbl(const char *precordTypename, const char *fields) { DBENTRY dbentry; DBENTRY *pdbentry=&dbentry; long status; int nfields = 0; - int ifield; char *fieldnames = 0; char **papfields = 0; @@ -121,25 +167,8 @@ long dbl(const char *precordTypename, const char *fields) if (fields && (*fields == '\0')) fields = NULL; if (fields) { - char *pnext; - fieldnames = epicsStrDup(fields); - nfields = 1; - pnext = fieldnames; - while (*pnext && (pnext = strchr(pnext,' '))) { - nfields++; - while (*pnext == ' ') pnext++; - } - papfields = dbCalloc(nfields,sizeof(char *)); - pnext = fieldnames; - for (ifield = 0; ifield < nfields; ifield++) { - papfields[ifield] = pnext; - if (ifield < nfields - 1) { - pnext = strchr(pnext, ' '); - *pnext++ = 0; - while (*pnext == ' ') pnext++; - } - } + nfields = splitFieldsList(fieldnames, &papfields); } dbInitEntry(pdbbase, pdbentry); if (!precordTypename) @@ -154,24 +183,7 @@ long dbl(const char *precordTypename, const char *fields) status = dbFirstRecord(pdbentry); while (!status) { printf("%s", dbGetRecordName(pdbentry)); - for (ifield = 0; ifield < nfields; ifield++) { - char *pvalue; - status = dbFindField(pdbentry, papfields[ifield]); - if (status) { - if (!strcmp(papfields[ifield], "recordType")) { - pvalue = dbGetRecordTypeName(pdbentry); - } - else { - printf(", "); - continue; - } - } - else { - pvalue = dbGetString(pdbentry); - } - printf(", \"%s\"", pvalue ? pvalue : ""); - } - printf("\n"); + printFieldsList(pdbentry, papfields, nfields); status = dbNextRecord(pdbentry); } if (precordTypename) @@ -283,14 +295,17 @@ long dbli(const char *pattern) return 0; } -long dbgrep(const char *pmask) +long dbglob(const char *pmask,const char *fields) { DBENTRY dbentry; DBENTRY *pdbentry = &dbentry; long status; + int nfields = 0; + char *fieldnames = 0; + char **papfields = 0; if (!pmask || !*pmask) { - printf("Usage: dbgrep \"pattern\"\n"); + printf("Usage: dbglob \"pattern\" \"fields\"\n"); return 1; } @@ -298,24 +313,38 @@ long dbgrep(const char *pmask) printf("No database loaded\n"); return 0; } - + if (fields && (*fields == '\0')) + fields = NULL; + if (fields) { + fieldnames = epicsStrDup(fields); + nfields = splitFieldsList(fieldnames, &papfields); + } dbInitEntry(pdbbase, pdbentry); status = dbFirstRecordType(pdbentry); while (!status) { status = dbFirstRecord(pdbentry); while (!status) { char *pname = dbGetRecordName(pdbentry); - if (epicsStrGlobMatch(pname, pmask)) - puts(pname); + if (epicsStrGlobMatch(pname, pmask)) { + printf("%s", pname); + printFieldsList(pdbentry, papfields, nfields); + } status = dbNextRecord(pdbentry); } status = dbNextRecordType(pdbentry); } - + if (nfields > 0) { + free((void *)papfields); + free((void *)fieldnames); + } dbFinishEntry(pdbentry); return 0; } - + +long dbgrep(const char *pname, const char *fields) { + return dbglob(pname, fields); +} + long dbgf(const char *pname) { /* declare buffer long just to ensure correct alignment */ @@ -680,7 +709,7 @@ long dbtpf(const char *pname, const char *pvalue) long dbior(const char *pdrvName,int interest_level) { drvSup *pdrvSup; - struct drvet *pdrvet; + drvet *pdrvet; dbRecordType *pdbRecordType; if (!pdbbase) { diff --git a/modules/database/src/ioc/db/dbTest.h b/modules/database/src/ioc/db/dbTest.h index f774b17514..5740ad1fc0 100644 --- a/modules/database/src/ioc/db/dbTest.h +++ b/modules/database/src/ioc/db/dbTest.h @@ -27,9 +27,11 @@ DBCORE_API long dbnr(int verbose); /* list aliases */ DBCORE_API long dbla(const char *pmask); /* list infos */ -DBCORE_API long dbli(const char *patern); -/*list records with mask*/ -DBCORE_API long dbgrep(const char *pmask); +DBCORE_API long dbli(const char *pattern); +/*list records filtered by glob pattern*/ +DBCORE_API long dbglob(const char *ppattern,const char *fields); +/*list records filtered by glob pattern; alias for dbglob*/ +DBCORE_API long dbgrep(const char *ppatern,const char *fields); /*get field value*/ DBCORE_API long dbgf(const char *pname); /*put field value*/ diff --git a/modules/database/src/ioc/db/dbUnitTest.c b/modules/database/src/ioc/db/dbUnitTest.c index bf4ec205b7..bcbe9050e2 100644 --- a/modules/database/src/ioc/db/dbUnitTest.c +++ b/modules/database/src/ioc/db/dbUnitTest.c @@ -14,6 +14,7 @@ #include #define EPICS_PRIVATE_API +#define USE_TYPED_DBEVENT #include "dbmf.h" #include "epicsUnitTest.h" @@ -34,6 +35,8 @@ #include "errSymTbl.h" #include "iocshRegisterCommon.h" +#define DBR_NAME(dbrType) (VALID_DB_REQ(dbrType) ? pamapdbfType[dbrType].strvalue+3 : "???") + static dbEventCtx testEvtCtx; static epicsMutexId testEvtLock; static ELLLIST testEvtList; /* holds testMonitor::node */ @@ -152,8 +155,8 @@ long testdbVPutField(const char* pv, short dbrType, va_list ap) OP(DBR_ENUM, int, enum16); #undef OP default: - testFail("invalid DBR: dbPutField(\"%s\", %d, ...)", - dbChannelName(chan), dbrType); + testFail("invalid DBR: dbPutField(\"%s\", DBR%s, ...)", + dbChannelName(chan), DBR_NAME(dbrType)); ret = S_db_badDbrtype; break; } @@ -173,7 +176,8 @@ void testdbPutFieldOk(const char* pv, int dbrType, ...) ret = testdbVPutField(pv, dbrType, ap); va_end(ap); - testOk(ret==0, "dbPutField(\"%s\", %d, ...) -> %#lx (%s)", pv, dbrType, ret, errSymMsg(ret)); + testOk(ret==0, "dbPutField(\"%s\", DBR%s, ...) -> %#lx (%s)", + pv, DBR_NAME(dbrType), ret, errSymMsg(ret)); } void testdbPutFieldFail(long status, const char* pv, int dbrType, ...) @@ -185,8 +189,8 @@ void testdbPutFieldFail(long status, const char* pv, int dbrType, ...) ret = testdbVPutField(pv, dbrType, ap); va_end(ap); - testOk(ret==status, "dbPutField(\"%s\", %d, ...) -> %#lx (%s) == %#lx (%s)", - pv, dbrType, status, errSymMsg(status), ret, errSymMsg(ret)); + testOk(ret==status, "dbPutField(\"%s\", DBR%s, ...) -> %#lx (%s) == %#lx (%s)", + pv, DBR_NAME(dbrType), status, errSymMsg(status), ret, errSymMsg(ret)); } void testdbGetFieldEqual(const char* pv, int dbrType, ...) @@ -201,7 +205,6 @@ void testdbGetFieldEqual(const char* pv, int dbrType, ...) void testdbVGetFieldEqual(const char* pv, short dbrType, va_list ap) { dbChannel *chan = dbChannelCreate(pv); - db_field_log *pfl = NULL; long nReq = 1; union anybuf pod; long status = S_dbLib_recNotFound; @@ -211,23 +214,14 @@ void testdbVGetFieldEqual(const char* pv, short dbrType, va_list ap) goto done; } - if(ellCount(&chan->filters)) { - pfl = db_create_read_log(chan); - if (!pfl) { - testFail("can't db_create_read_log w/ %s", pv); - goto done; - } - - pfl = dbChannelRunPreChain(chan, pfl); - pfl = dbChannelRunPostChain(chan, pfl); - } - - status = dbChannelGetField(chan, dbrType, pod.bytes, NULL, &nReq, pfl); + status = dbChannelGetField(chan, dbrType, pod.bytes, NULL, &nReq, NULL); if (status) { - testFail("dbGetField(\"%s\", %d, ...) -> %#lx (%s)", pv, dbrType, status, errSymMsg(status)); + testFail("dbGetField(\"%s\", DBR%s, ...) -> %#lx (%s)", + pv, DBR_NAME(dbrType), status, errSymMsg(status)); goto done; } else if(nReq==0) { - testFail("dbGetField(\"%s\", %d, ...) -> zero length", pv, dbrType); + testFail("dbGetField(\"%s\", DBR%s, ...) -> zero length", + pv, DBR_NAME(dbrType)); goto done; } @@ -235,13 +229,14 @@ void testdbVGetFieldEqual(const char* pv, short dbrType, va_list ap) case DBR_STRING: { const char *expect = va_arg(ap, char*); testOk(strcmp(expect, pod.valStr)==0, - "dbGetField(\"%s\", %d) -> \"%s\" == \"%s\"", - pv, dbrType, expect, pod.valStr); + "dbGetField(\"%s\", DBR%s) -> \"%s\" == \"%s\"", + pv, DBR_NAME(dbrType), expect, pod.valStr); break; } #define OP(DBR,Type,mem,pat) case DBR: {Type expect = va_arg(ap,Type); \ - testOk(expect==pod.val.mem, "dbGetField(\"%s\", %d) -> " pat " == " pat, \ - pv, dbrType, expect, (Type)pod.val.mem); break;} + testOk(expect==pod.val.mem||((expect!=expect)&&(pod.val.mem!=pod.val.mem)), \ + "dbGetField(\"%s\", DBR%s) -> " pat " == " pat, \ + pv, DBR_NAME(dbrType), expect, (Type)pod.val.mem); break;} OP(DBR_CHAR, int, int8, "%d"); OP(DBR_UCHAR, int, uInt8, "%d"); @@ -251,16 +246,15 @@ void testdbVGetFieldEqual(const char* pv, short dbrType, va_list ap) OP(DBR_ULONG, unsigned int, uInt32, "%u"); OP(DBR_INT64, long long, int64, "%lld"); OP(DBR_UINT64, unsigned long long, uInt64, "%llu"); - OP(DBR_FLOAT, double, float32, "%e"); - OP(DBR_DOUBLE, double, float64, "%e"); OP(DBR_ENUM, int, enum16, "%d"); + OP(DBR_FLOAT, double, float32, "%g"); + OP(DBR_DOUBLE, double, float64, "%g"); #undef OP default: - testFail("dbGetField(\"%s\", %d) -> unsupported dbf", pv, dbrType); + testFail("dbGetField(\"%s\", DBR%s) -> unsupported dbf", pv, DBR_NAME(dbrType)); } done: - db_delete_field_log(pfl); if(chan) dbChannelDelete(chan); } @@ -277,7 +271,7 @@ void testdbPutArrFieldOk(const char* pv, short dbrType, unsigned long count, con status = dbChannelPutField(chan, dbrType, pbuf, count); - testOk(status==0, "dbPutField(\"%s\", dbr=%d, count=%lu, ...) -> %ld", pv, dbrType, count, status); + testOk(status==0, "dbPutField(\"%s\", DBR%s, count=%lu, ...) -> %ld", pv, DBR_NAME(dbrType), count, status); done: if(chan) @@ -287,7 +281,6 @@ void testdbPutArrFieldOk(const char* pv, short dbrType, unsigned long count, con void testdbGetArrFieldEqual(const char* pv, short dbfType, long nRequest, unsigned long cnt, const void *pbufraw) { dbChannel *chan = dbChannelCreate(pv); - db_field_log *pfl = NULL; const long vSize = dbValueSize(dbfType); const long nStore = vSize * nRequest; long status = S_dbLib_recNotFound; @@ -299,33 +292,22 @@ void testdbGetArrFieldEqual(const char* pv, short dbfType, long nRequest, unsign goto done; } - if(ellCount(&chan->filters)) { - pfl = db_create_read_log(chan); - if (!pfl) { - testFail("can't db_create_read_log w/ %s", pv); - goto done; - } - - pfl = dbChannelRunPreChain(chan, pfl); - pfl = dbChannelRunPostChain(chan, pfl); - } - gbuf = gstore = malloc(nStore); if(!gbuf && nStore!=0) { /* note that malloc(0) is allowed to return NULL on success */ testFail("Allocation failed esize=%ld total=%ld", vSize, nStore); return; } - status = dbChannelGetField(chan, dbfType, gbuf, NULL, &nRequest, pfl); + status = dbChannelGetField(chan, dbfType, gbuf, NULL, &nRequest, NULL); if (status) { - testFail("dbGetField(\"%s\", %d, ...) -> %#lx", pv, dbfType, status); + testFail("dbGetField(\"%s\", DBR%s, ...) -> %#lx", pv, DBR_NAME(dbfType), status); } else { unsigned match = nRequest==cnt; long n, N = nRequest < cnt ? nRequest : cnt; if(!match) - testDiag("Length mis-match. expected=%lu actual=%lu", cnt, nRequest); + testDiag("Length mismatch. expected=%lu actual=%lu", cnt, nRequest); for(n=0; npre_chain) || ellCount(&chan->post_chain))) { + pfl = db_create_read_log(chan); + if (pfl) { + local_fl = 1; + pfl = dbChannelRunPreChain(chan, pfl); + pfl = dbChannelRunPostChain(chan, pfl); + } + } + switch(buffer_type) { case(oldDBR_STRING): status = dbChannelGet(chan, DBR_STRING, pbuffer, &zero, nRequest, pfl); @@ -800,6 +811,8 @@ int dbChannel_get_count( dbScanUnlock(dbChannelRecord(chan)); + if (local_fl) db_delete_field_log(pfl); + if (status) return -1; return 0; } @@ -1029,3 +1042,17 @@ int db_put_process(processNotify *ppn, notifyPutType type, ppn->status = notifyError; return 1; } + +void db_process(struct dbCommon *prec) +{ + if (prec->pact) { + if (dbAccessDebugPUTF && prec->tpro) + printf("%s: dbPutField to Active '%s', setting RPRO=1\n", + epicsThreadGetNameSelf(), prec->name); + prec->rpro = TRUE; + } else { + /* indicate that dbPutField called dbProcess */ + prec->putf = TRUE; + (void)dbProcess(prec); + } +} diff --git a/modules/database/src/ioc/db/db_access_routines.h b/modules/database/src/ioc/db/db_access_routines.h index ee2f07940a..1871b93434 100644 --- a/modules/database/src/ioc/db/db_access_routines.h +++ b/modules/database/src/ioc/db/db_access_routines.h @@ -22,6 +22,8 @@ extern "C" { #include "dbCoreAPI.h" +struct dbCommon; + DBCORE_API extern struct dbBase *pdbbase; DBCORE_API extern volatile int interruptAccept; @@ -36,7 +38,9 @@ DBCORE_API int dbChannel_put(struct dbChannel *chan, int src_type, const void *psrc, long no_elements); DBCORE_API int dbChannel_get_count(struct dbChannel *chan, int buffer_type, void *pbuffer, long *nRequest, void *pfl); - +#ifdef EPICS_DBCA_PRIVATE_API +DBCORE_API void db_process(struct dbCommon *prec); +#endif #ifdef __cplusplus } diff --git a/modules/database/src/ioc/db/menuYesNo.dbd.pod b/modules/database/src/ioc/db/menuYesNo.dbd.pod index d4237e7b88..0167ee699d 100644 --- a/modules/database/src/ioc/db/menuYesNo.dbd.pod +++ b/modules/database/src/ioc/db/menuYesNo.dbd.pod @@ -17,7 +17,8 @@ C or C would not be accepted as choices for the field. Also, the choices C, C, and C are not valid choices since they don't match the case of C or C. The integer values C<0> and C<1> may often be used instead however, they are -used as an index into the choices so C<0> becomes C and C<1> becomes . +used as an index into the choices so C<0> becomes C and C<1> becomes +C. =menu menuYesNo diff --git a/modules/database/src/ioc/db/recGbl.c b/modules/database/src/ioc/db/recGbl.c index c9bb62fdb9..002a7aebcc 100644 --- a/modules/database/src/ioc/db/recGbl.c +++ b/modules/database/src/ioc/db/recGbl.c @@ -62,7 +62,7 @@ void recGblRecordError(long status, void *pdbc, dbCommon *precord = pdbc; char errMsg[256] = ""; - if (status) + if ( status>0 ) errSymLookup(status, errMsg, sizeof(errMsg)); errlogPrintf("recGblRecordError: %s %s PV: %s\n", @@ -190,6 +190,7 @@ unsigned short recGblResetAlarms(void *precord) if(strcmp(pdbc->namsg, pdbc->amsg)!=0) { strcpy(pdbc->amsg, pdbc->namsg); + pdbc->namsg[0] = '\0'; stat_mask = DBE_ALARM; } @@ -259,8 +260,8 @@ int recGblSetSevr(void *precord, epicsEnum16 new_stat, epicsEnum16 new_sevr) return recGblSetSevrMsg(precord, new_stat, new_sevr, NULL); } -void recGblInheritSevr(int msMode, void *precord, epicsEnum16 stat, - epicsEnum16 sevr) +void recGblInheritSevrMsg(int msMode, void *precord, epicsEnum16 stat, + epicsEnum16 sevr, const char *msg) { switch (msMode) { case pvlOptNMS: @@ -273,11 +274,17 @@ void recGblInheritSevr(int msMode, void *precord, epicsEnum16 stat, recGblSetSevr(precord, LINK_ALARM, sevr); break; case pvlOptMSS: - recGblSetSevr(precord, stat, sevr); + /* Only MSS inherits msg */ + recGblSetSevrMsg(precord, stat, sevr, "%s", msg); break; } } +void recGblInheritSevr(int msMode, void *precord, epicsEnum16 stat, + epicsEnum16 sevr) +{ + recGblInheritSevrMsg(msMode, precord, stat, sevr, NULL); +} void recGblFwdLink(void *precord) { diff --git a/modules/database/src/ioc/db/recGbl.h b/modules/database/src/ioc/db/recGbl.h index cf3db4bc57..4a21e7f345 100644 --- a/modules/database/src/ioc/db/recGbl.h +++ b/modules/database/src/ioc/db/recGbl.h @@ -73,6 +73,8 @@ DBCORE_API int recGblSetSevr(void *precord, epicsEnum16 new_stat, epicsEnum16 new_sevr); DBCORE_API void recGblInheritSevr(int msMode, void *precord, epicsEnum16 stat, epicsEnum16 sevr); +DBCORE_API void recGblInheritSevrMsg(int msMode, void *precord, epicsEnum16 stat, + epicsEnum16 sevr, const char *msg); DBCORE_API int recGblSetSevrMsg(void *precord, epicsEnum16 new_stat, epicsEnum16 new_sevr, EPICS_PRINTF_FMT(const char *msg), ...) EPICS_PRINTF_STYLE(4,5); diff --git a/modules/database/src/ioc/dbStatic/Makefile b/modules/database/src/ioc/dbStatic/Makefile index e9bd95b8dc..bb178df41f 100644 --- a/modules/database/src/ioc/dbStatic/Makefile +++ b/modules/database/src/ioc/dbStatic/Makefile @@ -11,6 +11,8 @@ SRC_DIRS += $(IOCDIR)/dbStatic +USR_CFLAGS += -DUSE_TYPED_DRVET + INC += dbBase.h INC += dbFldTypes.h INC += dbStaticLib.h diff --git a/modules/database/src/ioc/dbStatic/dbBase.h b/modules/database/src/ioc/dbStatic/dbBase.h index 35ca0a6389..086c817c69 100644 --- a/modules/database/src/ioc/dbStatic/dbBase.h +++ b/modules/database/src/ioc/dbStatic/dbBase.h @@ -21,6 +21,7 @@ #include "dbDefs.h" #include "recSup.h" #include "devSup.h" +#include "drvSup.h" typedef struct dbMenu { ELLNODE node; @@ -33,7 +34,7 @@ typedef struct dbMenu { typedef struct drvSup { ELLNODE node; char *name; - struct drvet *pdrvet; + drvet *pdrvet; }drvSup; typedef struct devSup { @@ -118,10 +119,14 @@ typedef struct dbRecordNode { char *recordname; ELLLIST infoList; /*LIST head of info nodes*/ int flags; + /** Parse order of this record() + * @since 7.0.8.1 + */ + unsigned order; struct dbRecordNode *aliasedRecnode; /* NULL unless flags|DBRN_FLAGS_ISALIAS */ }dbRecordNode; -/*dbRecordAttribute is for "psuedo" fields */ +/*dbRecordAttribute is for "pseudo" fields */ /*pdbFldDes is so that other access routines work correctly*/ /*Until base supports char * value MUST be fixed length string*/ typedef struct dbRecordAttribute { @@ -184,5 +189,9 @@ typedef struct dbBase { struct gphPvt *pgpHash; short ignoreMissingMenus; short loadCdefs; + /** Total number of records. + * @since 7.0.8.1 + */ + unsigned no_records; }dbBase; #endif diff --git a/modules/database/src/ioc/dbStatic/dbLexRoutines.c b/modules/database/src/ioc/dbStatic/dbLexRoutines.c index 85af1f1109..06550d80e1 100644 --- a/modules/database/src/ioc/dbStatic/dbLexRoutines.c +++ b/modules/database/src/ioc/dbStatic/dbLexRoutines.c @@ -17,6 +17,7 @@ #include #include #include +#include #include "dbDefs.h" #include "dbmf.h" @@ -33,12 +34,14 @@ #include "dbStaticLib.h" #include "dbStaticPvt.h" #include "epicsExport.h" +#include "epicsAssert.h" #include "link.h" #include "special.h" #include "iocInit.h" /* This file is included from dbYacc.y - * Duplicate some declarations to avoid warnings from analysis tools which don't know about this. + * Duplicate some declarations to avoid warnings + * from analysis tools that don't know about this. */ static int yyerror(char *str); static long pvt_yy_parse(void); @@ -199,8 +202,9 @@ static void freeInputFileList(void) while((pinputFileNow=(inputFile *)ellFirst(&inputFileList))) { if(fclose(pinputFileNow->fp)) - errPrintf(0,__FILE__, __LINE__, - "Closing file %s",pinputFileNow->filename); + fprintf(stderr, ERL_WARNING + ": Error closing file '%s': %s\n", + pinputFileNow->filename, strerror(errno)); free((void *)pinputFileNow->filename); ellDelete(&inputFileList,(ELLNODE *)pinputFileNow); free((void *)pinputFileNow); @@ -225,7 +229,8 @@ static long dbReadCOM(DBBASE **ppdbbase,const char *filename, FILE *fp, char **macPairs; if (ellCount(&tempList)) { - epicsPrintf("dbReadCOM: Parser stack dirty %d\n", ellCount(&tempList)); + fprintf(stderr, ERL_WARNING + ": dbReadCOM: Parser stack dirty %d\n", ellCount(&tempList)); } if (getIocState() != iocVoid) { @@ -233,6 +238,8 @@ static long dbReadCOM(DBBASE **ppdbbase,const char *filename, FILE *fp, goto cleanup; } + errlogInit(0); /* Initialize the errSymTable */ + if(*ppdbbase == 0) *ppdbbase = dbAllocBase(); savedPdbbase = *ppdbbase; if(path && strlen(path)>0) { @@ -247,23 +254,23 @@ static long dbReadCOM(DBBASE **ppdbbase,const char *filename, FILE *fp, } my_buffer = dbCalloc(MY_BUFFER_SIZE,sizeof(char)); freeListInitPvt(&freeListPvt,sizeof(tempListNode),100); - if(substitutions) { - if(macCreateHandle(&macHandle,NULL)) { - epicsPrintf("macCreateHandle error\n"); - status = -1; - goto cleanup; - } - macParseDefns(macHandle,(char *)substitutions,&macPairs); - if(macPairs ==NULL) { - macDeleteHandle(macHandle); - macHandle = NULL; - } else { - macInstallMacros(macHandle,macPairs); - free((void *)macPairs); - mac_input_buffer = dbCalloc(MY_BUFFER_SIZE,sizeof(char)); - } - macSuppressWarning(macHandle,dbQuietMacroWarnings); + if (substitutions == NULL) + substitutions = ""; + if(macCreateHandle(&macHandle,NULL)) { + fprintf(stderr, ERL_ERROR ": macCreateHandle failed\n"); + status = -1; + goto cleanup; + } + macParseDefns(macHandle,substitutions,&macPairs); + if(macPairs == NULL) { + macDeleteHandle(macHandle); + macHandle = NULL; + } else { + macInstallMacros(macHandle,macPairs); + free(macPairs); + mac_input_buffer = dbCalloc(MY_BUFFER_SIZE,sizeof(char)); } + macSuppressWarning(macHandle,dbQuietMacroWarnings); pinputFile = dbCalloc(1,sizeof(inputFile)); if (filename) { pinputFile->filename = macEnvExpand(filename); @@ -274,8 +281,8 @@ static long dbReadCOM(DBBASE **ppdbbase,const char *filename, FILE *fp, if (pinputFile->filename) pinputFile->path = dbOpenFile(savedPdbbase, pinputFile->filename, &fp1); if (!pinputFile->filename || !fp1) { - errPrintf(0, __FILE__, __LINE__, - "dbRead opening file %s\n",pinputFile->filename); + fprintf(stderr, ERL_ERROR + ": Can't open file '%s'\n", pinputFile->filename); free((char*)pinputFile->filename); free(pinputFile); status = -1; @@ -294,7 +301,9 @@ static long dbReadCOM(DBBASE **ppdbbase,const char *filename, FILE *fp, status = pvt_yy_parse(); if (ellCount(&tempList) && !yyAbort) - epicsPrintf("dbReadCOM: Parser stack dirty w/o error. %d\n", ellCount(&tempList)); + fprintf(stderr, ERL_WARNING + ": dbReadCOM: Parser stack dirty w/o error. %d\n", + ellCount(&tempList)); while (ellCount(&tempList)) popFirstTemp(); /* Memory leak on parser failure */ @@ -345,14 +354,18 @@ static long dbReadCOM(DBBASE **ppdbbase,const char *filename, FILE *fp, return(status); } -long dbReadDatabase(DBBASE **ppdbbase,const char *filename, - const char *path,const char *substitutions) -{return (dbReadCOM(ppdbbase,filename,0,path,substitutions));} +long dbReadDatabase(DBBASE **ppdbbase, const char *filename, + const char *path, const char *substitutions) +{ + return dbReadCOM(ppdbbase, filename, 0, path, substitutions); +} + +long dbReadDatabaseFP(DBBASE **ppdbbase, FILE *fp, + const char *path, const char *substitutions) +{ + return dbReadCOM(ppdbbase, 0, fp, path, substitutions); +} -long dbReadDatabaseFP(DBBASE **ppdbbase,FILE *fp, - const char *path,const char *substitutions) -{return (dbReadCOM(ppdbbase,0,fp,path,substitutions));} - static int db_yyinput(char *buf, int max_size) { size_t l,n; @@ -368,7 +381,8 @@ static int db_yyinput(char *buf, int max_size) int exp = macExpandString(macHandle,mac_input_buffer, my_buffer,MY_BUFFER_SIZE); if (exp < 0) { - fprintf(stderr, "Warning: '%s' line %d has undefined macros\n", + fprintf(stderr, ERL_WARNING + ": '%s' line %d has undefined macros\n", pinputFileNow->filename, pinputFileNow->line_num+1); } } @@ -377,8 +391,9 @@ static int db_yyinput(char *buf, int max_size) } if(fgetsRtn) break; if(fclose(pinputFileNow->fp)) - errPrintf(0,__FILE__, __LINE__, - "Closing file %s",pinputFileNow->filename); + fprintf(stderr, ERL_WARNING + ": Error closing file '%s': %s\n", + pinputFileNow->filename, strerror(errno)); free((void *)pinputFileNow->filename); ellDelete(&inputFileList,(ELLNODE *)pinputFileNow); free((void *)pinputFileNow); @@ -401,15 +416,15 @@ static void dbIncludePrint(void) inputFile *pinputFile = pinputFileNow; while (pinputFile) { - epicsPrintf(" in"); + fprintf(stderr, " in"); if (pinputFile->path) - epicsPrintf(" path \"%s\" ",pinputFile->path); + fprintf(stderr, " path \"%s\" ",pinputFile->path); if (pinputFile->filename) { - epicsPrintf(" file \"%s\"",pinputFile->filename); + fprintf(stderr, " file \"%s\"",pinputFile->filename); } else { - epicsPrintf(" standard input"); + fprintf(stderr, " standard input"); } - epicsPrintf(" line %d\n",pinputFile->line_num); + fprintf(stderr, " line %d\n",pinputFile->line_num); pinputFile = (inputFile *)ellPrevious(&pinputFile->node); } return; @@ -434,7 +449,7 @@ static void dbIncludeNew(char *filename) pinputFile->filename = macEnvExpand(filename); pinputFile->path = dbOpenFile(savedPdbbase, pinputFile->filename, &fp); if (!fp) { - epicsPrintf("Can't open include file \"%s\"\n", filename); + fprintf(stderr, ERL_ERROR ": Can't open include file '%s'\n", filename); yyerror(NULL); free((void *)pinputFile->filename); free((void *)pinputFile); @@ -557,7 +572,7 @@ static void dbRecordtypeFieldHead(char *name,char *type) strcmp(pdbFldDes->name, "OUT")==0; i = dbFindFieldType(type); if (i < 0) - yyerrorAbort("Illegal Field Type"); + yyerrorAbort("Invalid Field Type"); pdbFldDes->field_type = i; } @@ -589,7 +604,7 @@ static void dbRecordtypeFieldItem(char *name,char *value) } else if(strcmp(value,"ASL1")==0) { pdbFldDes->as_level = ASL1; } else { - yyerror("Illegal Access Security value: Must be ASL0 or ASL1"); + yyerror("Invalid 'asl' value, must be ASL0 or ASL1"); } return; } @@ -616,7 +631,7 @@ static void dbRecordtypeFieldItem(char *name,char *value) if(sscanf(value,"%hd",&pdbFldDes->special)==1) { return; } - yyerror("Illegal 'special' value."); + yyerror("Invalid 'special' value."); return; } if(strcmp(name,"pp")==0) { @@ -625,13 +640,13 @@ static void dbRecordtypeFieldItem(char *name,char *value) } else if((strcmp(value,"NO")==0) || (strcmp(value,"FALSE")==0)) { pdbFldDes->process_passive = FALSE; } else { - yyerror("Illegal 'pp' value, must be YES/NO/TRUE/FALSE"); + yyerror("Invalid 'pp' value, must be YES/NO/TRUE/FALSE"); } return; } if(strcmp(name,"interest")==0) { if(sscanf(value,"%hd",&pdbFldDes->interest)!=1) - yyerror("Illegal 'interest' value, must be integer"); + yyerror("Invalid 'interest' value, must be integer"); return; } if(strcmp(name,"base")==0) { @@ -640,13 +655,13 @@ static void dbRecordtypeFieldItem(char *name,char *value) } else if(strcmp(value,"HEX")==0) { pdbFldDes->base = CT_HEX; } else { - yyerror("Illegal 'base' value, must be DECIMAL/HEX"); + yyerror("Invalid 'base' value, must be DECIMAL/HEX"); } return; } if(strcmp(name,"size")==0) { if(sscanf(value,"%hd",&pdbFldDes->size)!=1) - yyerror("Illegal 'size' value, must be integer"); + yyerror("Invalid 'size' value, must be integer"); return; } if(strcmp(name,"extra")==0) { @@ -696,7 +711,8 @@ static void dbRecordtypeEmpty(void) ptempListNode = (tempListNode *)ellFirst(&tempList); pdbRecordType = ptempListNode->item; - epicsPrintf("Declaration of recordtype(%s) preceeded full definition.\n", + fprintf(stderr, ERL_ERROR + ": Declaration of recordtype(%s) preceded full definition.\n", pdbRecordType->name); yyerrorAbort(NULL); } @@ -737,10 +753,12 @@ static void dbRecordtypeBody(void) field_type = pdbFldDes->field_type; if((field_type>=DBF_INLINK) && (field_type<=DBF_FWDLINK))no_links++; if((field_type==DBF_STRING) && (pdbFldDes->size==0)) - fprintf(stderr,"recordtype(%s).%s size not specified\n", + fprintf(stderr, ERL_ERROR + ": recordtype(%s).%s size not specified\n", pdbRecordType->name,pdbFldDes->name); if((field_type==DBF_NOACCESS) && (pdbFldDes->extra==0)) - fprintf(stderr,"recordtype(%s).%s extra not specified\n", + fprintf(stderr, ERL_ERROR + ": recordtype(%s).%s extra not specified\n", pdbRecordType->name,pdbFldDes->name); } if (ellCount(&tempList)) @@ -801,8 +819,9 @@ static void dbDevice(char *recordtype,char *linktype, int i,link_type; pgphentry = gphFind(savedPdbbase->pgpHash,recordtype,&savedPdbbase->recordTypeList); if(!pgphentry) { - epicsPrintf("Record type \"%s\" not found for device \"%s\"\n", - recordtype, choicestring); + fprintf(stderr, ERL_ERROR + ": Record type '%s' not found for device '%s'\n", + recordtype, choicestring); yyerror(NULL); return; } @@ -814,8 +833,9 @@ static void dbDevice(char *recordtype,char *linktype, } } if(link_type==-1) { - epicsPrintf("Bad link type \"%s\" for device \"%s\"\n", - linktype, choicestring); + fprintf(stderr, ERL_ERROR + ": Bad link type '%s' for device '%s'\n", + linktype, choicestring); yyerror(NULL); return; } @@ -1068,7 +1088,7 @@ int dbRecordNameValidate(const char *name) const char *pos = name; if (!*name) { - yyerrorAbort("Error: Record/Alias name can't be empty"); + yyerrorAbort(ERL_ERROR ": Record/Alias name can't be empty"); return 1; } @@ -1077,16 +1097,20 @@ int dbRecordNameValidate(const char *name) if(i==0) { /* first character restrictions */ if(c=='-' || c=='+' || c=='[' || c=='{') { - errlogPrintf("Warning: Record/Alias name '%s' should not begin with '%c'\n", name, c); + fprintf(stderr, ERL_WARNING + ": Record/Alias name '%s' should not begin with '%c'\n", + name, c); } } /* any character restrictions */ if(c < ' ') { - errlogPrintf("Warning: Record/Alias name '%s' should not contain non-printable 0x%02x\n", - name, c); + fprintf(stderr, ERL_WARNING + ": Record/Alias name '%s' contains non-printable 0x%02x\n", + name, c); } else if(c==' ' || c=='\t' || c=='"' || c=='\'' || c=='.' || c=='$') { - epicsPrintf("Error: Bad character '%c' in Record/Alias name \"%s\"\n", + fprintf(stderr, ERL_ERROR + ": Bad character '%c' in Record/Alias name \"%s\"\n", c, name); yyerrorAbort(NULL); return 1; @@ -1110,23 +1134,35 @@ static void dbRecordHead(char *recordType, char *name, int visible) allocTemp(pdbentry); if (recordType[0] == '*' && recordType[1] == 0) { - if (dbRecordsOnceOnly) - epicsPrintf("Record-type \"*\" not valid with dbRecordsOnceOnly\n"); - else { - status = dbFindRecord(pdbentry, name); - if (status == 0) - return; /* done */ - epicsPrintf("Record \"%s\" not found\n", name); - } + status = dbFindRecord(pdbentry, name); + if (status == 0) + return; /* done */ + fprintf(stderr, ERL_ERROR ": Record '%s' not found\n", name); yyerror(NULL); duplicate = TRUE; return; } + if (recordType[0] == '#' && recordType[1] == 0) { + status = dbFindRecord(pdbentry, name); + if (status == 0) { + dbDeleteRecord(pdbentry); + } else { + fprintf(stderr, ERL_WARNING + ": Record '%s' not found, can't delete\n" + " at file '%s', line %d\n", + name, pinputFileNow->filename, pinputFileNow->line_num); + } + popFirstTemp(); + dbFreeEntry(pdbentry); + duplicate = TRUE; + return; + } status = dbFindRecordType(pdbentry, recordType); if (status) { - epicsPrintf("Record \"%s\" is of unknown type \"%s\"\n", - name, recordType); + fprintf(stderr, ERL_ERROR + ": Record type '%s' for record '%s' not found\n", + recordType, name); yyerrorAbort(NULL); return; } @@ -1136,22 +1172,24 @@ static void dbRecordHead(char *recordType, char *name, int visible) status = dbCreateRecord(pdbentry,name); if (status == S_dbLib_recExists) { if (strcmp(recordType, dbGetRecordTypeName(pdbentry)) != 0) { - epicsPrintf("Record \"%s\" of type \"%s\" redefined with new type " - "\"%s\"\n", name, dbGetRecordTypeName(pdbentry), recordType); + fprintf(stderr, ERL_ERROR + ": %s record '%s' already exists, can't load %s record\n", + recordType, name, dbGetRecordTypeName(pdbentry)); yyerror(NULL); - duplicate = TRUE; return; } else if (dbRecordsOnceOnly) { - epicsPrintf("Record \"%s\" already defined (dbRecordsOnceOnly is " - "set)\n", name); + fprintf(stderr, ERL_ERROR + ": Record '%s' already defined; dbRecordsOnceOnly is set,\n" + " so can't modify record.\n", name); yyerror(NULL); duplicate = TRUE; } } else if (status) { - epicsPrintf("Can't create record \"%s\" of type \"%s\"\n", - name, recordType); + fprintf(stderr, ERL_ERROR + ": Can't create %s record '%s'\n", + recordType, name); yyerrorAbort(NULL); } @@ -1159,6 +1197,37 @@ static void dbRecordHead(char *recordType, char *name, int visible) dbVisibleRecord(pdbentry); } +/* For better suggestions for wrong field names + the following array contains pairs of often + confused fields. Thus, the number of elements + must be even. + For the last character, ranges like A-F are + allowed as a shortcut. Pairs must have matching + range size. + If extending this map, please add only field names + found in record types from base. + Each array element (i.e. both sides of a pair) + is tested against the faulty field name. + The first match (considering ranges) where the + other side of the pair is an existing field name + (after adjusting for ranges) will be suggested + as a replacement. + If no such match is found, the suggestion falls + back to weighted lexical similarity with existing + field names. +*/ + +static const char* const dbFieldConfusionMap [] = { + "INP","OUT", + "DOL","INP", + "ZNAM","ZRST", + "ONAM","ONST", + "INPA-J","DOL0-9", + "INPK-P","DOLA-F", + "INP0-9","INPA-J" +}; +STATIC_ASSERT(NELEMENTS(dbFieldConfusionMap)%2==0); + static void dbRecordField(char *name,char *value) { DBENTRY *pdbentry; @@ -1170,13 +1239,157 @@ static void dbRecordField(char *name,char *value) pdbentry = ptempListNode->item; status = dbFindField(pdbentry,name); if (status) { - epicsPrintf("Record \"%s\" does not have a field \"%s\"\n", - dbGetRecordName(pdbentry), name); + fprintf(stderr, ERL_ERROR + ": %s record '%s' doesn't have a field '%s'\n", + dbGetRecordTypeName(pdbentry), dbGetRecordName(pdbentry), name); + if(dbGetRecordName(pdbentry)) { + DBENTRY temp; + const dbFldDes *bestFld = NULL; + int i; + dbCopyEntryContents(pdbentry, &temp); + for(i = 0; i < NELEMENTS(dbFieldConfusionMap); i++) { + const char* fieldname = dbFieldConfusionMap[i]; + const char* replacement = dbFieldConfusionMap[i^1]; /* swap even with odd indices */ + const char* guess = NULL; + char buf[8]; /* no field name is so long */ + size_t l = strlen(fieldname); + if (l >= 3 && fieldname[l-2] == '-' && + strncmp(name, fieldname, l-3) == 0 && + name[l-3] >= fieldname[l-3] && + name[l-3] <= fieldname[l-1]) + { + /* range map (like XXXA-Z) */ + size_t l2 = strlen(replacement); + strncpy(buf, replacement, sizeof(buf)-1); + buf[l2-3] += name[l-3] - fieldname[l-3]; + buf[l2-2] = 0; + guess = buf; + } else if (strcmp(name, fieldname) == 0) { + /* simple map */ + guess = replacement; + } + if (guess && dbFindFieldPart(&temp, &guess) == 0) { + /* guessed field exists */ + bestFld = temp.pflddes; + break; + } + } + if (!bestFld) { + /* no map found, use weighted lexical similarity + the weights are a bit arbitrary */ + double bestSim = -1.0; + char quote = 0; + if (*value == '"' || *value == '\'') + quote = *value++; + for (status = dbFirstField(&temp, 0); !status; status = dbNextField(&temp, 0)) { + if (temp.pflddes->special == SPC_NOMOD || + temp.pflddes->special == SPC_DBADDR) /* cannot be configured */ + continue; + double sim = epicsStrSimilarity(name, temp.pflddes->name); + if (!temp.pflddes->promptgroup) + sim *= 0.5; /* no prompt: unlikely */ + if (temp.pflddes->interest) + sim *= 1.0 - 0.1 * temp.pflddes->interest; /* 10% less likely per interest level */ + if (sim == 0) + continue; + if (*value != quote) { + /* value given, check match to field type */ + long status = 0; + char* end = "e; + + switch (temp.pflddes->field_type) { + epicsAny dummy; + case DBF_CHAR: + status = epicsParseInt8(value, &dummy.int8, 0, &end); + break; + case DBF_UCHAR: + status = epicsParseUInt8(value, &dummy.uInt8, 0, &end); + break; + case DBF_SHORT: + status = epicsParseInt16(value, &dummy.int16, 0, &end); + break; + case DBF_USHORT: + case DBF_ENUM: + status = epicsParseUInt16(value, &dummy.uInt16, 0, &end); + break; + case DBF_LONG: + status = epicsParseInt32(value, &dummy.int32, 0, &end); + break; + case DBF_ULONG: + status = epicsParseUInt32(value, &dummy.uInt32, 0, &end); + break; + case DBF_INT64: + status = epicsParseInt64(value, &dummy.int64, 0, &end); + break; + case DBF_UINT64: + status = epicsParseUInt64(value, &dummy.uInt64, 0, &end); + break; + case DBF_FLOAT: + status = epicsParseFloat(value, &dummy.float32, &end); + break; + case DBF_DOUBLE: + status = epicsParseDouble(value, &dummy.float64, &end); + break; + case DBF_MENU: + case DBF_DEVICE: { + char** choices; + int nChoice; + int choice; + + if (temp.pflddes->field_type == DBF_MENU) { + dbMenu* menu = (dbMenu*)temp.pflddes->ftPvt; + choices = menu->papChoiceValue; + nChoice = menu->nChoice; + } else { + dbDeviceMenu* menu = (dbDeviceMenu*)temp.pflddes->ftPvt; + choices = menu->papChoice; + nChoice = menu->nChoice; + } + status = epicsParseUInt16(value, &dummy.uInt16, 0, &end); + if (!status && *end == quote && dummy.uInt16 < nChoice) { + if (temp.pflddes->field_type == DBF_DEVICE) + sim *= 0.5; /* numeric device type index is uncommon */ + break; + } + for (choice = 0; choice < nChoice; choice++) { + size_t len = strlen(choices[choice]); + end = value + len; + if (strncmp(value, choices[choice], len) == 0 && *end == quote) { + sim *= 1.5; /* boost for matching choice string */ + status = 0; + break; + } + } + if (choice == nChoice) + status = S_stdlib_noConversion; + break; + } + default: + break; + } + if (status || *end != quote) + sim *= 0.1; /* value type does not match field type: unlikely */ + } + if (sim > bestSim) { + bestSim = sim; + bestFld = temp.pflddes; + } + } + } + dbFinishEntry(&temp); + if (bestFld) { + fprintf(stderr, " Did you mean \"%s\"?", bestFld->name); + if(bestFld->prompt) + fprintf(stderr, " (%s)", bestFld->prompt); + fprintf(stderr, "\n"); + } + } yyerror(NULL); return; } if (pdbentry->indfield == 0) { - epicsPrintf("Can't set \"NAME\" field of record \"%s\"\n", + fprintf(stderr, ERL_ERROR + ": Can't set 'NAME' field of record '%s'\n", dbGetRecordName(pdbentry)); yyerror(NULL); return; @@ -1194,8 +1407,10 @@ static void dbRecordField(char *name,char *value) char msg[128]; errSymLookup(status, msg, sizeof(msg)); - epicsPrintf("Can't set \"%s.%s\" to \"%s\" %s : %s\n", - dbGetRecordName(pdbentry), name, value, pdbentry->message ? pdbentry->message : "", msg); + fprintf(stderr, ERL_ERROR + ": Can't set '%s.%s' to '%s' %s : %s\n", + dbGetRecordName(pdbentry), name, value, + pdbentry->message ? pdbentry->message : "", msg); dbPutStringSuggest(pdbentry, value); yyerror(NULL); return; @@ -1225,31 +1440,61 @@ static void dbRecordInfo(char *name, char *value) status = dbPutInfo(pdbentry,name,value); if (status) { - epicsPrintf("Can't set \"%s\" info \"%s\" to \"%s\"\n", - dbGetRecordName(pdbentry), name, value); + fprintf(stderr, ERL_ERROR + ": Can't set '%s' info(\"%s\") to '%s'\n", + dbGetRecordName(pdbentry), name, value); yyerror(NULL); return; } } +static long createAlias(DBENTRY *pdbentry, const char *alias) +{ + DBENTRY tempEntry; + long status; + dbRecordNode *precnode = pdbentry->precnode; + + if (precnode->aliasedRecnode) precnode = precnode->aliasedRecnode; + dbInitEntry(pdbentry->pdbbase, &tempEntry); + status = dbFindRecord(&tempEntry, alias); + if (status == 0) { + if (tempEntry.precnode->aliasedRecnode != precnode) { + if (tempEntry.precnode->aliasedRecnode) + fprintf(stderr, ERL_ERROR + ": Alias '%s' for record '%s' already aliases '%s'\n", + alias, dbGetRecordName(pdbentry), + tempEntry.precnode->aliasedRecnode->recordname); + else + fprintf(stderr, ERL_ERROR + ": Alias '%s' for record '%s' is already a record name.\n", + alias, dbGetRecordName(pdbentry)); + status = S_dbLib_recExists; + } else if (dbRecordsOnceOnly) { + fprintf(stderr, ERL_ERROR + ": Alias '%s' already defined; dbRecordsOnceOnly is set.\n", + alias); + status = S_dbLib_recExists; + } + } else { + status = dbCreateAlias(pdbentry, alias); + } + dbFinishEntry(&tempEntry); + return status; +} + static void dbRecordAlias(char *name) { DBENTRY *pdbentry; tempListNode *ptempListNode; - long status; - if(dbRecordNameValidate(name)) return; if (duplicate) return; ptempListNode = (tempListNode *)ellFirst(&tempList); pdbentry = ptempListNode->item; - status = dbCreateAlias(pdbentry, name); - if (status) { - epicsPrintf("Can't create alias \"%s\" for \"%s\"\n", - name, dbGetRecordName(pdbentry)); + + if (createAlias(pdbentry, name) != 0) { yyerror(NULL); - return; } } @@ -1263,13 +1508,12 @@ static void dbAlias(char *name, char *alias) dbInitEntry(savedPdbbase, pdbEntry); if (dbFindRecord(pdbEntry, name)) { - epicsPrintf("Alias \"%s\" refers to unknown record \"%s\"\n", - alias, name); + fprintf(stderr, ERL_ERROR + ": Alias '%s' names an unknown record '%s'\n", + alias, name); yyerror(NULL); } - else if (dbCreateAlias(pdbEntry, alias)) { - epicsPrintf("Can't create alias \"%s\" referring to \"%s\"\n", - alias, name); + else if (createAlias(pdbEntry, alias) != 0) { yyerror(NULL); } dbFinishEntry(pdbEntry); diff --git a/modules/database/src/ioc/dbStatic/dbStaticIocRegister.c b/modules/database/src/ioc/dbStatic/dbStaticIocRegister.c index 91f1ed1447..2cc96f5aa8 100644 --- a/modules/database/src/ioc/dbStatic/dbStaticIocRegister.c +++ b/modules/database/src/ioc/dbStatic/dbStaticIocRegister.c @@ -10,6 +10,7 @@ #include "iocsh.h" #include "errSymTbl.h" +#include "errlog.h" #include "dbStaticIocRegister.h" #include "dbStaticLib.h" @@ -49,7 +50,7 @@ static const iocshArg dbDumpMenuArg1 = { "menuName",iocshArgString}; static const iocshArg * const dbDumpMenuArgs[] = { &argPdbbase, &dbDumpMenuArg1}; static const iocshFuncDef dbDumpMenuFuncDef = {"dbDumpMenu",2,dbDumpMenuArgs, - "Dump information about the available menuNames and choices defined withing each menuName.\n" + "Dump information about the available menuNames and choices defined within each menuName.\n" "Example: dbDumpMenu pdbbase menuAlarmStat \n" "If last argument(s) are missing, dump all menuNames information in the database.\n"}; static void dbDumpMenuCallFunc(const iocshArgBuf *args) @@ -206,7 +207,7 @@ static const iocshFuncDef dbPvdTableSizeFuncDef = { }; static void dbPvdTableSizeCallFunc(const iocshArgBuf *args) { - dbPvdTableSize(args[0].ival); + iocshSetError(dbPvdTableSize(args[0].ival)); } /* dbReportDeviceConfig */ @@ -254,7 +255,7 @@ static void dbCreateAliasCallFunc(const iocshArgBuf *args) } dbFinishEntry(&ent); if(status) { - fprintf(stderr, "Error: %ld %s\n", status, errSymMsg(status)); + fprintf(stderr, ERL_ERROR ": %ld %s\n", status, errSymMsg(status)); iocshSetError(1); } } diff --git a/modules/database/src/ioc/dbStatic/dbStaticLib.c b/modules/database/src/ioc/dbStatic/dbStaticLib.c index 81556e0b70..08225c5c82 100644 --- a/modules/database/src/ioc/dbStatic/dbStaticLib.c +++ b/modules/database/src/ioc/dbStatic/dbStaticLib.c @@ -57,10 +57,10 @@ static char *pNullString = ""; */ STATIC_ASSERT(messagesize >= 21); -static char *ppstring[5]={" NPP"," PP"," CA"," CP"," CPP"}; -static char *msstring[4]={" NMS"," MS"," MSI"," MSS"}; +static const char *ppstring[5]={" NPP"," PP"," CA"," CP"," CPP"}; +static const char *msstring[4]={" NMS"," MS"," MSI"," MSS"}; -maplinkType pamaplinkType[LINK_NTYPES] = { +const maplinkType pamaplinkType[LINK_NTYPES] = { {"CONSTANT",CONSTANT}, {"PV_LINK",PV_LINK}, {"VME_IO",VME_IO}, @@ -89,7 +89,7 @@ static FILE *openOutstream(const char *filename) errno = 0; stream = fopen(filename,"w"); if(!stream) { - fprintf(stderr,"error opening %s %s\n",filename,strerror(errno)); + fprintf(stderr,ERL_ERROR " opening %s %s\n",filename,strerror(errno)); return 0; } return stream; @@ -637,7 +637,7 @@ void dbFinishEntry(DBENTRY *pdbentry) } } -DBENTRY * dbCopyEntry(DBENTRY *pdbentry) +DBENTRY * dbCopyEntry(const DBENTRY *pdbentry) { DBENTRY *pnew; @@ -647,7 +647,7 @@ DBENTRY * dbCopyEntry(DBENTRY *pdbentry) return(pnew); } -void dbCopyEntryContents(DBENTRY *pfrom,DBENTRY *pto) +void dbCopyEntryContents(const DBENTRY *pfrom,DBENTRY *pto) { *pto = *pfrom; pto->message = NULL; @@ -1219,7 +1219,9 @@ long dbNextRecordType(DBENTRY *pdbentry) char * dbGetRecordTypeName(DBENTRY *pdbentry) { - return(pdbentry->precordType->name); + dbRecordType *pdbRecordType = pdbentry->precordType; + if(!pdbRecordType) return NULL; + return(pdbRecordType->name); } int dbGetNRecordTypes(DBENTRY *pdbentry) @@ -1445,6 +1447,7 @@ long dbCreateRecord(DBENTRY *pdbentry,const char *precordName) pdbentry->precnode = pNewRecNode; ppvd = dbPvdAdd(pdbentry->pdbbase,precordType,pNewRecNode); if(!ppvd) {errMessage(-1,"Logic Err: Could not add to PVD");return(-1);} + pNewRecNode->order = pdbentry->pdbbase->no_records++; return(0); } @@ -1477,6 +1480,17 @@ long dbDeleteAliases(DBENTRY *pdbentry) return 0; } +static void dbDeleteRecordLinks(dbRecordType *rtyp, struct dbCommon *prec) +{ + short i; + + for (i=0; ino_links; i++) { + dbFldDes *pflddes = rtyp->papFldDes[rtyp->link_ind[i]]; + DBLINK *plink = (DBLINK *)(((char *)prec) + pflddes->offset); + dbFreeLinkContents(plink); + } +} + long dbDeleteRecord(DBENTRY *pdbentry) { dbBase *pdbbase = pdbentry->pdbbase; @@ -1492,6 +1506,7 @@ long dbDeleteRecord(DBENTRY *pdbentry) preclist = &precordType->recList; ellDelete(preclist, &precnode->node); dbPvdDelete(pdbbase, precnode); + dbDeleteRecordLinks(precordType, precnode->precord); while (!dbFirstInfo(pdbentry)) { dbDeleteInfo(pdbentry); } @@ -1686,6 +1701,7 @@ long dbCreateAlias(DBENTRY *pdbentry, const char *alias) } ellAdd(&precordType->recList, &pnewnode->node); + pnewnode->order = pdbentry->pdbbase->no_records++; precordType->no_aliases++; return 0; @@ -2198,18 +2214,18 @@ long dbInitRecordLinks(dbRecordType *rtyp, struct dbCommon *prec) if(!plink->text) continue; - if(dbParseLink(plink->text, pflddes->field_type, &link_info)!=0) { + if(dbParseLink(plink->text, pflddes->field_type, &link_info, prec->name, pflddes->name)!=0) { /* This was already parsed once when ->text was set. * Any syntax error messages were printed at that time. */ } else if(dbCanSetLink(plink, &link_info, devsup)!=0) { - errlogPrintf("Error: %s.%s: can't initialize link type %d with \"%s\" (type %d)\n", - prec->name, pflddes->name, plink->type, plink->text, link_info.ltype); + errlogPrintf(ERL_ERROR ": %s.%s: can't initialize link type %s with \"%s\" (type %s)\n", + prec->name, pflddes->name, pamaplinkType[plink->type].strvalue, plink->text, pamaplinkType[link_info.ltype].strvalue); } else if(dbSetLink(plink, &link_info, devsup)) { - errlogPrintf("Error: %s.%s: failed to initialize link type %d with \"%s\" (type %d)\n", - prec->name, pflddes->name, plink->type, plink->text, link_info.ltype); + errlogPrintf(ERL_ERROR ": %s.%s: failed to initialize link type %s with \"%s\" (type %s)\n", + prec->name, pflddes->name, pamaplinkType[plink->type].strvalue, plink->text, pamaplinkType[link_info.ltype].strvalue); } free(plink->text); plink->text = NULL; @@ -2227,7 +2243,7 @@ void dbFreeLinkInfo(dbLinkInfo *pinfo) pinfo->target = NULL; } -long dbParseLink(const char *str, short ftype, dbLinkInfo *pinfo) +long dbParseLink(const char *str, short ftype, dbLinkInfo *pinfo, const char *recname, const char *fieldname) { char *pstr; size_t len; @@ -2364,7 +2380,13 @@ long dbParseLink(const char *str, short ftype, dbLinkInfo *pinfo) /* filter modifiers based on link type */ switch(ftype) { case DBF_INLINK: /* accept all */ break; - case DBF_OUTLINK: pinfo->modifiers &= ~pvlOptCPP; break; + case DBF_OUTLINK: + if(pinfo->modifiers & (pvlOptCPP|pvlOptCP)){ + errlogPrintf(ERL_WARNING ": Discarding CP/CPP modifier in CA output link from %s.%s to %s.\n", + recname, fieldname, pinfo->target); + } + pinfo->modifiers &= ~(pvlOptCPP|pvlOptCP); + break; case DBF_FWDLINK: pinfo->modifiers &= pvlOptCA; break; } } @@ -2602,7 +2624,7 @@ long dbPutString(DBENTRY *pdbentry,const char *pstring) dbLinkInfo link_info; DBLINK *plink = (DBLINK *)pfield; - status = dbParseLink(pstring, pflddes->field_type, &link_info); + status = dbParseLink(pstring, pflddes->field_type, &link_info, dbGetRecordName(pdbentry), dbGetFieldName(pdbentry)); if (status) break; if (plink->type==CONSTANT && plink->value.constantStr==NULL) { @@ -2621,7 +2643,9 @@ long dbPutString(DBENTRY *pdbentry,const char *pstring) } } break; - + case DBF_NOACCESS: + dbMsgPrint(pdbentry, "Can't set array field before iocInit()"); + /* fall through */ default: return S_dbLib_badField; } @@ -3440,7 +3464,7 @@ void dbDumpDevice(DBBASE *pdbbase,const char *recordTypeName) " - get_ioint_info()" }; int i, n = pdevSup->pdset->number; - DEVSUPFUN *pfunc = &pdevSup->pdset->report; + DEVSUPFUN *pfunc = (DEVSUPFUN*) &pdevSup->pdset->report; printf("\t number: %d\n", n); for (i = 0; i < n; ++i, ++pfunc) { @@ -3584,7 +3608,7 @@ void dbReportDeviceConfig(dbBase *pdbbase, FILE *report) if (plink->text) { /* Not yet parsed */ dbLinkInfo linfo; - if (dbParseLink(plink->text, pdbentry->pflddes->field_type, &linfo)) + if (dbParseLink(plink->text, pdbentry->pflddes->field_type, &linfo, dbGetRecordName(pdbentry), dbGetFieldName(pdbentry))) continue; linkType = linfo.ltype; diff --git a/modules/database/src/ioc/dbStatic/dbStaticLib.h b/modules/database/src/ioc/dbStatic/dbStaticLib.h index 568517edd3..6f772f27dd 100644 --- a/modules/database/src/ioc/dbStatic/dbStaticLib.h +++ b/modules/database/src/ioc/dbStatic/dbStaticLib.h @@ -51,9 +51,9 @@ DBCORE_API void dbInitEntry(DBBASE *pdbbase, DBENTRY *pdbentry); DBCORE_API void dbFinishEntry(DBENTRY *pdbentry); -DBCORE_API DBENTRY * dbCopyEntry(DBENTRY *pdbentry); -DBCORE_API void dbCopyEntryContents(DBENTRY *pfrom, - DBENTRY *pto); +DBCORE_API DBENTRY * dbCopyEntry(const DBENTRY *pdbentry); +DBCORE_API void dbCopyEntryContents(const DBENTRY *pfrom, + DBENTRY *pto); DBCORE_API extern int dbBptNotMonotonic; diff --git a/modules/database/src/ioc/dbStatic/dbStaticPvt.h b/modules/database/src/ioc/dbStatic/dbStaticPvt.h index 033fde40c1..32287a1491 100644 --- a/modules/database/src/ioc/dbStatic/dbStaticPvt.h +++ b/modules/database/src/ioc/dbStatic/dbStaticPvt.h @@ -74,7 +74,7 @@ long dbInitRecordLinks(dbRecordType *rtyp, struct dbCommon *prec); /* Parse link string. no record locks needed. * on success caller must free pinfo->target */ -DBCORE_API long dbParseLink(const char *str, short ftype, dbLinkInfo *pinfo); +DBCORE_API long dbParseLink(const char *str, short ftype, dbLinkInfo *pinfo, const char *recordname, const char *fieldname); /* Check if link type allow the parsed link value pinfo * to be assigned to the given link. * Record containing plink must be locked. diff --git a/modules/database/src/ioc/dbStatic/dbStaticRun.c b/modules/database/src/ioc/dbStatic/dbStaticRun.c index 198ff5c1a9..a4e93d5cad 100644 --- a/modules/database/src/ioc/dbStatic/dbStaticRun.c +++ b/modules/database/src/ioc/dbStatic/dbStaticRun.c @@ -66,7 +66,17 @@ void devExtend(dsxt *pdsxt) pthisDevSup->pdsxt = pdsxt; } } - + +/* Ensure that: sizeof(dbCommonPvt) + pdbRecordType->rec_size + * accounts for necessary alignment of record type struct + */ +typedef struct { + dbCommonPvt prefix; + /* ensure no padding needed... */ + dbCommon suffix; +} dbCommonPvtAlignmentTest; +STATIC_ASSERT(offsetof(dbCommonPvtAlignmentTest, suffix)==sizeof(dbCommonPvt)); + long dbAllocRecord(DBENTRY *pdbentry,const char *precordName) { dbRecordType *pdbRecordType = pdbentry->precordType; @@ -90,8 +100,8 @@ long dbAllocRecord(DBENTRY *pdbentry,const char *precordName) precordName, pdbRecordType->name, pdbRecordType->rec_size); return(S_dbLib_noRecSup); } - ppvt = dbCalloc(1, offsetof(dbCommonPvt, common) + pdbRecordType->rec_size); - precord = &ppvt->common; + ppvt = dbCalloc(1, sizeof(dbCommonPvt) + pdbRecordType->rec_size); + precord = dbPvt2Rec(ppvt); ppvt->recnode = precnode; precord->rdes = pdbRecordType; precnode->precord = precord; @@ -143,7 +153,7 @@ long dbAllocRecord(DBENTRY *pdbentry,const char *precordName) status = dbPutStringNum(pdbentry,pflddes->initial); if(status) - epicsPrintf("Error initializing %s.%s initial %s\n", + epicsPrintf(ERL_ERROR " initializing %s.%s initial %s\n", pdbRecordType->name,pflddes->name,pflddes->initial); } break; diff --git a/modules/database/src/ioc/dbStatic/dbYacc.y b/modules/database/src/ioc/dbStatic/dbYacc.y index 75fb86cf01..aad7ca33d2 100644 --- a/modules/database/src/ioc/dbStatic/dbYacc.y +++ b/modules/database/src/ioc/dbStatic/dbYacc.y @@ -8,7 +8,7 @@ * in file LICENSE that is included with this distribution. \*************************************************************************/ %{ -static int yyerror(); +static int yyerror(char *str); static int yy_start; static long pvt_yy_parse(void); static int yyFailed = 0; @@ -370,14 +370,15 @@ json_value: jsonNULL { $$ = dbmfStrdup("null"); } static int yyerror(char *str) { if (str) - epicsPrintf("Error: %s\n", str); + fprintf(stderr, ERL_ERROR ": %s\n", str); else - epicsPrintf("Error"); + fprintf(stderr, ERL_ERROR ": "); if (!yyFailed) { /* Only print this stuff once */ - epicsPrintf(" at or before '%s'", yytext); + fprintf(stderr, " at or before '%s'", yytext); dbIncludePrint(); yyFailed = TRUE; } + fprintf(stderr, "\n %d | %s\n", pinputFileNow->line_num, my_buffer); return(0); } static long pvt_yy_parse(void) diff --git a/modules/database/src/ioc/dbStatic/link.h b/modules/database/src/ioc/dbStatic/link.h index 74607a1b11..6cd3ccd92f 100644 --- a/modules/database/src/ioc/dbStatic/link.h +++ b/modules/database/src/ioc/dbStatic/link.h @@ -43,11 +43,11 @@ extern "C" { #define VXI_IO 15 #define LINK_NTYPES 16 typedef struct maplinkType { - char *strvalue; + const char *strvalue; int value; } maplinkType; -DBCORE_API extern maplinkType pamaplinkType[]; +DBCORE_API extern const maplinkType pamaplinkType[LINK_NTYPES]; #define VXIDYNAMIC 0 #define VXISTATIC 1 @@ -72,19 +72,20 @@ DBCORE_API extern maplinkType pamaplinkType[]; /* DBLINK Flag bits */ #define DBLINK_FLAG_INITIALIZED 1 /* dbInitLink() called */ #define DBLINK_FLAG_TSELisTIME 2 /* Use TSEL to get timeStamp */ +#define DBLINK_FLAG_VISITED 4 /* Used in loop detection */ struct macro_link { char *macroStr; }; -struct dbCommon; -typedef long (*LINKCVT)(); +struct dbAddr; +typedef long (*FASTCONVERTFUNC)(const void *from, void *to, const struct dbAddr *paddr); struct pv_link { ELLNODE backlinknode; char *pvname; /* pvname link points to */ void *pvt; /* CA or DB private */ - LINKCVT getCvt; /* input conversion function */ + FASTCONVERTFUNC getCvt; /* input conversion function */ short pvlMask; /* Options mask */ short lastGetdbrType; /* last dbrType for DB or CA get */ }; @@ -188,12 +189,12 @@ union value { struct vxiio vxiio; /* vxi io */ }; +struct dbCommon; struct lset; - struct link { struct dbCommon *precord; /* Pointer to record owning link */ short type; - short flags; + unsigned short flags; struct lset *lset; char *text; /* Raw link text */ union value value; diff --git a/modules/database/src/ioc/dbtemplate/Makefile b/modules/database/src/ioc/dbtemplate/Makefile index 13b4f11fb6..1013c0abf3 100644 --- a/modules/database/src/ioc/dbtemplate/Makefile +++ b/modules/database/src/ioc/dbtemplate/Makefile @@ -14,8 +14,8 @@ SRC_DIRS += $(IOCDIR)/dbtemplate PROD_HOST += msi msi_SRCS = msi.cpp -msi_LIBS += Com -HTMLS += msi.html +msi_SYS_LIBS_WIN32 = shlwapi +DOCS += msi.md INC += dbLoadTemplate.h INC += dbtoolsIocRegister.h diff --git a/modules/database/src/ioc/dbtemplate/dbLoadTemplate.h b/modules/database/src/ioc/dbtemplate/dbLoadTemplate.h index 71aa9620a8..4644564f18 100644 --- a/modules/database/src/ioc/dbtemplate/dbLoadTemplate.h +++ b/modules/database/src/ioc/dbtemplate/dbLoadTemplate.h @@ -19,7 +19,7 @@ extern "C" { #endif DBCORE_API int dbLoadTemplate( - const char *sub_file, const char *cmd_collect); + const char *sub_file, const char *cmd_collect, const char *path); #ifdef __cplusplus } diff --git a/modules/database/src/ioc/dbtemplate/dbLoadTemplate.y b/modules/database/src/ioc/dbtemplate/dbLoadTemplate.y index b14f5b6069..26f9fab423 100644 --- a/modules/database/src/ioc/dbtemplate/dbLoadTemplate.y +++ b/modules/database/src/ioc/dbtemplate/dbLoadTemplate.y @@ -12,14 +12,19 @@ #include #include #include +#include #include "osiUnistd.h" #include "macLib.h" #include "dbmf.h" +#include "errlog.h" #include "epicsExport.h" #include "dbAccess.h" +#include "dbStaticLib.h" +#include "dbStaticPvt.h" #include "dbLoadTemplate.h" +#include "osiFileName.h" static int line_num; static int yyerror(char* str); @@ -40,6 +45,17 @@ static int var_count, sub_count; int dbTemplateMaxVars = 100; epicsExportAddress(int, dbTemplateMaxVars); +static +int msiLoadRecords(const char *fname, const char *subs) +{ + int ret = dbLoadRecords(fname, subs); + if(ret) { + fprintf(stderr, "dbLoadRecords(\"%s\", %s)\n", fname, subs); + yyerror("Error while reading included file"); + } + return ret; +} + %} %start substitution_file @@ -166,7 +182,7 @@ pattern_definition: global_definitions fprintf(stderr, "pattern_definition: pattern_values empty\n"); fprintf(stderr, " dbLoadRecords(%s)\n", sub_collect+1); #endif - dbLoadRecords(db_file_name, sub_collect+1); + if(msiLoadRecords(db_file_name, sub_collect+1)) YYABORT; } | O_BRACE pattern_values C_BRACE { @@ -174,7 +190,7 @@ pattern_definition: global_definitions fprintf(stderr, "pattern_definition:\n"); fprintf(stderr, " dbLoadRecords(%s)\n", sub_collect+1); #endif - dbLoadRecords(db_file_name, sub_collect+1); + if(msiLoadRecords(db_file_name, sub_collect+1)) YYABORT; *sub_locals = '\0'; sub_count = 0; } @@ -189,7 +205,7 @@ pattern_definition: global_definitions fprintf(stderr, "pattern_definition:\n"); fprintf(stderr, " dbLoadRecords(%s)\n", sub_collect+1); #endif - dbLoadRecords(db_file_name, sub_collect+1); + if(msiLoadRecords(db_file_name, sub_collect+1)) YYABORT; dbmfFree($1); *sub_locals = '\0'; sub_count = 0; @@ -249,7 +265,7 @@ variable_substitution: global_definitions fprintf(stderr, "variable_substitution: variable_definitions empty\n"); fprintf(stderr, " dbLoadRecords(%s)\n", sub_collect+1); #endif - dbLoadRecords(db_file_name, sub_collect+1); + if(msiLoadRecords(db_file_name, sub_collect+1)) YYABORT; } | O_BRACE variable_definitions C_BRACE { @@ -257,7 +273,7 @@ variable_substitution: global_definitions fprintf(stderr, "variable_substitution:\n"); fprintf(stderr, " dbLoadRecords(%s)\n", sub_collect+1); #endif - dbLoadRecords(db_file_name, sub_collect+1); + if(msiLoadRecords(db_file_name, sub_collect+1)) YYABORT; *sub_locals = '\0'; } | WORD O_BRACE variable_definitions C_BRACE @@ -271,7 +287,7 @@ variable_substitution: global_definitions fprintf(stderr, "variable_substitution:\n"); fprintf(stderr, " dbLoadRecords(%s)\n", sub_collect+1); #endif - dbLoadRecords(db_file_name, sub_collect+1); + if(msiLoadRecords(db_file_name, sub_collect+1)) YYABORT; dbmfFree($1); *sub_locals = '\0'; } @@ -323,10 +339,11 @@ static int yyerror(char* str) static int is_not_inited = 1; -int dbLoadTemplate(const char *sub_file, const char *cmd_collect) +int dbLoadTemplate(const char *sub_file, const char *cmd_collect, const char *path) { FILE *fp; int i; + int err; line_num = 1; @@ -337,14 +354,22 @@ int dbLoadTemplate(const char *sub_file, const char *cmd_collect) if (dbTemplateMaxVars < 1) { - fprintf(stderr,"Error: dbTemplateMaxVars = %d, must be +ve\n", + fprintf(stderr,ERL_ERROR ": dbTemplateMaxVars = %d, must be +ve\n", dbTemplateMaxVars); return -1; } fp = fopen(sub_file, "r"); + // If the file does not exist locally, and it is not an absolute path... + if (!fp && sub_file[0] != OSI_PATH_SEPARATOR[0]) { + if (!path || !*path) { + path = getenv("EPICS_DB_INCLUDE_PATH"); + } + dbPath(pdbbase, path); + dbOpenFile(pdbbase, sub_file, &fp); + } if (!fp) { - fprintf(stderr, "dbLoadTemplate: error opening sub file %s\n", sub_file); + fprintf(stderr, "dbLoadTemplate: error opening sub file %s: %s\n", sub_file, strerror(errno)); return -1; } @@ -376,7 +401,7 @@ int dbLoadTemplate(const char *sub_file, const char *cmd_collect) yyrestart(fp); } - yyparse(); + err = yyparse(); for (i = 0; i < var_count; i++) { dbmfFree(vars[i]); @@ -389,5 +414,5 @@ int dbLoadTemplate(const char *sub_file, const char *cmd_collect) dbmfFree(db_file_name); db_file_name = NULL; } - return 0; + return err; } diff --git a/modules/database/src/ioc/dbtemplate/dbtoolsIocRegister.c b/modules/database/src/ioc/dbtemplate/dbtoolsIocRegister.c index f4cfaf5ab8..5e6d234503 100644 --- a/modules/database/src/ioc/dbtemplate/dbtoolsIocRegister.c +++ b/modules/database/src/ioc/dbtemplate/dbtoolsIocRegister.c @@ -15,23 +15,24 @@ /* dbLoadTemplate */ static const iocshArg dbLoadTemplateArg0 = {"filename", iocshArgStringPath}; static const iocshArg dbLoadTemplateArg1 = {"var1=value1,var2=value2", iocshArgString}; -static const iocshArg * const dbLoadTemplateArgs[2] = { - &dbLoadTemplateArg0, &dbLoadTemplateArg1 -}; +static const iocshArg dbLoadTemplateArg2 = {"path1:path2:...", iocshArgString}; +static const iocshArg *const dbLoadTemplateArgs[3] = { + &dbLoadTemplateArg0, &dbLoadTemplateArg1, &dbLoadTemplateArg2}; static const iocshFuncDef dbLoadTemplateFuncDef = { "dbLoadTemplate", - 2, + 3, dbLoadTemplateArgs, "Load the substitution file given as first argument, apply the substitutions\n" "for each template in the substitution file, and load them using 'dbLoadRecords'.\n\n" "The second argument provides extra variables to substitute in the\n" - "template files (not the substitution file).\n\n" + "template files (not the substitution file). The third argument provides\n" + "a list of paths to search through for the substitution and template files.\n\n" "See 'help dbLoadRecords' for more information.\n\n" - "Example: dbLoadTemplate db/my.substitutions 'user=myself,host=myhost'\n", + "Example: dbLoadTemplate db/my.substitutions 'user=myself,host=myhost' 'path/to/subst:path2/to2/subst2'\n", }; static void dbLoadTemplateCallFunc(const iocshArgBuf *args) { - iocshSetError(dbLoadTemplate(args[0].sval, args[1].sval)); + iocshSetError(dbLoadTemplate(args[0].sval, args[1].sval, args[2].sval)); } diff --git a/modules/database/src/ioc/dbtemplate/msi.cpp b/modules/database/src/ioc/dbtemplate/msi.cpp index d504e3ea42..0da488cba7 100644 --- a/modules/database/src/ioc/dbtemplate/msi.cpp +++ b/modules/database/src/ioc/dbtemplate/msi.cpp @@ -27,6 +27,10 @@ #include #include +#ifdef _WIN32 +#include +#endif + #define MAX_BUFFER_SIZE 4096 #define MAX_DEPS 1024 @@ -61,7 +65,7 @@ typedef struct inputData inputData; static void inputConstruct(inputData **ppvt); static void inputDestruct(inputData * const pvt); static void inputAddPath(inputData * const pvt, const char * const pval); -static void inputBegin(inputData * const pvt, const char * const fileName); +static void inputBegin(inputData * const pvt, const char * const fileName, bool fromCmdLine); static char *inputNextLine(inputData * const pvt); static void inputNewIncludeFile(inputData * const pvt, const char * const name); static void inputErrPrint(const inputData * const pvt); @@ -83,7 +87,8 @@ static void addMacroReplacements(MAC_HANDLE * const macPvt, const char * const pval); static void makeSubstitutions(inputData * const inputPvt, MAC_HANDLE * const macPvt, - const char * const templateName); + const char * const templateName, + bool fromCmdLine); /*Global variables */ static int opt_V = 0; @@ -171,7 +176,7 @@ int main(int argc,char **argv) if (substitutionName.empty()) { STEP("Single template+substitutions file"); - makeSubstitutions(inputPvt, macPvt, templateName); + makeSubstitutions(inputPvt, macPvt, templateName, true); } else { subInfo *substitutePvt; @@ -203,7 +208,7 @@ int main(int argc,char **argv) macPushScope(macPvt); addMacroReplacements(macPvt, macStr); - makeSubstitutions(inputPvt, macPvt, filename); + makeSubstitutions(inputPvt, macPvt, filename, false); if (localScope) macPopScope(macPvt); @@ -264,7 +269,7 @@ static void addMacroReplacements(MAC_HANDLE * const macPvt, if (status) { status = macInstallMacros(macPvt, pairs); if (!status) { - fprintf(stderr, "Error from macInstallMacros\n"); + fprintf(stderr, ERL_ERROR " from macInstallMacros\n"); usageExit(1); } free(pairs); @@ -276,14 +281,15 @@ static const char *cmdNames[] = {"include","substitute"}; static void makeSubstitutions(inputData * const inputPvt, MAC_HANDLE * const macPvt, - const char * const templateName) + const char * const templateName, + bool fromCmdLine) { char *input; static char buffer[MAX_BUFFER_SIZE]; int n; ENTER; - inputBegin(inputPvt, templateName); + inputBegin(inputPvt, templateName, fromCmdLine); while ((input = inputNextLine(inputPvt))) { int expand=1; char *p; @@ -378,7 +384,7 @@ struct inputData { inputData() { memset(inputBuffer, 0, sizeof(inputBuffer) * sizeof(inputBuffer[0])); }; }; -static void inputOpenFile(inputData *pinputData, const char * const filename); +static void inputOpenFile(inputData *pinputData, const char * const filename, bool fromCmdLine); static void inputCloseFile(inputData *pinputData); static void inputCloseAllFiles(inputData *pinputData); @@ -431,11 +437,11 @@ static void inputAddPath(inputData * const pinputData, const char * const path) EXIT; } -static void inputBegin(inputData * const pinputData, const char * const fileName) +static void inputBegin(inputData * const pinputData, const char * const fileName, bool fromCmdLine) { ENTER; inputCloseAllFiles(pinputData); - inputOpenFile(pinputData, fileName); + inputOpenFile(pinputData, fileName, fromCmdLine); EXIT; } @@ -462,7 +468,7 @@ static void inputNewIncludeFile(inputData * const pinputData, const char * const name) { ENTER; - inputOpenFile(pinputData,name); + inputOpenFile(pinputData, name, false); EXIT; } @@ -493,7 +499,15 @@ static void inputErrPrint(const inputData *const pinputData) EXIT; } -static void inputOpenFile(inputData *pinputData, const char * const filename) +static int isPathRelative(const char * const path) { +#ifdef _WIN32 + return path && PathIsRelativeA(path); +#else + return path && path[0] != '/'; +#endif +} + +static void inputOpenFile(inputData *pinputData, const char * const filename, bool fromCmdLine) { std::list& pathList = pinputData->pathList; std::list::iterator pathIt = pathList.end(); @@ -505,7 +519,7 @@ static void inputOpenFile(inputData *pinputData, const char * const filename) STEP("Using stdin"); fp = stdin; } - else if (pathList.empty() || strchr(filename, '/')){ + else if (fromCmdLine || pathList.empty() || !isPathRelative(filename)){ STEPS("Opening ", filename); fp = fopen(filename, "r"); } @@ -522,7 +536,7 @@ static void inputOpenFile(inputData *pinputData, const char * const filename) } if (!fp) { - fprintf(stderr, "msi: Can't open file '%s'\n", filename); + fprintf(stderr, ERL_ERROR " msi: Can't open file '%s'\n", filename); inputErrPrint(pinputData); abortExit(1); } @@ -672,7 +686,7 @@ static void substituteOpen(subInfo **ppvt, const std::string& substitutionName) fp = fopen(substitutionName.c_str(), "r"); if (!fp) { - fprintf(stderr, "msi: Can't open file '%s'\n", substitutionName.c_str()); + fprintf(stderr, ERL_ERROR " msi: Can't open file '%s'\n", substitutionName.c_str()); abortExit(1); } diff --git a/modules/database/src/ioc/dbtemplate/msi.html b/modules/database/src/ioc/dbtemplate/msi.html deleted file mode 100644 index b5929fc79c..0000000000 --- a/modules/database/src/ioc/dbtemplate/msi.html +++ /dev/null @@ -1,445 +0,0 @@ - - - - - - - - - -

    msi: Macro Substitution and Include Tool

    - -

    Introduction

    - -

    msi is a general purpose macro substitution/include tool. It accepts as input -an ascii template file. It looks for lines containing two reserved command -names: include and substitute. It also looks for and performs -substitutions on macros of the form $(var) and ${var}. It uses the macLib -routines from EPICS Base to perform the substitutions, so it also accepts the -default value and value definition syntax that macLib implements.

    - -

    msi also allows substitutions to be specified via a separate substitution -file. This substitution file allows the same format as the substitution files -accepted by the EPICS IOC's dbLoadTemplate command.

    - -

    Command Syntax:

    - -
    msi -V -g -D -ooutfile -Idir -Msubs -Ssubfile template
    - -

    All parameters are optional. The -o, -I, -M, and -S switches may be -separated from their associated value string by spaces if desired. Output will -be written to stdout unless the -o option is given.

    - -

    Switches have the following meanings:

    - -
    -
    -V
    -
    Verbose warnings; if this parameter is specified then any undefined or - recursive macros discovered in the template will be considered an error and - will be marked in the output file. An error message will be shown, and when - msi terminates it will do so with an exit status of 2.
    - -
    -g
    -
    When this flag is given all macros defined in a substitution file will - have global scope and thus their values will persist until a new value is - given for this macro. This flag is provided for backwards compatibility as - this was the behavior of previous versions of msi, but it does not follow - common scoping rules and is discouraged.
    - -
    -D
    -
    Output dependency information suitable for including by a Makefile to - stdout instead of performing the macro substitutions. The -o option - must be given to specify the target name for the dependency rules. Other - options should be given exactly as will be used in the macro substitution - process.
    - -
    -o file
    -
    Output will be written to the specifed file rather than to the standard - output.
    - -
    -I dir
    -
    This parameter, which may be repeated or contain a colon-separated (or - semi-colon separated on Windows) list of directory paths, specifies a search - path for include commands. For example: - -
    -
    msi -I /home/mrk/examples:. -I.. template
    -
    - - specifies that all named files should be searched for in the following - locations in the order given: - -
      -
    1. /home/mrk/examples
    2. -
    3. . (the current directory)
    4. -
    5. .. (the parent of the current directory)
    6. -
    -
    - -
    -M substitutions
    -
    This parameter specifies macro values for the template instance. - Multiple macro values can be specified in one substitution parameter, or in - multiple -M parameters. For example: - -
    -
    msi -M "a=aval,b=bval" -Mc=cval template
    -
    - - specifies that in the template file each occurrence of: - -
    -
    $(a) or ${a} is replaced by aval
    -
    $(b) or ${b} is replaced by bval
    -
    $(c) or ${c} is replaced by cval
    -
    -
    - -
    -S subfile
    -
    The substitution file. See below for format.
    - -
    template
    -
    The input file. If no file is specified then input is taken from - stdin, i.e. msi can be used as a filter. See below for a description of - commands that can be embedded in the template file.
    -
    - -

    It is not possible to display usage by just typing msi since -executing the command with no arguments is a valid command. To show usage -specify an illegal switch, e.g.

    - -
    -
    msi -help
    -
    - -

    Exit Status

    - -
    -
    0
    Success. -
    1
    Can't open/create file, or other I/O error. -
    2
    Undefined macros encountered with the -V option specified. -
    - -

    Template File Format

    - -

    This file contains the text to be read and written to the output after macro -substitution is performed. If no file is given then input is read from stdin. -Variable instances to be substituted by macro values are expressed in the -template using the syntax $(name) or -${name}. The template can also provide default values -to be used when a macro has not been given a value, using the syntax -$(name=default) or -${name=default}.

    - -

    For example, using the command

    - -
    -
    msi -M name=Marty template
    -
    - -

    where the file template contains

    - -
    -
    My name is $(name)
    -My age is $(age=none of your business)
    -
    - -

    results in this output:

    - -
    -
    My name is Marty
    -My age is none of your business
    -
    - -

    Macro variables and their default values can be expressed in terms of other -macros if necessary, to almost any level of complexity. Recursive definitions -will generate warning messages on stderr and result in undefined output.

    - -

    The template file is read and processed one line at a time, where the -maximum length of a line before and/or after macro expansion is 1023 characters -— longer input or output lines will cause msi to fail. Within the context -of a single line, macro expansion does not occur when the variable instance -appears inside a single-quoted string, or where the dollar sign $ is -preceded by a back-slash character \, but as with the standard Unix -shells, variables inside double quoted strings are expanded properly.

    - -

    However neither back-slash characters nor quotes of either variety are -removed when generating the output file, so depending on what is being output -the single quote behaviour may not be useful and may even be a hinderance. It -cannot be disabled in the current version of msi.

    - -

    Template file commands

    - -

    In addition to the regular text and variable instances described above, the -template file may also contain commands which allow the insertion of other -template files and the ability to set macro values inside the template file -itself. These commands are:

    - -
    -
    include "file"
    -substitute "var=value,var=value,..."
    -
    - -

    Lines containing commands must be in one of these forms:

    - -
      -
    • include "filename"
    • -
    • substitute "name1=value1, name2=value2, ..."
    • -
    - -

    White space is allowed before and after the command verb, and after the -quoted string. If embedded quotes are needed, the backslash character -\ can be used as an escape character. For example

    - -
    -
    substitute "a=\"val\""
    -
    - -

    specifies that (unless a is subsequently redefined) wherever a -$(a) macro appears in the template below this point, the text -"val" (including the double quote characters) will appear in the -output instead.

    - -

    If a line does match either syntax above it is just passed to macLib for -processing without any notification. Thus the input line:

    - -
    -
    include "myfile" #include file
    -
    - -

    would just be passed to macLib, i.e. it would not be considered an -include command.

    - -

    As an example of these commands, let the Unix command be:

    - -
    -
    msi template
    -
    - -

    and file includeFile contain:

    - -
    -
    first name is ${first}
    -family name is ${family}
    -
    - -

    and template is

    - -
    -
    substitute "first=Marty,family=Kraimer"
    -include "includeFile"
    -substitute "first=Irma,family=Kraimer"
    -include "includeFile"
    -
    - -

    then the following is written to the output.

    - -
    -
    first name is Marty
    -family name is Kraimer
    -first name is Irma
    -family name is Kraimer
    -
    - -

    Note that the IOC's dbLoadTemplate command does not support the -substitute syntax in template files, although the include -syntax is supported.

    - -

    Substitution File Format

    - -

    The optional substitution file has three formats: regular, pattern, and -dbTemplate format. We will discuss each separately.

    - -

    Regular format

    - -
    -
    global {gbl_var1=gbl_val1, gbl_var2=gbl_val2, ...}
    -{var1=set1_val1, var2=set1_val2, ...}
    -{var2=set2_val2, var1=set2_val1, ...}
    -global {gbl_var1=gbl_val3, gbl_var2=gbl_val4, ...}
    -{var1=set3_val1, var2=set3_val2, ...}
    -{var2=set4_val2, var1=set4_val1, ...}
    -
    - -

    The template file is output with macro substitutions performed once for each -set of braces containing macro replacement values.

    - -

    Pattern format

    - -
    -
    global {gbl_var1=gbl_val1, gbl_var2=gbl_val2, ...}
    -pattern {var1, var2, ...}
    -{set1_val1, set1_val2, ...}
    -{set2_val1, set2_val2, ...}
    -pattern {var2, var1, ...}
    -global {gbl_var1=gbl_val3, gbl_var2=gbl_val4, ...}
    -{set3_val2, set3_val1, ...}
    -{set4_val2, set4_val2, ...}
    -
    - -

    This produces the same result as the regular format example above.

    - -

    dbLoadTemplate Format

    - -

    This format is an extension of the format accepted by the EPICS IOC command -dbLoadTemplate, and allows templates to be expanded on the host rather -by using dbLoadTemplate at IOC boot time.

    - -
    -
    global {gbl_var1=gbl_val1, gbl_var2=gbl_val2, ...}
    -file templatefile {
    -    pattern format or regular format
    -}
    -file "${WHERE}/template2" {
    -    pattern format or regular format
    -}
    -
    - -

    For the dbTemplate format, the template filename does not have to be given -on the command line, and is usually specified in the substitutions file -instead. If a template filename is given on the command line it will override -the filenames listed in the substitutions files.

    - -

    Syntax for all formats

    - -

    A comment line may appear anywhere in a substitution file, and will be -ignored. A comment line is any line beginning with the character #, -which must be the very first character on the line.

    - -

    Global definitions may supplement or override the macro values supplied on -the command-line using the -M switch, and set default values that will -survive for the remainder of the file unless another global definition of the -same macro changes it.

    - -

    For definitions within braces given in any of the file formats, a separator -must be given between items. A separator is either a comma, or one or more of -the standard white space characters (space, formfeed, newline, carriage return, -tab or vertical tab).

    - -

    Each item within braces can be an alphanumeric token, or a double-quoted -string. A back-slash character \ can be used to escape a quote -character needed inside a quoted string. These three sets of substitutions are -all equivalent:

    - -
    -
    {a=aa b=bb c="\"cc\""}
    -{b="bb",a=aa,c="\"cc\""}
    -{
    -    c="\"cc\""
    -    b=bb
    -    a="aa"
    -}
    -
    - -

    Within a substitutions file, the file name may appear inside double quotation -marks; these are required if the name contains certain characters or environment -variable macros of the form ${ENV_VAR} or $(ENV_VAR), which will be expanded -before the file is opened.

    - -

    Regular substitution example

    - -

    Let the command be:

    - -
    -
    msi -S substitute template
    -
    - -

    The file template contains

    - -
    -
    first name is ${first}
    -family name is ${family}
    -
    - -

    and the file substitute is

    - -
    -
    global {family=Kraimer}
    -{first=Marty}
    -{first=Irma}
    -
    - -

    The following is the output produced:

    - -
    -
    first name is Marty
    -family name is Kraimer
    -first name is Irma
    -family name is Kraimer
    -
    - -

    Pattern substitution example

    - -

    Let the command be:

    - -
    -
    msi -S pattern template
    -
    - -

    The file pattern contains

    - -
    -
    pattern {first,last}
    -{Marty,Kraimer}
    -{Irma,Kraimer}
    -
    - -

    and template is the same as the previous example:

    - -
    -
    first name is ${first}
    -family name is ${family}
    -
    - -

    This is the output:

    - -
    -
    first name is Marty
    -family name is Kraimer
    -first name is Irma
    -family name is Kraimer
    -
    - -

    dbTemplate example

    -Let the command be - -
    -
    msi -S xxx.substitutions
    -
    - -xxx.substitutions is - -
    -
    file template {
    -pattern {first,last}
    -{Marty,Kraimer}
    -{Irma,Kraimer}
    -pattern {last,first}
    -{Smith,Bill}
    -{Smith,Mary}
    -}
    -file template {
    -{first=Marty,last=Kraimer}
    -{first=Irma,last=Kraimer}
    -}
    -
    -template is the same as in the previous example.. - -

    The following is written to the output

    - -
    -
    first name is Marty
    -family name is Kraimer
    -first name is Irma
    -family name is Kraimer
    -first name is Bill
    -last name is Smith
    -first name is Mary
    -last name is Smith
    -first name is Marty
    -family name is Kraimer
    -first name is Irma
    -family name is Kraimer
    -
    - - - diff --git a/modules/database/src/ioc/dbtemplate/msi.md b/modules/database/src/ioc/dbtemplate/msi.md new file mode 100644 index 0000000000..8c1b92ec20 --- /dev/null +++ b/modules/database/src/ioc/dbtemplate/msi.md @@ -0,0 +1,410 @@ +# msi: Macro Substitution and Include Tool + +``` {program} msi +``` + +(msitool)= +## Introduction + +msi is a general purpose macro substitution/include tool. +It accepts as input an ascii template file. It looks for lines containing two reserved +command names: `include` and `substitute`. It also looks for and performs +substitutions on macros of the form `$(var)` and `${var}`. It uses the +macLib routines from EPICS Base to perform the substitutions, so it also +accepts the default value and value definition syntax that macLib +implements. + +msi also allows substitutions to be specified via a separate +substitution file. This substitution file allows the same format as the +substitution files accepted by the EPICS IOC's dbLoadTemplate command. + + +## Command Syntax + +``` bash +msi -V -g -o outfile -I dir -M subs -S subfile template +``` + +All parameters are optional. The -o, -I, -M, and -S switches may be +separated from their associated value string by spaces if desired. +Output will be written to stdout unless the -o option is given. + +Switches have the following meanings: + +::: {option} -V +Verbose warnings; if this parameter is specified then any undefined +macro discovered in the template file which does not have an +associated default value is considered an error. An error message is +generated, and when msi terminates it will do so with an exit status +of 2. +::: + +::: {option} -g +When this flag is given all macros defined in a substitution file +will have global scope and thus their values will persist until a +new value is given for this macro. This flag is provided for +backwards compatibility as this was the behavior of previous +versions of msi, but it does not follow common scoping rules and is +discouraged. +::: + +::: {option} -o +Output will be written to the specified \ rather than to the +standard output. +::: + +::: {option} -I +This parameter, which may be repeated or contain a colon-separated +(or semi-colon separated on Windows) list of directory paths, +specifies a search path for include commands. For example: + +``` bash +msi -I /home/mrk/examples:. -I.. template +``` + +specifies that all named files should be searched for in the following locations, +in the order given: + +1. `/home/mrk/examples` +2. `.` (the current directory) +3. `..` (the parent of the current directory) + +Note that relative path searching is handled as + + $ cat foo.substitutions + file rel/path/bar.template { + # contents + } + $ msi -I . -I /some/path foo.substitutions + +which will try to find `bar.template` at the path `./rel/path/` followed by +`/some/path/rel/path`. +::: + +::: {option} -M +This parameter specifies macro values for the template instance. +Multiple macro values can be specified in one substitution +parameter, or in multiple -M parameters. For example: + +``` bash +msi -M "a=aval,b=bval" -Mc=cval template +``` + +specifies that in the template file each occurrence of: + +- `$(a)` or `${a}` is replaced by _aval_ +- `$(b)` or `${b}` is replaced by _bval_ +- `$(c)` or `${c}` is replaced by _cval_ +::: + +::: {option} -S +The substitution file. See below for format. +::: + +::: {option}