Merge remote-tracking branch 'origin/7.0' into rtems5

This commit is contained in:
Brendan Chandler
2021-03-25 18:41:15 -05:00
74 changed files with 1272 additions and 668 deletions

View File

@ -31,6 +31,8 @@ skip_commits:
- 'documentation/*'
- 'startup/*'
- '.github/*'
- '.tools/*'
- '.gitattributes'
- '**/*.html'
- '**/*.md'

View File

@ -38,6 +38,8 @@ skip_commits:
- 'documentation/*'
- 'startup/*'
- '.github/*'
- '.tools/*'
- '.gitattributes'
- '**/*.html'
- '**/*.md'

3
.gitattributes vendored
View File

@ -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

View File

@ -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 }}

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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)

View File

@ -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)"

View File

@ -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.

View File

@ -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

View File

@ -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 &lt;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

View File

@ -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 &lt; 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 &mdash; 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

View File

@ -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>
&mdash; pre-release tag
</li>
<li>
<tt>R7.0.4.1-rc<i>n</i></tt>
<tt>R7.0.5-rc<i>n</i></tt>
&mdash; 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' &lt;module-version&gt;
git tag -m 'ANJ: Tag for EPICS 7.0.5' &lt;module-version&gt;
</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>

View File

@ -3,7 +3,6 @@
Documentation index
@ul
@li @ref releasenotes
@li @ref install
@li @ref recordrefmanual

View File

@ -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

View File

@ -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();

View File

@ -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

View File

@ -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);
}

View File

@ -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? */

View 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
}

View File

@ -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
*/

View File

@ -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);
}
}
}

View File

@ -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
}

View File

@ -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)

View File

@ -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
}

View File

@ -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);

View File

@ -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;

View File

@ -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));

View File

@ -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;
}

View File

@ -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 - servers 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);

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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 */

View File

@ -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
}

View File

@ -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;

View File

@ -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;

View File

@ -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;
}

View File

@ -1,2 +1,2 @@
include "xxxRecord.dbd"
device(xxx,CONSTANT,devXxxSoft,"SoftChannel")
device(xxx,CONSTANT,devXxxSoft,"Soft Channel")

View File

@ -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";
}

View File

@ -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));

View File

@ -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;
}

View File

@ -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;

View File

@ -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;

View File

@ -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 {

View File

@ -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));

View File

@ -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")) \

View File

@ -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"); \

View File

@ -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);

View File

@ -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';

View File

@ -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: Dont 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: Dont 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

View File

@ -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);

View File

@ -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();

View File

@ -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

View File

@ -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);

View File

@ -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",

View File

@ -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

View File

@ -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
}

View File

@ -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 */

View File

@ -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);

View File

@ -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);

View File

@ -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)
{

View File

@ -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)" > $@

View File

@ -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

View File

@ -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;