Merge remote-tracking branch 'origin/7.0' into rtems5
This commit is contained in:
@ -31,6 +31,8 @@ skip_commits:
|
||||
- 'documentation/*'
|
||||
- 'startup/*'
|
||||
- '.github/*'
|
||||
- '.tools/*'
|
||||
- '.gitattributes'
|
||||
- '**/*.html'
|
||||
- '**/*.md'
|
||||
|
||||
|
@ -38,6 +38,8 @@ skip_commits:
|
||||
- 'documentation/*'
|
||||
- 'startup/*'
|
||||
- '.github/*'
|
||||
- '.tools/*'
|
||||
- '.gitattributes'
|
||||
- '**/*.html'
|
||||
- '**/*.md'
|
||||
|
||||
|
3
.gitattributes
vendored
3
.gitattributes
vendored
@ -1,5 +1,6 @@
|
||||
.ci/ export-ignore
|
||||
.tools/ export-ignore
|
||||
.github/ export-ignore
|
||||
.appveyor/ export-ignore
|
||||
.appveyor.yml export-ignore
|
||||
.travis.yml export-ignore
|
||||
README export-subst
|
||||
|
22
.github/workflows/ci-scripts-build.yml
vendored
22
.github/workflows/ci-scripts-build.yml
vendored
@ -14,9 +14,19 @@ on:
|
||||
- 'documentation/*'
|
||||
- 'startup/*'
|
||||
- '.appveyor/*'
|
||||
- '.tools/*'
|
||||
- '.gitattributes'
|
||||
- '**/*.html'
|
||||
- '**/*.md'
|
||||
pull_request:
|
||||
paths-ignore:
|
||||
- 'documentation/*'
|
||||
- 'startup/*'
|
||||
- '.appveyor/*'
|
||||
- '.tools/*'
|
||||
- '.gitattributes'
|
||||
- '**/*.html'
|
||||
- '**/*.md'
|
||||
|
||||
env:
|
||||
SETUP_PATH: .ci-local:.ci
|
||||
@ -165,6 +175,12 @@ jobs:
|
||||
configuration: default
|
||||
name: "Ub-20 gcc-8"
|
||||
|
||||
- os: ubuntu-20.04
|
||||
cmp: gcc-9
|
||||
utoolchain: "9"
|
||||
configuration: default
|
||||
name: "Ub-20 gcc-9"
|
||||
|
||||
- os: ubuntu-20.04
|
||||
cmp: clang
|
||||
configuration: default
|
||||
@ -185,6 +201,11 @@ jobs:
|
||||
configuration: static
|
||||
name: "Win2019 MSC-19, static"
|
||||
|
||||
- os: windows-2019
|
||||
cmp: gcc
|
||||
configuration: default
|
||||
name: "Win2019 mingw"
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
@ -211,6 +232,7 @@ jobs:
|
||||
- name: Run main module tests
|
||||
run: python .ci/cue.py test
|
||||
- name: Upload tapfiles Artifact
|
||||
if: ${{ always() }}
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: tapfiles ${{ matrix.name }}
|
||||
|
@ -48,11 +48,11 @@ EPICS_VERSION = 7
|
||||
EPICS_REVISION = 0
|
||||
|
||||
# EPICS_MODIFICATION must be a number >=0 and <256
|
||||
EPICS_MODIFICATION = 4
|
||||
EPICS_MODIFICATION = 5
|
||||
|
||||
# EPICS_PATCH_LEVEL must be a number (win32 resource file requirement)
|
||||
# Not included in the official EPICS version number if zero
|
||||
EPICS_PATCH_LEVEL = 2
|
||||
EPICS_PATCH_LEVEL = 1
|
||||
|
||||
# Immediately after an official release the EPICS_PATCH_LEVEL is incremented
|
||||
# and the -DEV suffix is added (similar to the Maven -SNAPSHOT versions)
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
EPICS_CA_MAJOR_VERSION = 4
|
||||
EPICS_CA_MINOR_VERSION = 13
|
||||
EPICS_CA_MAINTENANCE_VERSION = 8
|
||||
EPICS_CA_MAINTENANCE_VERSION = 9
|
||||
|
||||
# Development flag, set to zero for release versions
|
||||
|
||||
|
@ -1,8 +1,8 @@
|
||||
# Version number for the database APIs and shared library
|
||||
|
||||
EPICS_DATABASE_MAJOR_VERSION = 3
|
||||
EPICS_DATABASE_MINOR_VERSION = 18
|
||||
EPICS_DATABASE_MAINTENANCE_VERSION = 2
|
||||
EPICS_DATABASE_MINOR_VERSION = 19
|
||||
EPICS_DATABASE_MAINTENANCE_VERSION = 1
|
||||
|
||||
# Development flag, set to zero for release versions
|
||||
|
||||
|
@ -1,8 +1,8 @@
|
||||
# Version number for the libcom APIs and shared library
|
||||
|
||||
EPICS_LIBCOM_MAJOR_VERSION = 3
|
||||
EPICS_LIBCOM_MINOR_VERSION = 18
|
||||
EPICS_LIBCOM_MAINTENANCE_VERSION = 2
|
||||
EPICS_LIBCOM_MINOR_VERSION = 19
|
||||
EPICS_LIBCOM_MAINTENANCE_VERSION = 1
|
||||
|
||||
# Development flag, set to zero for release versions
|
||||
|
||||
|
@ -196,12 +196,12 @@ endif
|
||||
#---------------------------------------------------------------
|
||||
# build dependancies, clean rule
|
||||
|
||||
inc: $(COMMON_INC) $(INSTALL_INC)
|
||||
inc: $(COMMON_INC) $(INSTALL_INC) $(COMMON_DBDS) $(COMMON_DBDCATS) \
|
||||
$(INSTALL_DBDS) $(INSTALL_DBD_INSTALLS)
|
||||
|
||||
build: $(COMMON_DBDS) $(COMMON_DBS) $(COMMON_DBDCATS) \
|
||||
$(INSTALL_DBDS) $(INSTALL_DBS) \
|
||||
build: $(COMMON_DBS) $(INSTALL_DBS) \
|
||||
$(DBDDEPENDS_FILES) $(TARGETS) \
|
||||
$(INSTALL_DB_INSTALLS) $(INSTALL_DBD_INSTALLS)
|
||||
$(INSTALL_DB_INSTALLS)
|
||||
|
||||
clean: db_clean
|
||||
|
||||
|
@ -103,17 +103,20 @@ include $(CONFIG)/RULES.Db
|
||||
#---------------------------------------------------------------
|
||||
# Include defines and rules for prod, library and test* targets
|
||||
|
||||
#ifneq (,$(strip $(PROD) $(TESTPROD) $(LIBRARY) $(TESTLIBRARY) $(LOADABLE_LIBRARY) ))
|
||||
ifneq (,$(strip $(PROD) $(TESTPROD) $(LIBRARY) $(TESTLIBRARY) \
|
||||
$(LOADABLE_LIBRARY)))
|
||||
include $(CONFIG)/RULES_TARGET
|
||||
#endif
|
||||
endif
|
||||
|
||||
#---------------------------------------------------------------
|
||||
# Read dependency files
|
||||
|
||||
ifneq (inc,$(strip $(MAKECMDGOALS)))
|
||||
ifneq (,$(strip $(HDEPENDS_FILES)))
|
||||
$(filter-out $(wildcard *$(DEP)), $(HDEPENDS_FILES)): | $(COMMON_INC)
|
||||
-include $(HDEPENDS_FILES)
|
||||
endif
|
||||
endif
|
||||
|
||||
#---------------------------------------------------------------
|
||||
# Products and Object libraries
|
||||
@ -161,12 +164,13 @@ build: inc
|
||||
build: $(OBJSNAME) $(LIBTARGETS) $(PRODTARGETS) $(TESTPRODTARGETS) \
|
||||
$(TARGETS) $(TESTSCRIPTS) $(INSTALL_LIB_INSTALLS)
|
||||
|
||||
inc: $(COMMON_INC) $(INSTALL_INC) $(INSTALL_CONFIGS)
|
||||
inc: $(COMMON_INC) $(INSTALL_INC) $(INSTALL_CONFIGS) $(INSTALLS_CFG) \
|
||||
$(INSTALL_HTMLS) $(INSTALLS_PERL_MODULES) $(INSTALL_SCRIPTS)
|
||||
|
||||
buildInstall: \
|
||||
$(INSTALL_SCRIPTS) $(INSTALL_PROD) $(INSTALL_MUNCHS) \
|
||||
$(INSTALL_PROD) $(INSTALL_MUNCHS) \
|
||||
$(INSTALL_TCLLIBS) $(INSTALL_TCLINDEX) \
|
||||
$(INSTALL_HTMLS) $(INSTALL_DOCS) \
|
||||
$(INSTALL_DOCS) \
|
||||
$(INSTALL_OBJS) \
|
||||
$(INSTALL_TEMPLATE) \
|
||||
$(INSTALL_BIN_INSTALLS)
|
||||
|
@ -21,7 +21,6 @@ $(foreach target, $(PROD) $(TESTPROD) $(LIBRARY) $(TESTLIBRARY) $(LOADABLE_LIBRA
|
||||
|
||||
#-----------------------------------------------------------------------
|
||||
|
||||
# This define block requires GNU make 3.81
|
||||
define PROD_template
|
||||
ifeq ($$(strip $$($(1)_OBJS) $$($(1)_SRCS) $$(PRODUCT_OBJS)),)
|
||||
$(1)_OBJS = $(1)$$(OBJ)
|
||||
|
@ -73,7 +73,7 @@ help:
|
||||
@echo "Usage: gnumake [options] [target] ..."
|
||||
@echo "Targets supported by all Makefiles:"
|
||||
@echo " all - Same as install (default rule)"
|
||||
@echo " inc - Installs header files"
|
||||
@echo " inc - Installs header, dbd and html files"
|
||||
@echo " build - Builds and installs all targets"
|
||||
@echo " install - Builds and installs all targets"
|
||||
@echo " buildInstall - Same as install (deprecated)"
|
||||
|
@ -107,7 +107,7 @@ BRIEF_MEMBER_DESC = YES
|
||||
# brief descriptions will be completely suppressed.
|
||||
# The default value is: YES.
|
||||
|
||||
REPEAT_BRIEF = YES
|
||||
REPEAT_BRIEF = NO
|
||||
|
||||
# This tag implements a quasi-intelligent brief description abbreviator that is
|
||||
# used to form the text in various listings. Each string in this list, if found
|
||||
@ -143,7 +143,7 @@ ALWAYS_DETAILED_SEC = NO
|
||||
# operators of the base classes will not be shown.
|
||||
# The default value is: NO.
|
||||
|
||||
INLINE_INHERITED_MEMB = NO
|
||||
INLINE_INHERITED_MEMB = YES
|
||||
|
||||
# If the FULL_PATH_NAMES tag is set to YES doxygen will prepend the full path
|
||||
# before files name in the file list and in the header files. If set to NO the
|
||||
@ -162,8 +162,8 @@ FULL_PATH_NAMES = YES
|
||||
# will be relative from the directory where doxygen is started.
|
||||
# This tag requires that the tag FULL_PATH_NAMES is set to YES.
|
||||
|
||||
STRIP_FROM_PATH = \
|
||||
../
|
||||
STRIP_FROM_PATH = @TOP@/include \
|
||||
..
|
||||
|
||||
# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the
|
||||
# path mentioned in the documentation of a class, which tells the reader which
|
||||
@ -172,7 +172,8 @@ STRIP_FROM_PATH = \
|
||||
# specify the list of include paths that are normally passed to the compiler
|
||||
# using the -I flag.
|
||||
|
||||
STRIP_FROM_INC_PATH = ../
|
||||
STRIP_FROM_INC_PATH = @TOP@/include \
|
||||
..
|
||||
|
||||
# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but
|
||||
# less readable) file names. This can be useful is your file systems doesn't
|
||||
@ -227,7 +228,7 @@ SEPARATE_MEMBER_PAGES = NO
|
||||
# uses this value to replace tabs by spaces in code fragments.
|
||||
# Minimum value: 1, maximum value: 16, default value: 4.
|
||||
|
||||
TAB_SIZE = 4
|
||||
TAB_SIZE = 8
|
||||
|
||||
# This tag can be used to specify a number of aliases that act as commands in
|
||||
# the documentation. An alias has the form:
|
||||
@ -241,12 +242,6 @@ TAB_SIZE = 4
|
||||
|
||||
ALIASES =
|
||||
|
||||
# This tag can be used to specify a number of word-keyword mappings (TCL only).
|
||||
# A mapping has the form "name=value". For example adding "class=itcl::class"
|
||||
# will allow you to use the command class in the itcl::class meaning.
|
||||
|
||||
TCL_SUBST =
|
||||
|
||||
# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources
|
||||
# only. Doxygen will then generate output that is more tailored for C. For
|
||||
# instance, some of the names that are used will be different. The list of all
|
||||
@ -420,7 +415,7 @@ LOOKUP_CACHE_SIZE = 0
|
||||
# normally produced when WARNINGS is set to YES.
|
||||
# The default value is: NO.
|
||||
|
||||
EXTRACT_ALL = YES
|
||||
EXTRACT_ALL = NO
|
||||
|
||||
# If the EXTRACT_PRIVATE tag is set to YES all private members of a class will
|
||||
# be included in the documentation.
|
||||
@ -548,7 +543,7 @@ INLINE_INFO = YES
|
||||
# name. If set to NO the members will appear in declaration order.
|
||||
# The default value is: YES.
|
||||
|
||||
SORT_MEMBER_DOCS = YES
|
||||
SORT_MEMBER_DOCS = NO
|
||||
|
||||
# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief
|
||||
# descriptions of file, namespace and class members alphabetically by member
|
||||
@ -767,7 +762,8 @@ WARN_LOGFILE =
|
||||
INPUT = ../mainpage.dox \
|
||||
../RELEASE_NOTES.md \
|
||||
../README.md \
|
||||
../RecordReference.md
|
||||
../RecordReference.md \
|
||||
@TOP@/include
|
||||
|
||||
# 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
|
||||
@ -787,48 +783,10 @@ INPUT_ENCODING = UTF-8
|
||||
# *.md, *.mm, *.dox, *.py, *.f90, *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf,
|
||||
# *.qsf, *.as and *.js.
|
||||
|
||||
FILE_PATTERNS = *.c \
|
||||
*.cc \
|
||||
*.cxx \
|
||||
*.cpp \
|
||||
*.c++ \
|
||||
*.java \
|
||||
*.ii \
|
||||
*.ixx \
|
||||
*.ipp \
|
||||
*.i++ \
|
||||
*.inl \
|
||||
*.idl \
|
||||
*.ddl \
|
||||
*.odl \
|
||||
*.h \
|
||||
*.hh \
|
||||
*.hxx \
|
||||
FILE_PATTERNS = *.h \
|
||||
*.hpp \
|
||||
*.h++ \
|
||||
*.cs \
|
||||
*.d \
|
||||
*.php \
|
||||
*.php4 \
|
||||
*.php5 \
|
||||
*.phtml \
|
||||
*.inc \
|
||||
*.m \
|
||||
*.markdown \
|
||||
*.md \
|
||||
*.mm \
|
||||
*.dox \
|
||||
*.py \
|
||||
*.f90 \
|
||||
*.f \
|
||||
*.for \
|
||||
*.tcl \
|
||||
*.vhd \
|
||||
*.vhdl \
|
||||
*.ucf \
|
||||
*.qsf \
|
||||
*.as \
|
||||
*.js
|
||||
*.dox
|
||||
|
||||
# The RECURSIVE tag can be used to specify whether or not subdirectories should
|
||||
# be searched for input files as well.
|
||||
@ -843,7 +801,8 @@ RECURSIVE = YES
|
||||
# Note that relative paths are relative to the directory from which doxygen is
|
||||
# run.
|
||||
|
||||
EXCLUDE =
|
||||
EXCLUDE = @TOP@/include/pv \
|
||||
@TOP@/include/pva
|
||||
|
||||
# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or
|
||||
# directories that are symbolic links (a Unix file system feature) are excluded
|
||||
@ -859,7 +818,7 @@ EXCLUDE_SYMLINKS = NO
|
||||
# Note that the wildcards are matched against the file with absolute path, so to
|
||||
# exclude all test directories for example use the pattern */test/*
|
||||
|
||||
EXCLUDE_PATTERNS = O.*
|
||||
EXCLUDE_PATTERNS = /O.*/
|
||||
|
||||
# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names
|
||||
# (namespaces, classes, functions, etc.) that should be excluded from the
|
||||
@ -2028,7 +1987,7 @@ 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 =
|
||||
PREDEFINED = __cplusplus
|
||||
|
||||
# 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
|
||||
@ -2047,7 +2006,7 @@ EXPAND_AS_DEFINED =
|
||||
# The default value is: YES.
|
||||
# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
|
||||
|
||||
SKIP_FUNCTION_MACROS = YES
|
||||
SKIP_FUNCTION_MACROS = NO
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
# Configuration options related to external references
|
||||
@ -2094,12 +2053,6 @@ EXTERNAL_GROUPS = YES
|
||||
|
||||
EXTERNAL_PAGES = YES
|
||||
|
||||
# The PERL_PATH should be the absolute path and name of the perl script
|
||||
# interpreter (i.e. the result of 'which perl').
|
||||
# The default file (with absolute path) is: /usr/bin/perl.
|
||||
|
||||
PERL_PATH = /usr/bin/perl
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
# Configuration options related to the dot tool
|
||||
#---------------------------------------------------------------------------
|
||||
@ -2113,15 +2066,6 @@ PERL_PATH = /usr/bin/perl
|
||||
|
||||
CLASS_DIAGRAMS = YES
|
||||
|
||||
# You can define message sequence charts within doxygen comments using the \msc
|
||||
# command. Doxygen will then run the mscgen tool (see:
|
||||
# http://www.mcternan.me.uk/mscgen/)) to produce the chart and insert it in the
|
||||
# documentation. The MSCGEN_PATH tag allows you to specify the directory where
|
||||
# the mscgen tool resides. If left empty the tool is assumed to be found in the
|
||||
# default search path.
|
||||
|
||||
MSCGEN_PATH =
|
||||
|
||||
# You can include diagrams made with dia in doxygen documentation. Doxygen will
|
||||
# then run dia to produce the diagram and insert it in the documentation. The
|
||||
# DIA_PATH tag allows you to specify the directory where the dia binary resides.
|
||||
|
@ -1,22 +1,24 @@
|
||||
TOP = ..
|
||||
include $(TOP)/configure/CONFIG
|
||||
|
||||
ifdef T_A
|
||||
ifeq ($(T_A),$(EPICS_HOST_ARCH))
|
||||
|
||||
DOXYGEN=doxygen
|
||||
|
||||
EXPAND = Doxyfile
|
||||
EXPAND = Doxyfile@
|
||||
|
||||
EXPAND_ME += EPICS_VERSION
|
||||
EXPAND_ME += EPICS_REVISION
|
||||
EXPAND_ME += EPICS_MODIFICATION
|
||||
EXPAND_ME += EPICS_PATCH_LEVEL
|
||||
EXPAND_ME += OS_CLASS CMPLR_CLASS
|
||||
|
||||
ME = documentation/O.$(T_A)/html
|
||||
GH_FILES = $(ME)/ $(ME)/.nojekyll $(ME)/*.* $(ME)/*/*.*
|
||||
|
||||
install: doxygen
|
||||
|
||||
doxygen: Doxyfile
|
||||
doxygen: Doxyfile ../mainpage.dox
|
||||
$(DOXYGEN)
|
||||
rsync -av $(TOP)/html/ html/
|
||||
|
||||
@ -24,10 +26,10 @@ doxygen: Doxyfile
|
||||
|
||||
commit: doxygen
|
||||
$(TOUCH) html/.nojekyll
|
||||
(cd $(TOP) && $(CURDIR)/../commit-gh.sh $(ME)/ $(ME)/.nojekyll $(ME)/*.* $(ME)/*/*.*)
|
||||
(cd $(TOP) && $(CURDIR)/../commit-gh.sh $(GH_FILES))
|
||||
|
||||
.PHONY: commit
|
||||
|
||||
endif # T_A
|
||||
endif # EPICS_HOST_ARCH
|
||||
|
||||
include $(TOP)/configure/RULES
|
||||
|
@ -1,6 +1,6 @@
|
||||
# Installation Instructions {#install}
|
||||
|
||||
## EPICS Base Release 7.0.4.1
|
||||
## EPICS Base Release 7.0.5
|
||||
|
||||
-----
|
||||
|
||||
@ -12,7 +12,6 @@
|
||||
- [Supported platforms](#0_0_4)
|
||||
- [Supported compilers](#0_0_5)
|
||||
- [Software requirements](#0_0_6)
|
||||
- [Host system storage requirements](#0_0_7)
|
||||
- [Documentation](#0_0_8)
|
||||
- [Directory Structure](#0_0_10)
|
||||
- [Build related components](#0_0_11)
|
||||
@ -39,7 +38,7 @@ interfaces) of various types.
|
||||
Please check the `RELEASE_NOTES` file in the distribution for
|
||||
description of changes and release migration details.
|
||||
|
||||
### <span id="0_0_3">Copyright</span>
|
||||
### <span id="0_0_3">Copyright Licenses</span>
|
||||
|
||||
Please review the LICENSE file included in the distribution for legal
|
||||
terms of usage.
|
||||
@ -68,10 +67,10 @@ path to do EPICS builds; check the definitions of CC and CCC in
|
||||
|
||||
**GNU make**
|
||||
You must use GNU make, gnumake, for any EPICS builds. Set your path so
|
||||
that a gnumake version 3.81 or later is available.
|
||||
that a gnumake version 4.1 or later is available.
|
||||
|
||||
**Perl**
|
||||
You must have Perl version 5.8.1 or later installed. The EPICS
|
||||
You must have Perl version 5.10 or later installed. The EPICS
|
||||
configuration files do not specify the perl full pathname, so the perl
|
||||
executable must be found through your normal search path.
|
||||
|
||||
@ -114,13 +113,6 @@ installed on linux-x86. Command-line editing and history will then be
|
||||
those supplied by the os. On vxWorks the ledLib command-line input
|
||||
library is used instead.
|
||||
|
||||
### <span id="0_0_7">Host system storage requirements</span>
|
||||
|
||||
The compressed tar file is approximately 1.6 MB in size. The
|
||||
distribution source tree takes up approximately 12 MB. Each host
|
||||
target will need around 40 MB for build files, and each cross-compiled
|
||||
target around 20 MB.
|
||||
|
||||
### <span id="0_0_8">Documentation</span>
|
||||
|
||||
EPICS documentation is available through the [EPICS
|
||||
@ -242,17 +234,13 @@ Before you can build or use this EPICS base, the environment variable
|
||||
the base/startup directory has been provided to help set
|
||||
`EPICS_HOST_ARCH.` You should have `EPICS_HOST_ARCH` set to your
|
||||
host operating system followed by a dash and then your host
|
||||
architecture, e.g. solaris-sparc. If you are not using the OS
|
||||
architecture, e.g. linux-x86_64. If you are not using the OS
|
||||
vendor's c/c++ compiler for host builds, you will need another dash
|
||||
followed by the alternate compiler name (e.g. "-gnu" for GNU c/c++
|
||||
compilers on a solaris host or "-mingw" for MinGW c/c++ compilers on
|
||||
a WIN32 host). See `configure/CONFIG_SITE` for a list of supported
|
||||
Windows). See `configure/CONFIG_SITE` for a list of supported
|
||||
`EPICS_HOST_ARCH` values.
|
||||
|
||||
* `PERLLIB`
|
||||
On WIN32, some versions of Perl require that the environment
|
||||
variable PERLLIB be set to <perl directory location>.
|
||||
|
||||
* `PATH`
|
||||
As already mentioned, you must have the perl executable and you may
|
||||
need C and C++ compilers in your search path. For building base you
|
||||
|
@ -13,7 +13,7 @@ should also be read to understand what has changed since earlier releases.
|
||||
|
||||
**This version of EPICS has not been released yet.**
|
||||
|
||||
## Changes made on the 7.0 branch since 7.0.4.1
|
||||
## Changes made on the 7.0 branch since 7.0.5
|
||||
|
||||
<!-- Insert new items immediately below here ... -->
|
||||
|
||||
@ -56,21 +56,79 @@ Known Issues:
|
||||
issues with DHCP, but network stack usable. Can load env from
|
||||
NVRAM.
|
||||
|
||||
-----
|
||||
|
||||
### Priority inversion safe posix mutexes
|
||||
## 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.
|
||||
Support for Posix implementations before POSIX.1-2001 (_XOPEN_SOURCE < 500,
|
||||
glibc version < 2.3.3) has been dropped.
|
||||
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 epicsMutexShowAll() function (available through IOC shell)
|
||||
will print "PI is enabled" if both libc and kernel support is present.
|
||||
The IOC shell's `epicsMutexShowAll` command prints "PI is enabled" if both
|
||||
libc and kernel support is present.
|
||||
|
||||
### Add epicsStrSimilarity()
|
||||
### Fix for Periodic Scan threads hanging on Windows
|
||||
|
||||
Add epicsStrSimilarity() to epicsString.h which uses edit distance as an approximate comparison.
|
||||
Enables a new "Did you mean ..." suggestion when a .db file provides an invalid value for a DBF_MENU or DBF_DEVICE field.
|
||||
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 (arm64) Processors
|
||||
|
||||
Thanks to Jeong Han Lee this release comes with build support for Apple's new
|
||||
M1 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"
|
||||
|
||||
@ -234,32 +292,35 @@ 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
|
||||
the output link can use TSEL links to fetch the timestamp which corresponds
|
||||
to the data processed by the output link.
|
||||
|
||||
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
|
||||
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()
|
||||
|
||||
Addition of registerAllRecordDeviceDrivers() as an iocsh function
|
||||
and in iocshRegisterCommon.h. This function uses dynamic lookup with
|
||||
`epicsFindSymbol()` to perform the same function as a generated
|
||||
`*_registerRecordDeviceDriver()` function.
|
||||
This allows dynamic loading/linking of support modules without code generation.
|
||||
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 IOC's registerRecordDeviceDriver.cpp file is still required
|
||||
to link everything into the executable). It also won't work with some static build
|
||||
configurations or where the symbol table has been stripped from the executable.
|
||||
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`
|
||||
|
||||
@ -282,6 +343,8 @@ 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
|
||||
@ -329,6 +392,8 @@ Bad character ' ' in record name "bad practice"
|
||||
if a record name begins with a minus, plus, left square bracket,
|
||||
or left curly bracket.
|
||||
|
||||
-----
|
||||
|
||||
## EPICS Release 7.0.4
|
||||
|
||||
### Bug fixes
|
||||
@ -430,7 +495,7 @@ 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
|
||||
[#type-safe-device-and-driver-support-tables](this entry below) for the
|
||||
[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`
|
||||
@ -513,6 +578,8 @@ devLsiEtherIP = {
|
||||
};
|
||||
```
|
||||
|
||||
-----
|
||||
|
||||
## EPICS Release 7.0.3.1
|
||||
|
||||
**IMPORTANT NOTE:** *Some record types in this release will not be compatible
|
||||
@ -706,6 +773,8 @@ 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
|
||||
@ -726,6 +795,8 @@ This may result in slightly fewer, but larger frames being sent.
|
||||
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
|
||||
@ -760,6 +831,8 @@ substantial than bug fixes.
|
||||
|
||||
Turns out this is ~10x slower to query than `CLOCK_MONOTONIC`.
|
||||
|
||||
-----
|
||||
|
||||
## EPICS Release 7.0.2.1
|
||||
|
||||
### Linking shared libraries on macOS
|
||||
@ -811,6 +884,8 @@ 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
|
||||
@ -828,6 +903,8 @@ 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
|
||||
@ -886,7 +963,11 @@ 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 between 3.16.1 and 3.16.2
|
||||
-----
|
||||
|
||||
## 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).
|
||||
@ -1093,6 +1174,8 @@ 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
|
||||
@ -1529,6 +1612,7 @@ 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
|
||||
|
||||
@ -1637,6 +1721,8 @@ header and removed the need for dbScan.c to reach into the internals of its
|
||||
`CALLBACK` objects.
|
||||
|
||||
|
||||
-----
|
||||
|
||||
# Changes incorporated from the 3.15 branch
|
||||
|
||||
|
||||
@ -1648,6 +1734,7 @@ The names of the generated junit xml test output files have been changed
|
||||
from `<testname>.xml` to `<testname>-results.xml`, to allow better
|
||||
distinction from other xml files. (I.e., for easy wildcard matching.)
|
||||
|
||||
-----
|
||||
|
||||
## Changes made between 3.15.7 and 3.15.8
|
||||
|
||||
@ -1753,6 +1840,7 @@ don't provide it any more.
|
||||
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
|
||||
|
||||
@ -1896,6 +1984,8 @@ into the htmls directory. Thanks to Tony Pietryla.
|
||||
|
||||
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
|
||||
@ -2121,6 +2211,8 @@ 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
|
||||
@ -2252,6 +2344,8 @@ 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"
|
||||
@ -2357,6 +2451,8 @@ variable to a non-zero value before loading the file, like this:
|
||||
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
|
||||
|
@ -147,17 +147,17 @@ starting at <a href="#ReleaseApproval">Release Approval</a>.</p>
|
||||
<td>Tag the module in Git, using these tag conventions:
|
||||
<ul>
|
||||
<li>
|
||||
<tt>R7.0.4.1-pre<i>n</i></tt>
|
||||
<tt>R7.0.5-pre<i>n</i></tt>
|
||||
— pre-release tag
|
||||
</li>
|
||||
<li>
|
||||
<tt>R7.0.4.1-rc<i>n</i></tt>
|
||||
<tt>R7.0.5-rc<i>n</i></tt>
|
||||
— release candidate tag
|
||||
</li>
|
||||
</ul>
|
||||
<blockquote><tt>
|
||||
cd base-7.0<br />
|
||||
git tag -m 'ANJ: Tagged for 7.0.4.1-rc1' R7.0.4.1-rc1
|
||||
git tag -m 'ANJ: Tagged for 7.0.5-rc1' R7.0.5-rc1
|
||||
</tt></blockquote>
|
||||
Note that submodules must <em>not</em> be tagged with the version used
|
||||
for the top-level, they each have their own separate version numbers
|
||||
@ -171,11 +171,11 @@ starting at <a href="#ReleaseApproval">Release Approval</a>.</p>
|
||||
files and directories that are only used for continuous integration:
|
||||
<blockquote><tt>
|
||||
cd base-7.0<br />
|
||||
./.tools/make-tar.sh R7.0.4.1-rc1 base-7.0.4.1-rc1.tar.gz base-7.0.4.1-rc1/
|
||||
./.tools/make-tar.sh R7.0.5-rc1 base-7.0.5-rc1.tar.gz base-7.0.5-rc1/
|
||||
</tt></blockquote>
|
||||
Create a GPG signature file of the tarfile as follows:
|
||||
<blockquote><tt>
|
||||
gpg --armor --sign --detach-sig base-7.0.4.1-rc1.tar.gz
|
||||
gpg --armor --sign --detach-sig base-7.0.5-rc1.tar.gz
|
||||
</tt></blockquote>
|
||||
</td>
|
||||
</tr>
|
||||
@ -298,7 +298,7 @@ starting at <a href="#ReleaseApproval">Release Approval</a>.</p>
|
||||
|
||||
<li>Tag the module:
|
||||
<blockquote><tt>
|
||||
git tag -m 'ANJ: Tag for EPICS 7.0.4.1' <module-version>
|
||||
git tag -m 'ANJ: Tag for EPICS 7.0.5' <module-version>
|
||||
</tt></blockquote>
|
||||
</li>
|
||||
|
||||
@ -355,7 +355,7 @@ starting at <a href="#ReleaseApproval">Release Approval</a>.</p>
|
||||
<td>Tag the epics-base module in Git:
|
||||
<blockquote><tt>
|
||||
cd base-7.0<br />
|
||||
git tag -m 'ANJ: Tagged for release' R7.0.4.1
|
||||
git tag -m 'ANJ: Tagged for release' R7.0.5
|
||||
</tt></blockquote>
|
||||
<p>Don't push these commits or the new tag to the Launchpad repository
|
||||
yet.</p>
|
||||
@ -387,12 +387,12 @@ starting at <a href="#ReleaseApproval">Release Approval</a>.</p>
|
||||
files and directories that are only used for continuous integration:
|
||||
<blockquote><tt>
|
||||
cd base-7.0<br />
|
||||
./.tools/make-tar.sh R7.0.4.1 ../base-7.0.4.1.tar.gz base-7.0.4.1/
|
||||
./.tools/make-tar.sh R7.0.5 ../base-7.0.5.tar.gz base-7.0.5/
|
||||
</tt></blockquote>
|
||||
Create a GPG signature file of the tarfile as follows:
|
||||
<blockquote><tt>
|
||||
cd ..<br />
|
||||
gpg --armor --sign --detach-sig base-7.0.4.1.tar.gz
|
||||
gpg --armor --sign --detach-sig base-7.0.5.tar.gz
|
||||
</tt></blockquote>
|
||||
</td>
|
||||
</tr>
|
||||
@ -457,7 +457,7 @@ starting at <a href="#ReleaseApproval">Release Approval</a>.</p>
|
||||
<td>Upload the tar file and its <tt>.asc</tt> signature file to the
|
||||
epics-controls web-server.
|
||||
<blockquote><tt>
|
||||
scp base-7.0.4.1.tar.gz base-7.0.4.1.tar.gz.asc epics-controls:download/base<br />
|
||||
scp base-7.0.5.tar.gz base-7.0.5.tar.gz.asc epics-controls:download/base<br />
|
||||
</tt></blockquote>
|
||||
</td>
|
||||
</tr>
|
||||
|
@ -3,7 +3,6 @@
|
||||
|
||||
Documentation index
|
||||
|
||||
@ul
|
||||
@li @ref releasenotes
|
||||
@li @ref install
|
||||
@li @ref recordrefmanual
|
||||
|
@ -38,6 +38,7 @@ ifeq ($(wildcard $(PERL_h)),)
|
||||
endif
|
||||
endif
|
||||
|
||||
ifneq (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.)
|
||||
@ -62,6 +63,7 @@ endif
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
|
||||
Cap5_SRCS = Cap5.xs
|
||||
Cap5_LIBS = ca Com
|
||||
|
@ -120,7 +120,9 @@ static void ascarCallFunc(const iocshArgBuf *args)
|
||||
}
|
||||
|
||||
/* asDumpHash */
|
||||
static const iocshFuncDef asDumpHashFuncDef = {"asDumpHash",0,0};
|
||||
static const iocshFuncDef asDumpHashFuncDef = {"asDumpHash",0,0,
|
||||
"Show the contents of the hash table used "
|
||||
"to locate UAGs and HAGs.\n"};
|
||||
static void asDumpHashCallFunc(const iocshArgBuf *args)
|
||||
{
|
||||
asDumpHash();
|
||||
|
@ -19,7 +19,10 @@ BPT_DBD += bptTypeJdegC.dbd
|
||||
BPT_DBD += bptTypeJdegF.dbd
|
||||
BPT_DBD += bptTypeKdegC.dbd
|
||||
BPT_DBD += bptTypeKdegF.dbd
|
||||
|
||||
ifneq (inc,$(strip $(MAKECMDGOALS)))
|
||||
DBD += $(BPT_DBD)
|
||||
endif
|
||||
|
||||
PROD_HOST += makeBpt
|
||||
|
||||
|
@ -339,7 +339,7 @@ static void getOptions(DBADDR *paddr, char **poriginal, long *options,
|
||||
dbCommon *pcommon;
|
||||
char *pbuffer = *poriginal;
|
||||
|
||||
if (!pfl || pfl->type == dbfl_type_rec)
|
||||
if (!pfl)
|
||||
field_type = paddr->field_type;
|
||||
else
|
||||
field_type = pfl->field_type;
|
||||
@ -349,7 +349,7 @@ static void getOptions(DBADDR *paddr, char **poriginal, long *options,
|
||||
if( (*options) & DBR_STATUS ) {
|
||||
unsigned short *pushort = (unsigned short *)pbuffer;
|
||||
|
||||
if (!pfl || pfl->type == dbfl_type_rec) {
|
||||
if (!pfl) {
|
||||
*pushort++ = pcommon->stat;
|
||||
*pushort++ = pcommon->sevr;
|
||||
} else {
|
||||
@ -383,7 +383,7 @@ static void getOptions(DBADDR *paddr, char **poriginal, long *options,
|
||||
if( (*options) & DBR_TIME ) {
|
||||
epicsUInt32 *ptime = (epicsUInt32 *)pbuffer;
|
||||
|
||||
if (!pfl || pfl->type == dbfl_type_rec) {
|
||||
if (!pfl) {
|
||||
*ptime++ = pcommon->time.secPastEpoch;
|
||||
*ptime++ = pcommon->time.nsec;
|
||||
} else {
|
||||
@ -904,22 +904,23 @@ long dbGet(DBADDR *paddr, short dbrType,
|
||||
if (nRequest && *nRequest == 0)
|
||||
return 0;
|
||||
|
||||
if (!pfl || pfl->type == dbfl_type_rec) {
|
||||
if (!pfl) {
|
||||
field_type = paddr->field_type;
|
||||
no_elements = capacity = paddr->no_elements;
|
||||
|
||||
/* Update field info from record
|
||||
* may modify paddr->pfield
|
||||
*/
|
||||
if (paddr->pfldDes->special == SPC_DBADDR &&
|
||||
(prset = dbGetRset(paddr)) &&
|
||||
prset->get_array_info) {
|
||||
status = prset->get_array_info(paddr, &no_elements, &offset);
|
||||
} else
|
||||
offset = 0;
|
||||
} else {
|
||||
field_type = pfl->field_type;
|
||||
no_elements = capacity = pfl->no_elements;
|
||||
}
|
||||
|
||||
/* Update field info from record (if neccessary);
|
||||
* may modify paddr->pfield.
|
||||
*/
|
||||
if (!dbfl_has_copy(pfl) &&
|
||||
paddr->pfldDes->special == SPC_DBADDR &&
|
||||
(prset = dbGetRset(paddr)) &&
|
||||
prset->get_array_info) {
|
||||
status = prset->get_array_info(paddr, &no_elements, &offset);
|
||||
} else {
|
||||
offset = 0;
|
||||
}
|
||||
|
||||
@ -951,7 +952,7 @@ long dbGet(DBADDR *paddr, short dbrType,
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (!pfl || pfl->type == dbfl_type_rec) {
|
||||
if (!dbfl_has_copy(pfl)) {
|
||||
status = dbFastGetConvertRoutine[field_type][dbrType]
|
||||
(paddr->pfield, pbuf, paddr);
|
||||
} else {
|
||||
@ -964,11 +965,9 @@ long dbGet(DBADDR *paddr, short dbrType,
|
||||
|
||||
localAddr.field_type = pfl->field_type;
|
||||
localAddr.field_size = pfl->field_size;
|
||||
/* not used by dbFastConvert: */
|
||||
localAddr.no_elements = pfl->no_elements;
|
||||
if (pfl->type == dbfl_type_val)
|
||||
localAddr.pfield = (char *) &pfl->u.v.field;
|
||||
else
|
||||
localAddr.pfield = (char *) pfl->u.r.field;
|
||||
localAddr.pfield = dbfl_pfield(pfl);
|
||||
status = dbFastGetConvertRoutine[field_type][dbrType]
|
||||
(localAddr.pfield, pbuf, &localAddr);
|
||||
}
|
||||
@ -979,6 +978,8 @@ long dbGet(DBADDR *paddr, short dbrType,
|
||||
if (nRequest) {
|
||||
if (no_elements < *nRequest)
|
||||
*nRequest = no_elements;
|
||||
if (capacity < *nRequest)
|
||||
*nRequest = capacity;
|
||||
n = *nRequest;
|
||||
} else {
|
||||
n = 1;
|
||||
@ -995,8 +996,8 @@ long dbGet(DBADDR *paddr, short dbrType,
|
||||
}
|
||||
/* convert data into the caller's buffer */
|
||||
if (n <= 0) {
|
||||
;/*do nothing*/
|
||||
} else if (!pfl || pfl->type == dbfl_type_rec) {
|
||||
; /*do nothing */
|
||||
} else if (!dbfl_has_copy(pfl)) {
|
||||
status = convert(paddr, pbuf, n, capacity, offset);
|
||||
} else {
|
||||
DBADDR localAddr = *paddr; /* Structure copy */
|
||||
@ -1008,11 +1009,9 @@ long dbGet(DBADDR *paddr, short dbrType,
|
||||
|
||||
localAddr.field_type = pfl->field_type;
|
||||
localAddr.field_size = pfl->field_size;
|
||||
/* not used by dbConvert, it uses the passed capacity instead: */
|
||||
localAddr.no_elements = pfl->no_elements;
|
||||
if (pfl->type == dbfl_type_val)
|
||||
localAddr.pfield = (char *) &pfl->u.v.field;
|
||||
else
|
||||
localAddr.pfield = (char *) pfl->u.r.field;
|
||||
localAddr.pfield = dbfl_pfield(pfl);
|
||||
status = convert(&localAddr, pbuf, n, capacity, offset);
|
||||
}
|
||||
|
||||
|
@ -52,14 +52,12 @@ typedef struct parseContext {
|
||||
|
||||
static void *dbChannelFreeList;
|
||||
static void *chFilterFreeList;
|
||||
static void *dbchStringFreeList;
|
||||
|
||||
void dbChannelExit(void)
|
||||
{
|
||||
freeListCleanup(dbChannelFreeList);
|
||||
freeListCleanup(chFilterFreeList);
|
||||
freeListCleanup(dbchStringFreeList);
|
||||
dbChannelFreeList = chFilterFreeList = dbchStringFreeList = NULL;
|
||||
dbChannelFreeList = chFilterFreeList = NULL;
|
||||
}
|
||||
|
||||
void dbChannelInit (void)
|
||||
@ -69,7 +67,6 @@ void dbChannelInit (void)
|
||||
|
||||
freeListInitPvt(&dbChannelFreeList, sizeof(dbChannel), 128);
|
||||
freeListInitPvt(&chFilterFreeList, sizeof(chFilter), 64);
|
||||
freeListInitPvt(&dbchStringFreeList, sizeof(epicsOldString), 128);
|
||||
db_init_event_freelists();
|
||||
}
|
||||
|
||||
@ -449,28 +446,6 @@ static long parseArrayRange(dbChannel* chan, const char *pname, const char **ppn
|
||||
return status;
|
||||
}
|
||||
|
||||
/* Stolen from dbAccess.c: */
|
||||
static short mapDBFToDBR[DBF_NTYPES] =
|
||||
{
|
||||
/* DBF_STRING => */DBR_STRING,
|
||||
/* DBF_CHAR => */DBR_CHAR,
|
||||
/* DBF_UCHAR => */DBR_UCHAR,
|
||||
/* DBF_SHORT => */DBR_SHORT,
|
||||
/* DBF_USHORT => */DBR_USHORT,
|
||||
/* DBF_LONG => */DBR_LONG,
|
||||
/* DBF_ULONG => */DBR_ULONG,
|
||||
/* DBF_INT64 => */DBR_INT64,
|
||||
/* DBF_UINT64 => */DBR_UINT64,
|
||||
/* DBF_FLOAT => */DBR_FLOAT,
|
||||
/* DBF_DOUBLE => */DBR_DOUBLE,
|
||||
/* DBF_ENUM, => */DBR_ENUM,
|
||||
/* DBF_MENU, => */DBR_ENUM,
|
||||
/* DBF_DEVICE => */DBR_ENUM,
|
||||
/* DBF_INLINK => */DBR_STRING,
|
||||
/* DBF_OUTLINK => */DBR_STRING,
|
||||
/* DBF_FWDLINK => */DBR_STRING,
|
||||
/* DBF_NOACCESS => */DBR_NOACCESS };
|
||||
|
||||
dbChannel * dbChannelCreate(const char *name)
|
||||
{
|
||||
const char *pname = name;
|
||||
@ -743,37 +718,24 @@ void dbChannelDelete(dbChannel *chan)
|
||||
freeListFree(dbChannelFreeList, chan);
|
||||
}
|
||||
|
||||
static void freeArray(db_field_log *pfl) {
|
||||
if (pfl->field_type == DBF_STRING && pfl->no_elements == 1) {
|
||||
freeListFree(dbchStringFreeList, pfl->u.r.field);
|
||||
} else {
|
||||
free(pfl->u.r.field);
|
||||
}
|
||||
}
|
||||
|
||||
void dbChannelMakeArrayCopy(void *pvt, db_field_log *pfl, dbChannel *chan)
|
||||
/*
|
||||
* Helper function to adjust no_elements, offset, and pfield
|
||||
* when copying an array from a record.
|
||||
*/
|
||||
void dbChannelGetArrayInfo(dbChannel *chan,
|
||||
void **pfield, long *no_elements, long *offset)
|
||||
{
|
||||
void *p;
|
||||
struct dbCommon *prec = dbChannelRecord(chan);
|
||||
|
||||
if (pfl->type != dbfl_type_rec) return;
|
||||
|
||||
pfl->type = dbfl_type_ref;
|
||||
pfl->stat = prec->stat;
|
||||
pfl->sevr = prec->sevr;
|
||||
pfl->time = prec->time;
|
||||
pfl->field_type = chan->addr.field_type;
|
||||
pfl->no_elements = chan->addr.no_elements;
|
||||
pfl->field_size = chan->addr.field_size;
|
||||
pfl->u.r.dtor = freeArray;
|
||||
pfl->u.r.pvt = pvt;
|
||||
if (pfl->field_type == DBF_STRING && pfl->no_elements == 1) {
|
||||
p = freeListCalloc(dbchStringFreeList);
|
||||
} else {
|
||||
p = calloc(pfl->no_elements, pfl->field_size);
|
||||
rset *prset;
|
||||
if (dbChannelSpecial(chan) == SPC_DBADDR &&
|
||||
(prset = dbGetRset(&chan->addr)) &&
|
||||
prset->get_array_info)
|
||||
{
|
||||
void *pfieldsave = dbChannelField(chan);
|
||||
/* it is expected that this call always succeeds */
|
||||
prset->get_array_info(&chan->addr, no_elements, offset);
|
||||
*pfield = dbChannelField(chan);
|
||||
dbChannelField(chan) = pfieldsave;
|
||||
}
|
||||
if (p) dbGet(&chan->addr, mapDBFToDBR[pfl->field_type], p, NULL, &pfl->no_elements, NULL);
|
||||
pfl->u.r.field = p;
|
||||
}
|
||||
|
||||
/* FIXME: Do these belong in a different file? */
|
||||
|
@ -65,8 +65,8 @@ typedef struct dbChannel {
|
||||
/* Prototype for the channel event function that is called in filter stacks
|
||||
*
|
||||
* When invoked the scan lock for the record associated with 'chan' _may_ be locked.
|
||||
* If pLog->type==dbfl_type_rec then dbScanLock() must be called before copying
|
||||
* data out of the associated record.
|
||||
* Unless dbfl_has_copy(pLog), it must call dbScanLock before accessing the data,
|
||||
* as this indicates the data is still owned by the record.
|
||||
*
|
||||
* This function has ownership of the field log pLog, if it wishes to discard
|
||||
* this update it should free the field log with db_delete_field_log() and
|
||||
@ -225,7 +225,8 @@ DBCORE_API void dbRegisterFilter(const char *key, const chFilterIf *fif, void *p
|
||||
DBCORE_API db_field_log* dbChannelRunPreChain(dbChannel *chan, db_field_log *pLogIn);
|
||||
DBCORE_API db_field_log* dbChannelRunPostChain(dbChannel *chan, db_field_log *pLogIn);
|
||||
DBCORE_API const chFilterPlugin * dbFindFilter(const char *key, size_t len);
|
||||
DBCORE_API void dbChannelMakeArrayCopy(void *pvt, db_field_log *pfl, dbChannel *chan);
|
||||
DBCORE_API void dbChannelGetArrayInfo(dbChannel *chan,
|
||||
void **pfield, long *no_elements, long *offset);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
@ -668,27 +668,21 @@ int db_post_extra_labor (dbEventCtx ctx)
|
||||
return DB_EVENT_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* DB_CREATE_EVENT_LOG()
|
||||
*
|
||||
* NOTE: This assumes that the db scan lock is already applied
|
||||
* (as it copies data from the record)
|
||||
*/
|
||||
db_field_log* db_create_event_log (struct evSubscrip *pevent)
|
||||
static db_field_log* db_create_field_log (struct dbChannel *chan, int use_val)
|
||||
{
|
||||
db_field_log *pLog = (db_field_log *) freeListCalloc(dbevFieldLogFreeList);
|
||||
|
||||
if (pLog) {
|
||||
struct dbChannel *chan = pevent->chan;
|
||||
struct dbCommon *prec = dbChannelRecord(chan);
|
||||
pLog->ctx = dbfl_context_event;
|
||||
if (pevent->useValque) {
|
||||
pLog->stat = prec->stat;
|
||||
pLog->sevr = prec->sevr;
|
||||
pLog->time = prec->time;
|
||||
pLog->field_type = dbChannelFieldType(chan);
|
||||
pLog->field_size = dbChannelFieldSize(chan);
|
||||
pLog->no_elements = dbChannelElements(chan);
|
||||
|
||||
if (use_val) {
|
||||
pLog->type = dbfl_type_val;
|
||||
pLog->stat = prec->stat;
|
||||
pLog->sevr = prec->sevr;
|
||||
pLog->time = prec->time;
|
||||
pLog->field_type = dbChannelFieldType(chan);
|
||||
pLog->no_elements = dbChannelElements(chan);
|
||||
/*
|
||||
* use memcpy to avoid a bus error on
|
||||
* union copy of char in the db at an odd
|
||||
@ -698,23 +692,46 @@ db_field_log* db_create_event_log (struct evSubscrip *pevent)
|
||||
dbChannelField(chan),
|
||||
dbChannelFieldSize(chan));
|
||||
} else {
|
||||
pLog->type = dbfl_type_rec;
|
||||
pLog->type = dbfl_type_ref;
|
||||
|
||||
/* don't make a copy yet, just reference the field value */
|
||||
pLog->u.r.field = dbChannelField(chan);
|
||||
/* indicate field value still owned by record */
|
||||
pLog->u.r.dtor = NULL;
|
||||
/* no private data yet, may be set by a filter */
|
||||
pLog->u.r.pvt = NULL;
|
||||
}
|
||||
}
|
||||
return pLog;
|
||||
}
|
||||
|
||||
/*
|
||||
* DB_CREATE_EVENT_LOG()
|
||||
*
|
||||
* NOTE: This assumes that the db scan lock is already applied
|
||||
* (as it calls rset->get_array_info)
|
||||
*/
|
||||
db_field_log* db_create_event_log (struct evSubscrip *pevent)
|
||||
{
|
||||
db_field_log *pLog = db_create_field_log(pevent->chan, pevent->useValque);
|
||||
if (pLog) {
|
||||
pLog->ctx = dbfl_context_event;
|
||||
}
|
||||
return pLog;
|
||||
}
|
||||
|
||||
/*
|
||||
* DB_CREATE_READ_LOG()
|
||||
*
|
||||
*/
|
||||
db_field_log* db_create_read_log (struct dbChannel *chan)
|
||||
{
|
||||
db_field_log *pLog = (db_field_log *) freeListCalloc(dbevFieldLogFreeList);
|
||||
|
||||
db_field_log *pLog = db_create_field_log(chan,
|
||||
dbChannelElements(chan) == 1 &&
|
||||
dbChannelSpecial(chan) != SPC_DBADDR &&
|
||||
dbChannelFieldSize(chan) <= sizeof(union native_value));
|
||||
if (pLog) {
|
||||
pLog->ctx = dbfl_context_read;
|
||||
pLog->type = dbfl_type_rec;
|
||||
}
|
||||
return pLog;
|
||||
}
|
||||
@ -737,20 +754,6 @@ static void db_queue_event_log (evSubscrip *pevent, db_field_log *pLog)
|
||||
|
||||
LOCKEVQUE (ev_que);
|
||||
|
||||
/*
|
||||
* if we have an event on the queue and both the last
|
||||
* event on the queue and the current event are emtpy
|
||||
* (i.e. of type dbfl_type_rec), simply ignore duplicate
|
||||
* events (saving empty events serves no purpose)
|
||||
*/
|
||||
if (pevent->npend > 0u &&
|
||||
(*pevent->pLastLog)->type == dbfl_type_rec &&
|
||||
pLog->type == dbfl_type_rec) {
|
||||
db_delete_field_log(pLog);
|
||||
UNLOCKEVQUE (ev_que);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* add to task local event que
|
||||
*/
|
||||
|
@ -14,11 +14,12 @@
|
||||
/*
|
||||
* Author: Ralph Lange <Ralph.Lange@bessy.de>
|
||||
*
|
||||
* based on dbConvert.c
|
||||
* based on dbConvert.c, see copyNoConvert
|
||||
* written by: Bob Dalesio, Marty Kraimer
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "epicsTypes.h"
|
||||
|
||||
@ -26,61 +27,30 @@
|
||||
#include "dbAddr.h"
|
||||
#include "dbExtractArray.h"
|
||||
|
||||
void dbExtractArrayFromRec(const dbAddr *paddr, void *pto,
|
||||
long nRequest, long no_elements, long offset, long increment)
|
||||
void dbExtractArray(const void *pfrom, void *pto, short field_size,
|
||||
long nRequest, long no_elements, long offset, long increment)
|
||||
{
|
||||
char *pdst = (char *) pto;
|
||||
char *psrc = (char *) paddr->pfield;
|
||||
long nUpperPart;
|
||||
int i;
|
||||
short srcSize = paddr->field_size;
|
||||
short dstSize = srcSize;
|
||||
char isString = (paddr->field_type == DBF_STRING);
|
||||
const char *psrc = (char *) pfrom;
|
||||
|
||||
if (nRequest > no_elements) nRequest = no_elements;
|
||||
if (isString && srcSize > MAX_STRING_SIZE) dstSize = MAX_STRING_SIZE;
|
||||
|
||||
if (increment == 1 && dstSize == srcSize) {
|
||||
nUpperPart = nRequest < no_elements - offset ? nRequest : no_elements - offset;
|
||||
memcpy(pdst, &psrc[offset * srcSize], dstSize * nUpperPart);
|
||||
if (nRequest > nUpperPart)
|
||||
memcpy(&pdst[dstSize * nUpperPart], psrc, dstSize * (nRequest - nUpperPart));
|
||||
if (isString)
|
||||
for (i = 1; i <= nRequest; i++)
|
||||
pdst[dstSize*i-1] = '\0';
|
||||
} else {
|
||||
for (; nRequest > 0; nRequest--, pdst += dstSize, offset += increment) {
|
||||
offset %= no_elements;
|
||||
memcpy(pdst, &psrc[offset*srcSize], dstSize);
|
||||
if (isString) pdst[dstSize-1] = '\0';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void dbExtractArrayFromBuf(const void *pfrom, void *pto,
|
||||
short field_size, short field_type,
|
||||
long nRequest, long no_elements, long offset, long increment)
|
||||
{
|
||||
char *pdst = (char *) pto;
|
||||
char *psrc = (char *) pfrom;
|
||||
int i;
|
||||
short srcSize = field_size;
|
||||
short dstSize = srcSize;
|
||||
char isString = (field_type == DBF_STRING);
|
||||
|
||||
if (nRequest > no_elements) nRequest = no_elements;
|
||||
if (offset > no_elements - 1) offset = no_elements - 1;
|
||||
if (isString && dstSize >= MAX_STRING_SIZE) dstSize = MAX_STRING_SIZE - 1;
|
||||
/* assert preconditions */
|
||||
assert(nRequest >= 0);
|
||||
assert(no_elements >= 0);
|
||||
assert(increment > 0);
|
||||
assert(0 <= offset);
|
||||
assert(offset < no_elements);
|
||||
|
||||
if (increment == 1) {
|
||||
memcpy(pdst, &psrc[offset * srcSize], dstSize * nRequest);
|
||||
if (isString)
|
||||
for (i = 1; i <= nRequest; i++)
|
||||
pdst[dstSize*i] = '\0';
|
||||
long nUpperPart =
|
||||
nRequest < no_elements - offset ? nRequest : no_elements - offset;
|
||||
memcpy(pdst, psrc + (offset * field_size), field_size * nUpperPart);
|
||||
if (nRequest > nUpperPart)
|
||||
memcpy(pdst + (field_size * nUpperPart), psrc,
|
||||
field_size * (nRequest - nUpperPart));
|
||||
} else {
|
||||
for (; nRequest > 0; nRequest--, pdst += srcSize, offset += increment) {
|
||||
memcpy(pdst, &psrc[offset*srcSize], dstSize);
|
||||
if (isString) pdst[dstSize] = '\0';
|
||||
for (; nRequest > 0; nRequest--, pdst += field_size, offset += increment) {
|
||||
offset %= no_elements;
|
||||
memcpy(pdst, psrc + (offset * field_size), field_size);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -22,11 +22,36 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
epicsShareFunc void dbExtractArrayFromRec(const DBADDR *paddr, void *pto,
|
||||
long nRequest, long no_elements, long offset, long increment);
|
||||
epicsShareFunc void dbExtractArrayFromBuf(const void *pfrom, void *pto,
|
||||
short field_size, short field_type,
|
||||
long nRequest, long no_elements, long offset, long increment);
|
||||
/** @brief Make a copy of parts of an array.
|
||||
*
|
||||
* The source array may or may not be a record field.
|
||||
*
|
||||
* The increment parameter is used to support array filters; it
|
||||
* means: copy only every increment'th element, starting at offset.
|
||||
*
|
||||
* The offset and no_elements parameters are used to support the
|
||||
* circular buffer feature of record fields: elements before offset
|
||||
* are treated as if they came right after no_elements.
|
||||
*
|
||||
* This function does not do any conversion on the array elements.
|
||||
*
|
||||
* Preconditions:
|
||||
* nRequest >= 0, no_elements >= 0, increment > 0
|
||||
* 0 <= offset < no_elements
|
||||
* pto points to a buffer with at least field_size*nRequest bytes
|
||||
* pfrom points to a buffer with exactly field_size*no_elements bytes
|
||||
*
|
||||
* @param pfrom Pointer to source array.
|
||||
* @param pto Pointer to target array.
|
||||
* @param field_size Size of an array element.
|
||||
* @param nRequest Number of elements to copy.
|
||||
* @param no_elements Number of elements in source array.
|
||||
* @param offset Wrap-around point in source array.
|
||||
* @param increment Copy only every increment'th element.
|
||||
*/
|
||||
epicsShareFunc void dbExtractArray(const void *pfrom, void *pto,
|
||||
short field_size, long nRequest, long no_elements, long offset,
|
||||
long increment);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
@ -112,12 +112,13 @@ union anybuf {
|
||||
|
||||
long testdbVPutField(const char* pv, short dbrType, va_list ap)
|
||||
{
|
||||
DBADDR addr;
|
||||
dbChannel *chan = dbChannelCreate(pv);
|
||||
union anybuf pod;
|
||||
long ret = S_dbLib_recNotFound;
|
||||
|
||||
if (dbNameToAddr(pv, &addr)) {
|
||||
testFail("Missing PV \"%s\"", pv);
|
||||
return S_dbLib_recNotFound;
|
||||
if(!chan || (ret=dbChannelOpen(chan))) {
|
||||
testFail("Channel error (%p, %ld) : %s", chan, ret, pv);
|
||||
goto done;
|
||||
}
|
||||
|
||||
switch(dbrType) {
|
||||
@ -125,14 +126,18 @@ long testdbVPutField(const char* pv, short dbrType, va_list ap)
|
||||
const char *uarg = va_arg(ap,char*);
|
||||
strncpy(pod.valStr, uarg, sizeof(pod.valStr));
|
||||
pod.valStr[sizeof(pod.valStr)-1] = '\0';
|
||||
return dbPutField(&addr, dbrType, pod.valStr, 1);
|
||||
ret = dbChannelPutField(chan, dbrType, pod.valStr, 1);
|
||||
break;
|
||||
}
|
||||
|
||||
/* The Type parameter takes into consideration
|
||||
* the C language rules for promotion of argument types
|
||||
* in variadic functions.
|
||||
*/
|
||||
#define OP(DBR,Type,mem) case DBR: {pod.val.mem = va_arg(ap,Type); break;}
|
||||
#define OP(DBR,Type,mem) case DBR: \
|
||||
pod.val.mem = va_arg(ap,Type); \
|
||||
ret = dbChannelPutField(chan, dbrType, pod.bytes, 1); \
|
||||
break;
|
||||
OP(DBR_CHAR, int, int8);
|
||||
OP(DBR_UCHAR, int, uInt8);
|
||||
OP(DBR_SHORT, int, int16);
|
||||
@ -147,11 +152,15 @@ long testdbVPutField(const char* pv, short dbrType, va_list ap)
|
||||
#undef OP
|
||||
default:
|
||||
testFail("invalid DBR: dbPutField(\"%s\", %d, ...)",
|
||||
addr.precord->name, dbrType);
|
||||
return S_db_badDbrtype;
|
||||
dbChannelName(chan), dbrType);
|
||||
ret = S_db_badDbrtype;
|
||||
break;
|
||||
}
|
||||
|
||||
return dbPutField(&addr, dbrType, pod.bytes, 1);
|
||||
done:
|
||||
if(chan)
|
||||
dbChannelDelete(chan);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void testdbPutFieldOk(const char* pv, int dbrType, ...)
|
||||
@ -190,23 +199,35 @@ void testdbGetFieldEqual(const char* pv, int dbrType, ...)
|
||||
|
||||
void testdbVGetFieldEqual(const char* pv, short dbrType, va_list ap)
|
||||
{
|
||||
DBADDR addr;
|
||||
dbChannel *chan = dbChannelCreate(pv);
|
||||
db_field_log *pfl = NULL;
|
||||
long nReq = 1;
|
||||
union anybuf pod;
|
||||
long status;
|
||||
long status = S_dbLib_recNotFound;
|
||||
|
||||
if(dbNameToAddr(pv, &addr)) {
|
||||
testFail("Missing PV \"%s\"", pv);
|
||||
return;
|
||||
if(!chan || (status=dbChannelOpen(chan))) {
|
||||
testFail("Channel error (%p, %ld) : %s", chan, status, pv);
|
||||
goto done;
|
||||
}
|
||||
|
||||
status = dbGetField(&addr, dbrType, pod.bytes, NULL, &nReq, NULL);
|
||||
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);
|
||||
if (status) {
|
||||
testFail("dbGetField(\"%s\", %d, ...) -> %#lx (%s)", pv, dbrType, status, errSymMsg(status));
|
||||
return;
|
||||
goto done;
|
||||
} else if(nReq==0) {
|
||||
testFail("dbGetField(\"%s\", %d, ...) -> zero length", pv, dbrType);
|
||||
return;
|
||||
goto done;
|
||||
}
|
||||
|
||||
switch(dbrType) {
|
||||
@ -236,35 +257,56 @@ void testdbVGetFieldEqual(const char* pv, short dbrType, va_list ap)
|
||||
default:
|
||||
testFail("dbGetField(\"%s\", %d) -> unsupported dbf", pv, dbrType);
|
||||
}
|
||||
|
||||
done:
|
||||
db_delete_field_log(pfl);
|
||||
if(chan)
|
||||
dbChannelDelete(chan);
|
||||
}
|
||||
|
||||
void testdbPutArrFieldOk(const char* pv, short dbrType, unsigned long count, const void *pbuf)
|
||||
{
|
||||
DBADDR addr;
|
||||
dbChannel *chan = dbChannelCreate(pv);
|
||||
long status;
|
||||
|
||||
if (dbNameToAddr(pv, &addr)) {
|
||||
testFail("Missing PV \"%s\"", pv);
|
||||
return;
|
||||
if(!chan || (status=dbChannelOpen(chan))) {
|
||||
testFail("Channel error (%p, %ld) : %s", chan, status, pv);
|
||||
goto done;
|
||||
}
|
||||
|
||||
status = dbPutField(&addr, dbrType, pbuf, count);
|
||||
status = dbChannelPutField(chan, dbrType, pbuf, count);
|
||||
|
||||
testOk(status==0, "dbPutField(\"%s\", dbr=%d, count=%lu, ...) -> %ld", pv, dbrType, count, status);
|
||||
|
||||
done:
|
||||
if(chan)
|
||||
dbChannelDelete(chan);
|
||||
}
|
||||
|
||||
void testdbGetArrFieldEqual(const char* pv, short dbfType, long nRequest, unsigned long cnt, const void *pbufraw)
|
||||
{
|
||||
DBADDR addr;
|
||||
dbChannel *chan = dbChannelCreate(pv);
|
||||
db_field_log *pfl = NULL;
|
||||
const long vSize = dbValueSize(dbfType);
|
||||
const long nStore = vSize * nRequest;
|
||||
long status;
|
||||
long status = S_dbLib_recNotFound;
|
||||
char *gbuf, *gstore;
|
||||
const char *pbuf = pbufraw;
|
||||
|
||||
if(dbNameToAddr(pv, &addr)) {
|
||||
testFail("Missing PV \"%s\"", pv);
|
||||
return;
|
||||
if(!chan || (status=dbChannelOpen(chan))) {
|
||||
testFail("Channel error (%p, %ld) : %s", chan, status, pv);
|
||||
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);
|
||||
@ -273,7 +315,7 @@ void testdbGetArrFieldEqual(const char* pv, short dbfType, long nRequest, unsign
|
||||
return;
|
||||
}
|
||||
|
||||
status = dbGetField(&addr, dbfType, gbuf, NULL, &nRequest, NULL);
|
||||
status = dbChannelGetField(chan, dbfType, gbuf, NULL, &nRequest, pfl);
|
||||
if (status) {
|
||||
testFail("dbGetField(\"%s\", %d, ...) -> %#lx", pv, dbfType, status);
|
||||
|
||||
@ -318,7 +360,10 @@ void testdbGetArrFieldEqual(const char* pv, short dbfType, long nRequest, unsign
|
||||
testOk(match, "dbGetField(\"%s\", dbrType=%d, nRequest=%ld ...) match", pv, dbfType, nRequest);
|
||||
}
|
||||
|
||||
done:
|
||||
free(gstore);
|
||||
if(chan)
|
||||
dbChannelDelete(chan);
|
||||
}
|
||||
|
||||
dbCommon* testdbRecordPtr(const char* pv)
|
||||
|
@ -57,20 +57,31 @@ union native_value {
|
||||
struct db_field_log;
|
||||
typedef void (dbfl_freeFunc)(struct db_field_log *pfl);
|
||||
|
||||
/* Types of db_field_log: rec = use record, val = val inside, ref = reference inside */
|
||||
/*
|
||||
* A db_field_log has one of two types:
|
||||
*
|
||||
* dbfl_type_ref - Reference to value
|
||||
* Used for variable size (array) data types. Meta-data
|
||||
* is stored in the field log, but value data is stored externally.
|
||||
* Only the dbfl_ref side of the data union is valid.
|
||||
*
|
||||
* dbfl_type_val - Internal value
|
||||
* Used to store small scalar data. Meta-data and value are
|
||||
* present in this structure and no external references are used.
|
||||
* Only the dbfl_val side of the data union is valid.
|
||||
*/
|
||||
typedef enum dbfl_type {
|
||||
dbfl_type_rec = 0,
|
||||
dbfl_type_val,
|
||||
dbfl_type_ref
|
||||
} dbfl_type;
|
||||
|
||||
/* Context of db_field_log: event = subscription update, read = read reply */
|
||||
typedef enum dbfl_context {
|
||||
dbfl_context_read = 0,
|
||||
dbfl_context_read,
|
||||
dbfl_context_event
|
||||
} dbfl_context;
|
||||
|
||||
#define dbflTypeStr(t) (t==dbfl_type_val?"val":t==dbfl_type_rec?"rec":"ref")
|
||||
#define dbflTypeStr(t) (t==dbfl_type_val?"val":"ref")
|
||||
|
||||
struct dbfl_val {
|
||||
union native_value field; /* Field value */
|
||||
@ -82,6 +93,8 @@ struct dbfl_val {
|
||||
* db_delete_field_log(). Any code which changes a dbfl_type_ref
|
||||
* field log to another type, or to reference different data,
|
||||
* must explicitly call the dtor function.
|
||||
* If the dtor is NULL and no_elements > 0, then this means the array
|
||||
* data is still owned by a record. See the macro dbfl_has_copy below.
|
||||
*/
|
||||
struct dbfl_ref {
|
||||
dbfl_freeFunc *dtor; /* Callback to free filter-allocated resources */
|
||||
@ -89,8 +102,11 @@ struct dbfl_ref {
|
||||
void *field; /* Field value */
|
||||
};
|
||||
|
||||
/*
|
||||
* Note that field_size may be larger than MAX_STRING_SIZE.
|
||||
*/
|
||||
typedef struct db_field_log {
|
||||
unsigned int type:2; /* type (union) selector */
|
||||
unsigned int type:1; /* type (union) selector */
|
||||
/* ctx is used for all types */
|
||||
unsigned int ctx:1; /* context (operation type) */
|
||||
/* the following are used for value and reference types */
|
||||
@ -98,8 +114,8 @@ typedef struct db_field_log {
|
||||
unsigned short stat; /* Alarm Status */
|
||||
unsigned short sevr; /* Alarm Severity */
|
||||
short field_type; /* DBF type of data */
|
||||
short field_size; /* Data size */
|
||||
long no_elements; /* No of array elements */
|
||||
short field_size; /* Size of a single element */
|
||||
long no_elements; /* No of valid array elements */
|
||||
union {
|
||||
struct dbfl_val v;
|
||||
struct dbfl_ref r;
|
||||
@ -107,27 +123,19 @@ typedef struct db_field_log {
|
||||
} db_field_log;
|
||||
|
||||
/*
|
||||
* A db_field_log will in one of three types:
|
||||
*
|
||||
* dbfl_type_rec - Reference to record
|
||||
* The field log stores no data itself. Data must instead be taken
|
||||
* via the dbChannel* which must always be provided when along
|
||||
* with the field log.
|
||||
* For this type only the 'type' and 'ctx' members are used.
|
||||
*
|
||||
* dbfl_type_ref - Reference to outside value
|
||||
* Used for variable size (array) data types. Meta-data
|
||||
* is stored in the field log, but value data is stored externally
|
||||
* (see struct dbfl_ref).
|
||||
* For this type all meta-data members are used. The dbfl_ref side of the
|
||||
* data union is used.
|
||||
*
|
||||
* dbfl_type_val - Internal value
|
||||
* Used to store small scalar data. Meta-data and value are
|
||||
* present in this structure and no external references are used.
|
||||
* For this type all meta-data members are used. The dbfl_val side of the
|
||||
* data union is used.
|
||||
* Whether a db_field_log* owns the field data. If this is the case, then the
|
||||
* db_field_log is fully decoupled from the record: there is no need to lock
|
||||
* the record when accessing the data, nor to call any rset methods (like
|
||||
* get_array_info) because this has already been done when we made the copy. A
|
||||
* special case here is that of no (remaining) data (i.e. no_elements==0). In
|
||||
* this case, making a copy is redundant, so we have no dtor. But conceptually
|
||||
* the db_field_log still owns the (empty) data.
|
||||
*/
|
||||
#define dbfl_has_copy(p)\
|
||||
((p) && ((p)->type==dbfl_type_val || (p)->u.r.dtor || (p)->no_elements==0))
|
||||
|
||||
#define dbfl_pfield(p)\
|
||||
((p)->type==dbfl_type_val ? &p->u.v.field : p->u.r.field)
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
@ -34,8 +34,10 @@ static void dbDumpPathCallFunc(const iocshArgBuf *args)
|
||||
static const iocshArg dbDumpRecordArg2 = { "interest level",iocshArgInt};
|
||||
static const iocshArg * const dbDumpRecordArgs[] =
|
||||
{&argPdbbase, &argRecType, &dbDumpRecordArg2};
|
||||
static const iocshFuncDef dbDumpRecordFuncDef =
|
||||
{"dbDumpRecord",3,dbDumpRecordArgs};
|
||||
static const iocshFuncDef dbDumpRecordFuncDef = {"dbDumpRecord",3,dbDumpRecordArgs,
|
||||
"Dump information about the recordTypeName with 'interest level' details.\n"
|
||||
"Example: dbDumpRecord ai 2\n"
|
||||
"If last argument(s) are missing, dump all recordType information in the database.\n"};
|
||||
static void dbDumpRecordCallFunc(const iocshArgBuf *args)
|
||||
{
|
||||
dbDumpRecord(*iocshPpdbbase,args[1].sval,args[2].ival);
|
||||
@ -45,7 +47,10 @@ static void dbDumpRecordCallFunc(const iocshArgBuf *args)
|
||||
static const iocshArg dbDumpMenuArg1 = { "menuName",iocshArgString};
|
||||
static const iocshArg * const dbDumpMenuArgs[] = {
|
||||
&argPdbbase, &dbDumpMenuArg1};
|
||||
static const iocshFuncDef dbDumpMenuFuncDef = {"dbDumpMenu",2,dbDumpMenuArgs};
|
||||
static const iocshFuncDef dbDumpMenuFuncDef = {"dbDumpMenu",2,dbDumpMenuArgs,
|
||||
"Dump information about the available menuNames and choices defined withing 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)
|
||||
{
|
||||
dbDumpMenu(*iocshPpdbbase,args[1].sval);
|
||||
@ -54,8 +59,10 @@ static void dbDumpMenuCallFunc(const iocshArgBuf *args)
|
||||
/* dbDumpRecordType */
|
||||
static const iocshArg * const dbDumpRecordTypeArgs[] =
|
||||
{&argPdbbase, &argRecType};
|
||||
static const iocshFuncDef dbDumpRecordTypeFuncDef =
|
||||
{"dbDumpRecordType",2,dbDumpRecordTypeArgs};
|
||||
static const iocshFuncDef dbDumpRecordTypeFuncDef = {"dbDumpRecordType",2,dbDumpRecordTypeArgs,
|
||||
"Dump information about available fields in the recortTypeName sorted by index and name.\n"
|
||||
"Example: dbDumpRecordType pdbbase calcout\n"
|
||||
"If last argument(s) are missing, dump fields information for all records in the database.\n"};
|
||||
static void dbDumpRecordTypeCallFunc(const iocshArgBuf *args)
|
||||
{
|
||||
dbDumpRecordType(*iocshPpdbbase,args[1].sval);
|
||||
@ -65,7 +72,11 @@ static void dbDumpRecordTypeCallFunc(const iocshArgBuf *args)
|
||||
static const iocshArg dbDumpFieldArg2 = { "fieldName",iocshArgString};
|
||||
static const iocshArg * const dbDumpFieldArgs[] =
|
||||
{&argPdbbase, &argRecType,&dbDumpFieldArg2};
|
||||
static const iocshFuncDef dbDumpFieldFuncDef = {"dbDumpField",3,dbDumpFieldArgs};
|
||||
static const iocshFuncDef dbDumpFieldFuncDef = {"dbDumpField",3,dbDumpFieldArgs,
|
||||
"Dump information about the fieldName in the recordTypeName.\n"
|
||||
"Example: dbDumpField pdbbase calcout A\n"
|
||||
"If last argument(s) are missing, dump information\n"
|
||||
"about all fieldName in all recordTypeName in the database.\n"};
|
||||
static void dbDumpFieldCallFunc(const iocshArgBuf *args)
|
||||
{
|
||||
dbDumpField(*iocshPpdbbase,args[1].sval,args[2].sval);
|
||||
@ -74,7 +85,11 @@ static void dbDumpFieldCallFunc(const iocshArgBuf *args)
|
||||
/* dbDumpDevice */
|
||||
static const iocshArg * const dbDumpDeviceArgs[] = {
|
||||
&argPdbbase, &argRecType};
|
||||
static const iocshFuncDef dbDumpDeviceFuncDef = {"dbDumpDevice",2,dbDumpDeviceArgs};
|
||||
static const iocshFuncDef dbDumpDeviceFuncDef = {"dbDumpDevice",2,dbDumpDeviceArgs,
|
||||
"Dump device support information for the recordTypeName.\n"
|
||||
"Example: dbDumpDevice pdbbase ai\n"
|
||||
"If last argument(s) are missing, dump device support\n"
|
||||
"information for all records in the database.\n"};
|
||||
static void dbDumpDeviceCallFunc(const iocshArgBuf *args)
|
||||
{
|
||||
dbDumpDevice(*iocshPpdbbase,args[1].sval);
|
||||
@ -82,7 +97,9 @@ static void dbDumpDeviceCallFunc(const iocshArgBuf *args)
|
||||
|
||||
/* dbDumpDriver */
|
||||
static const iocshArg * const dbDumpDriverArgs[] = { &argPdbbase};
|
||||
static const iocshFuncDef dbDumpDriverFuncDef = {"dbDumpDriver",1,dbDumpDriverArgs};
|
||||
static const iocshFuncDef dbDumpDriverFuncDef = {"dbDumpDriver",1,dbDumpDriverArgs,
|
||||
"Dump device support information.\n"
|
||||
"Example: dbDumpDriver pdbbase\n"};
|
||||
static void dbDumpDriverCallFunc(const iocshArgBuf *args)
|
||||
{
|
||||
dbDumpDriver(*iocshPpdbbase);
|
||||
@ -98,7 +115,10 @@ static void dbDumpLinkCallFunc(const iocshArgBuf *args)
|
||||
|
||||
/* dbDumpRegistrar */
|
||||
static const iocshArg * const dbDumpRegistrarArgs[] = { &argPdbbase};
|
||||
static const iocshFuncDef dbDumpRegistrarFuncDef = {"dbDumpRegistrar",1,dbDumpRegistrarArgs};
|
||||
static const iocshFuncDef dbDumpRegistrarFuncDef = {"dbDumpRegistrar",1,dbDumpRegistrarArgs,
|
||||
"Dump list of registered functions including ones for subroutine records,\n"
|
||||
"and ones that can be invoked from iocsh.\n"
|
||||
"Example: dbDumpRegistrar pdbbase\n"};
|
||||
static void dbDumpRegistrarCallFunc(const iocshArgBuf *args)
|
||||
{
|
||||
dbDumpRegistrar(*iocshPpdbbase);
|
||||
@ -106,7 +126,9 @@ static void dbDumpRegistrarCallFunc(const iocshArgBuf *args)
|
||||
|
||||
/* dbDumpFunction */
|
||||
static const iocshArg * const dbDumpFunctionArgs[] = { &argPdbbase};
|
||||
static const iocshFuncDef dbDumpFunctionFuncDef = {"dbDumpFunction",1,dbDumpFunctionArgs};
|
||||
static const iocshFuncDef dbDumpFunctionFuncDef = {"dbDumpFunction",1,dbDumpFunctionArgs,
|
||||
"Dump list of registered subroutine functions.\n"
|
||||
"Example: dbDumpFunction pddbase\n"};
|
||||
static void dbDumpFunctionCallFunc(const iocshArgBuf *args)
|
||||
{
|
||||
dbDumpFunction(*iocshPpdbbase);
|
||||
@ -114,7 +136,9 @@ static void dbDumpFunctionCallFunc(const iocshArgBuf *args)
|
||||
|
||||
/* dbDumpVariable */
|
||||
static const iocshArg * const dbDumpVariableArgs[] = { &argPdbbase};
|
||||
static const iocshFuncDef dbDumpVariableFuncDef = {"dbDumpVariable",1,dbDumpVariableArgs};
|
||||
static const iocshFuncDef dbDumpVariableFuncDef = {"dbDumpVariable",1,dbDumpVariableArgs,
|
||||
"Dump list of variables used in the database.\n"
|
||||
"Example: dbDumpVariable pddbase\n"};
|
||||
static void dbDumpVariableCallFunc(const iocshArgBuf *args)
|
||||
{
|
||||
dbDumpVariable(*iocshPpdbbase);
|
||||
@ -124,8 +148,7 @@ static void dbDumpVariableCallFunc(const iocshArgBuf *args)
|
||||
static const iocshArg dbDumpBreaktableArg1 = { "tableName",iocshArgString};
|
||||
static const iocshArg * const dbDumpBreaktableArgs[] =
|
||||
{&argPdbbase,&dbDumpBreaktableArg1};
|
||||
static const iocshFuncDef dbDumpBreaktableFuncDef =
|
||||
{"dbDumpBreaktable",2,dbDumpBreaktableArgs};
|
||||
static const iocshFuncDef dbDumpBreaktableFuncDef = {"dbDumpBreaktable",2,dbDumpBreaktableArgs};
|
||||
static void dbDumpBreaktableCallFunc(const iocshArgBuf *args)
|
||||
{
|
||||
dbDumpBreaktable(*iocshPpdbbase,args[1].sval);
|
||||
@ -145,8 +168,7 @@ static void dbPvdDumpCallFunc(const iocshArgBuf *args)
|
||||
static const iocshArg dbPvdTableSizeArg0 = { "size",iocshArgInt};
|
||||
static const iocshArg * const dbPvdTableSizeArgs[1] =
|
||||
{&dbPvdTableSizeArg0};
|
||||
static const iocshFuncDef dbPvdTableSizeFuncDef =
|
||||
{"dbPvdTableSize",1,dbPvdTableSizeArgs};
|
||||
static const iocshFuncDef dbPvdTableSizeFuncDef = {"dbPvdTableSize",1,dbPvdTableSizeArgs};
|
||||
static void dbPvdTableSizeCallFunc(const iocshArgBuf *args)
|
||||
{
|
||||
dbPvdTableSize(args[0].ival);
|
||||
@ -154,8 +176,7 @@ static void dbPvdTableSizeCallFunc(const iocshArgBuf *args)
|
||||
|
||||
/* dbReportDeviceConfig */
|
||||
static const iocshArg * const dbReportDeviceConfigArgs[] = {&argPdbbase};
|
||||
static const iocshFuncDef dbReportDeviceConfigFuncDef = {
|
||||
"dbReportDeviceConfig",1,dbReportDeviceConfigArgs};
|
||||
static const iocshFuncDef dbReportDeviceConfigFuncDef = {"dbReportDeviceConfig",1,dbReportDeviceConfigArgs};
|
||||
static void dbReportDeviceConfigCallFunc(const iocshArgBuf *args)
|
||||
{
|
||||
dbReportDeviceConfig(*iocshPpdbbase,stdout);
|
||||
|
@ -301,11 +301,11 @@ static void makeSubstitutions(inputData * const inputPvt,
|
||||
char *pstart;
|
||||
char *pend;
|
||||
int cmdind=-1;
|
||||
int i;
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < NELEMENTS(cmdNames); i++) {
|
||||
if (strstr(command, cmdNames[i])) {
|
||||
cmdind = i;
|
||||
cmdind = (int)i;
|
||||
}
|
||||
}
|
||||
if (cmdind < 0) goto endcmd;
|
||||
|
@ -20,35 +20,43 @@
|
||||
#include "miscIocRegister.h"
|
||||
|
||||
/* iocInit */
|
||||
static const iocshFuncDef iocInitFuncDef = {"iocInit",0,NULL};
|
||||
static const iocshFuncDef iocInitFuncDef = {"iocInit",0,NULL,
|
||||
"Initializes the various epics components and starts the IOC running.\n"};
|
||||
static void iocInitCallFunc(const iocshArgBuf *args)
|
||||
{
|
||||
iocshSetError(iocInit());
|
||||
}
|
||||
|
||||
/* iocBuild */
|
||||
static const iocshFuncDef iocBuildFuncDef = {"iocBuild",0,NULL};
|
||||
static const iocshFuncDef iocBuildFuncDef = {"iocBuild",0,NULL,
|
||||
"First step of the IOC initialization, puts the IOC into a ready-to-run (quiescent) state.\n"
|
||||
"Needs iocRun() to make the IOC live.\n"};
|
||||
static void iocBuildCallFunc(const iocshArgBuf *args)
|
||||
{
|
||||
iocshSetError(iocBuild());
|
||||
}
|
||||
|
||||
/* iocRun */
|
||||
static const iocshFuncDef iocRunFuncDef = {"iocRun",0,NULL};
|
||||
static const iocshFuncDef iocRunFuncDef = {"iocRun",0,NULL,
|
||||
"Bring the IOC out of its initial quiescent state to the running state.\n"
|
||||
"See more: iocBuild, iocPause"};
|
||||
static void iocRunCallFunc(const iocshArgBuf *args)
|
||||
{
|
||||
iocshSetError(iocRun());
|
||||
}
|
||||
|
||||
/* iocPause */
|
||||
static const iocshFuncDef iocPauseFuncDef = {"iocPause",0,NULL};
|
||||
static const iocshFuncDef iocPauseFuncDef = {"iocPause",0,NULL,
|
||||
"Brings a running IOC to a quiescent state with all record processing frozen.\n"
|
||||
"See more: iocBuild, iocRub, iocInit"};
|
||||
static void iocPauseCallFunc(const iocshArgBuf *args)
|
||||
{
|
||||
iocshSetError(iocPause());
|
||||
}
|
||||
|
||||
/* coreRelease */
|
||||
static const iocshFuncDef coreReleaseFuncDef = {"coreRelease",0,NULL};
|
||||
static const iocshFuncDef coreReleaseFuncDef = {"coreRelease",0,NULL,
|
||||
"Print release information for iocCore.\n"};
|
||||
static void coreReleaseCallFunc(const iocshArgBuf *args)
|
||||
{
|
||||
coreRelease ();
|
||||
@ -75,7 +83,11 @@ void miscIocRegister(void)
|
||||
#ifndef SYSTEM_UNAVAILABLE
|
||||
static const iocshArg systemArg0 = { "command string",iocshArgString};
|
||||
static const iocshArg * const systemArgs[] = {&systemArg0};
|
||||
static const iocshFuncDef systemFuncDef = {"system",1,systemArgs};
|
||||
static const iocshFuncDef systemFuncDef = {"system",1,systemArgs,
|
||||
"Send command string to the system command interpreter for execution.\n"
|
||||
"Not available on all OSs.\n"
|
||||
"To enable this command, add registrar(iocshSystemCommand) to an application dbd file,\n"
|
||||
"or include system.dbd\n"};
|
||||
static void systemCallFunc(const iocshArgBuf *args)
|
||||
{
|
||||
iocshSetError(system(args[0].sval));
|
||||
|
@ -185,6 +185,21 @@ registerAllRecordDeviceDrivers(DBBASE *pdbbase)
|
||||
registerJLinks(pdbbase, 1, ptr);
|
||||
}
|
||||
|
||||
// for each function()
|
||||
for(ELLNODE *cur = ellFirst(&pdbbase->functionList); cur; cur = ellNext(cur)) {
|
||||
dbText& reg = *CONTAINER(cur, dbText, node);
|
||||
|
||||
typedef void(*registrar)(void);
|
||||
registrar* ptr = lookupAs<registrar*>("pvar_func_register_func_", reg.text);
|
||||
|
||||
if(!ptr || !*ptr) {
|
||||
fprintf(stderr, "Unable to find function '%s' : %s\n", reg.text, epicsLoadError());
|
||||
return 1;
|
||||
}
|
||||
|
||||
runRegistrarOnce(*ptr);
|
||||
}
|
||||
|
||||
// for each registrar()
|
||||
for(ELLNODE *cur = ellFirst(&pdbbase->registrarList); cur; cur = ellNext(cur)) {
|
||||
dbText& reg = *CONTAINER(cur, dbText, node);
|
||||
@ -193,7 +208,7 @@ registerAllRecordDeviceDrivers(DBBASE *pdbbase)
|
||||
registrar* ptr = lookupAs<registrar*>("pvar_func_", reg.text);
|
||||
|
||||
if(!ptr || !*ptr) {
|
||||
fprintf(stderr, "Unable to find registar '%s' : %s\n", reg.text, epicsLoadError());
|
||||
fprintf(stderr, "Unable to find registrar '%s' : %s\n", reg.text, epicsLoadError());
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@ -20,7 +20,12 @@
|
||||
/* casr */
|
||||
static const iocshArg casrArg0 = { "level",iocshArgInt};
|
||||
static const iocshArg * const casrArgs[1] = {&casrArg0};
|
||||
static const iocshFuncDef casrFuncDef = {"casr",1,casrArgs};
|
||||
static const iocshFuncDef casrFuncDef = {"casr",1,casrArgs,
|
||||
"Channel Access Server Report with following levels:\n"
|
||||
" 0 - server’s protocol version level and summary for each attached client\n"
|
||||
" 1 - extends report with information about connected clients and beacons\n"
|
||||
" 2 - extends report with specific channel names and UDP search requests\n"
|
||||
" 3+ - expert\n"};
|
||||
static void casrCallFunc(const iocshArgBuf *args)
|
||||
{
|
||||
casr(args[0].ival);
|
||||
|
@ -47,19 +47,15 @@ static long init_record(dbCommon *pcommon)
|
||||
aaiRecord *prec = (aaiRecord *)pcommon;
|
||||
DBLINK *plink = &prec->inp;
|
||||
|
||||
/* This is pass 0, link hasn't been initialized yet */
|
||||
dbInitLink(plink, DBF_INLINK);
|
||||
/* Ask record to call us in pass 1 instead */
|
||||
if (prec->pact != AAI_DEVINIT_PASS1) {
|
||||
return AAI_DEVINIT_PASS1;
|
||||
}
|
||||
|
||||
if (dbLinkIsConstant(plink)) {
|
||||
long nRequest = prec->nelm;
|
||||
long status;
|
||||
|
||||
/* Allocate a buffer, record support hasn't done that yet */
|
||||
if (!prec->bptr) {
|
||||
prec->bptr = callocMustSucceed(nRequest, dbValueSize(prec->ftvl),
|
||||
"devAaiSoft: buffer calloc failed");
|
||||
}
|
||||
|
||||
status = dbLoadLinkArray(plink, prec->ftvl, prec->bptr, &nRequest);
|
||||
if (!status) {
|
||||
prec->nord = nRequest;
|
||||
|
@ -13,16 +13,14 @@
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include <freeList.h>
|
||||
#include <dbAccess.h>
|
||||
#include <dbExtractArray.h>
|
||||
#include <db_field_log.h>
|
||||
#include <dbLock.h>
|
||||
#include <recSup.h>
|
||||
#include <epicsExit.h>
|
||||
#include <special.h>
|
||||
#include <chfPlugin.h>
|
||||
#include <epicsExport.h>
|
||||
#include "chfPlugin.h"
|
||||
#include "dbAccessDefs.h"
|
||||
#include "dbExtractArray.h"
|
||||
#include "db_field_log.h"
|
||||
#include "dbLock.h"
|
||||
#include "epicsExit.h"
|
||||
#include "freeList.h"
|
||||
#include "epicsExport.h"
|
||||
|
||||
typedef struct myStruct {
|
||||
epicsInt32 start;
|
||||
@ -46,6 +44,8 @@ static void * allocPvt(void)
|
||||
myStruct *my = (myStruct*) freeListCalloc(myStructFreeList);
|
||||
if (!my) return NULL;
|
||||
|
||||
/* defaults */
|
||||
my->start = 0;
|
||||
my->incr = 1;
|
||||
my->end = -1;
|
||||
return (void *) my;
|
||||
@ -77,8 +77,6 @@ static void freeArray(db_field_log *pfl)
|
||||
static long wrapArrayIndices(long *start, const long increment, long *end,
|
||||
const long no_elements)
|
||||
{
|
||||
long len = 0;
|
||||
|
||||
if (*start < 0) *start = no_elements + *start;
|
||||
if (*start < 0) *start = 0;
|
||||
if (*start > no_elements) *start = no_elements;
|
||||
@ -87,85 +85,53 @@ static long wrapArrayIndices(long *start, const long increment, long *end,
|
||||
if (*end < 0) *end = 0;
|
||||
if (*end >= no_elements) *end = no_elements - 1;
|
||||
|
||||
if (*end - *start >= 0) len = 1 + (*end - *start) / increment;
|
||||
return len;
|
||||
if (*end - *start >= 0)
|
||||
return 1 + (*end - *start) / increment;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
static db_field_log* filter(void* pvt, dbChannel *chan, db_field_log *pfl)
|
||||
{
|
||||
myStruct *my = (myStruct*) pvt;
|
||||
struct dbCommon *prec;
|
||||
rset *prset;
|
||||
int must_lock;
|
||||
long start = my->start;
|
||||
long end = my->end;
|
||||
long nTarget = 0;
|
||||
long nTarget;
|
||||
void *pTarget;
|
||||
long offset = 0;
|
||||
long nSource = dbChannelElements(chan);
|
||||
long capacity = nSource;
|
||||
void *pdst;
|
||||
long nSource = pfl->no_elements;
|
||||
void *pSource = pfl->u.r.field;
|
||||
|
||||
switch (pfl->type) {
|
||||
case dbfl_type_val:
|
||||
/* Only filter arrays */
|
||||
/* TODO Treat scalars as arrays with 1 element */
|
||||
break;
|
||||
|
||||
case dbfl_type_rec:
|
||||
/* Extract from record */
|
||||
if (dbChannelSpecial(chan) == SPC_DBADDR &&
|
||||
nSource > 1 &&
|
||||
(prset = dbGetRset(&chan->addr)) &&
|
||||
prset->get_array_info)
|
||||
{
|
||||
void *pfieldsave = dbChannelField(chan);
|
||||
prec = dbChannelRecord(chan);
|
||||
dbScanLock(prec);
|
||||
prset->get_array_info(&chan->addr, &nSource, &offset);
|
||||
nTarget = wrapArrayIndices(&start, my->incr, &end, nSource);
|
||||
pfl->type = dbfl_type_ref;
|
||||
pfl->stat = prec->stat;
|
||||
pfl->sevr = prec->sevr;
|
||||
pfl->time = prec->time;
|
||||
pfl->field_type = dbChannelFieldType(chan);
|
||||
pfl->field_size = dbChannelFieldSize(chan);
|
||||
pfl->no_elements = nTarget;
|
||||
if (nTarget) {
|
||||
pdst = freeListCalloc(my->arrayFreeList);
|
||||
if (pdst) {
|
||||
pfl->u.r.dtor = freeArray;
|
||||
pfl->u.r.pvt = my->arrayFreeList;
|
||||
offset = (offset + start) % dbChannelElements(chan);
|
||||
dbExtractArrayFromRec(&chan->addr, pdst, nTarget, capacity,
|
||||
offset, my->incr);
|
||||
pfl->u.r.field = pdst;
|
||||
}
|
||||
}
|
||||
dbScanUnlock(prec);
|
||||
dbChannelField(chan) = pfieldsave;
|
||||
}
|
||||
break;
|
||||
|
||||
/* Extract from buffer */
|
||||
case dbfl_type_ref:
|
||||
pdst = NULL;
|
||||
nSource = pfl->no_elements;
|
||||
nTarget = wrapArrayIndices(&start, my->incr, &end, nSource);
|
||||
pfl->no_elements = nTarget;
|
||||
if (nTarget) {
|
||||
/* Copy the data out */
|
||||
void *psrc = pfl->u.r.field;
|
||||
|
||||
pdst = freeListCalloc(my->arrayFreeList);
|
||||
if (!pdst) break;
|
||||
offset = start;
|
||||
dbExtractArrayFromBuf(psrc, pdst, pfl->field_size, pfl->field_type,
|
||||
nTarget, nSource, offset, my->incr);
|
||||
must_lock = !pfl->u.r.dtor;
|
||||
if (must_lock) {
|
||||
dbScanLock(dbChannelRecord(chan));
|
||||
dbChannelGetArrayInfo(chan, &pSource, &nSource, &offset);
|
||||
}
|
||||
if (pfl->u.r.dtor) pfl->u.r.dtor(pfl);
|
||||
if (nTarget) {
|
||||
nTarget = wrapArrayIndices(&start, my->incr, &end, nSource);
|
||||
if (nTarget > 0) {
|
||||
/* copy the data */
|
||||
pTarget = freeListCalloc(my->arrayFreeList);
|
||||
if (!pTarget) break;
|
||||
/* must do the wrap-around with the original no_elements */
|
||||
offset = (offset + start) % pfl->no_elements;
|
||||
dbExtractArray(pSource, pTarget, pfl->field_size,
|
||||
nTarget, pfl->no_elements, offset, my->incr);
|
||||
if (pfl->u.r.dtor) pfl->u.r.dtor(pfl);
|
||||
pfl->u.r.field = pTarget;
|
||||
pfl->u.r.dtor = freeArray;
|
||||
pfl->u.r.pvt = my->arrayFreeList;
|
||||
pfl->u.r.field = pdst;
|
||||
}
|
||||
/* adjust no_elements (even if zero elements remain) */
|
||||
pfl->no_elements = nTarget;
|
||||
if (must_lock)
|
||||
dbScanUnlock(dbChannelRecord(chan));
|
||||
break;
|
||||
}
|
||||
return pfl;
|
||||
|
@ -12,21 +12,44 @@
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <chfPlugin.h>
|
||||
#include <dbLock.h>
|
||||
#include <db_field_log.h>
|
||||
#include <epicsExport.h>
|
||||
#include "chfPlugin.h"
|
||||
#include "db_field_log.h"
|
||||
#include "dbExtractArray.h"
|
||||
#include "dbLock.h"
|
||||
#include "epicsExport.h"
|
||||
|
||||
/*
|
||||
* The size of the data is different for each channel, and can even
|
||||
* change at runtime, so a freeList doesn't make much sense here.
|
||||
*/
|
||||
static void freeArray(db_field_log *pfl) {
|
||||
free(pfl->u.r.field);
|
||||
}
|
||||
|
||||
static db_field_log* filter(void* pvt, dbChannel *chan, db_field_log *pfl) {
|
||||
epicsTimeStamp now;
|
||||
epicsTimeGetCurrent(&now);
|
||||
|
||||
/* If string or array, must make a copy (to ensure coherence between time and data) */
|
||||
if (pfl->type == dbfl_type_rec) {
|
||||
dbScanLock(dbChannelRecord(chan));
|
||||
dbChannelMakeArrayCopy(pvt, pfl, chan);
|
||||
dbScanUnlock(dbChannelRecord(chan));
|
||||
/* If reference and not already copied,
|
||||
must make a copy (to ensure coherence between time and data) */
|
||||
if (pfl->type == dbfl_type_ref && !pfl->u.r.dtor) {
|
||||
void *pTarget = calloc(pfl->no_elements, pfl->field_size);
|
||||
void *pSource = pfl->u.r.field;
|
||||
if (pTarget) {
|
||||
long offset = 0;
|
||||
long nSource = pfl->no_elements;
|
||||
dbScanLock(dbChannelRecord(chan));
|
||||
dbChannelGetArrayInfo(chan, &pSource, &nSource, &offset);
|
||||
dbExtractArray(pSource, pTarget, pfl->field_size,
|
||||
nSource, pfl->no_elements, offset, 1);
|
||||
pfl->u.r.field = pTarget;
|
||||
pfl->u.r.dtor = freeArray;
|
||||
pfl->u.r.pvt = pvt;
|
||||
dbScanUnlock(dbChannelRecord(chan));
|
||||
}
|
||||
}
|
||||
|
||||
pfl->time = now;
|
||||
|
@ -112,16 +112,18 @@ static long init_record(struct dbCommon *pcommon, int pass)
|
||||
prec->ftvl = DBF_UCHAR;
|
||||
prec->nord = (prec->nelm == 1);
|
||||
|
||||
/* we must call pdset->init_record in pass 0
|
||||
because it may set prec->bptr which must
|
||||
not change after links are established before pass 1
|
||||
*/
|
||||
|
||||
/* call pdset->init_record() in pass 0 so it can do its own
|
||||
* memory allocation and set prec->bptr, which must be set by
|
||||
* the end of pass 0.
|
||||
*/
|
||||
if (pdset->common.init_record) {
|
||||
long status = pdset->common.init_record(pcommon);
|
||||
|
||||
/* init_record may set the bptr to point to the data */
|
||||
if (status)
|
||||
if (status == AAI_DEVINIT_PASS1) {
|
||||
/* requesting pass 1 callback, remember to do that */
|
||||
prec->pact = AAI_DEVINIT_PASS1;
|
||||
}
|
||||
else if (status)
|
||||
return status;
|
||||
}
|
||||
if (!prec->bptr) {
|
||||
@ -132,6 +134,14 @@ static long init_record(struct dbCommon *pcommon, int pass)
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (prec->pact == AAI_DEVINIT_PASS1) {
|
||||
/* device support asked for an init_record() callback in pass 1 */
|
||||
long status = pdset->common.init_record(pcommon);
|
||||
if (status)
|
||||
return status;
|
||||
prec->pact = FALSE;
|
||||
}
|
||||
|
||||
recGblInitSimm(pcommon, &prec->sscn, &prec->oldsimm, &prec->simm, &prec->siml);
|
||||
|
||||
/* must have read_aai function defined */
|
||||
|
@ -151,10 +151,15 @@ for more information on simulation mode and its fields.
|
||||
|
||||
static long init_record(aaiRecord *prec, int pass)
|
||||
|
||||
If device support includes C<init_record()>, it is called.
|
||||
If device support includes an C<init_record()> routine it is called, but unlike
|
||||
most record types this occurs in pass 0, which allows the device support to
|
||||
allocate the array buffer itself.
|
||||
|
||||
Since EPICS 7.0.5 the device support may return C<AAI_DEVINIT_PASS1> to request
|
||||
a second call to its C<init_record()> routine in pass 1.
|
||||
|
||||
Checks if device support allocated array space. If not, space for the array is
|
||||
allocated using NELM and FTVL. The array address is stored in the record.
|
||||
allocated using NELM and FTVL. The array address is stored in BPTR.
|
||||
|
||||
This routine initializes SIMM with the value of SIML if SIML type is CONSTANT
|
||||
link or creates a channel access link if SIML type is PV_LINK. VAL is likewise
|
||||
@ -294,10 +299,11 @@ Scan forward link if necessary, set PACT FALSE, and return.
|
||||
%/* Declare Device Support Entry Table */
|
||||
%struct aaiRecord;
|
||||
%typedef struct aaidset {
|
||||
% dset common; /*init_record returns: (-1,0)=>(failure,success)*/
|
||||
% dset common; /*init_record returns: (-1,0,AAI_DEVINIT_PASS1)=>(failure,success,callback)*/
|
||||
% long (*read_aai)(struct aaiRecord *prec); /*returns: (-1,0)=>(failure,success)*/
|
||||
%} aaidset;
|
||||
%#define HAS_aaidset
|
||||
%#define AAI_DEVINIT_PASS1 2
|
||||
%
|
||||
field(VAL,DBF_NOACCESS) {
|
||||
prompt("Value")
|
||||
@ -469,8 +475,19 @@ with C<after> set to 1.
|
||||
|
||||
long init_record(dbCommon *precord)
|
||||
|
||||
This routine is optional. If provided, it is called by the record support
|
||||
C<init_record()> routine.
|
||||
This routine is optional.
|
||||
If provided, it is called by the record support's C<init_record()> routine in
|
||||
pass 0.
|
||||
The device support may allocate memory for the VAL field's array (enough space
|
||||
for NELM elements of type FTVA) from its own memory pool if desired, and store
|
||||
the pointer to this buffer in the BPTR field.
|
||||
The record will use C<calloc()> for this memory allocation if BPTR has not been
|
||||
set by this routine.
|
||||
The routine must return 0 for success, -1 or a error status on failure.
|
||||
|
||||
Since EPICS 7.0.5 if this routine returns C<AAI_DEVINIT_PASS1> in pass 0, it
|
||||
will be called again in pass 1 with the PACT field set to C<AAI_DEVINIT_PASS1>.
|
||||
In pass 0 the PACT field is set to zero (FALSE).
|
||||
|
||||
=head4 get_ioint_info
|
||||
|
||||
@ -485,7 +502,8 @@ provided for any device type that can use the ioEvent scanner.
|
||||
|
||||
long read_aai(dbCommon *precord)
|
||||
|
||||
This routine must provide a new input value. It returns the following values:
|
||||
This routine should provide a new input value.
|
||||
It returns the following values:
|
||||
|
||||
=over
|
||||
|
||||
@ -501,16 +519,15 @@ Other: Error.
|
||||
|
||||
=head3 Device Support For Soft Records
|
||||
|
||||
The C<<< Soft Channel >>> device support module is provided to read values from
|
||||
other records and store them in arrays. If INP is a constant link, then read_aai
|
||||
does nothing. In this case, the record can be used to hold arrays written via
|
||||
dbPuts. If INP is a database or channel access link, the new array value is read
|
||||
from the link. NORD is set.
|
||||
The C<<< Soft Channel >>> device support is provided to read values from other
|
||||
records via the INP link, or to hold array values that are written into it.
|
||||
|
||||
This module places a value directly in VAL and NORD is set to the number of items
|
||||
in the array.
|
||||
If INP is a constant link the array value gets loaded from the link constant by
|
||||
the C<record_init()> routine, which also sets NORD.
|
||||
The C<read_aai()> routine does nothing in this case.
|
||||
|
||||
If the INP link type is constant, then NORD is set to zero.
|
||||
If INP is a database or channel access link, the C<read_aai()> routine gets a
|
||||
new array value from the link and sets NORD.
|
||||
|
||||
=cut
|
||||
}
|
||||
|
@ -106,7 +106,7 @@ static void monitor(compressRecord *prec)
|
||||
db_post_events(prec, &prec->nuse, monitor_mask);
|
||||
prec->ouse = prec->nuse;
|
||||
}
|
||||
db_post_events(prec, prec->bptr, monitor_mask);
|
||||
db_post_events(prec, (void*)&prec->val, monitor_mask);
|
||||
}
|
||||
|
||||
static void put_value(compressRecord *prec, double *psource, int n)
|
||||
@ -404,7 +404,6 @@ static long cvt_dbaddr(DBADDR *paddr)
|
||||
{
|
||||
compressRecord *prec = (compressRecord *) paddr->precord;
|
||||
|
||||
paddr->pfield = prec->bptr;
|
||||
paddr->no_elements = prec->nsam;
|
||||
paddr->field_type = DBF_DOUBLE;
|
||||
paddr->field_size = sizeof(double);
|
||||
@ -426,6 +425,8 @@ static long get_array_info(DBADDR *paddr, long *no_elements, long *offset)
|
||||
epicsUInt32 off = prec->off;
|
||||
epicsUInt32 nuse = prec->nuse;
|
||||
|
||||
paddr->pfield = prec->bptr;
|
||||
|
||||
if (prec->balg == bufferingALG_FIFO) {
|
||||
epicsUInt32 nsam = prec->nsam;
|
||||
|
||||
|
@ -111,7 +111,7 @@ static void wdogCallback(epicsCallback *arg)
|
||||
if (prec->mcnt > 0){
|
||||
dbScanLock((struct dbCommon *)prec);
|
||||
recGblGetTimeStamp(prec);
|
||||
db_post_events(prec, prec->bptr, DBE_VALUE | DBE_LOG);
|
||||
db_post_events(prec, (void*)&prec->val, DBE_VALUE | DBE_LOG);
|
||||
prec->mcnt = 0;
|
||||
dbScanUnlock((struct dbCommon *)prec);
|
||||
}
|
||||
@ -291,7 +291,7 @@ static void monitor(histogramRecord *prec)
|
||||
}
|
||||
/* send out monitors connected to the value field */
|
||||
if (monitor_mask)
|
||||
db_post_events(prec, prec->bptr, monitor_mask);
|
||||
db_post_events(prec, (void*)&prec->val, monitor_mask);
|
||||
|
||||
return;
|
||||
}
|
||||
@ -300,7 +300,6 @@ static long cvt_dbaddr(DBADDR *paddr)
|
||||
{
|
||||
histogramRecord *prec = (histogramRecord *) paddr->precord;
|
||||
|
||||
paddr->pfield = prec->bptr;
|
||||
paddr->no_elements = prec->nelm;
|
||||
paddr->field_type = DBF_ULONG;
|
||||
paddr->field_size = sizeof(epicsUInt32);
|
||||
@ -312,6 +311,7 @@ static long get_array_info(DBADDR *paddr, long *no_elements, long *offset)
|
||||
{
|
||||
histogramRecord *prec = (histogramRecord *) paddr->precord;
|
||||
|
||||
paddr->pfield = prec->bptr;
|
||||
*no_elements = prec->nelm;
|
||||
*offset = 0;
|
||||
return 0;
|
||||
|
@ -161,7 +161,6 @@ static long cvt_dbaddr(DBADDR *paddr)
|
||||
{
|
||||
subArrayRecord *prec = (subArrayRecord *) paddr->precord;
|
||||
|
||||
paddr->pfield = prec->bptr;
|
||||
paddr->no_elements = prec->malm;
|
||||
paddr->field_type = prec->ftvl;
|
||||
paddr->field_size = dbValueSize(prec->ftvl);
|
||||
@ -174,6 +173,7 @@ static long get_array_info(DBADDR *paddr, long *no_elements, long *offset)
|
||||
{
|
||||
subArrayRecord *prec = (subArrayRecord *) paddr->precord;
|
||||
|
||||
paddr->pfield = prec->bptr;
|
||||
if (prec->udf)
|
||||
*no_elements = 0;
|
||||
else
|
||||
@ -293,7 +293,7 @@ static void monitor(subArrayRecord *prec)
|
||||
monitor_mask = recGblResetAlarms(prec);
|
||||
monitor_mask |= (DBE_LOG|DBE_VALUE);
|
||||
|
||||
db_post_events(prec, prec->bptr, monitor_mask);
|
||||
db_post_events(prec, (void*)&prec->val, monitor_mask);
|
||||
|
||||
return;
|
||||
}
|
||||
|
@ -1,2 +1,2 @@
|
||||
include "xxxRecord.dbd"
|
||||
device(xxx,CONSTANT,devXxxSoft,"SoftChannel")
|
||||
device(xxx,CONSTANT,devXxxSoft,"Soft Channel")
|
||||
|
@ -67,13 +67,16 @@ sub toDeclaration {
|
||||
my $name = $this->name;
|
||||
my $macro_name = "${name}_NUM_CHOICES";
|
||||
my @choices = map {
|
||||
sprintf " %-31s /* %s */", @{$_}[0], escapeCcomment(@{$_}[1]);
|
||||
sprintf " %-31s /**< \@brief State string \"%s\" */",
|
||||
@{$_}[0], escapeCcomment(@{$_}[1]);
|
||||
} $this->choices;
|
||||
my $num = scalar @choices;
|
||||
return "#ifndef $macro_name\n" .
|
||||
"/** \@brief Enumerated type from menu $name */\n" .
|
||||
"typedef enum {\n" .
|
||||
join(",\n", @choices) .
|
||||
"\n} $name;\n" .
|
||||
"/** \@brief Number of states defined for menu $name */\n" .
|
||||
"#define $macro_name $num\n" .
|
||||
"#endif\n\n";
|
||||
}
|
||||
|
@ -29,6 +29,7 @@ use DBD::Function;
|
||||
use DBD::Variable;
|
||||
|
||||
our $debug=0;
|
||||
our $allowAutoDeclarations=0;
|
||||
|
||||
sub ParseDBD {
|
||||
(my $dbd, $_) = @_;
|
||||
@ -102,8 +103,11 @@ sub ParseDBD {
|
||||
unquote($1, $2, $3, $4);
|
||||
my $rtyp = $dbd->recordtype($record_type);
|
||||
if (!defined $rtyp) {
|
||||
my $msg = "Device '$choice' refers to unknown record type '$record_type'.";
|
||||
dieContext($msg, "DBD files must be combined in the correct order.")
|
||||
unless $allowAutoDeclarations;
|
||||
warn "$msg\nRecord type '$record_type' declared.\n";
|
||||
$rtyp = DBD::Recordtype->new($record_type);
|
||||
warn "Device using unknown record type '$record_type', declaration created\n";
|
||||
$dbd->add($rtyp);
|
||||
}
|
||||
$rtyp->add_device(DBD::Device->new($link_type, $dset, $choice));
|
||||
|
@ -185,7 +185,7 @@ sub toDeclaration {
|
||||
my $name = $this->C_name;
|
||||
my $result = sprintf " %-19s %-12s", $ctype, "$name;";
|
||||
my $prompt = $this->attribute('prompt');
|
||||
$result .= "/* $prompt */" if defined $prompt;
|
||||
$result .= "/**< \@brief $prompt */" if defined $prompt;
|
||||
return $result;
|
||||
}
|
||||
|
||||
@ -217,7 +217,7 @@ sub toDeclaration {
|
||||
my $size = $this->attribute('size');
|
||||
my $result = sprintf " %-19s %-12s", 'char', "${name}[${size}];";
|
||||
my $prompt = $this->attribute('prompt');
|
||||
$result .= "/* $prompt */" if defined $prompt;
|
||||
$result .= "/**< \@brief $prompt */" if defined $prompt;
|
||||
return $result;
|
||||
}
|
||||
|
||||
@ -540,7 +540,7 @@ sub toDeclaration {
|
||||
my $extra = $this->attribute('extra');
|
||||
my $result = sprintf " %-31s ", "$extra;";
|
||||
my $prompt = $this->attribute('prompt');
|
||||
$result .= "/* $prompt */" if defined $prompt;
|
||||
$result .= "/**< \@brief $prompt */" if defined $prompt;
|
||||
return $result;
|
||||
}
|
||||
|
||||
|
@ -132,10 +132,15 @@ sub toDeclaration {
|
||||
$_->toDeclaration
|
||||
} $this->fields;
|
||||
my $name = $this->name;
|
||||
$name .= "Record" unless $name eq "dbCommon";
|
||||
return "typedef struct $name {\n" .
|
||||
join("\n", @fields) .
|
||||
"\n} $name;\n\n";
|
||||
my $doc = $name;
|
||||
if ($name ne 'dbCommon') {
|
||||
$name .= 'Record';
|
||||
$doc .= ' record type.';
|
||||
}
|
||||
return "/** \@brief Declaration of $doc */\n" .
|
||||
"typedef struct $name {\n" .
|
||||
join("\n", @fields) .
|
||||
"\n} $name;\n\n";
|
||||
}
|
||||
|
||||
1;
|
||||
|
@ -11,6 +11,8 @@
|
||||
use FindBin qw($Bin);
|
||||
use lib ("$Bin/../../lib/perl");
|
||||
|
||||
use strict;
|
||||
|
||||
use EPICS::Getopts;
|
||||
use File::Basename;
|
||||
use DBD;
|
||||
@ -57,7 +59,9 @@ if ($opt_D) {
|
||||
print map { "$_:\n" } @uniqfiles;
|
||||
} else {
|
||||
open OUTFILE, ">$outfile" or die "$tool: Can't open $outfile: $!\n";
|
||||
print OUTFILE "/* $outbase generated from $inbase */\n\n",
|
||||
print OUTFILE "/** \@file $outbase\n",
|
||||
" * \@brief Declarations generated from $inbase\n",
|
||||
" */\n\n",
|
||||
"#ifndef $guard_name\n",
|
||||
"#define $guard_name\n\n";
|
||||
my $menus = $dbd->menus;
|
||||
|
@ -61,13 +61,19 @@ if ($opt_D) { # Output dependencies only, to stdout
|
||||
print "$outfile: ", join(" \\\n ", @uniqfiles), "\n\n";
|
||||
print map { "$_:\n" } @uniqfiles;
|
||||
} else {
|
||||
our ($rn, $rtyp) = each %{$rtypes};
|
||||
my $rtn = $rn;
|
||||
$rtn .= 'Record' if $rn ne 'dbCommon';
|
||||
|
||||
open OUTFILE, ">$outfile" or die "$tool: Can't open $outfile: $!\n";
|
||||
print OUTFILE "/* $outbase generated from $inbase */\n\n",
|
||||
print OUTFILE "/** \@file $outbase\n",
|
||||
" * \@brief Declarations for the \@ref $rtn \"$rn\" record type.\n",
|
||||
" *\n",
|
||||
" * This header was generated from $inbase\n",
|
||||
" */\n\n",
|
||||
"#ifndef $guard_name\n",
|
||||
"#define $guard_name\n\n";
|
||||
|
||||
our ($rn, $rtyp) = each %{$rtypes};
|
||||
|
||||
print OUTFILE $rtyp->toCdefs;
|
||||
|
||||
my @menu_fields = grep {
|
||||
@ -111,31 +117,47 @@ if ($opt_D) { # Output dependencies only, to stdout
|
||||
|
||||
sub oldtables {
|
||||
# Output compatible with R3.14.x
|
||||
|
||||
my @fields = $rtyp->fields;
|
||||
my $no_fields = scalar @fields;
|
||||
|
||||
print OUTFILE << "__EOF__";
|
||||
#include <epicsExport.h>
|
||||
#include <cantProceed.h>
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
static int ${rn}RecordSizeOffset(dbRecordType *prt)
|
||||
{
|
||||
${rn}Record *prec = 0;
|
||||
|
||||
if (prt->no_fields != ${no_fields}) {
|
||||
cantProceed("IOC build or installation error:\\n"
|
||||
" The ${rn}Record defined in the DBD file has %d fields,\\n"
|
||||
" but the record support code was built with ${no_fields}.\\n",
|
||||
prt->no_fields);
|
||||
}
|
||||
__EOF__
|
||||
|
||||
print OUTFILE
|
||||
"#include <epicsAssert.h>\n" .
|
||||
"#include <epicsExport.h>\n" .
|
||||
"#ifdef __cplusplus\n" .
|
||||
"extern \"C\" {\n" .
|
||||
"#endif\n" .
|
||||
"static int ${rn}RecordSizeOffset(dbRecordType *prt)\n" .
|
||||
"{\n" .
|
||||
" ${rn}Record *prec = 0;\n\n" .
|
||||
" assert(prt->no_fields == " . scalar($rtyp->fields) . ");\n" .
|
||||
join("\n", map {
|
||||
" prt->papFldDes[${rn}Record" . $_->name . "]->size = " .
|
||||
"sizeof(prec->" . $_->C_name . ");"
|
||||
} $rtyp->fields) . "\n" .
|
||||
join("\n", map {
|
||||
" prt->papFldDes[${rn}Record" . $_->name . "]->offset = (unsigned short)(" .
|
||||
"(char *)&prec->" . $_->C_name . " - (char *)prec);"
|
||||
} $rtyp->fields) . "\n" .
|
||||
" prt->rec_size = sizeof(*prec);\n" .
|
||||
" return 0;\n" .
|
||||
"}\n" .
|
||||
"epicsExportRegistrar(${rn}RecordSizeOffset);\n\n" .
|
||||
"#ifdef __cplusplus\n" .
|
||||
"}\n" .
|
||||
"#endif\n";
|
||||
my $fn = $_->name;
|
||||
my $cn = $_->C_name;
|
||||
" prt->papFldDes[${rn}Record${fn}]->size = " .
|
||||
"sizeof(prec->${cn});\n" .
|
||||
" prt->papFldDes[${rn}Record${fn}]->offset = " .
|
||||
"(unsigned short)((char *)&prec->${cn} - (char *)prec);"
|
||||
} @fields), << "__EOF__";
|
||||
|
||||
prt->rec_size = sizeof(*prec);
|
||||
return 0;
|
||||
}
|
||||
epicsExportRegistrar(${rn}RecordSizeOffset);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
__EOF__
|
||||
}
|
||||
|
||||
sub newtables {
|
||||
|
@ -31,6 +31,9 @@ my @path = map { split /[:;]/ } @opt_I; # FIXME: Broken on Win32?
|
||||
|
||||
my ($file, $subname, $bldTop) = @ARGV;
|
||||
|
||||
# Auto-declaration of record types is needed to build loadable modules
|
||||
$DBD::Parser::allowAutoDeclarations = 1;
|
||||
|
||||
my $dbd = DBD->new();
|
||||
ParseDBD($dbd, Readfile($file, "", \@path));
|
||||
|
||||
|
@ -131,7 +131,7 @@ static void check(short dbr_type) {
|
||||
memset(buf, 0, sizeof(buf)); \
|
||||
(void) dbPutField(&offaddr, DBR_LONG, &off, 1); \
|
||||
pfl = db_create_read_log(pch); \
|
||||
testOk(pfl && pfl->type == dbfl_type_rec, "Valid pfl, type = rec"); \
|
||||
testOk(pfl && pfl->type == dbfl_type_ref, "Valid pfl, type = ref"); \
|
||||
testOk(!dbChannelGetField(pch, DBR_LONG, buf, NULL, &req, pfl), "Got Field value"); \
|
||||
testOk(req == Size, "Got %ld elements (expected %d)", req, Size); \
|
||||
if (!testOk(!memcmp(buf, Expected, sizeof(Expected)), "Data correct")) \
|
||||
|
@ -73,9 +73,9 @@ static int fl_equals_array(short type, const db_field_log *pfl1, void *p2) {
|
||||
}
|
||||
break;
|
||||
case DBR_STRING:
|
||||
if (strtol(&((const char*)pfl1->u.r.field)[i*MAX_STRING_SIZE], NULL, 0) != ((epicsInt32*)p2)[i]) {
|
||||
if (strtol(&((const char*)pfl1->u.r.field)[i*pfl1->field_size], NULL, 0) != ((epicsInt32*)p2)[i]) {
|
||||
testDiag("at index=%d: field log has '%s', should be '%d'",
|
||||
i, &((const char*)pfl1->u.r.field)[i*MAX_STRING_SIZE], ((epicsInt32*)p2)[i]);
|
||||
i, &((const char*)pfl1->u.r.field)[i*pfl1->field_size], ((epicsInt32*)p2)[i]);
|
||||
return 0;
|
||||
}
|
||||
break;
|
||||
@ -120,7 +120,7 @@ static void testHead (const char *title, const char *typ = "") {
|
||||
off = Offset; \
|
||||
(void) dbPutField(&offaddr, DBR_LONG, &off, 1); \
|
||||
pfl = db_create_read_log(pch); \
|
||||
testOk(pfl->type == dbfl_type_rec, "original field log has type rec"); \
|
||||
testOk(pfl->type == dbfl_type_ref, "original field log has type ref"); \
|
||||
pfl2 = dbChannelRunPostChain(pch, pfl); \
|
||||
testOk(pfl2 == pfl, "call does not drop or replace field_log"); \
|
||||
testOk(pfl->type == dbfl_type_ref, "filtered field log has type ref"); \
|
||||
|
@ -130,7 +130,7 @@ MAIN(dbndTest)
|
||||
dbEventCtx evtctx;
|
||||
int logsFree, logsFinal;
|
||||
|
||||
testPlan(77);
|
||||
testPlan(72);
|
||||
|
||||
testdbPrepare();
|
||||
|
||||
@ -171,12 +171,9 @@ MAIN(dbndTest)
|
||||
"dbnd has one filter with argument in pre chain");
|
||||
testOk((ellCount(&pch->post_chain) == 0), "dbnd has no filter in post chain");
|
||||
|
||||
/* Field logs of type ref and rec: pass any update */
|
||||
|
||||
testHead("Field logs of type ref and rec");
|
||||
fl1.type = dbfl_type_rec;
|
||||
mustPassTwice(pch, &fl1, "abs field_log=rec", 0., 0);
|
||||
/* Field logs of type ref: pass any update */
|
||||
|
||||
testHead("Field logs of type ref");
|
||||
fl1.type = dbfl_type_ref;
|
||||
mustPassTwice(pch, &fl1, "abs field_log=ref", 0., 0);
|
||||
|
||||
|
@ -30,10 +30,12 @@ is_deeply $menu->choice(2), undef, 'Third choice undefined';
|
||||
|
||||
like $menu->toDeclaration, qr/ ^
|
||||
\s* \# \s* ifndef \s+ test_NUM_CHOICES \s* \n
|
||||
\s* \/\*\* [^*]* \*\/ \s* \n
|
||||
\s* typedef \s+ enum \s+ \{ \s* \n
|
||||
\s* ch1 \s+ \/\* [^*]* \*\/, \s* \n
|
||||
\s* ch2 \s+ \/\* [^*]* \*\/ \s* \n
|
||||
\s* ch1 \s+ \/\*\* [^*]* \*\/, \s* \n
|
||||
\s* ch2 \s+ \/\*\* [^*]* \*\/ \s* \n
|
||||
\s* \} \s* test \s* ; \s* \n
|
||||
\s* \/\*\* [^*]* \*\/ \s* \n
|
||||
\s* \# \s* define \s+ test_NUM_CHOICES \s+ 2 \s* \n
|
||||
\s* \# \s* endif \s* \n
|
||||
\s* $ /x, 'C declaration';
|
||||
|
@ -11,6 +11,20 @@
|
||||
#ifndef INC_errlog_H
|
||||
#define INC_errlog_H
|
||||
|
||||
/** \file errlog.h
|
||||
* \brief Functions for interacting with the errlog task
|
||||
*
|
||||
* This file contains functions for passing error messages with varying severity,
|
||||
* registering and un-registering listeners and modifying the log buffer size and
|
||||
* max message size.
|
||||
*
|
||||
* Some of these functions are similar to the standard C library functions printf
|
||||
* and vprintf. For details on the arguments and return codes it is useful to consult
|
||||
* any book that describes the standard C library such as
|
||||
* `The C Programming Language ANSI C Edition` by Kernighan and Ritchie.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
@ -22,8 +36,15 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* errlogListener function type.
|
||||
*
|
||||
* This is used when adding or removing log listeners in ::errlogAddListener
|
||||
* and ::errlogRemoveListeners.
|
||||
*/
|
||||
typedef void (*errlogListener)(void *pPrivate, const char *message);
|
||||
|
||||
/** errlog severity enums */
|
||||
typedef enum {
|
||||
errlogInfo,
|
||||
errlogMinor,
|
||||
@ -45,37 +66,171 @@ LIBCOM_API extern int errVerbose;
|
||||
LIBCOM_API extern const char * errlogSevEnumString[];
|
||||
#endif
|
||||
|
||||
/* errMessage is a macro so it can get the file and line number */
|
||||
/**
|
||||
* errMessage is a macro so it can get the file and line number. It prints the message,
|
||||
* the status symbol and string values, and the name of the task which invoked errMessage.
|
||||
* It also prints the name of the source file and the line number from which the call was issued.
|
||||
*
|
||||
* The status code used for the 1st argument is:
|
||||
* - 0: Find latest vxWorks or Unix error (errno value).
|
||||
* - -1: Don’t report status.
|
||||
* - Other: Use this status code and lookup the string value
|
||||
*
|
||||
* \param S Status code
|
||||
* \param PM The message to print
|
||||
*/
|
||||
#define errMessage(S, PM) \
|
||||
errPrintf(S, __FILE__, __LINE__, "%s", PM)
|
||||
/* epicsPrintf and epicsVprintf are old names for errlog routines*/
|
||||
|
||||
/** epicsPrintf is an old name for errlog routines */
|
||||
#define epicsPrintf errlogPrintf
|
||||
|
||||
/** epicsVprintf is an old name for errlog routines */
|
||||
#define epicsVprintf errlogVprintf
|
||||
|
||||
/**
|
||||
* errlogPrintf is like the printf function provided by the standard C library, except
|
||||
* that the output is sent to the errlog task. Unless configured not to, the output
|
||||
* will appear on the console as well.
|
||||
*/
|
||||
LIBCOM_API int errlogPrintf(const char *pformat, ...)
|
||||
EPICS_PRINTF_STYLE(1,2);
|
||||
|
||||
/**
|
||||
* errlogVprintf is like the vprintf function provided by the standard C library, except
|
||||
* that the output is sent to the errlog task. Unless configured not to, the output
|
||||
* will appear on the console as well.
|
||||
*/
|
||||
LIBCOM_API int errlogVprintf(const char *pformat, va_list pvar);
|
||||
|
||||
/**
|
||||
* This function is like ::errlogPrintf except that it adds the severity to the beginning
|
||||
* of the message in the form `sevr=<value>` where value is one of the enumerated
|
||||
* severities in ::errlogSevEnum. Also the message is suppressed if severity is less than
|
||||
* the current severity to suppress.
|
||||
*
|
||||
* \param severity One of the severity enums from ::errlogSevEnum
|
||||
* \param pFormat The message to log or print
|
||||
* \return int Consult printf documentation in C standard library
|
||||
*/
|
||||
LIBCOM_API int errlogSevPrintf(const errlogSevEnum severity,
|
||||
const char *pformat, ...) EPICS_PRINTF_STYLE(2,3);
|
||||
|
||||
/**
|
||||
* This function is like ::errlogVprintf except that it adds the severity to the beginning
|
||||
* of the message in the form `sevr=<value>` where value is one of the enumerated
|
||||
* severities in ::errlogSevEnum. Also the message is suppressed if severity is less than
|
||||
* the current severity to suppress. If epicsThreadIsOkToBlock is true, which is
|
||||
* true during iocInit, errlogSevVprintf does NOT send output to the
|
||||
* errlog task.
|
||||
*
|
||||
* \param severity One of the severity enums from ::errlogSevEnum
|
||||
* \param pFormat The message to log or print
|
||||
* \param pvar va_list
|
||||
* \return int Consult printf documentation in C standard library
|
||||
*/
|
||||
LIBCOM_API int errlogSevVprintf(const errlogSevEnum severity,
|
||||
const char *pformat, va_list pvar);
|
||||
|
||||
/**
|
||||
* Sends message to the errlog task.
|
||||
*
|
||||
* \param message The message to send
|
||||
*/
|
||||
LIBCOM_API int errlogMessage(const char *message);
|
||||
|
||||
/**
|
||||
* Gets the string value of severity.
|
||||
*
|
||||
* \param severity The severity from ::errlogSevEnum
|
||||
* \return The string value
|
||||
*/
|
||||
LIBCOM_API const char * errlogGetSevEnumString(errlogSevEnum severity);
|
||||
|
||||
/**
|
||||
* Sets the severity to log
|
||||
*
|
||||
* \param severity The severity from ::errlogSevEnum
|
||||
*/
|
||||
LIBCOM_API void errlogSetSevToLog(errlogSevEnum severity);
|
||||
|
||||
/**
|
||||
* Gets the current severity to log
|
||||
*
|
||||
* \return ::errlogSevEnum
|
||||
*/
|
||||
LIBCOM_API errlogSevEnum errlogGetSevToLog(void);
|
||||
|
||||
/**
|
||||
* Any code can receive errlog message. This function will add a listener callback.
|
||||
*
|
||||
* \param listener Function pointer of type ::errlogListener
|
||||
* \param pPrivate This will be passed as the first argument of listener()
|
||||
*/
|
||||
LIBCOM_API void errlogAddListener(errlogListener listener, void *pPrivate);
|
||||
|
||||
/**
|
||||
* This function will remove a listener callback.
|
||||
*
|
||||
* \param listener Function pointer of type ::errlogListener
|
||||
* \param pPrivate This will be passed as the first argument of listener()
|
||||
*/
|
||||
LIBCOM_API int errlogRemoveListeners(errlogListener listener,
|
||||
void *pPrivate);
|
||||
|
||||
/**
|
||||
* Normally the errlog system displays all messages on the console.
|
||||
* During error message storms this function can be used to suppress console messages.
|
||||
* A argument of 0 suppresses the messages, any other value lets messages go to the console.
|
||||
*
|
||||
* \param yesno (0=No, 1=Yes)
|
||||
* \return 0
|
||||
*/
|
||||
LIBCOM_API int eltc(int yesno);
|
||||
|
||||
/**
|
||||
* Sets a new stream to write the messages to
|
||||
*
|
||||
* \param stream Pointer to file handle
|
||||
* \return 0
|
||||
*/
|
||||
LIBCOM_API int errlogSetConsole(FILE *stream);
|
||||
|
||||
/**
|
||||
* Can be used to initialize the error logging system with a larger buffer. The default buffer size is 1280 bytes.
|
||||
*
|
||||
* \param bufsize The desired buffer size
|
||||
*/
|
||||
LIBCOM_API int errlogInit(int bufsize);
|
||||
|
||||
/**
|
||||
* errlogInit2 can be used to initialize the error logging system with a larger buffer and maximum message size.
|
||||
* The default buffer size is 1280 bytes, and the default maximum message size is 256.
|
||||
*
|
||||
* \param bufsize The desired buffer size
|
||||
* \param maxMsgSize The desired max message size
|
||||
*/
|
||||
LIBCOM_API int errlogInit2(int bufsize, int maxMsgSize);
|
||||
|
||||
/** Wakes up the errlog task and then waits until all messages are flushed from the queue. */
|
||||
LIBCOM_API void errlogFlush(void);
|
||||
|
||||
/**
|
||||
* Routine errPrintf is normally called as follows:
|
||||
* `errPrintf(status, __FILE__, __LINE__,"<fmt>",...); `
|
||||
*
|
||||
* Where status is defined as:
|
||||
* - 0: Find latest vxWorks or Unix error.
|
||||
* - -1: Don’t report status.
|
||||
* - Other: Use this status code and lookup the string value
|
||||
*
|
||||
* \param status See above
|
||||
* \param __FILE__ As shown or NULL if the file name and line number should not be printed.
|
||||
* \param __LINE__ As shown
|
||||
*
|
||||
* The remaining arguments are just like the arguments to the C printf routine.
|
||||
* ::errVerbose determines if the filename and line number are shown.
|
||||
*/
|
||||
LIBCOM_API void errPrintf(long status, const char *pFileName, int lineno,
|
||||
const char *pformat, ...) EPICS_PRINTF_STYLE(4,5);
|
||||
|
||||
@ -83,6 +238,13 @@ LIBCOM_API int errlogPrintfNoConsole(const char *pformat, ...)
|
||||
EPICS_PRINTF_STYLE(1,2);
|
||||
LIBCOM_API int errlogVprintfNoConsole(const char *pformat,va_list pvar);
|
||||
|
||||
/**
|
||||
* Lookup the status code and return the string value in pBuf
|
||||
*
|
||||
* \param status The status code to lookup
|
||||
* \param pBuf The char buffer to write the string value into
|
||||
* \param bufLength The max size of pBuf
|
||||
*/
|
||||
LIBCOM_API void errSymLookup(long status, char *pBuf, size_t bufLength);
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
@ -163,7 +163,12 @@ const iocshCmdDef * epicsStdCall iocshFindCommand(const char *name)
|
||||
static const iocshArg varCmdArg0 = { "[variable", iocshArgString};
|
||||
static const iocshArg varCmdArg1 = { "[value]]", iocshArgString};
|
||||
static const iocshArg *varCmdArgs[2] = {&varCmdArg0, &varCmdArg1};
|
||||
static const iocshFuncDef varFuncDef = {"var", 2, varCmdArgs};
|
||||
static const iocshFuncDef varFuncDef = {"var", 2, varCmdArgs,
|
||||
"Print all, print single variable or set value to single variable\n"
|
||||
" (default) - print all variables and their values"
|
||||
" defined in database definitions files\n"
|
||||
" variable - if only parameter print value for this variable\n"
|
||||
" value - set the value to variable\n"};
|
||||
|
||||
void epicsStdCall iocshRegisterVariable (const iocshVarDef *piocshVarDef)
|
||||
{
|
||||
@ -1139,7 +1144,10 @@ static void varCallFunc(const iocshArgBuf *args)
|
||||
/* iocshCmd */
|
||||
static const iocshArg iocshCmdArg0 = { "command",iocshArgString};
|
||||
static const iocshArg *iocshCmdArgs[1] = {&iocshCmdArg0};
|
||||
static const iocshFuncDef iocshCmdFuncDef = {"iocshCmd",1,iocshCmdArgs};
|
||||
static const iocshFuncDef iocshCmdFuncDef = {"iocshCmd",1,iocshCmdArgs,
|
||||
"Takes a single IOC shell command and executes it\n"
|
||||
" * This function is most useful to execute a single IOC shell command\n"
|
||||
" from vxWorks or RTEMS startup script (or command line)\n"};
|
||||
static void iocshCmdCallFunc(const iocshArgBuf *args)
|
||||
{
|
||||
iocshCmd(args[0].sval);
|
||||
@ -1149,7 +1157,9 @@ static void iocshCmdCallFunc(const iocshArgBuf *args)
|
||||
static const iocshArg iocshLoadArg0 = { "pathname",iocshArgString};
|
||||
static const iocshArg iocshLoadArg1 = { "macros", iocshArgString};
|
||||
static const iocshArg *iocshLoadArgs[2] = {&iocshLoadArg0, &iocshLoadArg1};
|
||||
static const iocshFuncDef iocshLoadFuncDef = {"iocshLoad",2,iocshLoadArgs};
|
||||
static const iocshFuncDef iocshLoadFuncDef = {"iocshLoad",2,iocshLoadArgs,
|
||||
"Execute IOC shell commands provided in file from first parameter\n"
|
||||
" * (optional) replace macros within the file with provided values\n"};
|
||||
static void iocshLoadCallFunc(const iocshArgBuf *args)
|
||||
{
|
||||
iocshLoad(args[0].sval, args[1].sval);
|
||||
@ -1159,7 +1169,10 @@ static void iocshLoadCallFunc(const iocshArgBuf *args)
|
||||
static const iocshArg iocshRunArg0 = { "command",iocshArgString};
|
||||
static const iocshArg iocshRunArg1 = { "macros", iocshArgString};
|
||||
static const iocshArg *iocshRunArgs[2] = {&iocshRunArg0, &iocshRunArg1};
|
||||
static const iocshFuncDef iocshRunFuncDef = {"iocshRun",2,iocshRunArgs};
|
||||
static const iocshFuncDef iocshRunFuncDef = {"iocshRun",2,iocshRunArgs,
|
||||
"Takes a single IOC shell command, replaces macros and executes it\n"
|
||||
" * This function is most useful to execute a single IOC shell command\n"
|
||||
" from vxWorks or RTEMS startup script (or command line)\n"};
|
||||
static void iocshRunCallFunc(const iocshArgBuf *args)
|
||||
{
|
||||
iocshRun(args[0].sval, args[1].sval);
|
||||
|
@ -46,7 +46,9 @@ void date(const char *format)
|
||||
|
||||
static const iocshArg dateArg0 = { "format",iocshArgString};
|
||||
static const iocshArg * const dateArgs[] = {&dateArg0};
|
||||
static const iocshFuncDef dateFuncDef = {"date", 1, dateArgs};
|
||||
static const iocshFuncDef dateFuncDef = {"date", 1, dateArgs,
|
||||
"Print current date and time\n"
|
||||
" (default) - '%Y/%m/%d %H:%M:%S.%06f'\n"};
|
||||
static void dateCallFunc (const iocshArgBuf *args)
|
||||
{
|
||||
date(args[0].sval);
|
||||
@ -64,7 +66,8 @@ IOCSH_STATIC_FUNC void echo(char* str)
|
||||
|
||||
static const iocshArg echoArg0 = { "string",iocshArgString};
|
||||
static const iocshArg * const echoArgs[1] = {&echoArg0};
|
||||
static const iocshFuncDef echoFuncDef = {"echo",1,echoArgs};
|
||||
static const iocshFuncDef echoFuncDef = {"echo",1,echoArgs,
|
||||
"Print string after expanding macros and environment variables\n"};
|
||||
static void echoCallFunc(const iocshArgBuf *args)
|
||||
{
|
||||
echo(args[0].sval);
|
||||
@ -73,7 +76,8 @@ static void echoCallFunc(const iocshArgBuf *args)
|
||||
/* chdir */
|
||||
static const iocshArg chdirArg0 = { "directory name",iocshArgString};
|
||||
static const iocshArg * const chdirArgs[1] = {&chdirArg0};
|
||||
static const iocshFuncDef chdirFuncDef = {"cd",1,chdirArgs};
|
||||
static const iocshFuncDef chdirFuncDef = {"cd",1,chdirArgs,
|
||||
"Change directory to new directory provided as parameter\n"};
|
||||
static void chdirCallFunc(const iocshArgBuf *args)
|
||||
{
|
||||
if (args[0].sval == NULL ||
|
||||
@ -83,7 +87,8 @@ static void chdirCallFunc(const iocshArgBuf *args)
|
||||
}
|
||||
|
||||
/* print current working directory */
|
||||
static const iocshFuncDef pwdFuncDef = { "pwd", 0, 0 };
|
||||
static const iocshFuncDef pwdFuncDef = {"pwd", 0, 0,
|
||||
"Print name of current/working directory\n"};
|
||||
static void pwdCallFunc (const iocshArgBuf *args)
|
||||
{
|
||||
char buf[256];
|
||||
@ -97,7 +102,8 @@ static void pwdCallFunc (const iocshArgBuf *args)
|
||||
static const iocshArg epicsEnvSetArg0 = { "name",iocshArgString};
|
||||
static const iocshArg epicsEnvSetArg1 = { "value",iocshArgString};
|
||||
static const iocshArg * const epicsEnvSetArgs[2] = {&epicsEnvSetArg0,&epicsEnvSetArg1};
|
||||
static const iocshFuncDef epicsEnvSetFuncDef = {"epicsEnvSet",2,epicsEnvSetArgs};
|
||||
static const iocshFuncDef epicsEnvSetFuncDef = {"epicsEnvSet",2,epicsEnvSetArgs,
|
||||
"Set environment variable name to value\n"};
|
||||
static void epicsEnvSetCallFunc(const iocshArgBuf *args)
|
||||
{
|
||||
char *name = args[0].sval;
|
||||
@ -117,7 +123,8 @@ static void epicsEnvSetCallFunc(const iocshArgBuf *args)
|
||||
/* epicsEnvUnset */
|
||||
static const iocshArg epicsEnvUnsetArg0 = { "name",iocshArgString};
|
||||
static const iocshArg * const epicsEnvUnsetArgs[1] = {&epicsEnvUnsetArg0};
|
||||
static const iocshFuncDef epicsEnvUnsetFuncDef = {"epicsEnvUnset",1,epicsEnvUnsetArgs};
|
||||
static const iocshFuncDef epicsEnvUnsetFuncDef = {"epicsEnvUnset",1,epicsEnvUnsetArgs,
|
||||
"Remove variable name from the environment\n"};
|
||||
static void epicsEnvUnsetCallFunc(const iocshArgBuf *args)
|
||||
{
|
||||
char *name = args[0].sval;
|
||||
@ -135,14 +142,16 @@ IOCSH_STATIC_FUNC void epicsParamShow()
|
||||
epicsPrtEnvParams ();
|
||||
}
|
||||
|
||||
static const iocshFuncDef epicsParamShowFuncDef = {"epicsParamShow",0,NULL};
|
||||
static const iocshFuncDef epicsParamShowFuncDef = {"epicsParamShow",0,NULL,
|
||||
"Show the environment variable parameters used by iocCore\n"};
|
||||
static void epicsParamShowCallFunc(const iocshArgBuf *args)
|
||||
{
|
||||
epicsParamShow ();
|
||||
}
|
||||
|
||||
/* epicsPrtEnvParams */
|
||||
static const iocshFuncDef epicsPrtEnvParamsFuncDef = {"epicsPrtEnvParams",0,0};
|
||||
static const iocshFuncDef epicsPrtEnvParamsFuncDef = {"epicsPrtEnvParams",0,0,
|
||||
"Show the environment variable parameters used by iocCore\n"};
|
||||
static void epicsPrtEnvParamsCallFunc(const iocshArgBuf *args)
|
||||
{
|
||||
epicsPrtEnvParams ();
|
||||
@ -151,21 +160,29 @@ static void epicsPrtEnvParamsCallFunc(const iocshArgBuf *args)
|
||||
/* epicsEnvShow */
|
||||
static const iocshArg epicsEnvShowArg0 = { "[name]",iocshArgString};
|
||||
static const iocshArg * const epicsEnvShowArgs[1] = {&epicsEnvShowArg0};
|
||||
static const iocshFuncDef epicsEnvShowFuncDef = {"epicsEnvShow",1,epicsEnvShowArgs};
|
||||
static const iocshFuncDef epicsEnvShowFuncDef = {"epicsEnvShow",1,epicsEnvShowArgs,
|
||||
"Show environment variables on your system\n"
|
||||
" (default) - show all environment variables\n"
|
||||
" name - show value of specific environment variable\n"};
|
||||
static void epicsEnvShowCallFunc(const iocshArgBuf *args)
|
||||
{
|
||||
epicsEnvShow (args[0].sval);
|
||||
}
|
||||
|
||||
/* registryDump */
|
||||
static const iocshFuncDef registryDumpFuncDef = {"registryDump",0,NULL};
|
||||
static const iocshFuncDef registryDumpFuncDef = {"registryDump",0,NULL,
|
||||
"Dump a hash table of EPICS registry\n"};
|
||||
static void registryDumpCallFunc(const iocshArgBuf *args)
|
||||
{
|
||||
registryDump ();
|
||||
}
|
||||
|
||||
/* iocLogInit */
|
||||
static const iocshFuncDef iocLogInitFuncDef = {"iocLogInit",0};
|
||||
static const iocshFuncDef iocLogInitFuncDef = {"iocLogInit",0,0,
|
||||
"Initialize IOC logging\n"
|
||||
" * EPICS environment variable 'EPICS_IOC_LOG_INET' has to be defined\n"
|
||||
" * Logging controled via 'iocLogDisable' variable\n"
|
||||
" see 'setIocLogDisable' command\n"};
|
||||
static void iocLogInitCallFunc(const iocshArgBuf *args)
|
||||
{
|
||||
iocLogInit ();
|
||||
@ -179,7 +196,10 @@ IOCSH_STATIC_FUNC void setIocLogDisable(int val)
|
||||
|
||||
static const iocshArg iocLogDisableArg0 = {"(0,1)=>(false,true)",iocshArgInt};
|
||||
static const iocshArg * const iocLogDisableArgs[1] = {&iocLogDisableArg0};
|
||||
static const iocshFuncDef iocLogDisableFuncDef = {"setIocLogDisable",1,iocLogDisableArgs};
|
||||
static const iocshFuncDef iocLogDisableFuncDef = {"setIocLogDisable",1,iocLogDisableArgs,
|
||||
"Controls the 'iocLogDisable' variable\n"
|
||||
" 0 - enable logging\n"
|
||||
" 1 - disable logging\n"};
|
||||
static void iocLogDisableCallFunc(const iocshArgBuf *args)
|
||||
{
|
||||
setIocLogDisable(args[0].ival);
|
||||
@ -188,7 +208,8 @@ static void iocLogDisableCallFunc(const iocshArgBuf *args)
|
||||
/* iocLogShow */
|
||||
static const iocshArg iocLogShowArg0 = {"level",iocshArgInt};
|
||||
static const iocshArg * const iocLogShowArgs[1] = {&iocLogShowArg0};
|
||||
static const iocshFuncDef iocLogShowFuncDef = {"iocLogShow",1,iocLogShowArgs};
|
||||
static const iocshFuncDef iocLogShowFuncDef = {"iocLogShow",1,iocLogShowArgs,
|
||||
"Determine if a IOC Log Prefix has been set\n"};
|
||||
static void iocLogShowCallFunc(const iocshArgBuf *args)
|
||||
{
|
||||
iocLogShow (args[0].ival);
|
||||
@ -197,17 +218,22 @@ static void iocLogShowCallFunc(const iocshArgBuf *args)
|
||||
/* eltc */
|
||||
static const iocshArg eltcArg0 = {"(0,1)=>(false,true)",iocshArgInt};
|
||||
static const iocshArg * const eltcArgs[1] = {&eltcArg0};
|
||||
static const iocshFuncDef eltcFuncDef = {"eltc",1,eltcArgs};
|
||||
static const iocshFuncDef eltcFuncDef = {"eltc",1,eltcArgs,
|
||||
"Control display of error log messages on console\n"
|
||||
" 0 - no\n"
|
||||
" 1 - yes (default)\n"};
|
||||
static void eltcCallFunc(const iocshArgBuf *args)
|
||||
{
|
||||
eltc(args[0].ival);
|
||||
}
|
||||
|
||||
/* errlogInit */
|
||||
static const iocshArg errlogInitArg0 = { "bufsize",iocshArgInt};
|
||||
static const iocshArg errlogInitArg0 = { "bufSize",iocshArgInt};
|
||||
static const iocshArg * const errlogInitArgs[1] = {&errlogInitArg0};
|
||||
static const iocshFuncDef errlogInitFuncDef =
|
||||
{"errlogInit",1,errlogInitArgs};
|
||||
{"errlogInit",1,errlogInitArgs,
|
||||
"Initialize error log client buffer size\n"
|
||||
" bufSize - size of circular buffer (default = 1280 bytes)\n"};
|
||||
static void errlogInitCallFunc(const iocshArgBuf *args)
|
||||
{
|
||||
errlogInit(args[0].ival);
|
||||
@ -219,7 +245,10 @@ static const iocshArg errlogInit2Arg1 = { "maxMsgSize",iocshArgInt};
|
||||
static const iocshArg * const errlogInit2Args[] =
|
||||
{&errlogInit2Arg0, &errlogInit2Arg1};
|
||||
static const iocshFuncDef errlogInit2FuncDef =
|
||||
{"errlogInit2", 2, errlogInit2Args};
|
||||
{"errlogInit2", 2, errlogInit2Args,
|
||||
"Initialize error log client buffer size and maximum message size\n"
|
||||
" bufSize - size of circular buffer (default = 1280 bytes)\n"
|
||||
" maxMsgSize - maximum size of error message (default = 256 bytes)\n"};
|
||||
static void errlogInit2CallFunc(const iocshArgBuf *args)
|
||||
{
|
||||
errlogInit2(args[0].ival, args[1].ival);
|
||||
@ -233,7 +262,8 @@ IOCSH_STATIC_FUNC void errlog(const char *message)
|
||||
|
||||
static const iocshArg errlogArg0 = { "message",iocshArgString};
|
||||
static const iocshArg * const errlogArgs[1] = {&errlogArg0};
|
||||
static const iocshFuncDef errlogFuncDef = {"errlog",1,errlogArgs};
|
||||
static const iocshFuncDef errlogFuncDef = {"errlog",1,errlogArgs,
|
||||
"Send message to errlog\n"};
|
||||
static void errlogCallFunc(const iocshArgBuf *args)
|
||||
{
|
||||
errlog(args[0].sval);
|
||||
@ -243,7 +273,8 @@ static void errlogCallFunc(const iocshArgBuf *args)
|
||||
/* iocLogPrefix */
|
||||
static const iocshArg iocLogPrefixArg0 = { "prefix",iocshArgString};
|
||||
static const iocshArg * const iocLogPrefixArgs[1] = {&iocLogPrefixArg0};
|
||||
static const iocshFuncDef iocLogPrefixFuncDef = {"iocLogPrefix",1,iocLogPrefixArgs};
|
||||
static const iocshFuncDef iocLogPrefixFuncDef = {"iocLogPrefix",1,iocLogPrefixArgs,
|
||||
"Create the prefix for all messages going into IOC log\n"};
|
||||
static void iocLogPrefixCallFunc(const iocshArgBuf *args)
|
||||
{
|
||||
iocLogPrefix(args[0].sval);
|
||||
@ -253,7 +284,8 @@ static void iocLogPrefixCallFunc(const iocshArgBuf *args)
|
||||
static const iocshArg epicsThreadShowAllArg0 = { "level",iocshArgInt};
|
||||
static const iocshArg * const epicsThreadShowAllArgs[1] = {&epicsThreadShowAllArg0};
|
||||
static const iocshFuncDef epicsThreadShowAllFuncDef =
|
||||
{"epicsThreadShowAll",1,epicsThreadShowAllArgs};
|
||||
{"epicsThreadShowAll",1,epicsThreadShowAllArgs,
|
||||
"Display info about all threads\n"};
|
||||
static void epicsThreadShowAllCallFunc(const iocshArgBuf *args)
|
||||
{
|
||||
epicsThreadShowAll(args[0].ival);
|
||||
@ -262,7 +294,8 @@ static void epicsThreadShowAllCallFunc(const iocshArgBuf *args)
|
||||
/* epicsThreadShow */
|
||||
static const iocshArg threadArg0 = { "[-level] [thread ...]", iocshArgArgv};
|
||||
static const iocshArg * const threadArgs[1] = { &threadArg0 };
|
||||
static const iocshFuncDef threadFuncDef = {"epicsThreadShow",1,threadArgs};
|
||||
static const iocshFuncDef threadFuncDef = {"epicsThreadShow",1,threadArgs,
|
||||
"Display info about the specified thread\n"};
|
||||
static void threadCallFunc(const iocshArgBuf *args)
|
||||
{
|
||||
int i = 1;
|
||||
@ -308,7 +341,8 @@ static void threadCallFunc(const iocshArgBuf *args)
|
||||
static const iocshArg taskwdShowArg0 = { "level",iocshArgInt};
|
||||
static const iocshArg * const taskwdShowArgs[1] = {&taskwdShowArg0};
|
||||
static const iocshFuncDef taskwdShowFuncDef =
|
||||
{"taskwdShow",1,taskwdShowArgs};
|
||||
{"taskwdShow",1,taskwdShowArgs,
|
||||
"Show number of tasks and monitors registered\n"};
|
||||
static void taskwdShowCallFunc(const iocshArgBuf *args)
|
||||
{
|
||||
taskwdShow(args[0].ival);
|
||||
@ -320,7 +354,10 @@ static const iocshArg epicsMutexShowAllArg1 = { "level",iocshArgInt};
|
||||
static const iocshArg * const epicsMutexShowAllArgs[2] =
|
||||
{&epicsMutexShowAllArg0,&epicsMutexShowAllArg1};
|
||||
static const iocshFuncDef epicsMutexShowAllFuncDef =
|
||||
{"epicsMutexShowAll",2,epicsMutexShowAllArgs};
|
||||
{"epicsMutexShowAll",2,epicsMutexShowAllArgs,
|
||||
"Display information about all epicsMutex semaphores\n"
|
||||
" onlyLocked - non-zero to show only locked semaphores\n"
|
||||
" level - desired information level to report\n"};
|
||||
static void epicsMutexShowAllCallFunc(const iocshArgBuf *args)
|
||||
{
|
||||
epicsMutexShowAll(args[0].ival,args[1].ival);
|
||||
@ -330,7 +367,8 @@ static void epicsMutexShowAllCallFunc(const iocshArgBuf *args)
|
||||
static const iocshArg epicsThreadSleepArg0 = { "seconds",iocshArgDouble};
|
||||
static const iocshArg * const epicsThreadSleepArgs[1] = {&epicsThreadSleepArg0};
|
||||
static const iocshFuncDef epicsThreadSleepFuncDef =
|
||||
{"epicsThreadSleep",1,epicsThreadSleepArgs};
|
||||
{"epicsThreadSleep",1,epicsThreadSleepArgs,
|
||||
"Pause execution of IOC shell for <seconds> seconds\n"};
|
||||
static void epicsThreadSleepCallFunc(const iocshArgBuf *args)
|
||||
{
|
||||
epicsThreadSleep(args[0].dval);
|
||||
@ -339,7 +377,10 @@ static void epicsThreadSleepCallFunc(const iocshArgBuf *args)
|
||||
/* epicsThreadResume */
|
||||
static const iocshArg epicsThreadResumeArg0 = { "[thread ...]", iocshArgArgv};
|
||||
static const iocshArg * const epicsThreadResumeArgs[1] = { &epicsThreadResumeArg0 };
|
||||
static const iocshFuncDef epicsThreadResumeFuncDef = {"epicsThreadResume",1,epicsThreadResumeArgs};
|
||||
static const iocshFuncDef epicsThreadResumeFuncDef = {"epicsThreadResume",1,epicsThreadResumeArgs,
|
||||
"Resume a suspended thread.\n"
|
||||
"Only do this if you know that it is safe to "
|
||||
"resume a suspended thread\n"};
|
||||
static void epicsThreadResumeCallFunc(const iocshArgBuf *args)
|
||||
{
|
||||
int i;
|
||||
@ -381,14 +422,19 @@ static void epicsThreadResumeCallFunc(const iocshArgBuf *args)
|
||||
/* generalTimeReport */
|
||||
static const iocshArg generalTimeReportArg0 = { "interest_level", iocshArgArgv};
|
||||
static const iocshArg * const generalTimeReportArgs[1] = { &generalTimeReportArg0 };
|
||||
static const iocshFuncDef generalTimeReportFuncDef = {"generalTimeReport",1,generalTimeReportArgs};
|
||||
static const iocshFuncDef generalTimeReportFuncDef = {"generalTimeReport",1,generalTimeReportArgs,
|
||||
"Display time providers and their priority levels"
|
||||
" for current and event times\n"};
|
||||
static void generalTimeReportCallFunc(const iocshArgBuf *args)
|
||||
{
|
||||
generalTimeReport(args[0].ival);
|
||||
}
|
||||
|
||||
/* installLastResortEventProvider */
|
||||
static const iocshFuncDef installLastResortEventProviderFuncDef = {"installLastResortEventProvider", 0, NULL};
|
||||
static const iocshFuncDef installLastResortEventProviderFuncDef = {"installLastResortEventProvider", 0, NULL,
|
||||
"Installs the optional Last Resort event provider"
|
||||
" at priority 999,\nwhich returns the current time"
|
||||
" for every event number\n"};
|
||||
static void installLastResortEventProviderCallFunc(const iocshArgBuf *args)
|
||||
{
|
||||
installLastResortEventProvider();
|
||||
|
@ -13,6 +13,20 @@
|
||||
#ifndef INC_epicsStdlib_H
|
||||
#define INC_epicsStdlib_H
|
||||
|
||||
/**
|
||||
* \file epicsStdlib.h
|
||||
* \brief Functions to convert strings to primative types
|
||||
*
|
||||
* These routines convert a string into an integer of the indicated type and
|
||||
* number base, or into a floating point type. The units pointer argument may
|
||||
* be NULL, but if not it will be left pointing to the first non-whitespace
|
||||
* character following the numeric string, or to the terminating zero byte.
|
||||
*
|
||||
* The return value from these routines is a status code, zero meaning OK.
|
||||
* For the macro functions beginning with `epicsScan` the return code is 0
|
||||
* or 1 (0=failure or 1=success, similar to the sscanf() function).
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <limits.h>
|
||||
|
||||
@ -25,57 +39,167 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/** Return code for `No digits to convert` */
|
||||
#define S_stdlib_noConversion (M_stdlib | 1) /* No digits to convert */
|
||||
/** Return code for `Extraneous characters` */
|
||||
#define S_stdlib_extraneous (M_stdlib | 2) /* Extraneous characters */
|
||||
/** Return code for `Too small to represent` */
|
||||
#define S_stdlib_underflow (M_stdlib | 3) /* Too small to represent */
|
||||
/** Return code for `Too large to represent` */
|
||||
#define S_stdlib_overflow (M_stdlib | 4) /* Too large to represent */
|
||||
/** Return code for `Number base not supported` */
|
||||
#define S_stdlib_badBase (M_stdlib | 5) /* Number base not supported */
|
||||
|
||||
|
||||
LIBCOM_API int
|
||||
/**
|
||||
* \brief Convert a string to a long type
|
||||
*
|
||||
* \param str Pointer to a constant character array
|
||||
* \param to Pointer to the specified type (this will be set during the conversion)
|
||||
* \param base The number base to use
|
||||
* \param units Pointer to a char * (this will be set with the units string)
|
||||
* \return Status code (0=OK, see macro definitions for possible errors)
|
||||
*/
|
||||
LIBCOM_API int
|
||||
epicsParseLong(const char *str, long *to, int base, char **units);
|
||||
|
||||
/**
|
||||
* \brief Convert a string to a unsigned long type
|
||||
* \copydetails epicsParseLong
|
||||
*/
|
||||
LIBCOM_API int
|
||||
epicsParseULong(const char *str, unsigned long *to, int base, char **units);
|
||||
|
||||
/**
|
||||
* \brief Convert a string to a long long type
|
||||
* \copydetails epicsParseLong
|
||||
*/
|
||||
LIBCOM_API int
|
||||
epicsParseLLong(const char *str, long long *to, int base, char **units);
|
||||
|
||||
/**
|
||||
* \brief Convert a string to a unsigned long long type
|
||||
* \copydetails epicsParseLong
|
||||
*/
|
||||
LIBCOM_API int
|
||||
epicsParseULLong(const char *str, unsigned long long *to, int base, char **units);
|
||||
|
||||
/**
|
||||
* \brief Convert a string to a double type
|
||||
*
|
||||
* \param str Pointer to a constant character array
|
||||
* \param to Pointer to the specified type (this will be set during the conversion)
|
||||
* \param units Pointer to a char * (this will be set with the units string)
|
||||
* \return Status code (0=OK, see macro definitions for possible errors)
|
||||
*/
|
||||
LIBCOM_API int
|
||||
epicsParseDouble(const char *str, double *to, char **units);
|
||||
|
||||
/**
|
||||
* \brief Convert a string to a float type
|
||||
* \copydetails epicsParseDouble
|
||||
*/
|
||||
LIBCOM_API int
|
||||
epicsParseFloat(const char *str, float *to, char **units);
|
||||
|
||||
/**
|
||||
* \brief Convert a string to an epicsInt8 type
|
||||
* \copydetails epicsParseLong
|
||||
*/
|
||||
LIBCOM_API int
|
||||
epicsParseInt8(const char *str, epicsInt8 *to, int base, char **units);
|
||||
|
||||
/**
|
||||
* \brief Convert a string to an epicsUInt8 type
|
||||
* \copydetails epicsParseLong
|
||||
*/
|
||||
LIBCOM_API int
|
||||
epicsParseUInt8(const char *str, epicsUInt8 *to, int base, char **units);
|
||||
|
||||
/**
|
||||
* \brief Convert a string to an epicsInt16 type
|
||||
* \copydetails epicsParseLong
|
||||
*/
|
||||
LIBCOM_API int
|
||||
epicsParseInt16(const char *str, epicsInt16 *to, int base, char **units);
|
||||
|
||||
/**
|
||||
* \brief Convert a string to an epicsUInt16 type
|
||||
* \copydetails epicsParseLong
|
||||
*/
|
||||
LIBCOM_API int
|
||||
epicsParseUInt16(const char *str, epicsUInt16 *to, int base, char **units);
|
||||
|
||||
/**
|
||||
* \brief Convert a string to an epicsInt32 type
|
||||
* \copydetails epicsParseLong
|
||||
*/
|
||||
LIBCOM_API int
|
||||
epicsParseInt32(const char *str, epicsInt32 *to, int base, char **units);
|
||||
|
||||
/**
|
||||
* \brief Convert a string to an epicsUInt32 type
|
||||
* \copydetails epicsParseLong
|
||||
*/
|
||||
LIBCOM_API int
|
||||
epicsParseUInt32(const char *str, epicsUInt32 *to, int base, char **units);
|
||||
|
||||
/**
|
||||
* \brief Convert a string to an epicsInt64 type
|
||||
* \copydetails epicsParseLong
|
||||
*/
|
||||
LIBCOM_API int
|
||||
epicsParseInt64(const char *str, epicsInt64 *to, int base, char **units);
|
||||
|
||||
/**
|
||||
* \brief Convert a string to an epicsUInt64 type
|
||||
* \copydetails epicsParseLong
|
||||
*/
|
||||
LIBCOM_API int
|
||||
epicsParseUInt64(const char *str, epicsUInt64 *to, int base, char **units);
|
||||
|
||||
/** Macro utilizing ::epicsParseFloat to convert */
|
||||
#define epicsParseFloat32(str, to, units) epicsParseFloat(str, to, units)
|
||||
/** Macro utilizing ::epicsParseDouble to convert */
|
||||
#define epicsParseFloat64(str, to, units) epicsParseDouble(str, to, units)
|
||||
|
||||
/* These macros return 1 if successful, 0 on failure.
|
||||
* This is analagous to the return value from sscanf()
|
||||
*/
|
||||
|
||||
/**
|
||||
* Macro utilizing ::epicsParseLong to convert
|
||||
* \return 0=failure, 1=success
|
||||
*/
|
||||
#define epicsScanLong(str, to, base) (!epicsParseLong(str, to, base, NULL))
|
||||
|
||||
/**
|
||||
* Macro utilizing ::epicsParseULong to convert
|
||||
* \return 0=failure, 1=success
|
||||
*/
|
||||
#define epicsScanULong(str, to, base) (!epicsParseULong(str, to, base, NULL))
|
||||
|
||||
/**
|
||||
* Macro utilizing ::epicsParseLLong to convert
|
||||
* \return 0=failure, 1=success
|
||||
*/
|
||||
#define epicsScanLLong(str, to, base) (!epicsParseLLong(str, to, base, NULL))
|
||||
|
||||
/**
|
||||
* Macro utilizing ::epicsParseULLong to convert
|
||||
* \return 0=failure, 1=success
|
||||
*/
|
||||
#define epicsScanULLong(str, to, base) (!epicsParseULLong(str, to, base, NULL))
|
||||
|
||||
/**
|
||||
* Macro utilizing ::epicsParseFloat to convert
|
||||
* \return 0=failure, 1=success
|
||||
*/
|
||||
#define epicsScanFloat(str, to) (!epicsParseFloat(str, to, NULL))
|
||||
|
||||
/**
|
||||
* Macro utilizing ::epicsParseDouble to convert
|
||||
* \return 0=failure, 1=success
|
||||
*/
|
||||
#define epicsScanDouble(str, to) (!epicsParseDouble(str, to, NULL))
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
@ -48,7 +48,7 @@ LIBCOM_API unsigned int epicsMemHash(const char *str, size_t length,
|
||||
* @returns 1.0 when A and B are identical, down to 0.0 when A and B are unrelated,
|
||||
* or < 0.0 on error.
|
||||
*
|
||||
* @since UNRELEASED
|
||||
* @since EPICS 7.0.5
|
||||
*/
|
||||
LIBCOM_API double epicsStrSimilarity(const char *A, const char *B);
|
||||
|
||||
|
@ -8,9 +8,11 @@
|
||||
* in file LICENSE that is included with this distribution.
|
||||
\*************************************************************************/
|
||||
|
||||
/*
|
||||
* Author: Jeff Hill
|
||||
* Date: 5-95
|
||||
/**
|
||||
* \file epicsTypes.h
|
||||
* \author: Jeff Hill
|
||||
*
|
||||
* \brief The core data types used by epics
|
||||
*/
|
||||
|
||||
#ifndef INC_epicsTypes_H
|
||||
@ -32,9 +34,12 @@ typedef enum {
|
||||
epicsTrue = 1
|
||||
} epicsBoolean EPICS_DEPRECATED;
|
||||
|
||||
/*
|
||||
/**
|
||||
* \name epicsTypes
|
||||
* Architecture Independent Data Types
|
||||
*
|
||||
* These are sufficient for all our current archs
|
||||
* @{
|
||||
*/
|
||||
typedef char epicsInt8;
|
||||
typedef unsigned char epicsUInt8;
|
||||
@ -49,25 +54,28 @@ typedef epicsUInt16 epicsEnum16;
|
||||
typedef float epicsFloat32;
|
||||
typedef double epicsFloat64;
|
||||
typedef epicsInt32 epicsStatus;
|
||||
/** @} */
|
||||
|
||||
#define MAX_STRING_SIZE 40
|
||||
|
||||
/**
|
||||
* \brief !! Dont use this - it may vanish in the future !!
|
||||
*/
|
||||
typedef struct {
|
||||
unsigned length;
|
||||
char *pString;
|
||||
} epicsString;
|
||||
|
||||
/*
|
||||
* !! Dont use this - it may vanish in the future !!
|
||||
/**
|
||||
* \brief !! Dont use this - it may vanish in the future !!
|
||||
*
|
||||
* Provided only for backwards compatibility with
|
||||
* db_access.h
|
||||
*
|
||||
*/
|
||||
#define MAX_STRING_SIZE 40
|
||||
typedef char epicsOldString[MAX_STRING_SIZE];
|
||||
|
||||
/*
|
||||
* union of all types
|
||||
/**
|
||||
* \brief Union of all types
|
||||
*
|
||||
* Strings included here as pointers only so that we support
|
||||
* large string types.
|
||||
@ -90,11 +98,11 @@ typedef union epics_any {
|
||||
epicsString string;
|
||||
} epicsAny;
|
||||
|
||||
/*
|
||||
* Corresponding Type Codes
|
||||
/**
|
||||
* \brief Corresponding Type Codes
|
||||
* (this enum must start at zero)
|
||||
*
|
||||
* !! Update epicsTypeToDBR_XXXX[] and DBR_XXXXToEpicsType
|
||||
* !! Update \ref epicsTypeToDBR_XXXX[] and \ref DBR_XXXXToEpicsType
|
||||
* in db_access.h if you edit this enum !!
|
||||
*/
|
||||
typedef enum {
|
||||
@ -116,8 +124,9 @@ typedef enum {
|
||||
#define invalidEpicsType(x) ((x<firstEpicsType) || (x>lastEpicsType))
|
||||
|
||||
|
||||
/*
|
||||
* The enumeration "epicsType" is an index to this array
|
||||
/**
|
||||
* \brief An array providing the names for each type
|
||||
* The enumeration \ref epicsType is an index to this array
|
||||
* of type name strings.
|
||||
*/
|
||||
#ifdef epicsTypesGLOBAL
|
||||
@ -138,8 +147,9 @@ const char *epicsTypeNames [lastEpicsType+1] = {
|
||||
LIBCOM_API extern const char *epicsTypeNames [lastEpicsType+1];
|
||||
#endif /* epicsTypesGLOBAL */
|
||||
|
||||
/*
|
||||
* The enumeration "epicsType" is an index to this array
|
||||
/**
|
||||
* \brief An array providing the names for each type code
|
||||
* The enumeration \ref epicsType is an index to this array
|
||||
* of type code name strings.
|
||||
*/
|
||||
#ifdef epicsTypesGLOBAL
|
||||
@ -160,6 +170,11 @@ const char *epicsTypeCodeNames [lastEpicsType+1] = {
|
||||
LIBCOM_API extern const char *epicsTypeCodeNames [lastEpicsType+1];
|
||||
#endif /* epicsTypesGLOBAL */
|
||||
|
||||
/**
|
||||
* \brief An array providing the sizes for each type
|
||||
* The enumeration \ref epicsType is an index to this array
|
||||
* of type code name strings.
|
||||
*/
|
||||
#ifdef epicsTypesGLOBAL
|
||||
const unsigned epicsTypeSizes [lastEpicsType+1] = {
|
||||
sizeof (epicsInt8),
|
||||
@ -178,10 +193,6 @@ const unsigned epicsTypeSizes [lastEpicsType+1] = {
|
||||
LIBCOM_API extern const unsigned epicsTypeSizes [lastEpicsType+1];
|
||||
#endif /* epicsTypesGLOBAL */
|
||||
|
||||
/*
|
||||
* The enumeration "epicsType" is an index to this array
|
||||
* of type class identifiers.
|
||||
*/
|
||||
typedef enum {
|
||||
epicsIntC,
|
||||
epicsUIntC,
|
||||
@ -191,6 +202,11 @@ typedef enum {
|
||||
epicsOldStringC
|
||||
} epicsTypeClass;
|
||||
|
||||
/**
|
||||
* \brief An array providing the class of each type
|
||||
* The enumeration \ref epicsType is an index to this array
|
||||
* of type class identifiers.
|
||||
*/
|
||||
#ifdef epicsTypesGLOBAL
|
||||
const epicsTypeClass epicsTypeClasses [lastEpicsType+1] = {
|
||||
epicsIntC,
|
||||
@ -209,7 +225,11 @@ const epicsTypeClass epicsTypeClasses [lastEpicsType+1] = {
|
||||
LIBCOM_API extern const epicsTypeClass epicsTypeClasses [lastEpicsType+1];
|
||||
#endif /* epicsTypesGLOBAL */
|
||||
|
||||
|
||||
/**
|
||||
* \brief An array providing the field name for each type
|
||||
* The enumeration \ref epicsType is an index to this array
|
||||
* of type code name strings.
|
||||
*/
|
||||
#ifdef epicsTypesGLOBAL
|
||||
const char *epicsTypeAnyFieldName [lastEpicsType+1] = {
|
||||
"int8",
|
||||
|
@ -13,11 +13,6 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*
|
||||
* epicsStrtod() for systems with broken strtod() routine
|
||||
*/
|
||||
LIBCOM_API double epicsStrtod(const char *str, char **endp);
|
||||
|
||||
/*
|
||||
* Microsoft apparently added strto[u]ll() in VS2013
|
||||
* Older compilers have these equivalents though
|
||||
@ -28,6 +23,19 @@ LIBCOM_API double epicsStrtod(const char *str, char **endp);
|
||||
# define strtoull _strtoui64
|
||||
#endif
|
||||
|
||||
/*
|
||||
* strtod works in MSVC 1900 and mingw, use
|
||||
* the OS version in those and our own otherwise
|
||||
*/
|
||||
#if (_MSC_VER < 1900) && !defined(_MINGW)
|
||||
/*
|
||||
* epicsStrtod() for systems with broken strtod() routine
|
||||
*/
|
||||
LIBCOM_API double epicsStrtod(const char *str, char **endp);
|
||||
#else
|
||||
# define epicsStrtod strtod
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
@ -194,6 +194,7 @@ void epicsMutexOsdShow(struct epicsMutexOSD * pmutex, unsigned int level)
|
||||
|
||||
void epicsMutexOsdShowAll(void)
|
||||
{
|
||||
#if defined _POSIX_THREAD_PRIO_INHERIT
|
||||
int proto = -1;
|
||||
int ret = pthread_mutexattr_getprotocol(&globalAttrRecursive, &proto);
|
||||
if(ret) {
|
||||
@ -201,4 +202,7 @@ void epicsMutexOsdShowAll(void)
|
||||
} else {
|
||||
printf("PI is%s enabled\n", proto==PTHREAD_PRIO_INHERIT ? "" : " not");
|
||||
}
|
||||
#else
|
||||
printf("PI not supported\n");
|
||||
#endif
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*************************************************************************\
|
||||
* Copyright (c) 2002 The University of Chicago, as Operator of Argonne
|
||||
* Copyright (c) 2021 The University of Chicago, as Operator of Argonne
|
||||
* National Laboratory.
|
||||
* Copyright (c) 2002 The Regents of the University of California, as
|
||||
* Operator of Los Alamos National Laboratory.
|
||||
@ -8,13 +8,22 @@
|
||||
* in file LICENSE that is included with this distribution.
|
||||
\*************************************************************************/
|
||||
|
||||
#ifndef osdThreadh
|
||||
#define osdThreadh
|
||||
#ifndef INC_osdThread_H
|
||||
#define INC_osdThread_H
|
||||
|
||||
/* VxWorks 6.9 and later can support joining threads */
|
||||
#include <epicsVersion.h>
|
||||
|
||||
#if (_WRS_VXWORKS_MAJOR == 6 && _WRS_VXWORKS_MINOR < 9)
|
||||
#undef EPICS_THREAD_CAN_JOIN
|
||||
#ifdef _WRS_VXWORKS_MAJOR
|
||||
# define VXWORKS_VERSION_INT VERSION_INT(_WRS_VXWORKS_MAJOR, \
|
||||
_WRS_VXWORKS_MINOR, _WRS_VXWORKS_MAINT, _WRS_VXWORKS_SVCPK)
|
||||
#else
|
||||
/* Version not available at compile-time, assume... */
|
||||
# define VXWORKS_VERSION_INT VERSION_INT(5, 5, 0, 0)
|
||||
#endif
|
||||
|
||||
#endif /* osdThreadh */
|
||||
#if VXWORKS_VERSION_INT < VERSION_INT(6, 9, 4, 1)
|
||||
/* VxWorks 6.9.4.1 and later can support joining threads */
|
||||
# undef EPICS_THREAD_CAN_JOIN
|
||||
#endif
|
||||
|
||||
#endif /* INC_osdThread_H */
|
||||
|
@ -58,14 +58,26 @@ static void ClockTimeSync(void *dummy);
|
||||
/* ClockTime_Report iocsh command */
|
||||
static const iocshArg ReportArg0 = { "interest_level", iocshArgArgv};
|
||||
static const iocshArg * const ReportArgs[1] = { &ReportArg0 };
|
||||
static const iocshFuncDef ReportFuncDef = {"ClockTime_Report", 1, ReportArgs};
|
||||
static const iocshFuncDef ReportFuncDef = {"ClockTime_Report", 1, ReportArgs,
|
||||
"Reports clock synchronization status:\n"
|
||||
" - On vxWorks and RTEMS:\n"
|
||||
" * synchronization state\n"
|
||||
" * last synchronization time with provider\n"
|
||||
" * synchronization interval\n"
|
||||
" - On workstation (WIN,*NIX):\n"
|
||||
" * minimal report\n"};
|
||||
static void ReportCallFunc(const iocshArgBuf *args)
|
||||
{
|
||||
ClockTime_Report(args[0].ival);
|
||||
}
|
||||
|
||||
/* ClockTime_Shutdown iocsh command */
|
||||
static const iocshFuncDef ShutdownFuncDef = {"ClockTime_Shutdown", 0, NULL};
|
||||
static const iocshFuncDef ShutdownFuncDef = {"ClockTime_Shutdown", 0, NULL,
|
||||
"Stops the OS synchronization thread\n"
|
||||
" - On vxWorks and RTEMS:\n"
|
||||
" * OS clock will free run\n"
|
||||
" - On workstation (WIN,*NIX):\n"
|
||||
" * no change\n"};
|
||||
static void ShutdownCallFunc(const iocshArgBuf *args)
|
||||
{
|
||||
ClockTime_Shutdown(NULL);
|
||||
|
@ -64,14 +64,21 @@ static void NTPTimeSync(void *dummy);
|
||||
/* NTPTime_Report iocsh command */
|
||||
static const iocshArg ReportArg0 = { "interest_level", iocshArgArgv};
|
||||
static const iocshArg * const ReportArgs[1] = { &ReportArg0 };
|
||||
static const iocshFuncDef ReportFuncDef = {"NTPTime_Report", 1, ReportArgs};
|
||||
static const iocshFuncDef ReportFuncDef = {"NTPTime_Report", 1, ReportArgs,
|
||||
"Display time provider synchronization state\n"
|
||||
" interest_level - with level 1 it also shows:\n"
|
||||
" * synchronization interval\n"
|
||||
" * time when last synchronized\n"
|
||||
" * nominal and measured system tick rates\n"
|
||||
" * server address (vxWorks only)\n"};
|
||||
static void ReportCallFunc(const iocshArgBuf *args)
|
||||
{
|
||||
NTPTime_Report(args[0].ival);
|
||||
}
|
||||
|
||||
/* NTPTime_Shutdown iocsh command */
|
||||
static const iocshFuncDef ShutdownFuncDef = {"NTPTime_Shutdown", 0, NULL};
|
||||
static const iocshFuncDef ShutdownFuncDef = {"NTPTime_Shutdown", 0, NULL,
|
||||
"Shuts down NTP time synchronization thread\n"};
|
||||
static void ShutdownCallFunc(const iocshArgBuf *args)
|
||||
{
|
||||
NTPTime_Shutdown(NULL);
|
||||
|
@ -29,6 +29,8 @@
|
||||
#include "yajl_encode.h"
|
||||
#include "yajl_bytestack.h"
|
||||
|
||||
#include <epicsStdlib.h>
|
||||
|
||||
#ifndef LLONG_MAX
|
||||
#define LLONG_MAX 0x7FFFFFFFFFFFFFFFLL
|
||||
#define LLONG_MIN (-0x7FFFFFFFFFFFFFFFLL - 1)
|
||||
@ -334,7 +336,7 @@ yajl_do_parse(yajl_handle hand, const unsigned char * jsonText,
|
||||
yajl_buf_clear(hand->decodeBuf);
|
||||
yajl_buf_append(hand->decodeBuf, buf, bufLen);
|
||||
buf = yajl_buf_data(hand->decodeBuf);
|
||||
d = strtod((char *) buf, NULL);
|
||||
d = epicsStrtod((char *) buf, NULL);
|
||||
if ((d == HUGE_VAL || d == -HUGE_VAL) &&
|
||||
errno == ERANGE)
|
||||
{
|
||||
|
@ -337,3 +337,10 @@ include $(TOP)/configure/RULES
|
||||
|
||||
rtemsTestData.c : $(TESTFILES) $(TOOLS)/epicsMakeMemFs.pl
|
||||
$(PERL) $(TOOLS)/epicsMakeMemFs.pl $@ epicsRtemsFSImage $(TESTFILES)
|
||||
|
||||
epicsLoadTest$(DEP): epicsInstallDir.h
|
||||
|
||||
# use INSTALL_LOCATION instead of FINAL_LOCATION since test executables are not installed.
|
||||
epicsInstallDir.h: $(TOP)/configure/CONFIG_SITE*
|
||||
$(ECHO) "INSTALL_LOCATION=$(INSTALL_LOCATION)"
|
||||
$(PERL) $(TOP)/modules/database/src/std/softIoc/makeInstallDir.pl "$(INSTALL_LOCATION)" > $@
|
||||
|
@ -16,6 +16,8 @@
|
||||
#include "epicsFindSymbol.h"
|
||||
#include "epicsThread.h"
|
||||
|
||||
#include "epicsInstallDir.h"
|
||||
|
||||
namespace {
|
||||
|
||||
void loadBad()
|
||||
@ -52,9 +54,9 @@ void loadCA()
|
||||
std::ostringstream strm;
|
||||
// running in eg. modules/libcom/test/O.linux-x86_64-debug
|
||||
#ifdef _WIN32
|
||||
strm<<"..\\..\\..\\..\\bin\\"<<envGetConfigParamPtr(&EPICS_BUILD_TARGET_ARCH)<<"\\ca.dll";
|
||||
strm<<EPICS_BASE "\\bin\\"<<envGetConfigParamPtr(&EPICS_BUILD_TARGET_ARCH)<<"\\ca.dll";
|
||||
#else
|
||||
strm<<"../../../../lib/"<<envGetConfigParamPtr(&EPICS_BUILD_TARGET_ARCH)<<"/";
|
||||
strm<<EPICS_BASE "/lib/"<<envGetConfigParamPtr(&EPICS_BUILD_TARGET_ARCH)<<"/";
|
||||
# ifdef __APPLE__
|
||||
strm<<"libca.dylib";
|
||||
# else
|
||||
|
Submodule modules/pvAccess updated: 4638c11c8c...e1c1a4bc1b
Submodule modules/pvData updated: ca86a63180...d3b4976ea2
Submodule modules/pvDatabase updated: b62b047f63...93a259cbde
Submodule modules/pva2pva updated: 527afaf856...ad8b77e19f
Submodule modules/pvaClient updated: 7722fdf353...efb2631905
@ -46,17 +46,20 @@ sub do_pod_link {
|
||||
return $ret;
|
||||
}
|
||||
|
||||
# Generate the same section IDs as Pod::Simple::XHTML
|
||||
# Generate legal section IDs
|
||||
|
||||
sub section_name_tidy {
|
||||
my($self, $section) = @_;
|
||||
$section =~ s/^\s+//;
|
||||
$section =~ s/\s+$//;
|
||||
$section =~ tr/ /-/;
|
||||
$section =~ s/[[:cntrl:][:^ascii:]]//g; # drop crazy characters
|
||||
$section = $self->unicode_escape_url($section);
|
||||
$section = '_' unless length $section;
|
||||
return $section;
|
||||
my($self, $t) = @_;
|
||||
for ($t) {
|
||||
s/<[^>]+>//g; # Strip HTML.
|
||||
s/&[^;]+;//g; # Strip entities.
|
||||
s/^\s+//; s/\s+$//; # Strip white space.
|
||||
s/^([^a-zA-Z]+)$/pod$1/; # Prepend "pod" if no valid chars.
|
||||
s/^[^a-zA-Z]+//; # First char must be a letter.
|
||||
s/[^-a-zA-Z0-9_:.]+/-/g; # All other chars must be valid.
|
||||
s/[-:.]+$//; # Strip trailing punctuation.
|
||||
}
|
||||
return $t;
|
||||
}
|
||||
|
||||
1;
|
||||
|
Reference in New Issue
Block a user