Compare commits
216 Commits
R7.0.2-rc1
...
R7.0.3
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
841ef37820 | ||
|
|
f550f278ad | ||
|
|
1706da98cc | ||
|
|
e8be04bc43 | ||
|
|
dee526f2f6 | ||
|
|
59cffd51ae | ||
|
|
9b5034f307 | ||
|
|
e2881fb37e | ||
|
|
afd9697290 | ||
|
|
9988be0906 | ||
|
|
a6812c5869 | ||
|
|
b89494a840 | ||
|
|
771ad6a442 | ||
|
|
d214d81794 | ||
|
|
c70b1a9a29 | ||
|
|
c9c4eea0f8 | ||
|
|
78ce2dac05 | ||
|
|
b2285bb8aa | ||
|
|
59cb5ba6a0 | ||
|
|
c8e5deca76 | ||
|
|
5b09bb6e71 | ||
|
|
ede52d96b8 | ||
|
|
ceccafe823 | ||
|
|
f955199805 | ||
|
|
2af0c10470 | ||
|
|
89f0f1338a | ||
|
|
6ce8dfec01 | ||
|
|
c1ab30142a | ||
|
|
2db2f1a53f | ||
|
|
b811d3402f | ||
|
|
a64cb4a877 | ||
|
|
e75a72ef76 | ||
|
|
33e099a51b | ||
|
|
345cfcffa8 | ||
|
|
655c5bed92 | ||
|
|
2c99c36ef9 | ||
|
|
71d4d3ecbd | ||
|
|
86138fdd23 | ||
|
|
8c993405b4 | ||
|
|
96259b7bdc | ||
|
|
bf8ff4e5f1 | ||
|
|
25576c316a | ||
|
|
2d3de1916b | ||
|
|
fe34c6f35e | ||
|
|
52cdfe0a44 | ||
|
|
16ddcaeaed | ||
|
|
297206e247 | ||
|
|
3110322f8c | ||
|
|
63bf8a8219 | ||
|
|
ec7193d0be | ||
|
|
17dea964a6 | ||
|
|
12283ce25a | ||
|
|
f486dba478 | ||
|
|
1f95d0db12 | ||
|
|
e02c5c3026 | ||
|
|
8e9d75ad71 | ||
|
|
630663caa8 | ||
|
|
7a84a1abba | ||
|
|
a9dbd4dd39 | ||
|
|
ccf34ae3d0 | ||
|
|
f559c36a6f | ||
|
|
8217fa86fe | ||
|
|
f0e118da91 | ||
|
|
5a55aa17d9 | ||
|
|
f79f2cf8fa | ||
|
|
f0f7a1ef06 | ||
|
|
1412abadf9 | ||
|
|
3a0371894b | ||
|
|
e92a9ae426 | ||
|
|
3a5fb898d6 | ||
|
|
2a14647eec | ||
|
|
597393a8ee | ||
|
|
bad8b25e4e | ||
|
|
7fe8373c32 | ||
|
|
72be690fec | ||
|
|
a5b3157ec1 | ||
|
|
9a062cd6a1 | ||
|
|
592b935146 | ||
|
|
638391249d | ||
|
|
cde682f7ba | ||
|
|
89da4130fc | ||
|
|
1d2637a04e | ||
|
|
a2123db9fb | ||
|
|
7f55bb0386 | ||
|
|
511a541f31 | ||
|
|
394c39da51 | ||
|
|
632d1f45c8 | ||
|
|
0f75e0aa7f | ||
|
|
c93ec231a2 | ||
|
|
d1149a0ba9 | ||
|
|
8c3c5a9731 | ||
|
|
95cb81c286 | ||
|
|
44510f2fb2 | ||
|
|
933733465e | ||
|
|
688f32cff0 | ||
|
|
d3feb1e2f9 | ||
|
|
62c11c22c9 | ||
|
|
736075daf6 | ||
|
|
9ef3b77348 | ||
|
|
d15c8093ec | ||
|
|
0211698b69 | ||
|
|
e860617389 | ||
|
|
e918994704 | ||
|
|
5eb7da4595 | ||
|
|
2206934ae2 | ||
|
|
318fc96912 | ||
|
|
a58cc37a5e | ||
|
|
b5e041b991 | ||
|
|
5e1bad2b34 | ||
|
|
0ae50485cf | ||
|
|
a9606dbf6e | ||
|
|
409ee26fae | ||
|
|
9a4787155c | ||
|
|
84dba0d2b7 | ||
|
|
87f6c3dec9 | ||
|
|
940814becf | ||
|
|
68a1a529b2 | ||
|
|
ce38caf41b | ||
|
|
f03f10e664 | ||
|
|
b4f4fb853d | ||
|
|
b518ebe85b | ||
|
|
e461d782f4 | ||
|
|
ef2a381e92 | ||
|
|
265d4962a4 | ||
|
|
db9267bbd5 | ||
|
|
1cf3fa9ba9 | ||
|
|
3e8b0028dc | ||
|
|
d8f18c27f4 | ||
|
|
801c01b9b6 | ||
|
|
0c1874bbfe | ||
|
|
bc7ee94e2c | ||
|
|
3fb10b6d59 | ||
|
|
a8fdf2efeb | ||
|
|
c9eda3ca48 | ||
|
|
b3f785964e | ||
|
|
bb4b23f658 | ||
|
|
8b31fba8cb | ||
|
|
8481ae3787 | ||
|
|
46535bde64 | ||
|
|
6ee928109c | ||
|
|
cff13b9aa7 | ||
|
|
58aa30ab0a | ||
|
|
5f46d6dcee | ||
|
|
d41355e0fc | ||
|
|
a5aa5459e3 | ||
|
|
6201d37756 | ||
|
|
67599e54e4 | ||
|
|
7bc0d6922b | ||
|
|
02d7304b3f | ||
|
|
7c90e6ed0b | ||
|
|
c5fd621337 | ||
|
|
ea409e79be | ||
|
|
6d5f57daf1 | ||
|
|
c59a18600a | ||
|
|
d8d14b11db | ||
|
|
68f6f361e1 | ||
|
|
84b7612036 | ||
|
|
f3cf1df503 | ||
|
|
9b385480d0 | ||
|
|
ec036cb26d | ||
|
|
64d9d1a4c9 | ||
|
|
e53244df1f | ||
|
|
fe3d68b5f7 | ||
|
|
49f5527cd7 | ||
|
|
ee90dffd40 | ||
|
|
6664ccfc64 | ||
|
|
444cac337c | ||
|
|
313afc4a4c | ||
|
|
0fae0fcc17 | ||
|
|
aab5693b45 | ||
|
|
6f919c3991 | ||
|
|
87761ebf29 | ||
|
|
10d951e2d7 | ||
|
|
d436561cb2 | ||
|
|
a43b805b65 | ||
|
|
0649a2f13f | ||
|
|
5e10b98a99 | ||
|
|
fc90d347d1 | ||
|
|
64a4c556b4 | ||
|
|
34115f83ba | ||
|
|
a38c1d07f5 | ||
|
|
9e999d2bef | ||
|
|
79690940b7 | ||
|
|
040f9013f4 | ||
|
|
0f16977caf | ||
|
|
694f045332 | ||
|
|
daad9b1ba1 | ||
|
|
937878e0a9 | ||
|
|
6e536e1ee0 | ||
|
|
383b6b1c36 | ||
|
|
bb19bd519e | ||
|
|
d70bfed75b | ||
|
|
3c2fe264cd | ||
|
|
716f102dd6 | ||
|
|
cae597d21c | ||
|
|
c23012d081 | ||
|
|
6ea6c6ff66 | ||
|
|
168d430921 | ||
|
|
9a8860b771 | ||
|
|
693c1020f2 | ||
|
|
63ddb2d4fc | ||
|
|
3d8e2d933d | ||
|
|
30268e3577 | ||
|
|
ebe7cf046e | ||
|
|
5f3f87a365 | ||
|
|
00d937cc02 | ||
|
|
072dbd53e7 | ||
|
|
560f43b561 | ||
|
|
301ded2dc1 | ||
|
|
d3927dea91 | ||
|
|
43322335df | ||
|
|
fcb5675040 | ||
|
|
3d88c8495b | ||
|
|
215c5d954b | ||
|
|
59ec8d897d | ||
|
|
b84ee89d87 |
@@ -48,6 +48,7 @@ environment:
|
||||
TOOLCHAIN: 2017
|
||||
- TOOLCHAIN: cygwin
|
||||
- TOOLCHAIN: mingw
|
||||
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015
|
||||
|
||||
# Platform: architecture
|
||||
platform:
|
||||
@@ -60,6 +61,10 @@ matrix:
|
||||
# VS Express installs don't have the 64 bit compiler
|
||||
- platform: x64
|
||||
TOOLCHAIN: 10.0
|
||||
# Cygwin static-debug has compiler problems
|
||||
- configuration: static-debug
|
||||
TOOLCHAIN: cygwin
|
||||
|
||||
|
||||
#---------------------------------#
|
||||
# building & testing #
|
||||
@@ -81,6 +86,9 @@ test_script:
|
||||
|
||||
notifications:
|
||||
|
||||
- provider: Slack
|
||||
incoming_webhook:
|
||||
secure: RYOm3FIUYeZGjWKaeTVKwq+C3fzK54AKwbmAoECED45mex3lN+8HmrC845a6mg9xPUJ/ND51RopWVaKDD9/UzaM0SO195RQLKqUTIUafiuM=
|
||||
- provider: Email
|
||||
to:
|
||||
- core-talk@aps.anl.gov
|
||||
on_build_success: false
|
||||
|
||||
- provider: GitHubPullRequest
|
||||
|
||||
@@ -42,13 +42,13 @@ if "%TOOLCHAIN%"=="mingw" (
|
||||
set "MAKE=mingw32-make"
|
||||
if "%OS%"=="64BIT" (
|
||||
set "EPICS_HOST_ARCH=windows-x64-mingw"
|
||||
set "INCLUDE=C:\tools\mingw64\include;%INCLUDE%"
|
||||
set "PATH=C:\tools\mingw64\bin;%PATH%"
|
||||
set "INCLUDE=C:\mingw-w64\x86_64-8.1.0-posix-seh-rt_v6-rev0\mingw64\include;%INCLUDE%"
|
||||
set "PATH=C:\mingw-w64\x86_64-8.1.0-posix-seh-rt_v6-rev0\mingw64\bin;%PATH%"
|
||||
echo [INFO] MinGW Toolchain 64bit
|
||||
) else (
|
||||
set "EPICS_HOST_ARCH=win32-x86-mingw"
|
||||
set "INCLUDE=C:\tools\mingw32\include;%INCLUDE%"
|
||||
set "PATH=C:\tools\mingw32\bin;%PATH%"
|
||||
set "INCLUDE=C:\mingw-w64\i686-6.3.0-posix-dwarf-rt_v5-rev1\mingw32\include;%INCLUDE%"
|
||||
set "PATH=C:\mingw-w64\i686-6.3.0-posix-dwarf-rt_v5-rev1\mingw32\bin;%PATH%"
|
||||
echo [INFO] MinGW Toolchain 32bit
|
||||
)
|
||||
echo [INFO] Compiler Version
|
||||
|
||||
@@ -55,16 +55,9 @@ if "%TOOLCHAIN%"=="mingw" (
|
||||
) || (
|
||||
echo [INFO] EPICS set up for optimized build
|
||||
)
|
||||
if "%OS%"=="64BIT" (
|
||||
echo [INFO] Installing MinGW 64bit
|
||||
cinst mingw || cinst mingw
|
||||
) else (
|
||||
echo [INFO] Installing MinGW 32bit
|
||||
cinst mingw --x86 || cinst mingw --x86
|
||||
)
|
||||
)
|
||||
|
||||
echo [INFO] Installing Make 4.1
|
||||
curl -fsS --retry 3 -o C:\tools\make-4.1.zip https://epics.anl.gov/download/tools/make-4.1-win64.zip
|
||||
echo [INFO] Installing Make 4.2.1 from ANL web site
|
||||
curl -fsS --retry 3 -o C:\tools\make-4.2.1.zip https://epics.anl.gov/download/tools/make-4.2.1-win64.zip
|
||||
cd \tools
|
||||
"C:\Program Files\7-Zip\7z" e make-4.1.zip
|
||||
"C:\Program Files\7-Zip\7z" e make-4.2.1.zip
|
||||
|
||||
@@ -73,5 +73,5 @@ make -j2 $EXTRA
|
||||
if [ "$TEST" != "NO" ]
|
||||
then
|
||||
make -j2 tapfiles
|
||||
make -j2 -s test-results
|
||||
make -s test-results
|
||||
fi
|
||||
|
||||
@@ -16,27 +16,53 @@ PREFIX="$3"
|
||||
if ! [ "$TOPREV" ]
|
||||
then
|
||||
cat <<EOF >&2
|
||||
usage: $0 [rev] [outfile.tar.gz] [prefix/]
|
||||
usage: $0 <rev> [<outfile> [<prefix>]]
|
||||
|
||||
<rev> may be any git revision spec. (tag, branch, or commit id).
|
||||
|
||||
Output file may be .tar.gz, .tar.bz2, or any extension supported by "tar -a".
|
||||
If output file name is omitted, "base-<rev>.tar.gz" will be used.
|
||||
If <prefix> is omitted, the default prefix is "base-<rev>/".
|
||||
If provided, <outfile> must end with ".tar", ".tar.gz" or ".tar.bz2".
|
||||
If <outfile> is omitted, "base-<rev>.tar.gz" will be used.
|
||||
If provided, <prefix> must end with "/". If <prefix> is omitted,
|
||||
the default is "base-<rev>/".
|
||||
EOF
|
||||
exit 1
|
||||
fi
|
||||
|
||||
[ "$FINALTAR" ] || FINALTAR="base-$TOPREV.tar.gz"
|
||||
[ "$PREFIX" ] || PREFIX="base-$TOPREV/"
|
||||
case "$FINALTAR" in
|
||||
"")
|
||||
TAROPT=-z
|
||||
FINALTAR="base-$TOPREV.tar.gz"
|
||||
;;
|
||||
*.tar)
|
||||
TAROPT=""
|
||||
;;
|
||||
*.tar.gz)
|
||||
TAROPT=-z
|
||||
;;
|
||||
*.tar.bz2)
|
||||
TAROPT=-j
|
||||
;;
|
||||
*)
|
||||
die "outfile must end with '.tar.gz' or '.tar.bz2'"
|
||||
;;
|
||||
esac
|
||||
|
||||
case "$PREFIX" in
|
||||
*/) ;;
|
||||
*) die "Prefix must end with '/'";;
|
||||
"")
|
||||
PREFIX="base-$TOPREV/"
|
||||
;;
|
||||
*/)
|
||||
;;
|
||||
*)
|
||||
die "Prefix must end with '/'"
|
||||
;;
|
||||
esac
|
||||
|
||||
# Check for both <tag> and R<tag>
|
||||
if ! [ `git tag -l $TOPREV` ]
|
||||
if [ "$TOPREV" = "HEAD" ]
|
||||
then
|
||||
true
|
||||
elif ! [ `git tag -l $TOPREV` ]
|
||||
then
|
||||
if [ `git tag -l R$TOPREV` ]
|
||||
then
|
||||
@@ -94,13 +120,11 @@ then
|
||||
fi
|
||||
|
||||
# Use the filtered list to build the final tar
|
||||
# The -a option chooses compression automatically based on output file name.
|
||||
|
||||
tar -C "$TDIR"/tar --files-from="$TDIR"/list.2 -caf "$FINALTAR"
|
||||
tar -c $TAROPT -C "$TDIR"/tar -T "$TDIR"/list.2 -f "$FINALTAR"
|
||||
|
||||
echo "Wrote $FINALTAR"
|
||||
|
||||
tar -taf "$FINALTAR" > "$TDIR"/list.3
|
||||
tar -t $TAROPT -f "$FINALTAR" > "$TDIR"/list.3
|
||||
|
||||
# make sure we haven't picked up anything extra
|
||||
if ! diff -u "$TDIR"/list.2 "$TDIR"/list.3
|
||||
|
||||
@@ -24,9 +24,14 @@ LD = $(GNU_BIN)/$(CMPLR_PREFIX)ld$(CMPLR_SUFFIX) -r
|
||||
CPP = $(CC) -x c -E
|
||||
RANLIB = $(GNU_BIN)/$(CMPLR_PREFIX)ranlib$(CMPLR_SUFFIX)
|
||||
|
||||
# Requires at least GCC 4.8 or LLVM (clang) 3.1
|
||||
ASAN_FLAGS_YES = -fsanitize=address
|
||||
ASAN_LDFLAGS_YES = $(ASAN_FLAGS_YES)
|
||||
|
||||
PROF_CFLAGS_YES = -p
|
||||
GPROF_CFLAGS_YES = -pg
|
||||
CODE_CFLAGS = $(PROF_CFLAGS_$(PROFILE)) $(GPROF_CFLAGS_$(GPROF))
|
||||
CODE_CFLAGS += $(ASAN_FLAGS_$(ENABLE_ASAN))
|
||||
WARN_CFLAGS_YES = -Wall
|
||||
WARN_CFLAGS_NO = -w
|
||||
OPT_CFLAGS_YES = -O3
|
||||
@@ -35,12 +40,14 @@ OPT_CFLAGS_NO = -g
|
||||
PROF_CXXFLAGS_YES = -p
|
||||
GPROF_CXXFLAGS_YES = -pg
|
||||
CODE_CXXFLAGS = $(PROF_CXXFLAGS_$(PROFILE)) $(GPROF_CXXFLAGS_$(GPROF))
|
||||
CODE_CXXFLAGS += $(ASAN_FLAGS_$(ENABLE_ASAN))
|
||||
WARN_CXXFLAGS_YES = -Wall
|
||||
WARN_CXXFLAGS_NO = -w
|
||||
OPT_CXXFLAGS_YES = -O3
|
||||
OPT_CXXFLAGS_NO = -g
|
||||
|
||||
CODE_LDFLAGS = $(PROF_CXXFLAGS_$(PROFILE)) $(GPROF_CXXFLAGS_$(GPROF))
|
||||
CODE_LDFLAGS += $(ASAN_LDFLAGS_$(ENABLE_ASAN))
|
||||
|
||||
PIPE_CFLAGS_YES_YES = -pipe
|
||||
PIPE_CFLAGS = $(PIPE_CFLAGS_$(GCC_PIPE)_$(GNU))
|
||||
|
||||
@@ -30,10 +30,11 @@ endif # BASE_TOP
|
||||
#---------------------------------------------------------------
|
||||
# Where to find the installed build tools
|
||||
# Windows does not like commands with relative paths starting ../
|
||||
# but the Perl scripts in TOP/src/tools are OK
|
||||
# so TOOLS must be an absolute path, although Perl scripts are OK.
|
||||
# FIND_TOOL is for scripts run before the build reaches src/tools.
|
||||
|
||||
TOOLS = $(abspath $(EPICS_BASE_HOST_BIN))
|
||||
FIND_TOOL = $(firstword $(wildcard $(TOOLS)/$(1) $(TOP)/src/tools/$(1)))
|
||||
FIND_TOOL = $(firstword $(wildcard $(TOOLS)/$(1) $(EPICS_BASE)/src/tools/$(1)))
|
||||
|
||||
#---------------------------------------------------------------
|
||||
# EPICS Base build tools and tool flags
|
||||
@@ -54,3 +55,8 @@ INSTALL_LIBRARY = $(INSTALL)
|
||||
# tools for making header dependencies and variable replacement
|
||||
MKMF = $(PERL) $(TOOLS)/mkmf.pl
|
||||
REPLACEVAR = $(PERL) $(TOOLS)/replaceVAR.pl
|
||||
|
||||
#---------------------------------------------------------------
|
||||
# tools for cleaning out unwanted files
|
||||
CVSCLEAN = $(call FIND_TOOL,cvsclean.pl)
|
||||
DEPCLEAN = $(call FIND_TOOL,depclean.pl)
|
||||
|
||||
@@ -48,23 +48,24 @@ EPICS_VERSION = 7
|
||||
EPICS_REVISION = 0
|
||||
|
||||
# EPICS_MODIFICATION must be a number >=0 and <256
|
||||
EPICS_MODIFICATION = 2
|
||||
EPICS_MODIFICATION = 3
|
||||
|
||||
# EPICS_PATCH_LEVEL must be a number (win32 resource file requirement)
|
||||
# Not included if zero
|
||||
# Not included in the official EPICS version number if zero
|
||||
EPICS_PATCH_LEVEL = 0
|
||||
|
||||
# This will end in -DEV between official releases
|
||||
# Between official releases, the EPICS_PATCH_LEVEL gets incremented
|
||||
# and a -DEV suffix is added (similar to the Maven -SNAPSHOT versions)
|
||||
EPICS_DEV_SNAPSHOT=
|
||||
#EPICS_DEV_SNAPSHOT=-DEV
|
||||
#EPICS_DEV_SNAPSHOT=-pre1
|
||||
#EPICS_DEV_SNAPSHOT=-pre1-DEV
|
||||
#EPICS_DEV_SNAPSHOT=-pre2
|
||||
#EPICS_DEV_SNAPSHOT=-pre2-DEV
|
||||
EPICS_DEV_SNAPSHOT=-rc1
|
||||
#EPICS_DEV_SNAPSHOT=-rc1
|
||||
#EPICS_DEV_SNAPSHOT=-rc1-DEV
|
||||
#EPICS_DEV_SNAPSHOT=-rc2
|
||||
#EPICS_DEV_SNAPSHOT=-rc2-DEV
|
||||
#EPICS_DEV_SNAPSHOT=
|
||||
|
||||
# No changes should be needed below here
|
||||
|
||||
|
||||
@@ -76,11 +76,14 @@ COMMON_DIR = ../O.Common
|
||||
IOCS_APPL_TOP = $(shell $(FULLPATHNAME) $(INSTALL_LOCATION))
|
||||
|
||||
#-------------------------------------------------------
|
||||
# Make echo output - suppress echoing if make's '-s' flag is set
|
||||
# Silencing the build - suppress messages during 'make -s'
|
||||
NOP = :
|
||||
ECHO = @$(if $(findstring s,$(patsubst T_A=%,,$(MAKEFLAGS))),$(NOP),echo)
|
||||
QUIET_FLAG := $(if $(findstring s,$(MAKEFLAGS)),-q,)
|
||||
QUESTION_FLAG := $(if $(findstring q,$(MAKEFLAGS)),-i,)
|
||||
ECHO = @$(if $(findstring s,$(MFLAGS)),$(NOP),echo)
|
||||
QUIET_FLAG := $(if $(findstring s,$(MFLAGS)),-q,)
|
||||
|
||||
#-------------------------------------------------------
|
||||
# Convert 'make -q' flag into '-i' for genVersionHeader.pl
|
||||
QUESTION_FLAG := $(if $(findstring q,$(MFLAGS)),-i,)
|
||||
|
||||
#-------------------------------------------------------
|
||||
ifdef T_A
|
||||
@@ -90,7 +93,7 @@ INSTALL_SHRLIB = $(INSTALL_LOCATION_LIB)/$(T_A)
|
||||
INSTALL_TCLLIB = $(INSTALL_LOCATION_LIB)/$(T_A)
|
||||
INSTALL_BIN = $(INSTALL_LOCATION_BIN)/$(T_A)
|
||||
|
||||
#Directories for libraries
|
||||
# Directories for libraries
|
||||
SHRLIB_SEARCH_DIRS = $(INSTALL_LIB)
|
||||
|
||||
#-------------------------------------------------------
|
||||
|
||||
@@ -34,14 +34,9 @@
|
||||
# The future dates below assume the rules don't get changed;
|
||||
# see http://www.timeanddate.com/time/dst/2018.html to check.
|
||||
#
|
||||
# DST for 2018 US: Mar 11 - Nov 04
|
||||
# EU: Mar 25 - Oct 28
|
||||
EPICS_TIMEZONE = CUS::360:031102:110402
|
||||
#EPICS_TIMEZONE = MET::-60:032502:102803
|
||||
#
|
||||
# DST for 2019 US: Mar 10 - Nov 03
|
||||
# EU: Mar 31 - Oct 27
|
||||
#EPICS_TIMEZONE = CUS::360:031002:110302
|
||||
EPICS_TIMEZONE = CUS::360:031002:110302
|
||||
#EPICS_TIMEZONE = MET::-60:033102:102703
|
||||
#
|
||||
# DST for 2020 US: Mar 08 - Nov 01
|
||||
@@ -58,6 +53,11 @@ EPICS_TIMEZONE = CUS::360:031102:110402
|
||||
# EU: Mar 27 - Oct 30
|
||||
#EPICS_TIMEZONE = CUS::360:031302:110602
|
||||
#EPICS_TIMEZONE = MET::-60:032702:103003
|
||||
#
|
||||
# DST for 2023 US: Mar 13 - Nov 06
|
||||
# EU: Mar 27 - Oct 30
|
||||
#EPICS_TIMEZONE = CUS::360:031202:110502
|
||||
#EPICS_TIMEZONE = MET::-60:032602:102903
|
||||
|
||||
# EPICS_TS_NTP_INET
|
||||
# NTP time server ip address for VxWorks and RTEMS.
|
||||
|
||||
@@ -43,6 +43,7 @@ LOADABLE_LIBRARY += $(LOADABLE_LIBRARY_HOST)
|
||||
OBJS += $(OBJS_HOST)
|
||||
PROD += $(PROD_HOST)
|
||||
SCRIPTS += $(SCRIPTS_HOST)
|
||||
TARGETS += $(TARGETS_HOST)
|
||||
TESTLIBRARY += $(TESTLIBRARY_HOST)
|
||||
TESTSCRIPTS += $(TESTSCRIPTS_HOST)
|
||||
TESTPROD += $(TESTPROD_HOST)
|
||||
@@ -54,6 +55,7 @@ LOADABLE_LIBRARY += $(LOADABLE_LIBRARY_IOC)
|
||||
OBJS += $(OBJS_IOC)
|
||||
PROD += $(PROD_IOC)
|
||||
SCRIPTS += $(SCRIPTS_IOC)
|
||||
TARGETS += $(TARGETS_IOC)
|
||||
TESTLIBRARY += $(TESTLIBRARY_IOC)
|
||||
TESTSCRIPTS += $(TESTSCRIPTS_IOC)
|
||||
TESTPROD += $(TESTPROD_IOC)
|
||||
@@ -200,9 +202,9 @@ endif
|
||||
|
||||
$(TESTPRODNAME) $(PRODNAME): $(PRODUCT_OBJS) $(PROD_RESS) $(PROD_DEPLIBS)
|
||||
|
||||
$(TESTPRODNAME) $(PRODNAME): %$(EXE):
|
||||
$(TESTPRODNAME) $(PRODNAME): %$(EXE): | $(INSTALL_LIB)
|
||||
@$(RM) $@
|
||||
$(DEBUGCMD) $(LINK.cpp)
|
||||
$(LINK.cpp)
|
||||
$(MT_EXE_COMMAND)
|
||||
|
||||
%_ctdt$(OBJ) : %_ctdt.c
|
||||
@@ -303,6 +305,10 @@ $(LOADABLE_SHRLIBNAME): $(LOADABLE_SHRLIB_PREFIX)%$(LOADABLE_SHRLIB_SUFFIX):
|
||||
$(LINK.shrlib)
|
||||
$(MT_DLL_COMMAND)
|
||||
|
||||
$(LIBNAME) $(SHRLIBNAME) $(LOADABLE_SHRLIBNAME): | $(INSTALL_LIB)
|
||||
$(INSTALL_LIB):
|
||||
@$(MKDIR) $@
|
||||
|
||||
#---------------------------------------------------------------
|
||||
# C++ munching for VxWorks
|
||||
|
||||
@@ -360,14 +366,17 @@ ifneq ($(TAPFILES),)
|
||||
ifdef RUNTESTS_ENABLED
|
||||
prove --failures --ext .tap --exec "$(CAT)" --color $(TAPFILES)
|
||||
endif
|
||||
|
||||
CURRENT_TAPFILES := $(wildcard $(TAPFILES))
|
||||
CURRENT_JUNITFILES := $(wildcard $(JUNITFILES))
|
||||
endif
|
||||
|
||||
clean-tests:
|
||||
ifneq ($(TAPFILES),)
|
||||
$(RM) $(TAPFILES)
|
||||
ifneq ($(CURRENT_TAPFILES),)
|
||||
$(RM) $(CURRENT_TAPFILES)
|
||||
endif
|
||||
ifneq ($(JUNITFILES),)
|
||||
$(RM) $(JUNITFILES)
|
||||
ifneq ($(CURRENT_JUNITFILES),)
|
||||
$(RM) $(CURRENT_JUNITFILES)
|
||||
endif
|
||||
|
||||
tapfiles: $(TESTSCRIPTS) $(TAPFILES)
|
||||
@@ -462,9 +471,11 @@ $(INSTALL_TCLLIB)/%: ../%
|
||||
@$(INSTALL) -d -m $(BIN_PERMISSIONS) $< $(INSTALL_TCLLIB)
|
||||
endif
|
||||
|
||||
ifneq ($(TCLINDEX),)
|
||||
$(INSTALL_TCLLIB)/$(TCLINDEX): $(INSTALL_TCLLIBS)
|
||||
$(ECHO) "Updating $@"
|
||||
$(ECHO) eval auto_mkindex $(INSTALL_TCLLIB) "$(TCLLIBNAME)" | tclsh
|
||||
endif
|
||||
|
||||
$(INSTALL_LOADABLE_SHRLIBS): $(INSTALL_SHRLIB)/%: %
|
||||
$(ECHO) "Installing loadable shared library $@"
|
||||
@@ -542,6 +553,6 @@ include $(CONFIG)/RULES_EXPAND
|
||||
include $(CONFIG)/RULES_COMMON
|
||||
|
||||
else
|
||||
$(warning RULES_BUILD included more than once. \
|
||||
Use 'make show-makefiles' to work out why.)
|
||||
$(warning Warning: Base configure/RULES_BUILD file included more than once. \
|
||||
Does configure/RELEASE have multiple pointers to $(EPICS_BASE)?)
|
||||
endif # BASE_RULES_BUILD
|
||||
|
||||
@@ -8,16 +8,18 @@
|
||||
# These rules show the set of Makefiles, config files and
|
||||
# rules files loaded by GNUmake.
|
||||
|
||||
# Protect against filenames containing colons (Windows)
|
||||
SAFE_MAKEFILES = $(subst :,__colon__,$(MAKEFILE_LIST))
|
||||
SHOW_MAKEFILES = $(SAFE_MAKEFILES:%=show-makefile.%)
|
||||
show-makefiles: $(SHOW_MAKEFILES)
|
||||
show-makefiles::
|
||||
@echo
|
||||
@echo Makefiles read:
|
||||
define SHOW_MAKEFILE_template
|
||||
show-makefiles::
|
||||
@echo " $(1)"
|
||||
endef
|
||||
$(foreach file,$(MAKEFILE_LIST), \
|
||||
$(eval $(call SHOW_MAKEFILE_template,$(file))))
|
||||
|
||||
# The sort prevents warnings about duplicate targets:
|
||||
$(sort $(SHOW_MAKEFILES)): show-makefile.%:
|
||||
@echo " $(subst __colon__,:,$(@:show-makefile.%=%))"
|
||||
.PHONY: show-makefiles
|
||||
|
||||
.PHONY: show-makefiles show-makefile.%
|
||||
|
||||
# These rules support printing a Makefile variable values.
|
||||
# Many variables are only set inside an O.<arch> build directory.
|
||||
@@ -30,6 +32,17 @@ PRINT.%:
|
||||
.PHONY: PRINT PRINT.%
|
||||
|
||||
|
||||
# Clean rules for recursively deleting editor backup files
|
||||
# and dependency (.d) files from CWD and below.
|
||||
|
||||
cvsclean:
|
||||
$(PERL) $(CVSCLEAN)
|
||||
depclean:
|
||||
$(PERL) $(DEPCLEAN)
|
||||
|
||||
.PHONY: cvsclean depclean
|
||||
|
||||
|
||||
# User specific rules
|
||||
#
|
||||
-include $(HOME)/configure/RULES_USER
|
||||
|
||||
@@ -86,10 +86,11 @@ $(DIRS) $(dirActionTargets) $(dirArchTargets) $(dirActionArchTargets) :
|
||||
$(ARCHS) $(ACTIONS) $(actionArchTargets) :%: \
|
||||
$(foreach dir, $(DIRS), $(dir)$(DIVIDER)%)
|
||||
|
||||
.PHONY : $(DIRS) all host rebuild
|
||||
.PHONY : $(ARCHS) $(ACTIONS)
|
||||
.PHONY : $(dirActionTargets) $(dirArchTargets)
|
||||
.PHONY : $(dirActionArchTargets)
|
||||
.PHONY : $(actionArchTargets)
|
||||
|
||||
.PHONY: $(DIRS) all host rebuild
|
||||
.PHONY: $(ARCHS) $(ACTIONS)
|
||||
.PHONY: $(dirActionTargets) $(dirArchTargets)
|
||||
.PHONY: $(dirActionArchTargets)
|
||||
.PHONY: $(actionArchTargets)
|
||||
|
||||
include $(CONFIG)/RULES_COMMON
|
||||
|
||||
47
configure/RULES_MODULES
Normal file
47
configure/RULES_MODULES
Normal file
@@ -0,0 +1,47 @@
|
||||
#*************************************************************************
|
||||
# EPICS BASE is distributed subject to a Software License Agreement found
|
||||
# in file LICENSE that is included with this distribution.
|
||||
#*************************************************************************
|
||||
|
||||
# Support modules can use these rules to build submodules too.
|
||||
#
|
||||
# The requirements to do so are:
|
||||
# 1. Create a file CONFIG_SITE.local in the same directory as the
|
||||
# Makefile, which defines these variables (the last one is empty):
|
||||
# PARENT_MODULE - The name submodules call their parent
|
||||
# INSTALL_LOCATION := $($(PARENT_MODULE))
|
||||
# CONFIG_INSTALLS =
|
||||
# 2. The Makefile must set TOP and include $(TOP)/configure/CONFIG and
|
||||
# CONFIG_SITE.local
|
||||
# 3. Submodules are added to the SUBMODULES variable in the Makefile
|
||||
# 4. Dependencies between submodules must be set using
|
||||
# <name>_DEPEND_DIRS = <prerequisites>
|
||||
# 5. The Makefile must end by including $(TOP)/configure/RULES_MODULES
|
||||
# 6. Submodules must have a configure/RELEASE file that contains
|
||||
# -include $(TOP)/../RELEASE.$(EPICS_HOST_ARCH).local
|
||||
# 7. Submodules must have a configure/CONFIG_SITE file that contains
|
||||
# -include $(TOP)/../CONFIG_SITE.local
|
||||
|
||||
# Add checked-out submodules to DIRS
|
||||
DIRS += $(subst /Makefile,,$(wildcard $(addsuffix /Makefile, $(SUBMODULES))))
|
||||
|
||||
include $(CONFIG)/RULES_DIRS
|
||||
|
||||
INSTALL_LOCATION_ABS := $(abspath $(INSTALL_LOCATION))
|
||||
RELEASE_LOCAL := RELEASE.$(EPICS_HOST_ARCH).local
|
||||
|
||||
# Ensure that RELEASE.<host>.local exists before doing anything else
|
||||
all host $(DIRS) $(ARCHS) $(ACTIONS) $(dirActionTargets) $(dirArchTargets) \
|
||||
$(dirActionArchTargets) $(actionArchTargets): | $(RELEASE_LOCAL)
|
||||
|
||||
# Convenience target
|
||||
RELEASE.host: $(RELEASE_LOCAL)
|
||||
|
||||
$(RELEASE_LOCAL): Makefile CONFIG_SITE.local
|
||||
$(ECHO) Creating $@ with
|
||||
$(ECHO) " $(PARENT_MODULE) = $(INSTALL_LOCATION_ABS)"
|
||||
@echo $(PARENT_MODULE) = $(INSTALL_LOCATION_ABS)> $@
|
||||
realclean:
|
||||
$(RM) $(wildcard RELEASE.*.local)
|
||||
|
||||
.PHONY: RELEASE.host realclean
|
||||
@@ -9,24 +9,30 @@
|
||||
|
||||
include $(CONFIG)/RULES_DIRS
|
||||
|
||||
# Disable most top rules when installing into a parent's tree, indicated
|
||||
# by PARENT_MODULE being set in the modules/CONFIG_SITE.local file and
|
||||
# INSTALL_LOCATION pointing to the the same place as in the parent.
|
||||
ifeq ($(origin PARENT_MODULE),file)
|
||||
ifeq ($(INSTALL_LOCATION),$($(PARENT_MODULE)))
|
||||
DISABLE_TOP_RULES=YES
|
||||
endif
|
||||
endif
|
||||
|
||||
ifndef DISABLE_TOP_RULES
|
||||
#
|
||||
# Rules for a regular application top directory
|
||||
#
|
||||
|
||||
distclean: realclean cvsclean realuninstall
|
||||
|
||||
CVSCLEAN = $(call FIND_TOOL,cvsclean.pl)
|
||||
cvsclean:
|
||||
$(PERL) $(CVSCLEAN)
|
||||
|
||||
DEPCLEAN = $(call FIND_TOOL,depclean.pl)
|
||||
depclean:
|
||||
$(PERL) $(DEPCLEAN)
|
||||
|
||||
realuninstall: uninstallDirs
|
||||
$(RMDIR) $(INSTALL_LOCATION_BIN)
|
||||
$(RMDIR) $(INSTALL_LOCATION_LIB)
|
||||
|
||||
UNINSTALL_DIRS += $(INSTALL_DBD) $(INSTALL_INCLUDE) $(INSTALL_DOC) \
|
||||
$(INSTALL_HTML) $(INSTALL_TEMPLATES) $(INSTALL_DB) $(DIRECTORY_TARGETS)
|
||||
UNINSTALL_DIRS += $(INSTALL_DB) $(INSTALL_DBD) $(INSTALL_DOC) $(INSTALL_HTML)
|
||||
UNINSTALL_DIRS += $(INSTALL_INCLUDE) $(INSTALL_TEMPLATES) $(DIRECTORY_TARGETS)
|
||||
ifneq ($(INSTALL_LOCATION),$(TOP))
|
||||
UNINSTALL_DIRS += $(INSTALL_CONFIG)
|
||||
UNINSTALL_DIRS += $(INSTALL_CONFIG)
|
||||
endif
|
||||
uninstallDirs:
|
||||
$(RMDIR) $(UNINSTALL_DIRS)
|
||||
@@ -40,6 +46,8 @@ uninstall$(DIVIDER)%:
|
||||
$(RMDIR) $(INSTALL_LOCATION_BIN)/$(archPart)
|
||||
$(RMDIR) $(INSTALL_LOCATION_LIB)/$(archPart)
|
||||
|
||||
# Remove the bin and lib directories if they have no sub-directories
|
||||
#
|
||||
cleandirs:
|
||||
@$(NOP)
|
||||
ifeq ($(wildcard $(INSTALL_LOCATION_BIN)/*),)
|
||||
@@ -49,6 +57,16 @@ ifeq ($(wildcard $(INSTALL_LOCATION_LIB)/*),)
|
||||
$(RMDIR) $(INSTALL_LOCATION_LIB)
|
||||
endif
|
||||
|
||||
else
|
||||
#
|
||||
# Using a disabled rule aborts
|
||||
#
|
||||
|
||||
cleandirs distclean uninstall realuninstall archuninstall:
|
||||
$(error Target '$@' not available in a submodule)
|
||||
|
||||
endif # DISABLE_TOP_RULES
|
||||
|
||||
|
||||
help:
|
||||
@echo "Usage: gnumake [options] [target] ..."
|
||||
@@ -64,33 +82,39 @@ help:
|
||||
@echo " Cannot be used within an O.<arch> dir"
|
||||
@echo " rebuild - Same as clean install"
|
||||
@echo " archclean - Removes O.<arch> dirs but not O.Common dir"
|
||||
@echo " runtests - Run self-tests, summarize results"
|
||||
@echo " depclean - Removes .d files from all O.<arch> dirs."
|
||||
@echo " cvsclean - Removes backup files etc. from all dirs below"
|
||||
@echo " runtests - Run self-tests, summarize results immediately"
|
||||
@echo " tapfiles - Run self-tests, save to O.<arch>/*.tap files"
|
||||
@echo " test-results - Summarize all O.<arch>/*.tap files"
|
||||
@echo " clean-tests - Removes all O.<arch>/*.tap files"
|
||||
@echo "\"Partial\" build targets supported by Makefiles:"
|
||||
@echo " host - Builds and installs $(EPICS_HOST_ARCH) only."
|
||||
@echo " inc$(DIVIDER)<arch> - Installs <arch> only header files."
|
||||
@echo " build$(DIVIDER)<arch> - Builds and installs <arch> only."
|
||||
@echo " install$(DIVIDER)<arch> - Builds and installs <arch> only."
|
||||
@echo " clean$(DIVIDER)<arch> - Cleans <arch> binaries in O.<arch> dirs only."
|
||||
@echo " uninstall$(DIVIDER)<arch> - Remove bin & lib directories for <arch> only."
|
||||
@echo "Targets supported by top level Makefile:"
|
||||
ifndef DISABLE_TOP_RULES
|
||||
@echo " archuninstall - Remove bin & lib directories created by this hostarch."
|
||||
@echo " uninstall$(DIVIDER)<arch> - Remove bin & lib directories for <arch> only."
|
||||
@echo " uninstall - Remove install directories created by this hostarch."
|
||||
@echo " realuninstall - Removes ALL install dirs"
|
||||
@echo " distclean - Same as realclean cvsclean realuninstall."
|
||||
@echo " depclean - Removes all .d files from O.<arch> directories."
|
||||
@echo " cvsclean - Removes cvs .#* files in all dirs of directory tree"
|
||||
endif
|
||||
@echo " help - Prints this list of valid make targets "
|
||||
@echo "Indiv. object targets are supported by O.<arch> level Makefile .e.g"
|
||||
@echo "Object targets are supported by the O.<arch> level Makefile .e.g"
|
||||
@echo " xxxRecord.o"
|
||||
|
||||
.PHONY: cleandirs distclean cvsclean depclean
|
||||
.PHONY: cleandirs distclean uninstall help
|
||||
.PHONY: realuninstall archuninstall uninstallDirs
|
||||
.PHONY: uninstall help
|
||||
|
||||
# Include <top>/cfg/TOP_RULES* files from tops defined in RELEASE* files
|
||||
#
|
||||
RELEASE_CFG_TOP_RULES = $(foreach top, $(RELEASE_TOPS), \
|
||||
$(wildcard $($(top))/cfg/TOP_RULES*))
|
||||
ifneq ($(RELEASE_CFG_TOP_RULES),)
|
||||
include $(RELEASE_CFG_TOP_RULES)
|
||||
ifndef DISABLE_TOP_RULES
|
||||
# Include <top>/cfg/TOP_RULES* files from tops defined in RELEASE* files
|
||||
#
|
||||
RELEASE_CFG_TOP_RULES = $(foreach top, $(RELEASE_TOPS), \
|
||||
$(wildcard $($(top))/cfg/TOP_RULES*))
|
||||
ifneq ($(RELEASE_CFG_TOP_RULES),)
|
||||
include $(RELEASE_CFG_TOP_RULES)
|
||||
endif
|
||||
endif
|
||||
|
||||
@@ -76,7 +76,7 @@ CPPFLAGS += $($(BUILD_CLASS)_CPPFLAGS) $(POSIX_CPPFLAGS) $(OPT_CPPFLAGS)\
|
||||
$(USR_CPPFLAGS) $(CMD_CPPFLAGS) $(ARCH_DEP_CPPFLAGS) $(OP_SYS_CPPFLAGS)\
|
||||
$(OP_SYS_INCLUDE_CPPFLAGS) $(CODE_CPPFLAGS)
|
||||
|
||||
ECHO = @$(if $(findstring s,$(patsubst T_A=%,,$(MAKEFLAGS))),$(NOP),echo)
|
||||
ECHO = @$(if $(findstring s,$(MFLAGS)),$(NOP),echo)
|
||||
|
||||
#--------------------------------------------------
|
||||
# Although RTEMS uses gcc, it wants to use gcc its own way
|
||||
|
||||
@@ -146,8 +146,10 @@ SHRLIB_CFLAGS =
|
||||
SHRLIB_LDFLAGS =
|
||||
|
||||
#--------------------------------------------------
|
||||
# Earlier versions of gcc don't understand -MF
|
||||
HDEPENDS_COMPFLAGS = -MM > $@
|
||||
# Don't use gcc 2.x for dependency generation
|
||||
|
||||
HDEPENDS_METHOD_2 = MKMF
|
||||
HDEPENDS_METHOD = $(firstword $(HDEPENDS_METHOD_$(VX_GNU_MAJOR_VERSION)) COMP)
|
||||
|
||||
#--------------------------------------------------
|
||||
# osithead use default stack, YES or NO override
|
||||
|
||||
@@ -65,7 +65,7 @@ GNU = NO
|
||||
#
|
||||
# Darwin shared libraries
|
||||
#
|
||||
SHRLIB_LDFLAGS = -dynamiclib -undefined dynamic_lookup \
|
||||
SHRLIB_LDFLAGS = -dynamiclib -flat_namespace -undefined dynamic_lookup \
|
||||
-install_name $(shell $(FULLPATHNAME) $(INSTALL_LIB))/$@ \
|
||||
$(addprefix -compatibility_version , $(SHRLIB_VERSION)) \
|
||||
$(addprefix -current_version , $(SHRLIB_VERSION))
|
||||
|
||||
@@ -26,3 +26,5 @@ endif
|
||||
# Needed to find dlls for base installed build tools (antelope,eflex,...)
|
||||
PATH := $(EPICS_BASE_BIN):$(PATH)
|
||||
|
||||
# Silence the tr1 namespace deprecation warnings
|
||||
USR_CXXFLAGS_WIN32 += -D_SILENCE_TR1_NAMESPACE_DEPRECATION_WARNING
|
||||
|
||||
@@ -54,3 +54,7 @@ OPT_CXXFLAGS_YES += -g
|
||||
# Tune GNU compiler output for a specific 32-bit cpu-type
|
||||
# (e.g. generic, native, i386, i686, pentium2/3/4, prescott, k6, athlon etc.)
|
||||
GNU_TUNE_CFLAGS = -mtune=generic
|
||||
|
||||
|
||||
# Developers using a suitable compiler may enable its address sanitizer:
|
||||
#ENABLE_ASAN = YES
|
||||
|
||||
@@ -50,3 +50,7 @@ OPT_CXXFLAGS_YES += -g
|
||||
# Tune GNU compiler output for a specific 64-bit cpu-type
|
||||
# (e.g. generic, native, core2, nocona, k8, opteron, athlon64, barcelona etc.)
|
||||
GNU_TUNE_CFLAGS = -mtune=generic
|
||||
|
||||
|
||||
# Developers using a suitable compiler may enable its address sanitizer:
|
||||
#ENABLE_ASAN = YES
|
||||
|
||||
@@ -4,17 +4,17 @@
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
|
||||
<title>Known Problems in EPICS 7.0.1</title>
|
||||
<title>Known Problems in EPICS 7.0.3</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h1 style="text-align: center">EPICS 7.0.1: Known Problems</h1>
|
||||
<h1 style="text-align: center">EPICS 7.0.3: Known Problems</h1>
|
||||
|
||||
<p>Any patch files linked below should be applied at the root of the
|
||||
base-7.0.1 tree. Download them, then use the GNU Patch program as
|
||||
base-7.0.3 tree. Download them, then use the GNU Patch program as
|
||||
follows:</p>
|
||||
|
||||
<blockquote><pre>% <b>cd <i>/path/to/</i>base-7.0.1</b>
|
||||
<blockquote><pre>% <b>cd <i>/path/to/</i>base-7.0.3</b>
|
||||
% <b>patch -p1 < <i>/path/to/</i>file.patch</b></pre></blockquote>
|
||||
|
||||
<p>The following problems were known by the developers at the time of this
|
||||
|
||||
@@ -1,24 +1,24 @@
|
||||
Installation Instructions
|
||||
|
||||
EPICS Base Release 7.0.1.1
|
||||
EPICS Base Release 7.0.3
|
||||
|
||||
--------------------------------------------------------------------------
|
||||
|
||||
Table of Contents
|
||||
|
||||
* What is EPICS base?
|
||||
* What is new in this release?
|
||||
* Copyright
|
||||
* Supported platforms
|
||||
* Supported compilers
|
||||
* Software requirements
|
||||
* Host system storage requirements
|
||||
* Documentation
|
||||
* Directory Structure
|
||||
* Build related components
|
||||
* Building EPICS base (Unix and Win32)
|
||||
* Example application and extension
|
||||
* Multiple host platforms
|
||||
* What is EPICS base?
|
||||
* What is new in this release?
|
||||
* Copyright
|
||||
* Supported platforms
|
||||
* Supported compilers
|
||||
* Software requirements
|
||||
* Host system storage requirements
|
||||
* Documentation
|
||||
* Directory Structure
|
||||
* Build related components
|
||||
* Building EPICS base (Unix and Win32)
|
||||
* Example application and extension
|
||||
* Multiple host platforms
|
||||
|
||||
--------------------------------------------------------------------------
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
<BODY>
|
||||
<CENTER>
|
||||
<H1>Installation Instructions</H1>
|
||||
<H2>EPICS Base Release 7.0.1.1</H2><BR>
|
||||
<H2>EPICS Base Release 7.0.3</H2><BR>
|
||||
</CENTER>
|
||||
<HR>
|
||||
<H3> Table of Contents</H3>
|
||||
|
||||
@@ -9,14 +9,18 @@
|
||||
<body lang="en">
|
||||
|
||||
<p>These release notes describe changes that have been made since the previous
|
||||
release of this series of EPICS Base. Note that changes which were merged up
|
||||
release of this series of EPICS Base. <b>Note that changes which were merged up
|
||||
from commits to new releases in an older Base series are not described at the
|
||||
top of this file but have entries that appear lower down, under the series to
|
||||
which they were originally committed. Thus it is important to read more than
|
||||
which they were originally committed.</b> Thus it is important to read more than
|
||||
just the first section to understand everything that has changed in each
|
||||
release.</p>
|
||||
|
||||
<h1 align="center">EPICS Release 7.0.2</h1>
|
||||
<p>The external PVA submodules each have their own separate set of release notes
|
||||
which should also be read to understand what has changed since an earlier
|
||||
release.</p>
|
||||
|
||||
<h1 align="center">EPICS Release 7.0.3</h1>
|
||||
|
||||
<!-- Insert new items immediately below this template ...
|
||||
|
||||
@@ -26,6 +30,124 @@ release.</p>
|
||||
|
||||
-->
|
||||
|
||||
<h3>epicsTimeGetCurrent() optimization</h3>
|
||||
|
||||
<p>Add a fast path to epicsTimeGetCurrent() and related calls
|
||||
in the common case where only the default OS current time provider is
|
||||
registered. This path does not take the global mutex guarding
|
||||
the time providers list, potentially reducing lock contention.</p>
|
||||
|
||||
<h3>dbEvent tweak Queue size</h3>
|
||||
|
||||
<p>The size of the queue used by dbEvent to push monitor updates
|
||||
has been slightly increased based on DBR_TIME_DOUBLE to better fill
|
||||
an ethernet frame. This may result in slightly fewer, but larger
|
||||
frames being sent.</p>
|
||||
|
||||
<h3>mbbo/mbbiDirect number of bits as precision</h3>
|
||||
|
||||
<p>Report NOBT as "precision" through the dbAccess API.
|
||||
This is not accessible through CA, but is planned to be used through
|
||||
QSRV.</p>
|
||||
|
||||
<h1 align="center">EPICS Release 7.0.2.2</h1>
|
||||
|
||||
<h3>Build System changes</h3>
|
||||
|
||||
<ul>
|
||||
|
||||
<li>The GNUmake build targets <tt>cvsclean</tt> and <tt>depclean</tt> are now
|
||||
available from any directory; previously they were only available from
|
||||
application top directories.</li>
|
||||
|
||||
<li>The approach that EPICS Base uses for building submodules inside the parent
|
||||
module looks useful for support modules too. The rules for building submodules
|
||||
have been modified and extracted into a new <tt>RULES_MODULES</tt> file, so a
|
||||
support module will be able to use them too without having to copy them into its
|
||||
own <tt>modules/Makefile</tt>. There are some specific requirements that support
|
||||
modules and their submodules must follow, which are described as comments in the
|
||||
new <tt>base/configure/RULES_MODULES</tt> file itself.</li>
|
||||
|
||||
</ul>
|
||||
|
||||
<h3>EPICS_BASE_VERSION Update Policy change</h3>
|
||||
|
||||
<p>In the past, a build of EPICS using sources checked out from the repository
|
||||
branch between official releases would have shown the version number of the
|
||||
previous release, followed by a <q>-DEV</q> suffix, for example
|
||||
<q>7.0.2.1-DEV</q>.</p>
|
||||
|
||||
<p>The policy that controls when the number gets updated has been changed, and
|
||||
now immediately after a release has been tagged the version number will be
|
||||
updated to the next patch release version, plus the <q>-DEV</q> suffix as
|
||||
before. Thus following <q>7.0.2.2</q> the version number will show as
|
||||
<q>7.0.2.3-DEV</q>. This does not require the next official release to be
|
||||
numbered <q>7.0.2.3</q> though, it could become <q>7.0.3</q> or even
|
||||
<q>7.1.0</q> if the changes incorporated into it are more substantial than bug
|
||||
fixes.</p>
|
||||
|
||||
<h3>Drop CLOCK_MONOTONIC_RAW from posix/osdMonotonic.c</h3>
|
||||
|
||||
<p>Turns out this is ~10x slower to query than CLOCK_MONOTONIC.</p>
|
||||
|
||||
|
||||
<h1 align="center">EPICS Release 7.0.2.1</h1>
|
||||
|
||||
<h3>Linking shared libraries on macOS</h3>
|
||||
|
||||
<p>The linker flag <tt>-flat_namespace</tt> has been restored for creating
|
||||
shared libraries, although not for loadable libraries (bundles). This was
|
||||
required for building using the latest versions of Apple XCode.</p>
|
||||
|
||||
<h3>Fix DB_LINK loop breaking</h3>
|
||||
|
||||
<p>A regression was introduced in 7.0.2 which caused record chains with loops to
|
||||
be incorrectly broken. Processing should be skipped when a DB_LINK with Process
|
||||
Passive (PP) closes a loop to a synchronous record.</p>
|
||||
|
||||
<p>Instead in 7.0.2 the targeted record would be processed if processing began
|
||||
with a remote action (or some other caller of <tt>dbPutField()</tt>). This would
|
||||
result in the loop running a second time. The loop would be broken on the second
|
||||
iteration.</p>
|
||||
|
||||
<p><a href="https://bugs.launchpad.net/epics-base/+bug/1809570">See lp:
|
||||
#1809570</a></p>
|
||||
|
||||
<h3>Old dbStaticLib APIs removed</h3>
|
||||
|
||||
<p>Support for some obsolete dbStaticLib Database Configuration Tool (DCT) APIs
|
||||
was removed some time ago, but vestiges of them still remained. The following
|
||||
routines and macros and have now finally been removed:</p>
|
||||
|
||||
<ul>
|
||||
<li>int dbGetFieldType(DBENTRY *pdbentry)</li>
|
||||
<li>int dbGetLinkType(DBENTRY *pdbentry)</li>
|
||||
<li>DCT_STRING</li>
|
||||
<li>DCT_INTEGER</li>
|
||||
<li>DCT_REAL</li>
|
||||
<li>DCT_MENU</li>
|
||||
<li>DCT_MENUFORM</li>
|
||||
<li>DCT_INLINK</li>
|
||||
<li>DCT_OUTLINK</li>
|
||||
<li>DCT_FWDLINK</li>
|
||||
<li>DCT_NOACCESS</li>
|
||||
<li>DCT_LINK_CONSTANT</li>
|
||||
<li>DCT_LINK_FORM</li>
|
||||
<li>DCT_LINK_PV</li>
|
||||
</ul>
|
||||
|
||||
<h3>Fix for <tt>dbhcr</tt> before <tt>iocInit</tt></h3>
|
||||
|
||||
<p>The <tt>dbhcr</tt> command used to work before <tt>iocInit</tt> as well as
|
||||
afterwards. It displays all records that have hardware addresses (VME_IO,
|
||||
CAMAC_IO, GPIB_IO, INST_IO etc.) but stopped working if run before iocInit due
|
||||
to the rewrite of the link address parser code in dbStaticLib. This release
|
||||
fixes that issue, although in some cases the output may be slightly different
|
||||
than it used to be.</p>
|
||||
|
||||
|
||||
<h1 align="center">EPICS Release 7.0.2</h1>
|
||||
|
||||
<h3>Launchpad Bugs</h3>
|
||||
|
||||
<p>The list of tracked bugs fixed in this release can be found on the
|
||||
@@ -108,9 +230,30 @@ code than documentation. Send questions to the tech-talk mailing list and we'll
|
||||
be happy to try and answer them!</p>
|
||||
|
||||
|
||||
<h2 align="center">Changes from the 3.16 branch since 3.16.1</h2>
|
||||
<h2 align="center">Changes between 3.16.1 and 3.16.2</h2>
|
||||
|
||||
<!-- Insert inherited items immediately below here ... -->
|
||||
<p>The list of tracked bugs fixed in this release can be found on the
|
||||
<a href="https://launchpad.net/epics-base/+milestone/3.16.2">Launchpad Milestone
|
||||
page for EPICS Base 3.16.2</a>.</p>
|
||||
|
||||
<h3>Status reporting for the callback and scanOnce task queues</h3>
|
||||
|
||||
<p>Two new iocsh commands and some associated underlying APIs have been added to
|
||||
show the state of the queues that feed the three callback tasks and the scanOnce
|
||||
task, including a high-water mark which can optionally be reset. The new iocsh
|
||||
commands are <tt>callbackQueueShow</tt> and <tt>scanOnceQueueShow</tt>; both
|
||||
take an optional integer argument which must be non-zero to reset the
|
||||
high-water mark.</p>
|
||||
|
||||
<h3>Support for event codes greater than or equal to NUM_TIME_EVENTS</h3>
|
||||
|
||||
<p>Event numbers greater than or equal to NUM_TIME_EVENTS are now allowed if
|
||||
supported by the registered event time provider, which must provide its own
|
||||
advancing timestamp validation for such events.</p>
|
||||
|
||||
<p>Time events numbered 0 through (NUM_TIME_EVENTS-1) are still validated by
|
||||
code in epicsGeneralTime.c that checks for advancing timestamps and enforces
|
||||
that restriction.</p>
|
||||
|
||||
<h3>Type-safe Device and Driver Support Tables</h3>
|
||||
|
||||
@@ -894,6 +1037,55 @@ of its CALLBACK objects.</p>
|
||||
|
||||
<!-- Insert inherited items immediately below here ... -->
|
||||
|
||||
<h3>Cleaning up with Multiple CA contexts in a Process</h3>
|
||||
|
||||
<p>Bruno Martins reported a problem with the CA client library at shutdown in a
|
||||
process that uses multiple CA client contexts. The first context that triggers
|
||||
the CA client exit handler prevents any others from being able to clean up
|
||||
because it resets the ID of an internal epicsThreadPrivate variable which is
|
||||
shared by all clients. This action has been removed from the client library,
|
||||
which makes cleanup of clients like this possible.</p>
|
||||
|
||||
<h3>Perl CA bindings fixed for macOS Mojave</h3>
|
||||
|
||||
<p>Apple removed some Perl header files from macOS Mojave that were available
|
||||
in their SDK, requiring a change to the include paths used when compiling the
|
||||
CA bindings. The new version should build on new and older macOS versions, and
|
||||
these changes may also help other targets that have an incomplete installation
|
||||
of Perl (the build will continue after printing a warning that the Perl CA
|
||||
bindings could not be built).</p>
|
||||
|
||||
<h3>Routine <tt>epicsTempName()</tt> removed from libCom</h3>
|
||||
|
||||
<p>This routine was a simple wrapper around the C89 function <tt>tmpnam()</tt>
|
||||
which is now seen as unsafe and causes warning messages to be generated by
|
||||
most modern compilers. The two internal uses of this function have been
|
||||
modified to call <tt>epicsTempFile()</tt> instead. We were unable to find any
|
||||
published code that used this function, so it was removed immediately instead
|
||||
of being deprecated.</p>
|
||||
|
||||
<h3>DBD Parsing of Record Types</h3>
|
||||
|
||||
<p>The Perl DBD file parser has been made slightly more liberal; the order in
|
||||
which DBD files must be parsed is now more flexible, so that a record type
|
||||
definition can now be parsed after a device support that referred to that
|
||||
record type. A warning message will be displayed when the device support is
|
||||
seen, but the subsequent loading of the record type will be accepted without
|
||||
triggering an error. See
|
||||
<a href="https://bugs.launchpad.net/epics-base/+bug/1801145">Launchpad bug
|
||||
#1801145</a>.</p>
|
||||
|
||||
<h3>menuScan and several record types documented with POD</h3>
|
||||
|
||||
<p>The EPICS Wiki pages describing a number of standard record types has been
|
||||
converted into the Perl POD documentation format and added to the DBD files,
|
||||
so at build-time an HTML version of these documents is generated and installed
|
||||
into the htmls directory. Thanks to Tony Pietryla.</p>
|
||||
|
||||
<h3>CA client tools learned <tt>-V</tt> option</h3>
|
||||
|
||||
<p>This displays the version numbers of EPICS Base and the CA protocol.</p>
|
||||
|
||||
|
||||
<h2 align="center">Changes made between 3.15.5 and 3.15.6</h2>
|
||||
|
||||
|
||||
@@ -37,6 +37,8 @@ that should be performed when creating production releases of EPICS Base.</p>
|
||||
|
||||
<h3>The Release Process</h3>
|
||||
|
||||
<h4>Full Process</h4>
|
||||
|
||||
<p>The version released on the Feature Freeze date is designated the first
|
||||
pre-release, <tt>-pre1</tt>. The first release candidate <tt>-rc1</tt> is the
|
||||
first version that has undergone testing by the developers and has shown no
|
||||
@@ -49,6 +51,13 @@ release candidate has been available for 2 weeks without any new problems being
|
||||
reported or major changes having to be committed, the final release can be
|
||||
made.</p>
|
||||
|
||||
<h4>Short Process for Patch Releases</h4>
|
||||
|
||||
<p>The Patch Release date and its scope are agreed upon a few weeks ahead of the
|
||||
release. If no blocking issues are raised, the release is made by the Release
|
||||
Manager on or as soon as possible after that date, following the steps below
|
||||
starting at <a href="#ReleaseApproval">Release Approval</a>.</p>
|
||||
|
||||
<h3>Roles</h3>
|
||||
|
||||
<p>The following roles are used below:</p>
|
||||
@@ -64,6 +73,7 @@ made.</p>
|
||||
<dd>Responsible for the EPICS website</dd>
|
||||
</dl>
|
||||
|
||||
<form>
|
||||
<table border="1" width="100%">
|
||||
<tbody>
|
||||
<tr>
|
||||
@@ -116,80 +126,81 @@ made.</p>
|
||||
<tr>
|
||||
<td> </td>
|
||||
<td>Website Manager</td>
|
||||
<td>Create a release milestone on Launchpad if necessary and set the
|
||||
expected release date. Note that pre-release and release-candidate
|
||||
versions do not appear on Launchpad, only the final release.</td>
|
||||
<td>Create a release milestone on Launchpad. If a target release date is
|
||||
known set "Date Targeted" to the expected release date. Note that
|
||||
pre-release and release-candidate versions should not get Launchpad
|
||||
milestones, only the final release.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th colspan="3">Creating pre-release and release-candidate versions</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td> </td>
|
||||
<td><input type="checkbox"></td>
|
||||
<td>Release Manager</td>
|
||||
<!-- Submodules... -->
|
||||
<td>Edit and commit changes to the EPICS version number file
|
||||
configure/CONFIG_BASE_VERSION.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td> </td>
|
||||
<td><input type="checkbox"></td>
|
||||
<td>Release Manager</td>
|
||||
<td>Tag the module in Git, using these tag conventions:
|
||||
<ul>
|
||||
<li>
|
||||
<tt>R7.0.2-pre<i>n</i></tt>
|
||||
<tt>R7.0.3-pre<i>n</i></tt>
|
||||
— pre-release tag
|
||||
</li>
|
||||
<li>
|
||||
<tt>R7.0.2-rc<i>n</i></tt>
|
||||
<tt>R7.0.3-rc<i>n</i></tt>
|
||||
— release candidate tag
|
||||
</li>
|
||||
</ul>
|
||||
<blockquote><tt>
|
||||
cd base-7.0<br />
|
||||
git tag -m 'ANJ: Tagged for 7.0.2-rc1' R7.0.2-rc1
|
||||
git tag -m 'ANJ: Tagged for 7.0.3-rc1' R7.0.3-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
|
||||
that are only tagged at the final release.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td> </td>
|
||||
<td><input type="checkbox"></td>
|
||||
<td>Release Manager</td>
|
||||
<td>Export the tagged version into a tarfile. The <tt>make-tar.sh</tt>
|
||||
script generates a gzipped tarfile directly from the tag, excluding the
|
||||
files and directories that are only used for continuous integration:
|
||||
<blockquote><tt>
|
||||
cd base-7.0<br />
|
||||
.ci/make-tar.sh R7.0.2-rc1 base-7.0.2-rc1.tar.gz base-7.0.2-rc1/
|
||||
./.tools/make-tar.sh R7.0.3-rc1 base-7.0.3-rc1.tar.gz base-7.0.3-rc1/
|
||||
</tt></blockquote>
|
||||
Create a GPG signature file of the tarfile as follows:
|
||||
<blockquote><tt>
|
||||
gpg --armor --sign --detach-sig base-7.0.2-rc1.tar.gz
|
||||
gpg --armor --sign --detach-sig base-7.0.3-rc1.tar.gz
|
||||
</tt></blockquote>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td> </td>
|
||||
<td><input type="checkbox"></td>
|
||||
<td>Release Manager</td>
|
||||
<td>Test the tarfile by extracting its contents and building it on at
|
||||
least one supported platform.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td> </td>
|
||||
<td><input type="checkbox"></td>
|
||||
<td>Website Manager</td>
|
||||
<td>Copy the tarfile and its signature to the Base download area of the
|
||||
website and add the new files to the website Base download index
|
||||
page.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td> </td>
|
||||
<td><input type="checkbox"></td>
|
||||
<td>Website Manager</td>
|
||||
<td>Create or update a website subdirectory to hold the release
|
||||
documentation, and copy in selected files from the base/documentation
|
||||
and base/html directories of the tarfile.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td> </td>
|
||||
<td><input type="checkbox"></td>
|
||||
<td>Website Manager</td>
|
||||
<td>Create or modify the webpage for the new release with links to the
|
||||
release documents and tar file. Pre-release and release-candidate
|
||||
@@ -249,28 +260,11 @@ made.</p>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<th colspan="3">To Do</th>
|
||||
<th colspan="3" id="ReleaseApproval">
|
||||
Release Approval</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td> </td>
|
||||
<td>Release Manager</td>
|
||||
<td>Discuss and resolve before final release:
|
||||
<ul>
|
||||
<li>Update version numbers in individual
|
||||
<tt>modules/*/configure/*VERSION</tt> files?</li>
|
||||
<li>Unifying module Release Notes for easier publication.</li>
|
||||
<li>Unifying module documentation directories and Doxygen
|
||||
config's.</li>
|
||||
<li>Publishing releases on the new website.</li>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<th colspan="3">Release Approval</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td> </td>
|
||||
<td><input type="checkbox"></td>
|
||||
<td>Release Manager</td>
|
||||
<td>Obtain a positive <q>Ok to release</q> from all platform developers
|
||||
once a release candidate version has gone for 2 weeks without any major
|
||||
@@ -280,101 +274,170 @@ made.</p>
|
||||
<th colspan="3">Creating the final release version</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td> </td>
|
||||
<td><input type="checkbox"></td>
|
||||
<td>Release Manager</td>
|
||||
<td>Edit and commit changes to the EPICS version number file
|
||||
configure/CONFIG_BASE_VERSION.</td>
|
||||
<td>
|
||||
<p>For each external submodule, check if the module's release version
|
||||
number (and SHRLIB_VERSION setting) has been updated if appropriate,
|
||||
and that its Release Notes have been updated to cover any changes.
|
||||
Commit any fixes and tag the module if that hasn't already been
|
||||
done.</p>
|
||||
<p>Update all external submodules on the Base-7.0 branch and
|
||||
commit.</p></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td> </td>
|
||||
<td><input type="checkbox"></td>
|
||||
<td>Release Manager</td>
|
||||
<td>Tag the module in Git:
|
||||
<td>Edit and commit changes to the EPICS Base version number file and
|
||||
the embedded module version files:
|
||||
<ul>
|
||||
<li>configure/CONFIG_BASE_VERSION </li>
|
||||
<li>modules/libcom/configure/CONFIG_LIBCOM_VERSION</li>
|
||||
<li>modules/ca/configure/CONFIG_CA_VERSION</li>
|
||||
<li>modules/database/configure/CONFIG_DATABASE_VERSION</li>
|
||||
</ul></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><input type="checkbox"></td>
|
||||
<td>Release Manager</td>
|
||||
<td>Tag the epics-base module in Git:
|
||||
<blockquote><tt>
|
||||
cd base-7.0<br />
|
||||
git tag -m 'ANJ: Tagged for 7.0.2' R7.0.2</i>
|
||||
git tag -m 'ANJ: Tagged for 7.0.3' R7.0.3</i>
|
||||
</tt></blockquote>
|
||||
</td>
|
||||
Don't push the new tag to the Launchpad repository yet.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td> </td>
|
||||
<td><input type="checkbox"></td>
|
||||
<td>Release Manager</td>
|
||||
<td>Export the tagged version into a tarfile. The <tt>make-tar.sh</tt>
|
||||
script generates a gzipped tarfile directly from the tag, excluding the
|
||||
files and directories that are only used for continuous integration:
|
||||
<blockquote><tt>
|
||||
cd base-7.0<br />
|
||||
.ci/make-tar.sh R7.0.2 base-7.0.2.tar.gz base-7.0.2/
|
||||
./.tools/make-tar.sh R7.0.3 base-7.0.3.tar.gz base-7.0.3/
|
||||
</tt></blockquote>
|
||||
Create a GPG signature file of the tarfile as follows:
|
||||
<blockquote><tt>
|
||||
gpg --armor --sign --detach-sig base-7.0.2.tar.gz
|
||||
gpg --armor --sign --detach-sig base-7.0.3.tar.gz
|
||||
</tt></blockquote>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td> </td>
|
||||
<td><input type="checkbox"></td>
|
||||
<td>Release Manager</td>
|
||||
<td>Test the tar file by extracting its contents and building it on at
|
||||
least one supported platform</td>
|
||||
least one supported platform. When this succeeds the new git tag can be
|
||||
pushed to the Launchpad repository:
|
||||
<blockquote><tt>
|
||||
git push --follow-tags upstream 7.0
|
||||
</tt></blockquote>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td> </td>
|
||||
<td><input type="checkbox"></td>
|
||||
<td>Release Manager</td>
|
||||
<td>Edit and commit changes to the EPICS Base version number file and
|
||||
the embedded module version files:
|
||||
<ul>
|
||||
<li>configure/CONFIG_BASE_VERSION </li>
|
||||
<li>modules/libcom/configure/CONFIG_LIBCOM_VERSION</li>
|
||||
<li>modules/ca/configure/CONFIG_CA_VERSION</li>
|
||||
<li>modules/database/configure/CONFIG_DATABASE_VERSION</li>
|
||||
</ul>
|
||||
Version numbers should be set to the next expected patch release,
|
||||
with a "-DEV" tag added (where applicable).
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<th colspan="3">Publish to epics.anl.gov</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><input type="checkbox"></td>
|
||||
<td>Release Manager</td>
|
||||
<td>Copy the tarfile and its signature to the Base download area of the
|
||||
website.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td> </td>
|
||||
<td>Release Manager</td>
|
||||
<td>Find all Launchpad bug reports with the status Fix Committed which
|
||||
have been fixed in this release and mark them Fix Released.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th colspan="3">Publish and Announce it</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td> </td>
|
||||
<td>Website Manager</td>
|
||||
<td>Copy the tar file and its <tt>.asc</tt> signature file to the Base
|
||||
download area of the website.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td> </td>
|
||||
<td><input type="checkbox"></td>
|
||||
<td>Website Manager</td>
|
||||
<td>Update the website subdirectory that holds the release
|
||||
documentation, and copy in the files from the base/documentation
|
||||
directory of the tarfile.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td> </td>
|
||||
<td><input type="checkbox"></td>
|
||||
<td>Website Manager</td>
|
||||
<td>Update the webpage for the new release with links to the release
|
||||
documents and tar file.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td> </td>
|
||||
<td><input type="checkbox"></td>
|
||||
<td>Website Manager</td>
|
||||
<td>Add the new release tar file to the website Base download index
|
||||
page.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td> </td>
|
||||
<td><input type="checkbox"></td>
|
||||
<td>Website Manager</td>
|
||||
<td>Link to the release webpage from other relevent areas of the
|
||||
website - update front page and sidebars.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td> </td>
|
||||
<td><input type="checkbox"></td>
|
||||
<td>Website Manager</td>
|
||||
<td>Add an entry to the website News page, linking to the new version
|
||||
webpage.</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td> </td>
|
||||
<th colspan="3">Publish to epics-controls</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><input type="checkbox"></td>
|
||||
<td>Website Manager</td>
|
||||
<td>Upload the tar file and its <tt>.asc</tt> signature file to the
|
||||
epics-controls web-server [ToDo: ssh-key, location]</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><input type="checkbox"></td>
|
||||
<td>Website Manager</td>
|
||||
<td>Follow instructions on
|
||||
<a href="https://epics-controls.org/resources-and-support/documents/epics-website-documentation/adding-a-page-for-a-new-release/">
|
||||
Add a page for a new release</a> to create a new release webpage (not
|
||||
required for a patch release though, just edit the existing page).</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<th colspan="3">Publish to Launchpad</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><input type="checkbox"></td>
|
||||
<td>Website Manager</td>
|
||||
<td>Go to the Launchpad milestone for this release. Click the Create
|
||||
release button and add the release date. Put a URL for the release page
|
||||
in the Release notes box, and click the Create release button. Upload
|
||||
the tar file and its <tt>.asc</tt> signature file to the new Launchpad
|
||||
release page.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><input type="checkbox"></td>
|
||||
<td>Release Manager</td>
|
||||
<td>Find all Launchpad bug reports with the status Fix Committed which
|
||||
have been fixed in this release and mark them Fix Released.</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<th colspan="3">Make Announcement</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><input type="checkbox"></td>
|
||||
<td>Release Manager</td>
|
||||
<td>Announce the release on the tech-talk mailing list.</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</form>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -3,8 +3,11 @@
|
||||
# in file LICENSE that is included with this distribution.
|
||||
#*************************************************************************
|
||||
|
||||
# When building submodules, this should always be true:
|
||||
INSTALL_LOCATION = $(EPICS_BASE)
|
||||
# The name our submodules know us by:
|
||||
PARENT_MODULE = EPICS_BASE
|
||||
|
||||
# Stop submodules from installing their configuration files:
|
||||
# When building submodules, this should always be true:
|
||||
INSTALL_LOCATION := $($(PARENT_MODULE))
|
||||
|
||||
# Stop submodules installing their configure/ files into our area
|
||||
CONFIG_INSTALLS =
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
|
||||
TOP = ..
|
||||
include $(TOP)/configure/CONFIG
|
||||
include CONFIG_SITE.local
|
||||
|
||||
# Submodules for bundle build
|
||||
SUBMODULES += libcom
|
||||
@@ -39,20 +40,4 @@ example_DEPEND_DIRS = pva2pva pvaClient
|
||||
# Allow sites to add extra submodules
|
||||
-include Makefile.local
|
||||
|
||||
# Add only checked-out submodules to DIRS
|
||||
DIRS += $(subst /Makefile,,$(wildcard $(addsuffix /Makefile, $(SUBMODULES))))
|
||||
|
||||
include $(TOP)/configure/RULES_DIRS
|
||||
|
||||
INSTALL_LOCATION_ABS := $(abspath $(INSTALL_LOCATION))
|
||||
RELEASE_LOCAL := RELEASE.$(EPICS_HOST_ARCH).local
|
||||
|
||||
# Ensure that RELEASE.<host>.local exists before doing anything else
|
||||
all host $(DIRS) $(ARCHS) $(ACTIONS) $(dirActionTargets) $(dirArchTargets) \
|
||||
$(dirActionArchTargets) $(actionArchTargets): | $(RELEASE_LOCAL)
|
||||
|
||||
$(RELEASE_LOCAL):
|
||||
$(ECHO) Creating $@, EPICS_BASE = $(INSTALL_LOCATION_ABS)
|
||||
@echo EPICS_BASE = $(INSTALL_LOCATION_ABS)> $@
|
||||
realclean:
|
||||
$(RM) $(RELEASE_LOCAL)
|
||||
include $(TOP)/configure/RULES_MODULES
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
EPICS_CA_MAJOR_VERSION = 4
|
||||
EPICS_CA_MINOR_VERSION = 13
|
||||
EPICS_CA_MAINTENANCE_VERSION = 2
|
||||
EPICS_CA_DEVELOPMENT_FLAG = 1
|
||||
EPICS_CA_MAINTENANCE_VERSION = 4
|
||||
EPICS_CA_DEVELOPMENT_FLAG = 0
|
||||
|
||||
@@ -3143,7 +3143,8 @@ indicating the current state of the channel.</p>
|
||||
<dl>
|
||||
<dt><code>COUNT</code></dt>
|
||||
<dd>The element count to be read from the specified channel. A count of
|
||||
zero means use the current element count from the server.</dd>
|
||||
zero means use the current element count from the server, effectively
|
||||
resulting in a variable size array subscription.</dd>
|
||||
</dl>
|
||||
<dl>
|
||||
<dt><code>CHID</code></dt>
|
||||
|
||||
@@ -125,7 +125,7 @@ EXPANDVARS += EPICS_CA_DEVELOPMENT_FLAG
|
||||
EXPANDFLAGS += $(foreach var,$(EXPANDVARS),-D$(var)="$(strip $($(var)))")
|
||||
|
||||
# shared library ABI version.
|
||||
SHRLIB_VERSION = 4.13.1
|
||||
SHRLIB_VERSION = $(EPICS_CA_MAJOR_VERSION).$(EPICS_CA_MINOR_VERSION).$(EPICS_CA_MAINTENANCE_VERSION)
|
||||
|
||||
include $(TOP)/configure/RULES
|
||||
|
||||
|
||||
@@ -45,20 +45,12 @@ static epicsThreadOnceId cacOnce = EPICS_THREAD_ONCE_INIT;
|
||||
|
||||
const unsigned ca_client_context :: flushBlockThreshold = 0x58000;
|
||||
|
||||
extern "C" void cacExitHandler ( void *)
|
||||
{
|
||||
epicsThreadPrivateDelete ( caClientCallbackThreadId );
|
||||
caClientCallbackThreadId = 0;
|
||||
delete ca_client_context::pDefaultServiceInstallMutex;
|
||||
}
|
||||
|
||||
// runs once only for each process
|
||||
extern "C" void cacOnceFunc ( void * )
|
||||
{
|
||||
caClientCallbackThreadId = epicsThreadPrivateCreate ();
|
||||
assert ( caClientCallbackThreadId );
|
||||
ca_client_context::pDefaultServiceInstallMutex = newEpicsMutex;
|
||||
epicsAtExit ( cacExitHandler,0 );
|
||||
}
|
||||
|
||||
extern epicsThreadPrivateId caClientContextId;
|
||||
@@ -67,6 +59,8 @@ cacService * ca_client_context::pDefaultService = 0;
|
||||
epicsMutex * ca_client_context::pDefaultServiceInstallMutex;
|
||||
|
||||
ca_client_context::ca_client_context ( bool enablePreemptiveCallback ) :
|
||||
mutex(__FILE__, __LINE__),
|
||||
cbMutex(__FILE__, __LINE__),
|
||||
createdByThread ( epicsThreadGetIdSelf () ),
|
||||
ca_exception_func ( 0 ), ca_exception_arg ( 0 ),
|
||||
pVPrintfFunc ( errlogVprintf ), fdRegFunc ( 0 ), fdRegArg ( 0 ),
|
||||
|
||||
@@ -289,7 +289,6 @@ private:
|
||||
};
|
||||
|
||||
extern "C" void cacOnceFunc ( void * );
|
||||
extern "C" void cacExitHandler ( void *);
|
||||
|
||||
struct ca_client_context : public cacContextNotify
|
||||
{
|
||||
@@ -429,7 +428,6 @@ private:
|
||||
ca_client_context & operator = ( const ca_client_context & );
|
||||
|
||||
friend void cacOnceFunc ( void * );
|
||||
friend void cacExitHandler ( void *);
|
||||
static cacService * pDefaultService;
|
||||
static epicsMutex * pDefaultServiceInstallMutex;
|
||||
static const unsigned flushBlockThreshold;
|
||||
|
||||
@@ -37,6 +37,7 @@ repeaterSubscribeTimer::repeaterSubscribeTimer (
|
||||
epicsMutex & cbMutexIn, cacContextNotify & ctxNotifyIn ) :
|
||||
timer ( queueIn.createTimer () ), iiu ( iiuIn ),
|
||||
cbMutex ( cbMutexIn ),ctxNotify ( ctxNotifyIn ),
|
||||
stateMutex(__FILE__, __LINE__),
|
||||
attempts ( 0 ), registered ( false ), once ( false )
|
||||
{
|
||||
}
|
||||
|
||||
@@ -961,7 +961,7 @@ bool udpiiu::pushDatagramMsg ( epicsGuard < epicsMutex > & guard,
|
||||
|
||||
udpiiu :: SearchDestUDP :: SearchDestUDP (
|
||||
const osiSockAddr & destAddr, udpiiu & udpiiuIn ) :
|
||||
_destAddr ( destAddr ), _udpiiu ( udpiiuIn )
|
||||
_lastError (0u), _destAddr ( destAddr ), _udpiiu ( udpiiuIn )
|
||||
{
|
||||
}
|
||||
|
||||
@@ -976,6 +976,13 @@ void udpiiu :: SearchDestUDP :: searchRequest (
|
||||
int status = sendto ( _udpiiu.sock, const_cast<char *>(pBuf), bufSizeAsInt, 0,
|
||||
& _destAddr.sa, sizeof ( _destAddr.sa ) );
|
||||
if ( status == bufSizeAsInt ) {
|
||||
if ( _lastError ) {
|
||||
char buf[64];
|
||||
sockAddrToDottedIP ( &_destAddr.sa, buf, sizeof ( buf ) );
|
||||
errlogPrintf (
|
||||
"CAC: ok sending UDP msg to %s\n", buf);
|
||||
}
|
||||
_lastError = 0;
|
||||
break;
|
||||
}
|
||||
if ( status >= 0 ) {
|
||||
@@ -1002,7 +1009,9 @@ void udpiiu :: SearchDestUDP :: searchRequest (
|
||||
else if ( localErrno == SOCK_EBADF ) {
|
||||
break;
|
||||
}
|
||||
else {
|
||||
else if ( localErrno == _lastError) {
|
||||
break;
|
||||
} else {
|
||||
char sockErrBuf[64];
|
||||
epicsSocketConvertErrnoToString (
|
||||
sockErrBuf, sizeof ( sockErrBuf ) );
|
||||
@@ -1011,6 +1020,8 @@ void udpiiu :: SearchDestUDP :: searchRequest (
|
||||
errlogPrintf (
|
||||
"CAC: error = \"%s\" sending UDP msg to %s\n",
|
||||
sockErrBuf, buf);
|
||||
|
||||
_lastError = localErrno;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -123,6 +123,7 @@ private:
|
||||
void show (
|
||||
epicsGuard < epicsMutex > &, unsigned level ) const;
|
||||
private:
|
||||
int _lastError;
|
||||
osiSockAddr _destAddr;
|
||||
udpiiu & _udpiiu;
|
||||
};
|
||||
|
||||
@@ -8,6 +8,18 @@
|
||||
TOP = ../..
|
||||
include $(TOP)/configure/CONFIG
|
||||
|
||||
ifdef T_A
|
||||
PERL_VERSION = $(shell $(PERL) ../perlConfig.pl version)
|
||||
PERL_ARCHNAME = $(shell $(PERL) ../perlConfig.pl archname)
|
||||
PERL_ARCHPATH := $(PERL_VERSION)/$(PERL_ARCHNAME)
|
||||
|
||||
PERL_ARCHLIB := $(shell $(PERL) ../perlConfig.pl archlib)
|
||||
PERL_h = $(PERL_ARCHLIB)/CORE/perl.h
|
||||
|
||||
EXTUTILS := $(shell $(PERL) ../perlConfig.pl privlib)/ExtUtils
|
||||
PERLBIN := $(shell $(PERL) ../perlConfig.pl bin)
|
||||
XSUBPP := $(firstword $(wildcard $(PERLBIN)/xsubpp $(EXTUTILS)/xsubpp))
|
||||
|
||||
# Special settings for Darwin:
|
||||
ifeq ($(OS_CLASS),Darwin)
|
||||
# Use hdepends command (not GNU compiler flags)
|
||||
@@ -18,22 +30,23 @@ ifeq ($(OS_CLASS),Darwin)
|
||||
# Perl loadable libraries on Darwin have funny names
|
||||
LOADABLE_SHRLIB_PREFIX =
|
||||
LOADABLE_SHRLIB_SUFFIX = .$(shell $(PERL) ../perlConfig.pl dlext)
|
||||
|
||||
ifeq ($(wildcard $(PERL_h)),)
|
||||
# Perl's headers moved in Mojave
|
||||
SDK_PATH := $(shell xcodebuild -version -sdk macosx Path)
|
||||
PERL_ARCHLIB := $(SDK_PATH)/$(PERL_ARCHLIB)
|
||||
endif
|
||||
endif
|
||||
|
||||
ifdef T_A
|
||||
PERL_VERSION = $(shell $(PERL) ../perlConfig.pl version)
|
||||
PERL_ARCHNAME = $(shell $(PERL) ../perlConfig.pl archname)
|
||||
PERL_ARCHPATH := $(PERL_VERSION)/$(PERL_ARCHNAME)
|
||||
|
||||
EXTUTILS := $(shell $(PERL) ../perlConfig.pl privlib)/ExtUtils
|
||||
PERLBIN := $(shell $(PERL) ../perlConfig.pl bin)
|
||||
XSUBPP := $(firstword $(wildcard $(PERLBIN)/xsubpp $(EXTUTILS)/xsubpp))
|
||||
|
||||
ifeq ($(T_A),$(EPICS_HOST_ARCH)) # No cross-builds (wrong Perl!)
|
||||
ifeq ($(strip $(XSUBPP)),)
|
||||
$(warning Perl's xsubpp program was not found.)
|
||||
$(warning The Perl CA module will not be built.)
|
||||
else
|
||||
ifeq ($(T_A),$(EPICS_HOST_ARCH)) # No cross-builds (wrong Perl!)
|
||||
ifeq ($(wildcard $(PERL_h)),)
|
||||
$(warning Perl's C header files were not found.)
|
||||
$(warning The Perl CA module will not be built.)
|
||||
else
|
||||
ifeq ($(findstring $(OS_CLASS),WIN32 cygwin32),) # Doesn't build on WIN32
|
||||
LOADABLE_LIBRARY_HOST = Cap5
|
||||
|
||||
@@ -50,10 +63,11 @@ endif
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
|
||||
Cap5_SRCS = Cap5.xs
|
||||
Cap5_LIBS = ca Com
|
||||
Cap5_INCLUDES = -I$(shell $(PERL) ../perlConfig.pl archlib)/CORE
|
||||
Cap5_INCLUDES = -I$(PERL_ARCHLIB)/CORE
|
||||
Cap5_CFLAGS = $(shell $(PERL) ../perlConfig.pl ccflags)
|
||||
|
||||
CLEANS += Cap5.c
|
||||
|
||||
@@ -34,6 +34,7 @@
|
||||
#include <alarm.h>
|
||||
#include <cadef.h>
|
||||
#include <epicsGetopt.h>
|
||||
#include "epicsVersion.h"
|
||||
|
||||
#include "tool_lib.h"
|
||||
|
||||
@@ -55,6 +56,7 @@ static void usage (void)
|
||||
{
|
||||
fprintf (stderr, "\nUsage: caget [options] <PV name> ...\n\n"
|
||||
" -h: Help: Print this message\n"
|
||||
" -V: Version: Show EPICS and CA versions\n"
|
||||
"Channel Access options:\n"
|
||||
" -w <sec>: Wait time, specifies CA timeout, default is %f second(s)\n"
|
||||
" -c: Asynchronous get (use ca_get_callback and wait for completion)\n"
|
||||
@@ -389,11 +391,14 @@ int main (int argc, char *argv[])
|
||||
|
||||
LINE_BUFFER(stdout); /* Configure stdout buffering */
|
||||
|
||||
while ((opt = getopt(argc, argv, ":taicnhsSe:f:g:l:#:d:0:w:p:F:")) != -1) {
|
||||
while ((opt = getopt(argc, argv, ":taicnhsSVe:f:g:l:#:d:0:w:p:F:")) != -1) {
|
||||
switch (opt) {
|
||||
case 'h': /* Print usage */
|
||||
usage();
|
||||
return 0;
|
||||
case 'V':
|
||||
printf( "\nEPICS Version %s, CA Protocol version %s\n", EPICS_VERSION_STRING, ca_version() );
|
||||
return 0;
|
||||
case 't': /* Terse output mode */
|
||||
complainIfNotPlainAndSet(&format, terse);
|
||||
break;
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
|
||||
#include <stdio.h>
|
||||
#include <epicsStdlib.h>
|
||||
#include "epicsVersion.h"
|
||||
|
||||
#include <cadef.h>
|
||||
#include <epicsGetopt.h>
|
||||
@@ -36,12 +37,14 @@ void usage (void)
|
||||
{
|
||||
fprintf (stderr, "\nUsage: cainfo [options] <PV name> ...\n\n"
|
||||
" -h: Help: Print this message\n"
|
||||
" -V: Version: Show EPICS and CA versions\n"
|
||||
"Channel Access options:\n"
|
||||
" -w <sec>: Wait time, specifies CA timeout, default is %f second(s)\n"
|
||||
" -s <level>: Call ca_client_status with the specified interest level\n"
|
||||
" -p <prio>: CA priority (0-%u, default 0=lowest)\n"
|
||||
"\nExample: cainfo my_channel another_channel\n\n"
|
||||
, DEFAULT_TIMEOUT, CA_PRIORITY_MAX);
|
||||
fprintf (stderr, "\nEPICS Version %s, CA Protocol version %s\n", EPICS_VERSION_STRING, ca_version() );
|
||||
}
|
||||
|
||||
|
||||
@@ -137,11 +140,14 @@ int main (int argc, char *argv[])
|
||||
|
||||
LINE_BUFFER(stdout); /* Configure stdout buffering */
|
||||
|
||||
while ((opt = getopt(argc, argv, ":nhw:s:p:")) != -1) {
|
||||
while ((opt = getopt(argc, argv, ":nhVw:s:p:")) != -1) {
|
||||
switch (opt) {
|
||||
case 'h': /* Print usage */
|
||||
usage();
|
||||
return 0;
|
||||
case 'V':
|
||||
printf( "\nEPICS Version %s, CA Protocol version %s\n", EPICS_VERSION_STRING, ca_version() );
|
||||
return 0;
|
||||
case 'w': /* Set CA timeout value */
|
||||
if(epicsScanDouble(optarg, &caTimeout) != 1)
|
||||
{
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
#include <stdio.h>
|
||||
#include <epicsStdlib.h>
|
||||
#include <string.h>
|
||||
#include "epicsVersion.h"
|
||||
|
||||
#include <cadef.h>
|
||||
#include <epicsGetopt.h>
|
||||
@@ -44,7 +45,8 @@ void usage (void)
|
||||
{
|
||||
fprintf (stderr, "\nUsage: camonitor [options] <PV name> ...\n"
|
||||
"\n"
|
||||
" -h: Help; Print this message\n"
|
||||
" -h: Help: Print this message\n"
|
||||
" -V: Version: Show EPICS and CA versions\n"
|
||||
"Channel Access options:\n"
|
||||
" -w <sec>: Wait time, specifies CA timeout, default is %f second(s)\n"
|
||||
" -m <msk>: Specify CA event mask to use. <msk> is any combination of\n"
|
||||
@@ -209,11 +211,14 @@ int main (int argc, char *argv[])
|
||||
|
||||
LINE_BUFFER(stdout); /* Configure stdout buffering */
|
||||
|
||||
while ((opt = getopt(argc, argv, ":nhm:sSe:f:g:l:#:0:w:t:p:F:")) != -1) {
|
||||
while ((opt = getopt(argc, argv, ":nhVm:sSe:f:g:l:#:0:w:t:p:F:")) != -1) {
|
||||
switch (opt) {
|
||||
case 'h': /* Print usage */
|
||||
usage();
|
||||
return 0;
|
||||
case 'V':
|
||||
printf( "\nEPICS Version %s, CA Protocol version %s\n", EPICS_VERSION_STRING, ca_version() );
|
||||
return 0;
|
||||
case 'n': /* Print ENUM as index numbers */
|
||||
enumAsNr=1;
|
||||
break;
|
||||
|
||||
@@ -37,6 +37,7 @@
|
||||
#include <epicsGetopt.h>
|
||||
#include <epicsEvent.h>
|
||||
#include <epicsString.h>
|
||||
#include "epicsVersion.h"
|
||||
|
||||
#include "tool_lib.h"
|
||||
|
||||
@@ -59,6 +60,7 @@ void usage (void)
|
||||
fprintf (stderr, "\nUsage: caput [options] <PV name> <PV value> ...\n"
|
||||
" caput -a [options] <PV name> <no of values> <PV value> ...\n\n"
|
||||
" -h: Help: Print this message\n"
|
||||
" -V: Version: Show EPICS and CA versions\n"
|
||||
"Channel Access options:\n"
|
||||
" -w <sec>: Wait time, specifies CA timeout, default is %f second(s)\n"
|
||||
" -c: Asynchronous put (use ca_put_callback and wait for completion)\n"
|
||||
@@ -281,11 +283,14 @@ int main (int argc, char *argv[])
|
||||
LINE_BUFFER(stdout); /* Configure stdout buffering */
|
||||
putenv("POSIXLY_CORRECT="); /* Behave correct on GNU getopt systems */
|
||||
|
||||
while ((opt = getopt(argc, argv, ":cnlhatsS#:w:p:F:")) != -1) {
|
||||
while ((opt = getopt(argc, argv, ":cnlhatsVS#:w:p:F:")) != -1) {
|
||||
switch (opt) {
|
||||
case 'h': /* Print usage */
|
||||
usage();
|
||||
return 0;
|
||||
case 'V':
|
||||
printf( "\nEPICS Version %s, CA Protocol version %s\n", EPICS_VERSION_STRING, ca_version() );
|
||||
return 0;
|
||||
case 'n': /* Force interpret ENUM as index number */
|
||||
enumAsNr = 1;
|
||||
enumAsString = 0;
|
||||
|
||||
@@ -25,7 +25,7 @@ INSTALL_LOCATION = $(TOP)
|
||||
BASE_CPPFLAGS += -DUSE_TYPED_RSET
|
||||
|
||||
# Shared library ABI version.
|
||||
SHRLIB_VERSION = 3.17.0
|
||||
SHRLIB_VERSION = $(EPICS_DATABASE_MAJOR_VERSION).$(EPICS_DATABASE_MINOR_VERSION).$(EPICS_DATABASE_MAINTENANCE_VERSION)
|
||||
|
||||
# CONFIG_SITE files contain other build configuration settings
|
||||
include $(TOP)/configure/CONFIG_SITE
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
EPICS_DATABASE_MAJOR_VERSION = 3
|
||||
EPICS_DATABASE_MINOR_VERSION = 17
|
||||
EPICS_DATABASE_MAINTENANCE_VERSION = 1
|
||||
EPICS_DATABASE_DEVELOPMENT_FLAG = 1
|
||||
EPICS_DATABASE_MAINTENANCE_VERSION = 4
|
||||
EPICS_DATABASE_DEVELOPMENT_FLAG = 0
|
||||
|
||||
@@ -25,11 +25,6 @@ INC += databaseVersionNum.h
|
||||
|
||||
PROD_LIBS = Com
|
||||
|
||||
EPICS_DATABASE_MAJOR_VERSION = 3
|
||||
EPICS_DATABASE_MINOR_VERSION = 17
|
||||
EPICS_DATABASE_MAINTENANCE_VERSION = 0
|
||||
EPICS_DATABASE_DEVELOPMENT_FLAG = 1
|
||||
|
||||
include $(IOCDIR)/as/Makefile
|
||||
include $(IOCDIR)/bpt/Makefile
|
||||
include $(IOCDIR)/db/Makefile
|
||||
|
||||
@@ -61,7 +61,7 @@ DBDINC += menuScan
|
||||
DBDINC += dbCommon
|
||||
|
||||
dbMenusPod = $(notdir $(wildcard ../db/menu*.dbd.pod))
|
||||
HTMLS += $(patsubst %.dbd.pod,%.html,$(menusPod))
|
||||
HTMLS += $(patsubst %.dbd.pod,%.html,$(dbMenusPod))
|
||||
|
||||
dbCore_SRCS += dbLock.c
|
||||
dbCore_SRCS += dbAccess.c
|
||||
|
||||
@@ -54,7 +54,8 @@ typedef struct cbQueueSet {
|
||||
epicsEventId semWakeUp;
|
||||
epicsRingPointerId queue;
|
||||
int queueOverflow;
|
||||
int shutdown;
|
||||
int queueOverflows;
|
||||
int shutdown; // use atomic
|
||||
int threadsConfigured;
|
||||
int threadsRunning;
|
||||
} cbQueueSet;
|
||||
@@ -70,12 +71,15 @@ epicsExportAddress(int,callbackParallelThreadsDefault);
|
||||
/* Timer for Delayed Requests */
|
||||
static epicsTimerQueueId timerQueue;
|
||||
|
||||
/* Shutdown handling */
|
||||
enum ctl {ctlInit, ctlRun, ctlPause, ctlExit};
|
||||
static volatile enum ctl cbCtl;
|
||||
static epicsEventId startStopEvent;
|
||||
enum cbState_t {
|
||||
cbInit, /* before callbackInit() and after callbackCleanup() */
|
||||
cbRun, /* after callbackInit() and before callbackStop() */
|
||||
cbStop, /* after callbackStop() and before callbackCleanup() */
|
||||
};
|
||||
|
||||
static int callbackIsInit;
|
||||
static int cbState; // holdscbState_t, use atomic ops
|
||||
|
||||
static epicsEventId startStopEvent;
|
||||
|
||||
/* Static data */
|
||||
static char *threadNamePrefix[NUM_CALLBACK_PRIORITIES] = {
|
||||
@@ -95,7 +99,7 @@ static int priorityValue[NUM_CALLBACK_PRIORITIES] = {0, 1, 2};
|
||||
|
||||
int callbackSetQueueSize(int size)
|
||||
{
|
||||
if (callbackIsInit) {
|
||||
if (epicsAtomicGetIntT(&cbState)!=cbInit) {
|
||||
fprintf(stderr, "Callback system already initialized\n");
|
||||
return -1;
|
||||
}
|
||||
@@ -103,9 +107,54 @@ int callbackSetQueueSize(int size)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int callbackQueueStatus(const int reset, callbackQueueStats *result)
|
||||
{
|
||||
int ret;
|
||||
if (epicsAtomicGetIntT(&cbState)==cbInit) return -1;
|
||||
if (result) {
|
||||
int prio;
|
||||
result->size = callbackQueueSize;
|
||||
for(prio = 0; prio < NUM_CALLBACK_PRIORITIES; prio++) {
|
||||
epicsRingPointerId qId = callbackQueue[prio].queue;
|
||||
result->numUsed[prio] = epicsRingPointerGetUsed(qId);
|
||||
result->maxUsed[prio] = epicsRingPointerGetHighWaterMark(qId);
|
||||
result->numOverflow[prio] = epicsAtomicGetIntT(&callbackQueue[prio].queueOverflows);
|
||||
}
|
||||
ret = 0;
|
||||
} else {
|
||||
ret = -2;
|
||||
}
|
||||
if (reset) {
|
||||
int prio;
|
||||
for(prio = 0; prio < NUM_CALLBACK_PRIORITIES; prio++) {
|
||||
epicsRingPointerResetHighWaterMark(callbackQueue[prio].queue);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void callbackQueueShow(const int reset)
|
||||
{
|
||||
callbackQueueStats stats;
|
||||
if (callbackQueueStatus(reset, &stats) == -1) {
|
||||
fprintf(stderr, "Callback system not initialized, yet. Please run "
|
||||
"iocInit before using this command.\n");
|
||||
} else {
|
||||
int prio;
|
||||
printf("PRIORITY HIGH-WATER MARK ITEMS IN Q Q SIZE %% USED Q OVERFLOWS\n");
|
||||
for (prio = 0; prio < NUM_CALLBACK_PRIORITIES; prio++) {
|
||||
double qusage = 100.0 * stats.numUsed[prio] / stats.size;
|
||||
printf("%8s %15d %10d %6d %6.1f %11d\n",
|
||||
threadNamePrefix[prio], stats.maxUsed[prio],
|
||||
stats.numUsed[prio], stats.size, qusage,
|
||||
stats.numOverflow[prio]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int callbackParallelThreads(int count, const char *prio)
|
||||
{
|
||||
if (callbackIsInit) {
|
||||
if (epicsAtomicGetIntT(&cbState)!=cbInit) {
|
||||
fprintf(stderr, "Callback system already initialized\n");
|
||||
return -1;
|
||||
}
|
||||
@@ -161,7 +210,7 @@ static void callbackTask(void *arg)
|
||||
taskwdInsert(0, NULL, NULL);
|
||||
epicsEventSignal(startStopEvent);
|
||||
|
||||
while(!mySet->shutdown) {
|
||||
while(!epicsAtomicGetIntT(&mySet->shutdown)) {
|
||||
void *ptr;
|
||||
if (epicsRingPointerIsEmpty(mySet->queue))
|
||||
epicsEventMustWait(mySet->semWakeUp);
|
||||
@@ -184,11 +233,10 @@ void callbackStop(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (cbCtl == ctlExit) return;
|
||||
cbCtl = ctlExit;
|
||||
if (epicsAtomicCmpAndSwapIntT(&cbState, cbRun, cbStop)!=cbRun) return;
|
||||
|
||||
for (i = 0; i < NUM_CALLBACK_PRIORITIES; i++) {
|
||||
callbackQueue[i].shutdown = 1;
|
||||
epicsAtomicSetIntT(&callbackQueue[i].shutdown, 1);
|
||||
epicsEventSignal(callbackQueue[i].semWakeUp);
|
||||
}
|
||||
|
||||
@@ -206,6 +254,10 @@ void callbackCleanup(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
if(epicsAtomicCmpAndSwapIntT(&cbState, cbStop, cbInit)!=cbStop) {
|
||||
fprintf(stderr, "callbackCleanup() but not stopped\n");
|
||||
}
|
||||
|
||||
for (i = 0; i < NUM_CALLBACK_PRIORITIES; i++) {
|
||||
cbQueueSet *mySet = &callbackQueue[i];
|
||||
|
||||
@@ -215,7 +267,6 @@ void callbackCleanup(void)
|
||||
}
|
||||
|
||||
epicsTimerQueueRelease(timerQueue);
|
||||
callbackIsInit = 0;
|
||||
memset(callbackQueue, 0, sizeof(callbackQueue));
|
||||
}
|
||||
|
||||
@@ -225,15 +276,14 @@ void callbackInit(void)
|
||||
int j;
|
||||
char threadName[32];
|
||||
|
||||
if (callbackIsInit) {
|
||||
errlogMessage("Warning: callbackInit called again before callbackCleanup\n");
|
||||
if (epicsAtomicCmpAndSwapIntT(&cbState, cbInit, cbRun)!=cbInit) {
|
||||
fprintf(stderr, "Warning: callbackInit called again before callbackCleanup\n");
|
||||
return;
|
||||
}
|
||||
callbackIsInit = 1;
|
||||
|
||||
if(!startStopEvent)
|
||||
startStopEvent = epicsEventMustCreate(epicsEventEmpty);
|
||||
cbCtl = ctlRun;
|
||||
|
||||
timerQueue = epicsTimerQueueAllocate(0, epicsThreadPriorityScanHigh);
|
||||
|
||||
for (i = 0; i < NUM_CALLBACK_PRIORITIES; i++) {
|
||||
@@ -290,6 +340,7 @@ int callbackRequest(CALLBACK *pcallback)
|
||||
if (!pushOK) {
|
||||
epicsInterruptContextMessage(fullMessage[priority]);
|
||||
mySet->queueOverflow = TRUE;
|
||||
epicsAtomicIncrIntT(&mySet->queueOverflows);
|
||||
return S_db_bufFull;
|
||||
}
|
||||
epicsEventSignal(mySet->semWakeUp);
|
||||
|
||||
@@ -48,6 +48,13 @@ typedef epicsCallback CALLBACK;
|
||||
|
||||
typedef void (*CALLBACKFUNC)(struct callbackPvt*);
|
||||
|
||||
typedef struct callbackQueueStats {
|
||||
int size;
|
||||
int numUsed[NUM_CALLBACK_PRIORITIES];
|
||||
int maxUsed[NUM_CALLBACK_PRIORITIES];
|
||||
int numOverflow[NUM_CALLBACK_PRIORITIES];
|
||||
} callbackQueueStats;
|
||||
|
||||
#define callbackSetCallback(PFUN, PCALLBACK) \
|
||||
( (PCALLBACK)->callback = (PFUN) )
|
||||
#define callbackSetPriority(PRIORITY, PCALLBACK) \
|
||||
@@ -73,6 +80,8 @@ epicsShareFunc void callbackCancelDelayed(CALLBACK *pcallback);
|
||||
epicsShareFunc void callbackRequestProcessCallbackDelayed(
|
||||
CALLBACK *pCallback, int Priority, void *pRec, double seconds);
|
||||
epicsShareFunc int callbackSetQueueSize(int size);
|
||||
epicsShareFunc int callbackQueueStatus(const int reset, callbackQueueStats *result);
|
||||
epicsShareFunc void callbackQueueShow(const int reset);
|
||||
epicsShareFunc int callbackParallelThreads(int count, const char *prio);
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
@@ -601,6 +601,31 @@ all_done:
|
||||
return status;
|
||||
}
|
||||
|
||||
long dbEntryToAddr(const DBENTRY *pdbentry, DBADDR *paddr)
|
||||
{
|
||||
dbFldDes *pflddes = pdbentry->pflddes;
|
||||
short dbfType = pflddes->field_type;
|
||||
|
||||
paddr->precord = pdbentry->precnode->precord;
|
||||
paddr->pfield = pdbentry->pfield;
|
||||
paddr->pfldDes = pflddes;
|
||||
paddr->no_elements = 1;
|
||||
paddr->field_type = dbfType;
|
||||
paddr->field_size = pflddes->size;
|
||||
paddr->special = pflddes->special;
|
||||
paddr->dbr_field_type = mapDBFToDBR[dbfType];
|
||||
|
||||
if (paddr->special == SPC_DBADDR) {
|
||||
const rset *prset = dbGetRset(paddr);
|
||||
|
||||
/* Let record type modify paddr */
|
||||
if (prset && prset->cvt_dbaddr) {
|
||||
return prset->cvt_dbaddr(paddr);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Fill out a database structure (*paddr) for
|
||||
* a record given by the name "pname."
|
||||
@@ -611,9 +636,7 @@ all_done:
|
||||
long dbNameToAddr(const char *pname, DBADDR *paddr)
|
||||
{
|
||||
DBENTRY dbEntry;
|
||||
dbFldDes *pflddes;
|
||||
long status = 0;
|
||||
short dbfType;
|
||||
|
||||
if (!pname || !*pname || !pdbbase)
|
||||
return S_db_notFound;
|
||||
@@ -628,46 +651,28 @@ long dbNameToAddr(const char *pname, DBADDR *paddr)
|
||||
status = dbGetAttributePart(&dbEntry, &pname);
|
||||
if (status) goto finish;
|
||||
|
||||
pflddes = dbEntry.pflddes;
|
||||
dbfType = pflddes->field_type;
|
||||
|
||||
paddr->precord = dbEntry.precnode->precord;
|
||||
paddr->pfield = dbEntry.pfield;
|
||||
paddr->pfldDes = pflddes;
|
||||
paddr->no_elements = 1;
|
||||
paddr->field_type = dbfType;
|
||||
paddr->field_size = pflddes->size;
|
||||
paddr->special = pflddes->special;
|
||||
paddr->dbr_field_type = mapDBFToDBR[dbfType];
|
||||
|
||||
if (paddr->special == SPC_DBADDR) {
|
||||
rset *prset = dbGetRset(paddr);
|
||||
|
||||
/* Let record type modify paddr */
|
||||
if (prset && prset->cvt_dbaddr) {
|
||||
status = prset->cvt_dbaddr(paddr);
|
||||
if (status)
|
||||
goto finish;
|
||||
dbfType = paddr->field_type;
|
||||
}
|
||||
}
|
||||
status = dbEntryToAddr(&dbEntry, paddr);
|
||||
if (status) goto finish;
|
||||
|
||||
/* Handle field modifiers */
|
||||
if (*pname++ == '$') {
|
||||
short dbfType = paddr->field_type;
|
||||
|
||||
/* Some field types can be accessed as char arrays */
|
||||
if (dbfType == DBF_STRING) {
|
||||
paddr->no_elements = paddr->field_size;
|
||||
paddr->field_type = DBF_CHAR;
|
||||
paddr->field_size = 1;
|
||||
paddr->dbr_field_type = DBR_CHAR;
|
||||
} else if (dbfType >= DBF_INLINK && dbfType <= DBF_FWDLINK) {
|
||||
}
|
||||
else if (dbfType >= DBF_INLINK && dbfType <= DBF_FWDLINK) {
|
||||
/* Clients see a char array, but keep original dbfType */
|
||||
paddr->no_elements = PVLINK_STRINGSZ;
|
||||
paddr->field_size = 1;
|
||||
paddr->dbr_field_type = DBR_CHAR;
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
status = S_dbLib_fieldNotFound;
|
||||
goto finish;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -679,7 +684,7 @@ finish:
|
||||
void dbInitEntryFromAddr(struct dbAddr *paddr, DBENTRY *pdbentry)
|
||||
{
|
||||
struct dbCommon *prec = paddr->precord;
|
||||
dbCommonPvt *ppvt = CONTAINER(prec, dbCommonPvt, common);
|
||||
dbCommonPvt *ppvt = dbRec2Pvt(prec);
|
||||
|
||||
memset((char *)pdbentry,'\0',sizeof(DBENTRY));
|
||||
|
||||
@@ -693,7 +698,7 @@ void dbInitEntryFromAddr(struct dbAddr *paddr, DBENTRY *pdbentry)
|
||||
|
||||
void dbInitEntryFromRecord(struct dbCommon *prec, DBENTRY *pdbentry)
|
||||
{
|
||||
dbCommonPvt *ppvt = CONTAINER(prec, dbCommonPvt, common);
|
||||
dbCommonPvt *ppvt = dbRec2Pvt(prec);
|
||||
|
||||
memset((char *)pdbentry,'\0',sizeof(DBENTRY));
|
||||
|
||||
|
||||
@@ -204,6 +204,8 @@ struct dbr_alDouble {DBRalDouble};
|
||||
#define S_db_notInit (M_dbAccess|67) /*Not initialized*/
|
||||
#define S_db_bufFull (M_dbAccess|68) /*Buffer full*/
|
||||
|
||||
struct dbEntry;
|
||||
|
||||
epicsShareFunc long dbPutSpecial(struct dbAddr *paddr,int pass);
|
||||
epicsShareFunc rset * dbGetRset(const struct dbAddr *paddr);
|
||||
epicsShareFunc long dbPutAttribute(
|
||||
@@ -213,8 +215,29 @@ epicsShareFunc int dbGetFieldIndex(const struct dbAddr *paddr);
|
||||
epicsShareFunc long dbScanPassive(
|
||||
struct dbCommon *pfrom,struct dbCommon *pto);
|
||||
epicsShareFunc long dbProcess(struct dbCommon *precord);
|
||||
epicsShareFunc long dbNameToAddr(
|
||||
const char *pname,struct dbAddr *);
|
||||
epicsShareFunc long dbNameToAddr(const char *pname, struct dbAddr *paddr);
|
||||
|
||||
/** Initialize DBADDR from a dbEntry
|
||||
* Also handles SPC_DBADDR processing. This is really an internal
|
||||
* routine for use by dbNameToAddr() and dbChannelCreate().
|
||||
*/
|
||||
epicsShareFunc long dbEntryToAddr(const struct dbEntry *pdbentry,
|
||||
struct dbAddr *paddr);
|
||||
|
||||
/** Initialize DBENTRY from a valid dbAddr*
|
||||
* Constant time equivalent of dbInitEntry() then dbFindRecord(),
|
||||
* and finally dbFollowAlias().
|
||||
*/
|
||||
epicsShareFunc void dbInitEntryFromAddr(struct dbAddr *paddr,
|
||||
struct dbEntry *pdbentry);
|
||||
|
||||
/** Initialize DBENTRY from a valid record (dbCommon*)
|
||||
* Constant time equivalent of dbInitEntry() then dbFindRecord(),
|
||||
* and finally dbFollowAlias() when no field is specified.
|
||||
*/
|
||||
epicsShareFunc void dbInitEntryFromRecord(struct dbCommon *prec,
|
||||
struct dbEntry *pdbentry);
|
||||
|
||||
epicsShareFunc devSup* dbDTYPtoDevSup(dbRecordType *prdes, int dtyp);
|
||||
epicsShareFunc devSup* dbDSETtoDevSup(dbRecordType *prdes, struct dset *pdset);
|
||||
epicsShareFunc long dbGetField(
|
||||
|
||||
@@ -703,7 +703,12 @@ static long doLocked(struct link *plink, dbLinkUserCallback rtn, void *priv)
|
||||
caLink *pca;
|
||||
long status;
|
||||
|
||||
pcaGetCheck
|
||||
assert(plink);
|
||||
if (plink->type != CA_LINK) return -1;
|
||||
pca = (caLink *)plink->value.pv_link.pvt;
|
||||
assert(pca);
|
||||
epicsMutexMustLock(pca->lock);
|
||||
assert(pca->plink);
|
||||
status = rtn(plink, priv);
|
||||
epicsMutexUnlock(pca->lock);
|
||||
return status;
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
#include "cantProceed.h"
|
||||
#include "epicsAssert.h"
|
||||
#include "epicsString.h"
|
||||
#include "epicsStdio.h"
|
||||
#include "errlog.h"
|
||||
#include "freeList.h"
|
||||
#include "gpHash.h"
|
||||
@@ -472,9 +473,7 @@ dbChannel * dbChannelCreate(const char *name)
|
||||
dbChannel *chan = NULL;
|
||||
char *cname;
|
||||
dbAddr *paddr;
|
||||
dbFldDes *pflddes;
|
||||
long status;
|
||||
short dbfType;
|
||||
|
||||
if (!name || !*name || !pdbbase)
|
||||
return NULL;
|
||||
@@ -497,32 +496,14 @@ dbChannel * dbChannelCreate(const char *name)
|
||||
ellInit(&chan->post_chain);
|
||||
|
||||
paddr = &chan->addr;
|
||||
pflddes = dbEntry.pflddes;
|
||||
dbfType = pflddes->field_type;
|
||||
|
||||
paddr->precord = dbEntry.precnode->precord;
|
||||
paddr->pfield = dbEntry.pfield;
|
||||
paddr->pfldDes = pflddes;
|
||||
paddr->no_elements = 1;
|
||||
paddr->field_type = dbfType;
|
||||
paddr->field_size = pflddes->size;
|
||||
paddr->special = pflddes->special;
|
||||
paddr->dbr_field_type = mapDBFToDBR[dbfType];
|
||||
|
||||
if (paddr->special == SPC_DBADDR) {
|
||||
rset *prset = dbGetRset(paddr);
|
||||
|
||||
/* Let record type modify paddr */
|
||||
if (prset && prset->cvt_dbaddr) {
|
||||
status = prset->cvt_dbaddr(paddr);
|
||||
if (status)
|
||||
goto finish;
|
||||
dbfType = paddr->field_type;
|
||||
}
|
||||
}
|
||||
status = dbEntryToAddr(&dbEntry, paddr);
|
||||
if (status)
|
||||
goto finish;
|
||||
|
||||
/* Handle field modifiers */
|
||||
if (*pname) {
|
||||
short dbfType = paddr->field_type;
|
||||
|
||||
if (*pname == '$') {
|
||||
/* Some field types can be accessed as char arrays */
|
||||
if (dbfType == DBF_STRING) {
|
||||
@@ -530,12 +511,14 @@ dbChannel * dbChannelCreate(const char *name)
|
||||
paddr->field_type = DBF_CHAR;
|
||||
paddr->field_size = 1;
|
||||
paddr->dbr_field_type = DBR_CHAR;
|
||||
} else if (dbfType >= DBF_INLINK && dbfType <= DBF_FWDLINK) {
|
||||
}
|
||||
else if (dbfType >= DBF_INLINK && dbfType <= DBF_FWDLINK) {
|
||||
/* Clients see a char array, but keep original dbfType */
|
||||
paddr->no_elements = PVLINK_STRINGSZ;
|
||||
paddr->field_size = 1;
|
||||
paddr->dbr_field_type = DBR_CHAR;
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
status = S_dbLib_fieldNotFound;
|
||||
goto finish;
|
||||
}
|
||||
|
||||
@@ -1,14 +1,27 @@
|
||||
#ifndef DBCOMMONPVT_H
|
||||
#define DBCOMMONPVT_H
|
||||
|
||||
#include <compilerDependencies.h>
|
||||
#include <dbDefs.h>
|
||||
#include "dbCommon.h"
|
||||
|
||||
struct epicsThreadOSD;
|
||||
|
||||
/** Base internal additional information for every record
|
||||
*/
|
||||
typedef struct dbCommonPvt {
|
||||
struct dbRecordNode *recnode;
|
||||
|
||||
/* Thread which is currently processing this record */
|
||||
struct epicsThreadOSD* procThread;
|
||||
|
||||
struct dbCommon common;
|
||||
} dbCommonPvt;
|
||||
|
||||
static EPICS_ALWAYS_INLINE
|
||||
dbCommonPvt* dbRec2Pvt(struct dbCommon *prec)
|
||||
{
|
||||
return CONTAINER(prec, dbCommonPvt, common);
|
||||
}
|
||||
|
||||
#endif // DBCOMMONPVT_H
|
||||
|
||||
@@ -56,7 +56,7 @@
|
||||
#include "dbAddr.h"
|
||||
#include "dbBase.h"
|
||||
#include "dbBkpt.h"
|
||||
#include "dbCommon.h"
|
||||
#include "dbCommonPvt.h"
|
||||
#include "dbConvertFast.h"
|
||||
#include "dbConvert.h"
|
||||
#include "db_field_log.h"
|
||||
@@ -386,8 +386,11 @@ static long processTarget(dbCommon *psrc, dbCommon *pdst)
|
||||
{
|
||||
char context[40] = "";
|
||||
int trace = dbAccessDebugPUTF && *dbLockSetAddrTrace(psrc);
|
||||
int claim_src = dbRec2Pvt(psrc)->procThread==NULL;
|
||||
int claim_dst = psrc!=pdst && dbRec2Pvt(pdst)->procThread==NULL;
|
||||
long status;
|
||||
epicsUInt8 pact = psrc->pact;
|
||||
epicsThreadId self = epicsThreadGetIdSelf();
|
||||
|
||||
psrc->pact = TRUE;
|
||||
|
||||
@@ -406,14 +409,11 @@ static long processTarget(dbCommon *psrc, dbCommon *pdst)
|
||||
printf("%s: '%s' -> '%s' with PUTF=%u\n",
|
||||
context, psrc->name, pdst->name, psrc->putf);
|
||||
|
||||
if (pdst->putf)
|
||||
errlogPrintf("Warning: '%s.PUTF' found true with PACT false\n",
|
||||
pdst->name);
|
||||
|
||||
pdst->putf = psrc->putf;
|
||||
}
|
||||
else if (psrc->putf) {
|
||||
/* The dst record is busy (awaiting async reprocessing) and
|
||||
else if (psrc->putf && claim_dst) {
|
||||
/* The dst record is busy (awaiting async reprocessing),
|
||||
* not being processed recursively by us, and
|
||||
* we were originally triggered by a call to dbPutField(),
|
||||
* so we mark the dst record for reprocessing once the async
|
||||
* completion is over.
|
||||
@@ -426,17 +426,43 @@ static long processTarget(dbCommon *psrc, dbCommon *pdst)
|
||||
pdst->rpro = TRUE;
|
||||
}
|
||||
else {
|
||||
/* The dst record is busy, but we weren't triggered by a call
|
||||
* to dbPutField(). Do nothing.
|
||||
/* The dst record is busy, but either is being processed recursively,
|
||||
* or wasn't triggered by a call to dbPutField(). Do nothing.
|
||||
*/
|
||||
if (trace)
|
||||
printf("%s: '%s' -> Active '%s', done\n",
|
||||
context, psrc->name, pdst->name);
|
||||
}
|
||||
|
||||
if(claim_src) {
|
||||
dbRec2Pvt(psrc)->procThread = self;
|
||||
}
|
||||
if(claim_dst) {
|
||||
dbRec2Pvt(pdst)->procThread = self;
|
||||
}
|
||||
|
||||
if(dbRec2Pvt(psrc)->procThread!=self ||
|
||||
dbRec2Pvt(pdst)->procThread!=self) {
|
||||
errlogPrintf("Logic Error: processTarget 1 from %p, %s(%p) -> %s(%p)\n",
|
||||
self, psrc->name, dbRec2Pvt(psrc), pdst->name, dbRec2Pvt(pdst));
|
||||
}
|
||||
|
||||
status = dbProcess(pdst);
|
||||
|
||||
psrc->pact = pact;
|
||||
|
||||
if(dbRec2Pvt(psrc)->procThread!=self ||
|
||||
dbRec2Pvt(pdst)->procThread!=self) {
|
||||
errlogPrintf("Logic Error: processTarget 2 from %p, %s(%p) -> %s(%p)\n",
|
||||
self, psrc->name, dbRec2Pvt(psrc), pdst->name, dbRec2Pvt(pdst));
|
||||
}
|
||||
|
||||
if(claim_src) {
|
||||
dbRec2Pvt(psrc)->procThread = NULL;
|
||||
}
|
||||
if(claim_dst) {
|
||||
dbRec2Pvt(pdst)->procThread = NULL;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
@@ -49,7 +49,13 @@
|
||||
#include "link.h"
|
||||
#include "special.h"
|
||||
|
||||
#define EVENTSPERQUE 32
|
||||
/* Queue size based on Ethernet MTU of 1500 bytes.
|
||||
* Assume <=66 bytes of ethernet+IP+TCP overhead
|
||||
* and 40 byte CA messages (DBF_TIME_DOUBLE).
|
||||
*
|
||||
* (1500-66)/40 -> 35
|
||||
*/
|
||||
#define EVENTSPERQUE 36
|
||||
#define EVENTENTRIES 4 /* the number of que entries for each event */
|
||||
#define EVENTQUESIZE (EVENTENTRIES * EVENTSPERQUE)
|
||||
#define EVENTQEMPTY ((struct evSubscrip *)NULL)
|
||||
|
||||
@@ -296,6 +296,17 @@ static void scanOnceSetQueueSizeCallFunc(const iocshArgBuf *args)
|
||||
scanOnceSetQueueSize(args[0].ival);
|
||||
}
|
||||
|
||||
/* scanOnceQueueShow */
|
||||
static const iocshArg scanOnceQueueShowArg0 = { "reset",iocshArgInt};
|
||||
static const iocshArg * const scanOnceQueueShowArgs[1] =
|
||||
{&scanOnceQueueShowArg0};
|
||||
static const iocshFuncDef scanOnceQueueShowFuncDef =
|
||||
{"scanOnceQueueShow",1,scanOnceQueueShowArgs};
|
||||
static void scanOnceQueueShowCallFunc(const iocshArgBuf *args)
|
||||
{
|
||||
scanOnceQueueShow(args[0].ival);
|
||||
}
|
||||
|
||||
/* scanppl */
|
||||
static const iocshArg scanpplArg0 = { "rate",iocshArgDouble};
|
||||
static const iocshArg * const scanpplArgs[1] = {&scanpplArg0};
|
||||
@@ -335,6 +346,17 @@ static void callbackSetQueueSizeCallFunc(const iocshArgBuf *args)
|
||||
callbackSetQueueSize(args[0].ival);
|
||||
}
|
||||
|
||||
/* callbackQueueShow */
|
||||
static const iocshArg callbackQueueShowArg0 = { "reset", iocshArgInt};
|
||||
static const iocshArg * const callbackQueueShowArgs[1] =
|
||||
{&callbackQueueShowArg0};
|
||||
static const iocshFuncDef callbackQueueShowFuncDef =
|
||||
{"callbackQueueShow",1,callbackQueueShowArgs};
|
||||
static void callbackQueueShowCallFunc(const iocshArgBuf *args)
|
||||
{
|
||||
callbackQueueShow(args[0].ival);
|
||||
}
|
||||
|
||||
/* callbackParallelThreads */
|
||||
static const iocshArg callbackParallelThreadsArg0 = { "no of threads", iocshArgInt};
|
||||
static const iocshArg callbackParallelThreadsArg1 = { "priority", iocshArgString};
|
||||
@@ -441,12 +463,14 @@ void dbIocRegister(void)
|
||||
iocshRegister(&dbLockShowLockedFuncDef,dbLockShowLockedCallFunc);
|
||||
|
||||
iocshRegister(&scanOnceSetQueueSizeFuncDef,scanOnceSetQueueSizeCallFunc);
|
||||
iocshRegister(&scanOnceQueueShowFuncDef,scanOnceQueueShowCallFunc);
|
||||
iocshRegister(&scanpplFuncDef,scanpplCallFunc);
|
||||
iocshRegister(&scanpelFuncDef,scanpelCallFunc);
|
||||
iocshRegister(&postEventFuncDef,postEventCallFunc);
|
||||
iocshRegister(&scanpiolFuncDef,scanpiolCallFunc);
|
||||
|
||||
iocshRegister(&callbackSetQueueSizeFuncDef,callbackSetQueueSizeCallFunc);
|
||||
iocshRegister(&callbackQueueShowFuncDef,callbackQueueShowCallFunc);
|
||||
iocshRegister(&callbackParallelThreadsFuncDef,callbackParallelThreadsCallFunc);
|
||||
|
||||
/* Needed before callback system is initialized */
|
||||
|
||||
@@ -86,12 +86,6 @@ typedef struct notifyGlobal {
|
||||
|
||||
static notifyGlobal *pnotifyGlobal = 0;
|
||||
|
||||
/*Local routines*/
|
||||
static void notifyInit(processNotify *ppn);
|
||||
static void notifyCleanup(processNotify *ppn);
|
||||
static void restartCheck(processNotifyRecord *ppnr);
|
||||
static void callDone(dbCommon *precord,processNotify *ppn);
|
||||
static void processNotifyCommon(processNotify *ppn,dbCommon *precord);
|
||||
static void notifyCallback(CALLBACK *pcallback);
|
||||
|
||||
#define ellSafeAdd(list,listnode) \
|
||||
@@ -210,7 +204,7 @@ static void callDone(dbCommon *precord, processNotify *ppn)
|
||||
return;
|
||||
}
|
||||
|
||||
static void processNotifyCommon(processNotify *ppn,dbCommon *precord)
|
||||
static void processNotifyCommon(processNotify *ppn, dbCommon *precord, int first)
|
||||
{
|
||||
notifyPvt *pnotifyPvt = (notifyPvt *) ppn->pnotifyPvt;
|
||||
int didPut = 0;
|
||||
@@ -256,6 +250,9 @@ static void processNotifyCommon(processNotify *ppn,dbCommon *precord)
|
||||
doProcess = 1;
|
||||
|
||||
if (doProcess) {
|
||||
if (first) {
|
||||
precord->putf = TRUE;
|
||||
}
|
||||
ppn->wasProcessed = 1;
|
||||
precord->ppn = ppn;
|
||||
ellSafeAdd(&pnotifyPvt->waitList, &precord->ppnr->waitNode);
|
||||
@@ -298,7 +295,7 @@ static void notifyCallback(CALLBACK *pcallback)
|
||||
return;
|
||||
}
|
||||
if(pnotifyPvt->state == notifyRestartCallbackRequested) {
|
||||
processNotifyCommon(ppn, precord);
|
||||
processNotifyCommon(ppn, precord, 0);
|
||||
return;
|
||||
}
|
||||
/* All done. Clean up and call userCallback */
|
||||
@@ -382,7 +379,7 @@ void dbProcessNotify(processNotify *ppn)
|
||||
precord->ppnr->precord = precord;
|
||||
ellInit(&precord->ppnr->restartList);
|
||||
}
|
||||
processNotifyCommon(ppn, precord);
|
||||
processNotifyCommon(ppn, precord, 1);
|
||||
}
|
||||
|
||||
void dbNotifyCancel(processNotify *ppn)
|
||||
@@ -582,7 +579,7 @@ static void tpnThread(void *pvt)
|
||||
processNotify *ppn = (processNotify *) ptpnInfo->ppn;
|
||||
|
||||
dbProcessNotify(ppn);
|
||||
epicsEventWait(ptpnInfo->callbackDone);
|
||||
epicsEventMustWait(ptpnInfo->callbackDone);
|
||||
dbNotifyCancel(ppn);
|
||||
epicsEventDestroy(ptpnInfo->callbackDone);
|
||||
dbChannelDelete(ppn->chan);
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
#include "cantProceed.h"
|
||||
#include "dbDefs.h"
|
||||
#include "ellLib.h"
|
||||
#include "epicsAtomic.h"
|
||||
#include "epicsEvent.h"
|
||||
#include "epicsMutex.h"
|
||||
#include "epicsPrint.h"
|
||||
@@ -63,6 +64,7 @@ static volatile enum ctl scanCtl;
|
||||
static int onceQueueSize = 1000;
|
||||
static epicsEventId onceSem;
|
||||
static epicsRingBytesId onceQ;
|
||||
static int onceQOverruns = 0;
|
||||
static epicsThreadId onceTaskId;
|
||||
static void *exitOnce;
|
||||
|
||||
@@ -676,6 +678,7 @@ int scanOnceCallback(struct dbCommon *precord, once_complete cb, void *usr)
|
||||
if (!pushOK) {
|
||||
if (newOverflow) errlogPrintf("scanOnce: Ring buffer overflow\n");
|
||||
newOverflow = FALSE;
|
||||
epicsAtomicIncrIntT(&onceQOverruns);
|
||||
} else {
|
||||
newOverflow = TRUE;
|
||||
}
|
||||
@@ -722,6 +725,40 @@ int scanOnceSetQueueSize(int size)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int scanOnceQueueStatus(const int reset, scanOnceQueueStats *result)
|
||||
{
|
||||
int ret;
|
||||
if (!onceQ) return -1;
|
||||
if (result) {
|
||||
result->size = epicsRingBytesSize(onceQ) / sizeof(onceEntry);
|
||||
result->numUsed = epicsRingBytesUsedBytes(onceQ) / sizeof(onceEntry);
|
||||
result->maxUsed = epicsRingBytesHighWaterMark(onceQ) / sizeof(onceEntry);
|
||||
result->numOverflow = epicsAtomicGetIntT(&onceQOverruns);
|
||||
ret = 0;
|
||||
} else {
|
||||
ret = -2;
|
||||
}
|
||||
if (reset) {
|
||||
epicsRingBytesResetHighWaterMark(onceQ);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void scanOnceQueueShow(const int reset)
|
||||
{
|
||||
scanOnceQueueStats stats;
|
||||
if (scanOnceQueueStatus(reset, &stats) == -1) {
|
||||
fprintf(stderr, "scanOnce system not initialized, yet. Please run "
|
||||
"iocInit before using this command.\n");
|
||||
} else {
|
||||
double qusage = 100.0 * stats.numUsed / stats.size;
|
||||
printf("PRIORITY HIGH-WATER MARK ITEMS IN Q Q SIZE %% USED Q OVERFLOWS\n");
|
||||
printf("%8s %15d %10d %6d %6.1f %11d\n", "scanOnce", stats.maxUsed,
|
||||
stats.numUsed, stats.size, qusage,
|
||||
epicsAtomicGetIntT(&onceQOverruns));
|
||||
}
|
||||
}
|
||||
|
||||
static void initOnce(void)
|
||||
{
|
||||
if ((onceQ = epicsRingBytesLockedCreate(sizeof(onceEntry)*onceQueueSize)) == NULL) {
|
||||
|
||||
@@ -42,6 +42,13 @@ struct dbCommon;
|
||||
typedef void (*io_scan_complete)(void *usr, IOSCANPVT, int prio);
|
||||
typedef void (*once_complete)(void *usr, struct dbCommon*);
|
||||
|
||||
typedef struct scanOnceQueueStats {
|
||||
int size;
|
||||
int numUsed;
|
||||
int maxUsed;
|
||||
int numOverflow;
|
||||
} scanOnceQueueStats;
|
||||
|
||||
epicsShareFunc long scanInit(void);
|
||||
epicsShareFunc void scanRun(void);
|
||||
epicsShareFunc void scanPause(void);
|
||||
@@ -57,6 +64,8 @@ epicsShareFunc double scanPeriod(int scan);
|
||||
epicsShareFunc int scanOnce(struct dbCommon *);
|
||||
epicsShareFunc int scanOnceCallback(struct dbCommon *, once_complete cb, void *usr);
|
||||
epicsShareFunc int scanOnceSetQueueSize(int size);
|
||||
epicsShareFunc int scanOnceQueueStatus(const int reset, scanOnceQueueStats *result);
|
||||
epicsShareFunc void scanOnceQueueShow(const int reset);
|
||||
|
||||
/*print periodic lists*/
|
||||
epicsShareFunc int scanppl(double rate);
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "dbDefs.h"
|
||||
#include "ellLib.h"
|
||||
#include "envDefs.h"
|
||||
#include "epicsStdio.h"
|
||||
@@ -29,6 +30,7 @@ static char *stateNames[] = {
|
||||
int dbRegisterServer(dbServer *psrv)
|
||||
{
|
||||
const char * ignore = envGetConfigParamPtr(&EPICS_IOC_IGNORE_SERVERS);
|
||||
ELLNODE *node;
|
||||
|
||||
if (!psrv || !psrv->name || state != registering)
|
||||
return -1;
|
||||
@@ -55,10 +57,14 @@ int dbRegisterServer(dbServer *psrv)
|
||||
}
|
||||
}
|
||||
|
||||
if (ellNext(&psrv->node) || ellLast(&serverList) == &psrv->node) {
|
||||
fprintf(stderr, "dbRegisterServer: '%s' registered twice?\n",
|
||||
psrv->name);
|
||||
return -1;
|
||||
for(node=ellFirst(&serverList); node; node = ellNext(node)) {
|
||||
dbServer *cur = CONTAINER(node, dbServer, node);
|
||||
if(cur==psrv) {
|
||||
return 0; /* already registered identically. */
|
||||
} else if(strcmp(cur->name, psrv->name)==0) {
|
||||
fprintf(stderr, "dbRegisterServer: Can't redefine '%s'.\n", cur->name);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
ellAdd(&serverList, &psrv->node);
|
||||
|
||||
@@ -1,11 +1,48 @@
|
||||
#*************************************************************************
|
||||
# Copyright (c) 2008 UChicago Argonne LLC, as Operator of Argonne
|
||||
# Copyright (c) 2018 UChicago Argonne LLC, as Operator of Argonne
|
||||
# National Laboratory.
|
||||
# Copyright (c) 2002 The Regents of the University of California, as
|
||||
# Operator of Los Alamos National Laboratory.
|
||||
# EPICS BASE is distributed subject to a Software License Agreement found
|
||||
# in file LICENSE that is included with this distribution.
|
||||
#*************************************************************************
|
||||
|
||||
=head1 Menu menuScan
|
||||
|
||||
This menu is used for the C<SCAN> field of all record types.
|
||||
|
||||
The set of periodic scan rates may be modified for an individual IOC by
|
||||
copying the F<menuScan.dbd> file from Base into the IOC's source
|
||||
directory and changing it to contain the desired scan rates.
|
||||
|
||||
The scan periods are extracted from the choice strings at runtime, which
|
||||
must be expressed as a number with any of the following units appended:
|
||||
|
||||
=over 4
|
||||
|
||||
second
|
||||
seconds
|
||||
minute
|
||||
minutes
|
||||
hour
|
||||
hours
|
||||
Hertz
|
||||
Hz
|
||||
|
||||
=back
|
||||
|
||||
At IOC start-up a separate scan thread will be created for each period,
|
||||
with thread priority increasing further down the list, so faster periods
|
||||
should appear after slower ones.
|
||||
|
||||
Scan rates that cannot be achieved will generate a warning message from
|
||||
the C<iocInit> command.
|
||||
|
||||
|
||||
=menu menuScan
|
||||
|
||||
=cut
|
||||
|
||||
menu(menuScan) {
|
||||
choice(menuScanPassive,"Passive")
|
||||
choice(menuScanEvent,"Event")
|
||||
@@ -434,6 +434,10 @@ static void dbMenuHead(char *name)
|
||||
dbMenu *pdbMenu;
|
||||
GPHENTRY *pgphentry;
|
||||
|
||||
if (!*name) {
|
||||
yyerrorAbort("dbMenuHead: Menu name can't be empty");
|
||||
return;
|
||||
}
|
||||
pgphentry = gphFind(pdbbase->pgpHash,name,&pdbbase->menuList);
|
||||
if(pgphentry) {
|
||||
duplicate = TRUE;
|
||||
@@ -447,6 +451,10 @@ static void dbMenuHead(char *name)
|
||||
|
||||
static void dbMenuChoice(char *name,char *value)
|
||||
{
|
||||
if (!*name) {
|
||||
yyerror("dbMenuChoice: Menu choice name can't be empty");
|
||||
return;
|
||||
}
|
||||
if(duplicate) return;
|
||||
allocTemp(epicsStrDup(name));
|
||||
allocTemp(epicsStrDup(value));
|
||||
@@ -494,6 +502,10 @@ static void dbRecordtypeHead(char *name)
|
||||
dbRecordType *pdbRecordType;
|
||||
GPHENTRY *pgphentry;
|
||||
|
||||
if (!*name) {
|
||||
yyerrorAbort("dbRecordtypeHead: Recordtype name can't be empty");
|
||||
return;
|
||||
}
|
||||
pgphentry = gphFind(pdbbase->pgpHash,name,&pdbbase->recordTypeList);
|
||||
if(pgphentry) {
|
||||
duplicate = TRUE;
|
||||
@@ -512,6 +524,10 @@ static void dbRecordtypeFieldHead(char *name,char *type)
|
||||
dbFldDes *pdbFldDes;
|
||||
int i;
|
||||
|
||||
if (!*name) {
|
||||
yyerrorAbort("dbRecordtypeFieldHead: Field name can't be empty");
|
||||
return;
|
||||
}
|
||||
if(duplicate) return;
|
||||
pdbFldDes = dbCalloc(1,sizeof(dbFldDes));
|
||||
allocTemp(pdbFldDes);
|
||||
@@ -580,7 +596,7 @@ static void dbRecordtypeFieldItem(char *name,char *value)
|
||||
if(sscanf(value,"%hd",&pdbFldDes->special)==1) {
|
||||
return;
|
||||
}
|
||||
yyerror("Illegal special value.");
|
||||
yyerror("Illegal 'special' value.");
|
||||
return;
|
||||
}
|
||||
if(strcmp(name,"pp")==0) {
|
||||
@@ -589,13 +605,13 @@ static void dbRecordtypeFieldItem(char *name,char *value)
|
||||
} else if((strcmp(value,"NO")==0) || (strcmp(value,"FALSE")==0)) {
|
||||
pdbFldDes->process_passive = FALSE;
|
||||
} else {
|
||||
yyerror("Illegal value. Must be NO or YES");
|
||||
yyerror("Illegal 'pp' value, must be YES/NO/TRUE/FALSE");
|
||||
}
|
||||
return;
|
||||
}
|
||||
if(strcmp(name,"interest")==0) {
|
||||
if(sscanf(value,"%hd",&pdbFldDes->interest)!=1)
|
||||
yyerror("Illegal value. Must be integer");
|
||||
yyerror("Illegal 'interest' value, must be integer");
|
||||
return;
|
||||
}
|
||||
if(strcmp(name,"base")==0) {
|
||||
@@ -604,13 +620,13 @@ static void dbRecordtypeFieldItem(char *name,char *value)
|
||||
} else if(strcmp(value,"HEX")==0) {
|
||||
pdbFldDes->base = CT_HEX;
|
||||
} else {
|
||||
yyerror("Illegal value. Must be CT_DECIMAL or CT_HEX");
|
||||
yyerror("Illegal 'base' value, must be DECIMAL/HEX");
|
||||
}
|
||||
return;
|
||||
}
|
||||
if(strcmp(name,"size")==0) {
|
||||
if(sscanf(value,"%hd",&pdbFldDes->size)!=1)
|
||||
yyerror("Illegal value. Must be integer");
|
||||
yyerror("Illegal 'size' value, must be integer");
|
||||
return;
|
||||
}
|
||||
if(strcmp(name,"extra")==0) {
|
||||
@@ -802,6 +818,10 @@ static void dbDriver(char *name)
|
||||
drvSup *pdrvSup;
|
||||
GPHENTRY *pgphentry;
|
||||
|
||||
if (!*name) {
|
||||
yyerrorAbort("dbDriver: Driver name can't be empty");
|
||||
return;
|
||||
}
|
||||
pgphentry = gphFind(pdbbase->pgpHash,name,&pdbbase->drvList);
|
||||
if(pgphentry) {
|
||||
return;
|
||||
@@ -841,6 +861,10 @@ static void dbRegistrar(char *name)
|
||||
dbText *ptext;
|
||||
GPHENTRY *pgphentry;
|
||||
|
||||
if (!*name) {
|
||||
yyerrorAbort("dbRegistrar: Registrar name can't be empty");
|
||||
return;
|
||||
}
|
||||
pgphentry = gphFind(pdbbase->pgpHash,name,&pdbbase->registrarList);
|
||||
if(pgphentry) {
|
||||
return;
|
||||
@@ -860,6 +884,10 @@ static void dbFunction(char *name)
|
||||
dbText *ptext;
|
||||
GPHENTRY *pgphentry;
|
||||
|
||||
if (!*name) {
|
||||
yyerrorAbort("dbFunction: Function name can't be empty");
|
||||
return;
|
||||
}
|
||||
pgphentry = gphFind(pdbbase->pgpHash,name,&pdbbase->functionList);
|
||||
if(pgphentry) {
|
||||
return;
|
||||
@@ -879,6 +907,10 @@ static void dbVariable(char *name, char *type)
|
||||
dbVariableDef *pvar;
|
||||
GPHENTRY *pgphentry;
|
||||
|
||||
if (!*name) {
|
||||
yyerrorAbort("dbVariable: Variable name can't be empty");
|
||||
return;
|
||||
}
|
||||
pgphentry = gphFind(pdbbase->pgpHash,name,&pdbbase->variableList);
|
||||
if(pgphentry) {
|
||||
return;
|
||||
@@ -899,6 +931,10 @@ static void dbBreakHead(char *name)
|
||||
brkTable *pbrkTable;
|
||||
GPHENTRY *pgphentry;
|
||||
|
||||
if (!*name) {
|
||||
yyerrorAbort("dbBreakHead: Breaktable name can't be empty");
|
||||
return;
|
||||
}
|
||||
pgphentry = gphFind(pdbbase->pgpHash,name,&pdbbase->bptList);
|
||||
if(pgphentry) {
|
||||
duplicate = TRUE;
|
||||
@@ -1001,6 +1037,10 @@ static void dbRecordHead(char *recordType, char *name, int visible)
|
||||
DBENTRY *pdbentry;
|
||||
long status;
|
||||
|
||||
if (!*name) {
|
||||
yyerrorAbort("dbRecordHead: Record name can't be empty");
|
||||
return;
|
||||
}
|
||||
badch = strpbrk(name, " \"'.$");
|
||||
if (badch) {
|
||||
epicsPrintf("Bad character '%c' in record name \"%s\"\n",
|
||||
@@ -1108,6 +1148,10 @@ static void dbRecordInfo(char *name, char *value)
|
||||
tempListNode *ptempListNode;
|
||||
long status;
|
||||
|
||||
if (!*name) {
|
||||
yyerrorAbort("dbRecordInfo: Info item name can't be empty");
|
||||
return;
|
||||
}
|
||||
if (duplicate) return;
|
||||
ptempListNode = (tempListNode *)ellFirst(&tempList);
|
||||
pdbentry = ptempListNode->item;
|
||||
@@ -1132,6 +1176,10 @@ static void dbRecordAlias(char *name)
|
||||
tempListNode *ptempListNode;
|
||||
long status;
|
||||
|
||||
if (!*name) {
|
||||
yyerrorAbort("dbRecordAlias: Alias name can't be empty");
|
||||
return;
|
||||
}
|
||||
if (duplicate) return;
|
||||
ptempListNode = (tempListNode *)ellFirst(&tempList);
|
||||
pdbentry = ptempListNode->item;
|
||||
@@ -1149,6 +1197,10 @@ static void dbAlias(char *name, char *alias)
|
||||
DBENTRY dbEntry;
|
||||
DBENTRY *pdbEntry = &dbEntry;
|
||||
|
||||
if (!*alias) {
|
||||
yyerrorAbort("dbAlias: Alias name can't be empty");
|
||||
return;
|
||||
}
|
||||
dbInitEntry(pdbbase, pdbEntry);
|
||||
if (dbFindRecord(pdbEntry, name)) {
|
||||
epicsPrintf("Alias \"%s\" refers to unknown record \"%s\"\n",
|
||||
|
||||
@@ -79,16 +79,6 @@ epicsShareDef maplinkType pamaplinkType[LINK_NTYPES] = {
|
||||
{"VXI_IO",VXI_IO}
|
||||
};
|
||||
|
||||
static int mapDBFtoDCT[DBF_NOACCESS+1] = {
|
||||
DCT_STRING,
|
||||
DCT_INTEGER,DCT_INTEGER,DCT_INTEGER,DCT_INTEGER,DCT_INTEGER,DCT_INTEGER,
|
||||
DCT_REAL,DCT_REAL,
|
||||
DCT_INTEGER,
|
||||
DCT_MENU,
|
||||
DCT_MENUFORM,
|
||||
DCT_INLINK,DCT_OUTLINK,DCT_FWDLINK,
|
||||
DCT_NOACCESS};
|
||||
|
||||
/*forward references for private routines*/
|
||||
static void dbMsgPrint(DBENTRY *pdbentry, const char *fmt, ...)
|
||||
EPICS_PRINTF_STYLE(2,3);
|
||||
@@ -166,38 +156,6 @@ void dbFreePath(DBBASE *pdbbase)
|
||||
}
|
||||
|
||||
|
||||
static void entryErrMessage(DBENTRY *pdbentry,long status,char *mess)
|
||||
{
|
||||
char message[200];
|
||||
char *pmessage=&message[0];
|
||||
dbRecordNode *precnode = pdbentry->precnode;
|
||||
dbFldDes *pflddes = pdbentry->pflddes;
|
||||
char *pname = NULL;
|
||||
|
||||
*pmessage=0;
|
||||
if(pdbentry->precordType) pname = pdbentry->precordType->name;
|
||||
if(pname) {
|
||||
strcat(pmessage,"RecordType:");
|
||||
strcat(pmessage,pname);
|
||||
}
|
||||
if(precnode){
|
||||
if (dbIsAlias(pdbentry))
|
||||
strcat(pmessage," Record Alias:");
|
||||
else
|
||||
strcat(pmessage," Record:");
|
||||
strcat(pmessage,(char *)precnode->precord);
|
||||
}
|
||||
if(pflddes) {
|
||||
char *pstr=pflddes->name;
|
||||
|
||||
strcat(pmessage," Field:");
|
||||
strcat(pmessage,pstr);
|
||||
}
|
||||
strcat(pmessage,"\n");
|
||||
strcat(pmessage,mess);
|
||||
errMessage(status,pmessage);
|
||||
}
|
||||
|
||||
static void zeroDbentry(DBENTRY *pdbentry)
|
||||
{
|
||||
/*NOTE that pdbbase and message MUST NOT be set to NULL*/
|
||||
@@ -1398,19 +1356,6 @@ long dbNextField(DBENTRY *pdbentry,int dctonly)
|
||||
}
|
||||
}
|
||||
|
||||
int dbGetFieldType(DBENTRY *pdbentry)
|
||||
{
|
||||
dbFldDes *pflddes = pdbentry->pflddes;
|
||||
long status;
|
||||
|
||||
if(!pflddes){
|
||||
status = S_dbLib_flddesNotFound;
|
||||
entryErrMessage(pdbentry,status,"dbGetFieldType");
|
||||
return(status);
|
||||
}
|
||||
return(mapDBFtoDCT[pflddes->field_type]);
|
||||
}
|
||||
|
||||
int dbGetNFields(DBENTRY *pdbentry,int dctonly)
|
||||
{
|
||||
dbRecordType *precordType = pdbentry->precordType;
|
||||
@@ -3216,48 +3161,21 @@ int dbGetNLinks(DBENTRY *pdbentry)
|
||||
return((int)precordType->no_links);
|
||||
}
|
||||
|
||||
long dbGetLinkField(DBENTRY *pdbentry,int index)
|
||||
long dbGetLinkField(DBENTRY *pdbentry, int index)
|
||||
{
|
||||
dbRecordType *precordType = pdbentry->precordType;
|
||||
dbFldDes *pflddes;
|
||||
dbRecordType *precordType = pdbentry->precordType;
|
||||
dbFldDes *pflddes;
|
||||
|
||||
if (!precordType)
|
||||
return S_dbLib_recordTypeNotFound;
|
||||
|
||||
if (index < 0 || index >= precordType->no_links)
|
||||
return S_dbLib_badLink;
|
||||
|
||||
if(!precordType) return(S_dbLib_recordTypeNotFound);
|
||||
if(index<0 || index>=precordType->no_links) return(S_dbLib_badLink);
|
||||
pdbentry->indfield = precordType->link_ind[index];
|
||||
pdbentry->pflddes = pflddes = precordType->papFldDes[pdbentry->indfield];
|
||||
dbGetFieldAddress(pdbentry);
|
||||
return(0);
|
||||
}
|
||||
|
||||
int dbGetLinkType(DBENTRY *pdbentry)
|
||||
{
|
||||
dbFldDes *pflddes;
|
||||
DBLINK *plink;
|
||||
int field_type;
|
||||
|
||||
dbGetFieldAddress(pdbentry);
|
||||
pflddes = pdbentry->pflddes;
|
||||
if(!pflddes) return(-1);
|
||||
plink = (DBLINK *)pdbentry->pfield;
|
||||
if(!plink) return(-1);
|
||||
field_type = pflddes->field_type;
|
||||
switch (field_type) {
|
||||
case DBF_INLINK:
|
||||
case DBF_OUTLINK:
|
||||
case DBF_FWDLINK:
|
||||
switch(plink->type) {
|
||||
case CONSTANT:
|
||||
return(DCT_LINK_CONSTANT);
|
||||
case PV_LINK:
|
||||
case PN_LINK:
|
||||
case DB_LINK:
|
||||
case CA_LINK:
|
||||
return(DCT_LINK_PV);
|
||||
default:
|
||||
return(DCT_LINK_FORM);
|
||||
}
|
||||
}
|
||||
return(-1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void dbDumpPath(DBBASE *pdbbase)
|
||||
@@ -3556,68 +3474,110 @@ void dbDumpBreaktable(DBBASE *pdbbase,const char *name)
|
||||
return;
|
||||
}
|
||||
|
||||
static char *bus[VXI_IO+1] = {"","","VME","CAMAC","AB",
|
||||
"GPIB","BITBUS","","","","","","INST","BBGPIB","VXI"};
|
||||
void dbReportDeviceConfig(dbBase *pdbbase,FILE *report)
|
||||
static char *bus[LINK_NTYPES] = {
|
||||
"", /* CONSTANT */
|
||||
NULL, /* PV_LINK */
|
||||
"VME",
|
||||
"CAMAC",
|
||||
"AB",
|
||||
"GPIB",
|
||||
"BITBUS",
|
||||
NULL, /* MACRO_LINK */
|
||||
NULL, /* JSON_LINK */
|
||||
NULL, /* PN_LINK */
|
||||
NULL, /* DB_LINK */
|
||||
NULL, /* CA_LINK */
|
||||
"INST",
|
||||
"BBGPIB",
|
||||
"VXI"
|
||||
};
|
||||
void dbReportDeviceConfig(dbBase *pdbbase, FILE *report)
|
||||
{
|
||||
DBENTRY dbentry;
|
||||
DBENTRY *pdbentry=&dbentry;
|
||||
long status;
|
||||
char linkValue[messagesize];
|
||||
char dtypValue[50];
|
||||
char cvtValue[40];
|
||||
int ilink,nlinks;
|
||||
struct link *plink;
|
||||
int linkType;
|
||||
FILE *stream = (report==0) ? stdout : report;
|
||||
DBENTRY dbentry, *pdbentry = &dbentry;
|
||||
long status;
|
||||
FILE *stream = report ? report : stdout;
|
||||
|
||||
if(!pdbbase) {
|
||||
fprintf(stderr,"pdbbase not specified\n");
|
||||
return;
|
||||
if (!pdbbase) {
|
||||
fprintf(stderr, "dbReportDeviceConfig: pdbbase not specified\n");
|
||||
return;
|
||||
}
|
||||
|
||||
dbInitEntry(pdbbase,pdbentry);
|
||||
status = dbFirstRecordType(pdbentry);
|
||||
while(!status) {
|
||||
status = dbFirstRecord(pdbentry);
|
||||
while(!status) {
|
||||
nlinks = dbGetNLinks(pdbentry);
|
||||
for(ilink=0; ilink<nlinks; ilink++) {
|
||||
status = dbGetLinkField(pdbentry,ilink);
|
||||
if(status || dbGetLinkType(pdbentry)!=DCT_LINK_FORM) continue;
|
||||
plink = pdbentry->pfield;
|
||||
linkType = plink->type;
|
||||
if(bus[linkType][0]==0) continue;
|
||||
strncpy(linkValue, dbGetString(pdbentry), NELEMENTS(linkValue)-1);
|
||||
linkValue[NELEMENTS(linkValue)-1] = '\0';
|
||||
status = dbFindField(pdbentry,"DTYP");
|
||||
if(status) break;
|
||||
strcpy(dtypValue,dbGetString(pdbentry));
|
||||
status = dbFindField(pdbentry,"LINR");
|
||||
if(status) {
|
||||
cvtValue[0] = 0;
|
||||
} else {
|
||||
if(strcmp(dbGetString(pdbentry),"LINEAR")!=0) {
|
||||
cvtValue[0] = 0;
|
||||
} else {
|
||||
strcpy(cvtValue,"cvt(");
|
||||
status = dbFindField(pdbentry,"EGUL");
|
||||
if(!status) strcat(cvtValue,dbGetString(pdbentry));
|
||||
status = dbFindField(pdbentry,"EGUF");
|
||||
if(!status) {
|
||||
strcat(cvtValue,",");
|
||||
strcat(cvtValue,dbGetString(pdbentry));
|
||||
}
|
||||
strcat(cvtValue,")");
|
||||
}
|
||||
}
|
||||
fprintf(stream,"%-8s %-20s %-20s %-20s %-s\n",
|
||||
bus[linkType],linkValue,dtypValue,
|
||||
dbGetRecordName(pdbentry),cvtValue);
|
||||
break;
|
||||
}
|
||||
status = dbNextRecord(pdbentry);
|
||||
}
|
||||
status = dbNextRecordType(pdbentry);
|
||||
while (!status) {
|
||||
const int nlinks = dbGetNLinks(pdbentry);
|
||||
|
||||
status = dbFirstRecord(pdbentry);
|
||||
while (!status) {
|
||||
int ilink;
|
||||
|
||||
for (ilink=0; ilink<nlinks; ilink++) {
|
||||
char linkValue[messagesize];
|
||||
char dtypValue[50];
|
||||
char cvtValue[40];
|
||||
struct link *plink;
|
||||
int linkType;
|
||||
|
||||
status = dbGetLinkField(pdbentry, ilink);
|
||||
if (status)
|
||||
continue;
|
||||
|
||||
plink = pdbentry->pfield;
|
||||
linkType = plink->type;
|
||||
if (plink->text) { /* Not yet parsed */
|
||||
dbLinkInfo linfo;
|
||||
|
||||
if (dbParseLink(plink->text, pdbentry->pflddes->field_type, &linfo))
|
||||
continue;
|
||||
|
||||
linkType = linfo.ltype;
|
||||
if (linkType && bus[linkType])
|
||||
strncpy(linkValue, plink->text, messagesize-1);
|
||||
|
||||
dbFreeLinkInfo(&linfo);
|
||||
}
|
||||
else {
|
||||
strncpy(linkValue, dbGetString(pdbentry), messagesize-1);
|
||||
}
|
||||
|
||||
if (!linkType || !bus[linkType])
|
||||
continue;
|
||||
linkValue[messagesize-1] = '\0';
|
||||
|
||||
status = dbFindField(pdbentry, "DTYP");
|
||||
if (status)
|
||||
break; /* Next record type */
|
||||
|
||||
strcpy(dtypValue, dbGetString(pdbentry));
|
||||
status = dbFindField(pdbentry, "LINR");
|
||||
if (status) {
|
||||
cvtValue[0] = 0;
|
||||
}
|
||||
else {
|
||||
if (strcmp(dbGetString(pdbentry), "LINEAR") != 0) {
|
||||
cvtValue[0] = 0;
|
||||
}
|
||||
else {
|
||||
strcpy(cvtValue,"cvt(");
|
||||
status = dbFindField(pdbentry, "EGUL");
|
||||
if (!status)
|
||||
strcat(cvtValue, dbGetString(pdbentry));
|
||||
status = dbFindField(pdbentry, "EGUF");
|
||||
if (!status) {
|
||||
strcat(cvtValue, ",");
|
||||
strcat(cvtValue, dbGetString(pdbentry));
|
||||
}
|
||||
strcat(cvtValue, ")");
|
||||
}
|
||||
}
|
||||
fprintf(stream,"%-8s %-20s %-20s %-20s %-s\n",
|
||||
bus[linkType], linkValue, dtypValue,
|
||||
dbGetRecordName(pdbentry), cvtValue);
|
||||
break;
|
||||
}
|
||||
status = dbNextRecord(pdbentry);
|
||||
}
|
||||
status = dbNextRecordType(pdbentry);
|
||||
}
|
||||
dbFinishEntry(pdbentry);
|
||||
finishOutstream(stream);
|
||||
|
||||
@@ -28,25 +28,9 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*Field types as seen by static database access clients*/
|
||||
#define DCT_STRING 0
|
||||
#define DCT_INTEGER 1
|
||||
#define DCT_REAL 2
|
||||
#define DCT_MENU 3
|
||||
#define DCT_MENUFORM 4
|
||||
#define DCT_INLINK 5
|
||||
#define DCT_OUTLINK 6
|
||||
#define DCT_FWDLINK 7
|
||||
#define DCT_NOACCESS 8
|
||||
|
||||
/*Link types as seen by static database access clients*/
|
||||
#define DCT_LINK_CONSTANT 0
|
||||
#define DCT_LINK_FORM 1
|
||||
#define DCT_LINK_PV 2
|
||||
|
||||
typedef dbBase DBBASE;
|
||||
|
||||
typedef struct{
|
||||
typedef struct dbEntry {
|
||||
DBBASE *pdbbase;
|
||||
dbRecordType *precordType;
|
||||
dbFldDes *pflddes;
|
||||
@@ -57,9 +41,6 @@ typedef struct{
|
||||
short indfield;
|
||||
} DBENTRY;
|
||||
|
||||
struct dbAddr;
|
||||
struct dbCommon;
|
||||
|
||||
/* Static database access routines*/
|
||||
epicsShareFunc DBBASE * dbAllocBase(void);
|
||||
epicsShareFunc void dbFreeBase(DBBASE *pdbbase);
|
||||
@@ -68,18 +49,6 @@ epicsShareFunc void dbFreeEntry(DBENTRY *pdbentry);
|
||||
epicsShareFunc void dbInitEntry(DBBASE *pdbbase,
|
||||
DBENTRY *pdbentry);
|
||||
|
||||
/** Initialize DBENTRY from a valid dbAddr*.
|
||||
* Constant time equivalent of dbInitEntry() then dbFindRecord(),
|
||||
* and finally dbFollowAlias()
|
||||
*/
|
||||
epicsShareFunc void dbInitEntryFromAddr(struct dbAddr *paddr, DBENTRY *pdbentry);
|
||||
|
||||
/** Initialize DBENTRY from a valid record (dbCommon*).
|
||||
* Constant time equivalent of dbInitEntry() then dbFindRecord(),
|
||||
* and finally dbFollowAlias() when no field is specified.
|
||||
*/
|
||||
epicsShareFunc void dbInitEntryFromRecord(struct dbCommon *prec, DBENTRY *pdbentry);
|
||||
|
||||
epicsShareFunc void dbFinishEntry(DBENTRY *pdbentry);
|
||||
epicsShareFunc DBENTRY * dbCopyEntry(DBENTRY *pdbentry);
|
||||
epicsShareFunc void dbCopyEntryContents(DBENTRY *pfrom,
|
||||
@@ -139,7 +108,6 @@ epicsShareFunc long dbGetAttributePart(DBENTRY *pdbentry,
|
||||
|
||||
epicsShareFunc long dbFirstField(DBENTRY *pdbentry, int dctonly);
|
||||
epicsShareFunc long dbNextField(DBENTRY *pdbentry, int dctonly);
|
||||
epicsShareFunc int dbGetFieldType(DBENTRY *pdbentry);
|
||||
epicsShareFunc int dbGetNFields(DBENTRY *pdbentry, int dctonly);
|
||||
epicsShareFunc char * dbGetFieldName(DBENTRY *pdbentry);
|
||||
epicsShareFunc char * dbGetDefault(DBENTRY *pdbentry);
|
||||
@@ -231,7 +199,6 @@ epicsShareFunc linkSup * dbFindLinkSup(dbBase *pdbbase,
|
||||
|
||||
epicsShareFunc int dbGetNLinks(DBENTRY *pdbentry);
|
||||
epicsShareFunc long dbGetLinkField(DBENTRY *pdbentry, int index);
|
||||
epicsShareFunc int dbGetLinkType(DBENTRY *pdbentry);
|
||||
|
||||
/* Dump routines */
|
||||
epicsShareFunc void dbDumpPath(DBBASE *pdbbase);
|
||||
|
||||
@@ -179,7 +179,7 @@ long dbFreeRecord(DBENTRY *pdbentry)
|
||||
if(!pdbRecordType) return(S_dbLib_recordTypeNotFound);
|
||||
if(!precnode) return(S_dbLib_recNotFound);
|
||||
if(!precnode->precord) return(S_dbLib_recNotFound);
|
||||
free(CONTAINER(precnode->precord, dbCommonPvt, common));
|
||||
free(dbRec2Pvt(precnode->precord));
|
||||
precnode->precord = NULL;
|
||||
return(0);
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ SRC_DIRS += $(IOCDIR)/dbtemplate
|
||||
|
||||
PROD_HOST += msi
|
||||
|
||||
msi_SRCS = msi.c
|
||||
msi_SRCS = msi.cpp
|
||||
msi_LIBS += Com
|
||||
HTMLS += msi.html
|
||||
|
||||
|
||||
@@ -9,6 +9,9 @@
|
||||
|
||||
/* msi - macro substitutions and include */
|
||||
|
||||
#include <string>
|
||||
#include <list>
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
@@ -18,7 +21,6 @@
|
||||
|
||||
#include <dbDefs.h>
|
||||
#include <macLib.h>
|
||||
#include <ellLib.h>
|
||||
#include <errlog.h>
|
||||
#include <epicsString.h>
|
||||
#include <osiFileName.h>
|
||||
@@ -56,32 +58,35 @@ int din = 0;
|
||||
typedef struct inputData inputData;
|
||||
|
||||
static void inputConstruct(inputData **ppvt);
|
||||
static void inputDestruct(inputData *pvt);
|
||||
static void inputAddPath(inputData *pvt, char *pval);
|
||||
static void inputBegin(inputData *pvt, char *fileName);
|
||||
static char *inputNextLine(inputData *pvt);
|
||||
static void inputNewIncludeFile(inputData *pvt, char *name);
|
||||
static void inputErrPrint(inputData *pvt);
|
||||
static void inputDestruct(inputData * const pvt);
|
||||
static void inputAddPath(inputData * const pvt, const char * const pval);
|
||||
static void inputBegin(inputData * const pvt, const char * const fileName);
|
||||
static char *inputNextLine(inputData * const pvt);
|
||||
static void inputNewIncludeFile(inputData * const pvt, const char * const name);
|
||||
static void inputErrPrint(const inputData * const pvt);
|
||||
|
||||
/* Module to read the substitution file */
|
||||
typedef struct subInfo subInfo;
|
||||
|
||||
static void substituteOpen(subInfo **ppvt, char *substitutionName);
|
||||
static void substituteDestruct(subInfo *pvt);
|
||||
static int substituteGetNextSet(subInfo *pvt, char **filename);
|
||||
static int substituteGetGlobalSet(subInfo *pvt);
|
||||
static char *substituteGetReplacements(subInfo *pvt);
|
||||
static char *substituteGetGlobalReplacements(subInfo *pvt);
|
||||
static void substituteOpen(subInfo **ppvt, const std::string& substitutionName);
|
||||
static void substituteDestruct(subInfo * const pvt);
|
||||
static bool substituteGetNextSet(subInfo * const pvt, char **filename);
|
||||
static bool substituteGetGlobalSet(subInfo * const pvt);
|
||||
static const char *substituteGetReplacements(subInfo * const pvt);
|
||||
static const char *substituteGetGlobalReplacements(subInfo * const pvt);
|
||||
|
||||
/* Forward references to local routines */
|
||||
static void usageExit(int status);
|
||||
static void abortExit(int status);
|
||||
static void addMacroReplacements(MAC_HANDLE *macPvt, char *pval);
|
||||
static void makeSubstitutions(inputData *inputPvt, MAC_HANDLE *macPvt, char *templateName);
|
||||
static void usageExit(const int status);
|
||||
static void abortExit(const int status);
|
||||
static void addMacroReplacements(MAC_HANDLE * const macPvt,
|
||||
const char * const pval);
|
||||
static void makeSubstitutions(inputData * const inputPvt,
|
||||
MAC_HANDLE * const macPvt,
|
||||
const char * const templateName);
|
||||
|
||||
/*Global variables */
|
||||
static int opt_V = 0;
|
||||
static int opt_D = 0;
|
||||
static bool opt_D = false;
|
||||
|
||||
static char *outFile = 0;
|
||||
static int numDeps = 0, depHashes[MAX_DEPS];
|
||||
@@ -92,23 +97,21 @@ int main(int argc,char **argv)
|
||||
inputData *inputPvt;
|
||||
MAC_HANDLE *macPvt;
|
||||
char *pval;
|
||||
int narg;
|
||||
char *substitutionName = 0;
|
||||
std::string substitutionName;
|
||||
char *templateName = 0;
|
||||
int i;
|
||||
int localScope = 1;
|
||||
bool localScope = true;
|
||||
|
||||
inputConstruct(&inputPvt);
|
||||
macCreateHandle(&macPvt, 0);
|
||||
while ((argc > 1) && (argv[1][0] == '-')) {
|
||||
narg = (strlen(argv[1]) == 2) ? 2 : 1;
|
||||
int narg = (strlen(argv[1]) == 2) ? 2 : 1;
|
||||
pval = (narg == 1) ? (argv[1] + 2) : argv[2];
|
||||
|
||||
if (strncmp(argv[1], "-I", 2) == 0) {
|
||||
inputAddPath(inputPvt, pval);
|
||||
}
|
||||
else if (strcmp(argv[1], "-D") == 0) {
|
||||
opt_D = 1;
|
||||
opt_D = true;
|
||||
narg = 1; /* no argument for this option */
|
||||
}
|
||||
else if(strncmp(argv[1], "-o", 2) == 0) {
|
||||
@@ -118,14 +121,14 @@ int main(int argc,char **argv)
|
||||
addMacroReplacements(macPvt, pval);
|
||||
}
|
||||
else if(strncmp(argv[1], "-S", 2) == 0) {
|
||||
substitutionName = epicsStrDup(pval);
|
||||
substitutionName = pval;
|
||||
}
|
||||
else if (strcmp(argv[1], "-V") == 0) {
|
||||
opt_V = 1;
|
||||
narg = 1; /* no argument for this option */
|
||||
}
|
||||
else if (strcmp(argv[1], "-g") == 0) {
|
||||
localScope = 0;
|
||||
localScope = false;
|
||||
narg = 1; /* no argument for this option */
|
||||
}
|
||||
else if (strcmp(argv[1], "-h") == 0) {
|
||||
@@ -137,7 +140,7 @@ int main(int argc,char **argv)
|
||||
}
|
||||
|
||||
argc -= narg;
|
||||
for (i = 1; i < argc; i++)
|
||||
for (int i = 1; i < argc; i++)
|
||||
argv[i] = argv[i + narg];
|
||||
}
|
||||
|
||||
@@ -165,24 +168,24 @@ int main(int argc,char **argv)
|
||||
if (argc == 2)
|
||||
templateName = epicsStrDup(argv[1]);
|
||||
|
||||
if (!substitutionName) {
|
||||
if (substitutionName.empty()) {
|
||||
STEP("Single template+substitutions file");
|
||||
makeSubstitutions(inputPvt, macPvt, templateName);
|
||||
}
|
||||
else {
|
||||
subInfo *substitutePvt;
|
||||
char *filename = 0;
|
||||
int isGlobal, isFile;
|
||||
bool isGlobal, isFile;
|
||||
|
||||
STEPS("Substitutions from file", substitutionName);
|
||||
STEPS("Substitutions from file", substitutionName.c_str());
|
||||
substituteOpen(&substitutePvt, substitutionName);
|
||||
do {
|
||||
isGlobal = substituteGetGlobalSet(substitutePvt);
|
||||
if (isGlobal) {
|
||||
STEP("Handling global macros");
|
||||
pval = substituteGetGlobalReplacements(substitutePvt);
|
||||
if (pval)
|
||||
addMacroReplacements(macPvt, pval);
|
||||
const char *macStr = substituteGetGlobalReplacements(substitutePvt);
|
||||
if (macStr)
|
||||
addMacroReplacements(macPvt, macStr);
|
||||
}
|
||||
else if ((isFile = substituteGetNextSet(substitutePvt, &filename))) {
|
||||
if (templateName)
|
||||
@@ -193,11 +196,12 @@ int main(int argc,char **argv)
|
||||
}
|
||||
|
||||
STEPS("Handling template file", filename);
|
||||
while ((pval = substituteGetReplacements(substitutePvt))) {
|
||||
const char *macStr;
|
||||
while ((macStr = substituteGetReplacements(substitutePvt))) {
|
||||
if (localScope)
|
||||
macPushScope(macPvt);
|
||||
|
||||
addMacroReplacements(macPvt, pval);
|
||||
addMacroReplacements(macPvt, macStr);
|
||||
makeSubstitutions(inputPvt, macPvt, filename);
|
||||
|
||||
if (localScope)
|
||||
@@ -207,18 +211,18 @@ int main(int argc,char **argv)
|
||||
} while (isGlobal || isFile);
|
||||
substituteDestruct(substitutePvt);
|
||||
}
|
||||
errlogFlush();
|
||||
macDeleteHandle(macPvt);
|
||||
errlogFlush(); // macLib calls errlogPrintf()
|
||||
inputDestruct(inputPvt);
|
||||
if (opt_D) {
|
||||
printf("\n");
|
||||
}
|
||||
fflush(stdout);
|
||||
free(templateName);
|
||||
free(substitutionName);
|
||||
return opt_V & 2;
|
||||
}
|
||||
|
||||
void usageExit(int status)
|
||||
void usageExit(const int status)
|
||||
{
|
||||
fprintf(stderr,
|
||||
"Usage: msi [options] [template]\n"
|
||||
@@ -236,7 +240,7 @@ void usageExit(int status)
|
||||
exit(status);
|
||||
}
|
||||
|
||||
void abortExit(int status)
|
||||
void abortExit(const int status)
|
||||
{
|
||||
if (outFile) {
|
||||
fclose(stdout);
|
||||
@@ -245,7 +249,8 @@ void abortExit(int status)
|
||||
exit(status);
|
||||
}
|
||||
|
||||
static void addMacroReplacements(MAC_HANDLE *macPvt, char *pval)
|
||||
static void addMacroReplacements(MAC_HANDLE * const macPvt,
|
||||
const char * const pval)
|
||||
{
|
||||
char **pairs;
|
||||
long status;
|
||||
@@ -268,7 +273,9 @@ static void addMacroReplacements(MAC_HANDLE *macPvt, char *pval)
|
||||
typedef enum {cmdInclude,cmdSubstitute} cmdType;
|
||||
static const char *cmdNames[] = {"include","substitute"};
|
||||
|
||||
static void makeSubstitutions(inputData *inputPvt, MAC_HANDLE *macPvt, char *templateName)
|
||||
static void makeSubstitutions(inputData * const inputPvt,
|
||||
MAC_HANDLE * const macPvt,
|
||||
const char * const templateName)
|
||||
{
|
||||
char *input;
|
||||
static char buffer[MAX_BUFFER_SIZE];
|
||||
@@ -292,7 +299,6 @@ static void makeSubstitutions(inputData *inputPvt, MAC_HANDLE *macPvt, char *tem
|
||||
if (command) {
|
||||
char *pstart;
|
||||
char *pend;
|
||||
char *copy;
|
||||
int cmdind=-1;
|
||||
int i;
|
||||
|
||||
@@ -325,16 +331,15 @@ static void makeSubstitutions(inputData *inputPvt, MAC_HANDLE *macPvt, char *tem
|
||||
/*skip quote and any trailing blanks*/
|
||||
while (*++p == ' ') ;
|
||||
if (*p != '\n' && *p != 0) goto endcmd;
|
||||
copy = calloc(pend-pstart + 1, sizeof(char));
|
||||
strncpy(copy, pstart, pend-pstart);
|
||||
std::string copy = std::string(pstart, pend);
|
||||
|
||||
switch(cmdind) {
|
||||
case cmdInclude:
|
||||
inputNewIncludeFile(inputPvt,copy);
|
||||
inputNewIncludeFile(inputPvt, copy.c_str());
|
||||
break;
|
||||
|
||||
case cmdSubstitute:
|
||||
addMacroReplacements(macPvt,copy);
|
||||
addMacroReplacements(macPvt, copy.c_str());
|
||||
break;
|
||||
|
||||
default:
|
||||
@@ -342,7 +347,6 @@ static void makeSubstitutions(inputData *inputPvt, MAC_HANDLE *macPvt, char *tem
|
||||
inputErrPrint(inputPvt);
|
||||
abortExit(1);
|
||||
}
|
||||
free(copy);
|
||||
expand = 0;
|
||||
}
|
||||
|
||||
@@ -361,94 +365,72 @@ endcmd:
|
||||
}
|
||||
|
||||
typedef struct inputFile {
|
||||
ELLNODE node;
|
||||
char *filename;
|
||||
std::string filename;
|
||||
FILE *fp;
|
||||
int lineNum;
|
||||
} inputFile;
|
||||
|
||||
typedef struct pathNode {
|
||||
ELLNODE node;
|
||||
char *directory;
|
||||
} pathNode;
|
||||
|
||||
struct inputData {
|
||||
ELLLIST inputFileList;
|
||||
ELLLIST pathList;
|
||||
std::list<inputFile> inputFileList;
|
||||
std::list<std::string> pathList;
|
||||
char inputBuffer[MAX_BUFFER_SIZE];
|
||||
inputData() { memset(inputBuffer, 0, sizeof(inputBuffer) * sizeof(inputBuffer[0])); };
|
||||
};
|
||||
|
||||
static void inputOpenFile(inputData *pinputData, char *filename);
|
||||
static void inputOpenFile(inputData *pinputData, const char * const filename);
|
||||
static void inputCloseFile(inputData *pinputData);
|
||||
static void inputCloseAllFiles(inputData *pinputData);
|
||||
|
||||
static void inputConstruct(inputData **ppvt)
|
||||
{
|
||||
inputData *pinputData;
|
||||
|
||||
pinputData = calloc(1, sizeof(inputData));
|
||||
ellInit(&pinputData->inputFileList);
|
||||
ellInit(&pinputData->pathList);
|
||||
*ppvt = pinputData;
|
||||
*ppvt = new inputData;
|
||||
}
|
||||
|
||||
static void inputDestruct(inputData *pinputData)
|
||||
static void inputDestruct(inputData * const pinputData)
|
||||
{
|
||||
pathNode *ppathNode;
|
||||
|
||||
inputCloseAllFiles(pinputData);
|
||||
while ((ppathNode = (pathNode *) ellFirst(&pinputData->pathList))) {
|
||||
ellDelete(&pinputData->pathList, &ppathNode->node);
|
||||
free(ppathNode->directory);
|
||||
free(ppathNode);
|
||||
}
|
||||
free(pinputData);
|
||||
delete(pinputData);
|
||||
}
|
||||
|
||||
static void inputAddPath(inputData *pinputData, char *path)
|
||||
static void inputAddPath(inputData * const pinputData, const char * const path)
|
||||
{
|
||||
ELLLIST *ppathList = &pinputData->pathList;
|
||||
pathNode *ppathNode;
|
||||
const char *pcolon;
|
||||
const char *pdir;
|
||||
size_t len;
|
||||
int emptyName;
|
||||
const char sep = *OSI_PATH_LIST_SEPARATOR;
|
||||
|
||||
ENTER;
|
||||
pdir = path;
|
||||
/*an empty name at beginning, middle, or end means current directory*/
|
||||
while (pdir && *pdir) {
|
||||
emptyName = ((*pdir == sep) ? 1 : 0);
|
||||
bool emptyName = (*pdir == sep);
|
||||
if (emptyName) ++pdir;
|
||||
|
||||
ppathNode = (pathNode *) calloc(1, sizeof(pathNode));
|
||||
ellAdd(ppathList, &ppathNode->node);
|
||||
|
||||
std::string directory;
|
||||
if (!emptyName) {
|
||||
pcolon = strchr(pdir, sep);
|
||||
len = (pcolon ? (pcolon - pdir) : strlen(pdir));
|
||||
if (len > 0) {
|
||||
ppathNode->directory = (char *) calloc(len + 1, sizeof(char));
|
||||
strncpy(ppathNode->directory, pdir, len);
|
||||
directory = std::string(pdir, len);
|
||||
pdir = pcolon;
|
||||
/*unless at end skip past first colon*/
|
||||
if (pdir && *(pdir + 1) != 0) ++pdir;
|
||||
}
|
||||
else { /*must have been trailing : */
|
||||
emptyName = 1;
|
||||
emptyName = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (emptyName) {
|
||||
ppathNode->directory = (char *) calloc(2, sizeof(char));
|
||||
strcpy(ppathNode->directory, ".");
|
||||
directory = ".";
|
||||
}
|
||||
|
||||
pinputData->pathList.push_back(directory);
|
||||
}
|
||||
EXIT;
|
||||
}
|
||||
|
||||
static void inputBegin(inputData *pinputData, char *fileName)
|
||||
static void inputBegin(inputData * const pinputData, const char * const fileName)
|
||||
{
|
||||
ENTER;
|
||||
inputCloseAllFiles(pinputData);
|
||||
@@ -456,16 +438,16 @@ static void inputBegin(inputData *pinputData, char *fileName)
|
||||
EXIT;
|
||||
}
|
||||
|
||||
static char *inputNextLine(inputData *pinputData)
|
||||
static char *inputNextLine(inputData * const pinputData)
|
||||
{
|
||||
inputFile *pinputFile;
|
||||
char *pline;
|
||||
std::list<inputFile>& inFileList = pinputData->inputFileList;
|
||||
|
||||
ENTER;
|
||||
while ((pinputFile = (inputFile *) ellFirst(&pinputData->inputFileList))) {
|
||||
pline = fgets(pinputData->inputBuffer, MAX_BUFFER_SIZE, pinputFile->fp);
|
||||
while (!inFileList.empty()) {
|
||||
inputFile& inFile = inFileList.front();
|
||||
char *pline = fgets(pinputData->inputBuffer, MAX_BUFFER_SIZE, inFile.fp);
|
||||
if (pline) {
|
||||
++pinputFile->lineNum;
|
||||
++inFile.lineNum;
|
||||
EXITS(pline);
|
||||
return pline;
|
||||
}
|
||||
@@ -475,32 +457,31 @@ static char *inputNextLine(inputData *pinputData)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void inputNewIncludeFile(inputData *pinputData, char *name)
|
||||
static void inputNewIncludeFile(inputData * const pinputData,
|
||||
const char * const name)
|
||||
{
|
||||
ENTER;
|
||||
inputOpenFile(pinputData,name);
|
||||
EXIT;
|
||||
}
|
||||
|
||||
static void inputErrPrint(inputData *pinputData)
|
||||
static void inputErrPrint(const inputData *const pinputData)
|
||||
{
|
||||
inputFile *pinputFile;
|
||||
|
||||
ENTER;
|
||||
fprintf(stderr, "input: '%s' at ", pinputData->inputBuffer);
|
||||
pinputFile = (inputFile *) ellFirst(&pinputData->inputFileList);
|
||||
while (pinputFile) {
|
||||
fprintf(stderr, "line %d of ", pinputFile->lineNum);
|
||||
const std::list<inputFile>& inFileList = pinputData->inputFileList;
|
||||
std::list<inputFile>::const_iterator inFileIt = inFileList.begin();
|
||||
while (inFileIt != inFileList.end()) {
|
||||
fprintf(stderr, "line %d of ", inFileIt->lineNum);
|
||||
|
||||
if (pinputFile->filename) {
|
||||
fprintf(stderr, " file %s\n", pinputFile->filename);
|
||||
if (!inFileIt->filename.empty()) {
|
||||
fprintf(stderr, " file %s\n", inFileIt->filename.c_str());
|
||||
}
|
||||
else {
|
||||
fprintf(stderr, "stdin:\n");
|
||||
}
|
||||
|
||||
pinputFile = (inputFile *) ellNext(&pinputFile->node);
|
||||
if (pinputFile) {
|
||||
if (++inFileIt != inFileList.end()) {
|
||||
fprintf(stderr, " included from ");
|
||||
}
|
||||
else {
|
||||
@@ -511,12 +492,11 @@ static void inputErrPrint(inputData *pinputData)
|
||||
EXIT;
|
||||
}
|
||||
|
||||
static void inputOpenFile(inputData *pinputData,char *filename)
|
||||
static void inputOpenFile(inputData *pinputData, const char * const filename)
|
||||
{
|
||||
ELLLIST *ppathList = &pinputData->pathList;
|
||||
pathNode *ppathNode = 0;
|
||||
inputFile *pinputFile;
|
||||
char *fullname = 0;
|
||||
std::list<std::string>& pathList = pinputData->pathList;
|
||||
std::list<std::string>::iterator pathIt = pathList.end();
|
||||
std::string fullname;
|
||||
FILE *fp = 0;
|
||||
|
||||
ENTER;
|
||||
@@ -524,24 +504,19 @@ static void inputOpenFile(inputData *pinputData,char *filename)
|
||||
STEP("Using stdin");
|
||||
fp = stdin;
|
||||
}
|
||||
else if ((ellCount(ppathList) == 0) || strchr(filename, '/')){
|
||||
else if (pathList.empty() || strchr(filename, '/')){
|
||||
STEPS("Opening ", filename);
|
||||
fp = fopen(filename, "r");
|
||||
}
|
||||
else {
|
||||
ppathNode = (pathNode *) ellFirst(ppathList);
|
||||
while (ppathNode) {
|
||||
fullname = calloc(strlen(filename) + strlen(ppathNode->directory) + 2,
|
||||
sizeof(char));
|
||||
strcpy(fullname, ppathNode->directory);
|
||||
strcat(fullname, "/");
|
||||
strcat(fullname, filename);
|
||||
pathIt = pathList.begin();
|
||||
while(pathIt != pathList.end()) {
|
||||
fullname = *pathIt + "/" + filename;
|
||||
STEPS("Trying", filename);
|
||||
fp = fopen(fullname, "r");
|
||||
fp = fopen(fullname.c_str(), "r");
|
||||
if (fp)
|
||||
break;
|
||||
free(fullname);
|
||||
ppathNode = (pathNode *) ellNext(&ppathNode->node);
|
||||
++pathIt;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -552,20 +527,20 @@ static void inputOpenFile(inputData *pinputData,char *filename)
|
||||
}
|
||||
|
||||
STEP("File opened");
|
||||
pinputFile = calloc(1, sizeof(inputFile));
|
||||
inputFile inFile = inputFile();
|
||||
|
||||
if (ppathNode) {
|
||||
pinputFile->filename = fullname;
|
||||
if (pathIt != pathList.end()) {
|
||||
inFile.filename = fullname;
|
||||
}
|
||||
else if (filename) {
|
||||
pinputFile->filename = epicsStrDup(filename);
|
||||
inFile.filename = filename;
|
||||
}
|
||||
else {
|
||||
pinputFile->filename = epicsStrDup("stdin");
|
||||
inFile.filename = "stdin";
|
||||
}
|
||||
|
||||
if (opt_D) {
|
||||
int hash = epicsStrHash(pinputFile->filename, 12345);
|
||||
int hash = epicsStrHash(inFile.filename.c_str(), 12345);
|
||||
int i = 0;
|
||||
int match = 0;
|
||||
|
||||
@@ -578,7 +553,7 @@ static void inputOpenFile(inputData *pinputData,char *filename)
|
||||
if (!match) {
|
||||
const char *wrap = numDeps ? " \\\n" : "";
|
||||
|
||||
printf("%s %s", wrap, pinputFile->filename);
|
||||
printf("%s %s", wrap, inFile.filename.c_str());
|
||||
if (numDeps < MAX_DEPS) {
|
||||
depHashes[numDeps++] = hash;
|
||||
}
|
||||
@@ -589,33 +564,29 @@ static void inputOpenFile(inputData *pinputData,char *filename)
|
||||
}
|
||||
}
|
||||
|
||||
pinputFile->fp = fp;
|
||||
ellInsert(&pinputData->inputFileList, 0, &pinputFile->node);
|
||||
inFile.fp = fp;
|
||||
pinputData->inputFileList.push_front(inFile);
|
||||
EXIT;
|
||||
}
|
||||
|
||||
static void inputCloseFile(inputData *pinputData)
|
||||
{
|
||||
inputFile *pinputFile;
|
||||
|
||||
std::list<inputFile>& inFileList = pinputData->inputFileList;
|
||||
ENTER;
|
||||
pinputFile = (inputFile *) ellFirst(&pinputData->inputFileList);
|
||||
if (pinputFile) {
|
||||
ellDelete(&pinputData->inputFileList, &pinputFile->node);
|
||||
if (fclose(pinputFile->fp))
|
||||
fprintf(stderr, "msi: Can't close input file '%s'\n", pinputFile->filename);
|
||||
free(pinputFile->filename);
|
||||
free(pinputFile);
|
||||
if(!inFileList.empty()) {
|
||||
inputFile& inFile = inFileList.front();
|
||||
if (fclose(inFile.fp))
|
||||
fprintf(stderr, "msi: Can't close input file '%s'\n", inFile.filename.c_str());
|
||||
inFileList.erase(inFileList.begin());
|
||||
}
|
||||
EXIT;
|
||||
}
|
||||
|
||||
static void inputCloseAllFiles(inputData *pinputData)
|
||||
{
|
||||
inputFile *pinputFile;
|
||||
|
||||
ENTER;
|
||||
while ((pinputFile = (inputFile *) ellFirst(&pinputData->inputFileList))) {
|
||||
const std::list<inputFile>& inFileList = pinputData->inputFileList;
|
||||
while(!inFileList.empty()) {
|
||||
inputCloseFile(pinputData);
|
||||
}
|
||||
EXIT;
|
||||
@@ -627,7 +598,7 @@ typedef enum {
|
||||
} tokenType;
|
||||
|
||||
typedef struct subFile {
|
||||
char *substitutionName;
|
||||
std::string substitutionName;
|
||||
FILE *fp;
|
||||
int lineNum;
|
||||
char inputBuffer[MAX_BUFFER_SIZE];
|
||||
@@ -636,25 +607,20 @@ typedef struct subFile {
|
||||
char string[MAX_BUFFER_SIZE];
|
||||
} subFile;
|
||||
|
||||
typedef struct patternNode {
|
||||
ELLNODE node;
|
||||
char *var;
|
||||
} patternNode;
|
||||
|
||||
struct subInfo {
|
||||
subFile *psubFile;
|
||||
int isFile;
|
||||
bool isFile;
|
||||
char *filename;
|
||||
int isPattern;
|
||||
ELLLIST patternList;
|
||||
size_t size;
|
||||
size_t curLength;
|
||||
char *macroReplacements;
|
||||
bool isPattern;
|
||||
std::list<std::string> patternList;
|
||||
std::string macroReplacements;
|
||||
subInfo() : psubFile(NULL), isFile(false), filename(NULL),
|
||||
isPattern(false) {};
|
||||
};
|
||||
|
||||
static char *subGetNextLine(subFile *psubFile);
|
||||
static tokenType subGetNextToken(subFile *psubFile);
|
||||
static void subFileErrPrint(subFile *psubFile,char * message);
|
||||
static void subFileErrPrint(subFile *psubFile, const char * message);
|
||||
static void freeSubFile(subInfo *psubInfo);
|
||||
static void freePattern(subInfo *psubInfo);
|
||||
static void catMacroReplacements(subInfo *psubInfo,const char *value);
|
||||
@@ -668,7 +634,7 @@ void freeSubFile(subInfo *psubInfo)
|
||||
if (fclose(psubFile->fp))
|
||||
fprintf(stderr, "msi: Can't close substitution file\n");
|
||||
}
|
||||
free(psubFile);
|
||||
delete(psubFile);
|
||||
free(psubInfo->filename);
|
||||
psubInfo->psubFile = 0;
|
||||
EXIT;
|
||||
@@ -676,43 +642,36 @@ void freeSubFile(subInfo *psubInfo)
|
||||
|
||||
void freePattern(subInfo *psubInfo)
|
||||
{
|
||||
patternNode *ppatternNode;
|
||||
|
||||
ENTER;
|
||||
while ((ppatternNode = (patternNode *) ellFirst(&psubInfo->patternList))) {
|
||||
ellDelete(&psubInfo->patternList, &ppatternNode->node);
|
||||
free(ppatternNode->var);
|
||||
free(ppatternNode);
|
||||
}
|
||||
psubInfo->isPattern = 0;
|
||||
psubInfo->patternList.clear();
|
||||
psubInfo->isPattern = false;
|
||||
EXIT;
|
||||
}
|
||||
|
||||
static void substituteDestruct(subInfo *psubInfo)
|
||||
static void substituteDestruct(subInfo * const psubInfo)
|
||||
{
|
||||
ENTER;
|
||||
freeSubFile(psubInfo);
|
||||
freePattern(psubInfo);
|
||||
free(psubInfo);
|
||||
delete(psubInfo);
|
||||
EXIT;
|
||||
}
|
||||
|
||||
static void substituteOpen(subInfo **ppvt, char *substitutionName)
|
||||
static void substituteOpen(subInfo **ppvt, const std::string& substitutionName)
|
||||
{
|
||||
subInfo *psubInfo;
|
||||
subFile *psubFile;
|
||||
FILE *fp;
|
||||
|
||||
ENTER;
|
||||
psubInfo = calloc(1, sizeof(subInfo));
|
||||
psubInfo = new subInfo;
|
||||
*ppvt = psubInfo;
|
||||
psubFile = calloc(1, sizeof(subFile));
|
||||
psubFile = new subFile;
|
||||
psubInfo->psubFile = psubFile;
|
||||
ellInit(&psubInfo->patternList);
|
||||
|
||||
fp = fopen(substitutionName, "r");
|
||||
fp = fopen(substitutionName.c_str(), "r");
|
||||
if (!fp) {
|
||||
fprintf(stderr, "msi: Can't open file '%s'\n", substitutionName);
|
||||
fprintf(stderr, "msi: Can't open file '%s'\n", substitutionName.c_str());
|
||||
abortExit(1);
|
||||
}
|
||||
|
||||
@@ -725,7 +684,7 @@ static void substituteOpen(subInfo **ppvt, char *substitutionName)
|
||||
EXIT;
|
||||
}
|
||||
|
||||
static int substituteGetGlobalSet(subInfo *psubInfo)
|
||||
static bool substituteGetGlobalSet(subInfo * const psubInfo)
|
||||
{
|
||||
subFile *psubFile = psubInfo->psubFile;
|
||||
|
||||
@@ -737,17 +696,16 @@ static int substituteGetGlobalSet(subInfo *psubInfo)
|
||||
strcmp(psubFile->string, "global") == 0) {
|
||||
subGetNextToken(psubFile);
|
||||
EXITD(1);
|
||||
return 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
EXITD(0);
|
||||
return 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
static int substituteGetNextSet(subInfo *psubInfo,char **filename)
|
||||
static bool substituteGetNextSet(subInfo * const psubInfo,char **filename)
|
||||
{
|
||||
subFile *psubFile = psubInfo->psubFile;
|
||||
patternNode *ppatternNode;
|
||||
|
||||
ENTER;
|
||||
*filename = 0;
|
||||
@@ -756,7 +714,7 @@ static int substituteGetNextSet(subInfo *psubInfo,char **filename)
|
||||
|
||||
if (psubFile->token == tokenEOF) {
|
||||
EXITD(0);
|
||||
return 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (psubFile->token == tokenString &&
|
||||
@@ -764,7 +722,7 @@ static int substituteGetNextSet(subInfo *psubInfo,char **filename)
|
||||
size_t len;
|
||||
|
||||
STEP("Parsed 'file'");
|
||||
psubInfo->isFile = 1;
|
||||
psubInfo->isFile = true;
|
||||
if (subGetNextToken(psubFile) != tokenString) {
|
||||
subFileErrPrint(psubFile, "Parse error, expecting a filename");
|
||||
abortExit(1);
|
||||
@@ -799,7 +757,7 @@ static int substituteGetNextSet(subInfo *psubInfo,char **filename)
|
||||
|
||||
if (psubFile->token == tokenLBrace) {
|
||||
EXITD(1);
|
||||
return 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (psubFile->token == tokenRBrace) {
|
||||
@@ -815,7 +773,7 @@ static int substituteGetNextSet(subInfo *psubInfo,char **filename)
|
||||
|
||||
STEP("Parsed 'pattern'");
|
||||
freePattern(psubInfo);
|
||||
psubInfo->isPattern = 1;
|
||||
psubInfo->isPattern = true;
|
||||
|
||||
while (subGetNextToken(psubFile) == tokenSeparator);
|
||||
|
||||
@@ -825,15 +783,13 @@ static int substituteGetNextSet(subInfo *psubInfo,char **filename)
|
||||
}
|
||||
STEP("Parsed '{'");
|
||||
|
||||
while (1) {
|
||||
while (true) {
|
||||
while (subGetNextToken(psubFile) == tokenSeparator);
|
||||
|
||||
if (psubFile->token != tokenString)
|
||||
break;
|
||||
|
||||
ppatternNode = calloc(1, sizeof(patternNode));
|
||||
ellAdd(&psubInfo->patternList, &ppatternNode->node);
|
||||
ppatternNode->var = epicsStrDup(psubFile->string);
|
||||
psubInfo->patternList.push_back(psubFile->string);
|
||||
}
|
||||
|
||||
if (psubFile->token != tokenRBrace) {
|
||||
@@ -843,23 +799,21 @@ static int substituteGetNextSet(subInfo *psubInfo,char **filename)
|
||||
|
||||
subGetNextToken(psubFile);
|
||||
EXITD(1);
|
||||
return 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
static char *substituteGetGlobalReplacements(subInfo *psubInfo)
|
||||
static const char *substituteGetGlobalReplacements(subInfo * const psubInfo)
|
||||
{
|
||||
subFile *psubFile = psubInfo->psubFile;
|
||||
|
||||
ENTER;
|
||||
if (psubInfo->macroReplacements)
|
||||
psubInfo->macroReplacements[0] = 0;
|
||||
psubInfo->curLength = 0;
|
||||
psubInfo->macroReplacements.clear();
|
||||
|
||||
while (psubFile->token == tokenSeparator)
|
||||
subGetNextToken(psubFile);
|
||||
|
||||
if (psubFile->token == tokenRBrace && psubInfo->isFile) {
|
||||
psubInfo->isFile = 0;
|
||||
psubInfo->isFile = false;
|
||||
free(psubInfo->filename);
|
||||
psubInfo->filename = 0;
|
||||
freePattern(psubInfo);
|
||||
@@ -877,12 +831,12 @@ static char *substituteGetGlobalReplacements(subInfo *psubInfo)
|
||||
return 0;
|
||||
}
|
||||
|
||||
while (1) {
|
||||
while (true) {
|
||||
switch(subGetNextToken(psubFile)) {
|
||||
case tokenRBrace:
|
||||
subGetNextToken(psubFile);
|
||||
EXITS(psubInfo->macroReplacements);
|
||||
return psubInfo->macroReplacements;
|
||||
EXITS(psubInfo->macroReplacements.c_str());
|
||||
return psubInfo->macroReplacements.c_str();
|
||||
|
||||
case tokenSeparator:
|
||||
catMacroReplacements(psubInfo, ",");
|
||||
@@ -902,21 +856,18 @@ static char *substituteGetGlobalReplacements(subInfo *psubInfo)
|
||||
}
|
||||
}
|
||||
|
||||
static char *substituteGetReplacements(subInfo *psubInfo)
|
||||
static const char *substituteGetReplacements(subInfo * const psubInfo)
|
||||
{
|
||||
subFile *psubFile = psubInfo->psubFile;
|
||||
patternNode *ppatternNode;
|
||||
|
||||
ENTER;
|
||||
if (psubInfo->macroReplacements)
|
||||
psubInfo->macroReplacements[0] = 0;
|
||||
psubInfo->curLength = 0;
|
||||
psubInfo->macroReplacements.clear();
|
||||
|
||||
while (psubFile->token == tokenSeparator)
|
||||
subGetNextToken(psubFile);
|
||||
|
||||
if (psubFile->token==tokenRBrace && psubInfo->isFile) {
|
||||
psubInfo->isFile = 0;
|
||||
psubInfo->isFile = false;
|
||||
free(psubInfo->filename);
|
||||
psubInfo->filename = 0;
|
||||
freePattern(psubInfo);
|
||||
@@ -936,15 +887,16 @@ static char *substituteGetReplacements(subInfo *psubInfo)
|
||||
}
|
||||
|
||||
if (psubInfo->isPattern) {
|
||||
int gotFirstPattern = 0;
|
||||
bool gotFirstPattern = false;
|
||||
|
||||
while (subGetNextToken(psubFile) == tokenSeparator);
|
||||
ppatternNode = (patternNode *) ellFirst(&psubInfo->patternList);
|
||||
while (1) {
|
||||
std::list<std::string>& patternList = psubInfo->patternList;
|
||||
std::list<std::string>::iterator patternIt = patternList.begin();
|
||||
while (true) {
|
||||
if (psubFile->token == tokenRBrace) {
|
||||
subGetNextToken(psubFile);
|
||||
EXITS(psubInfo->macroReplacements);
|
||||
return psubInfo->macroReplacements;
|
||||
EXITS(psubInfo->macroReplacements.c_str());
|
||||
return psubInfo->macroReplacements.c_str();
|
||||
}
|
||||
|
||||
if (psubFile->token != tokenString) {
|
||||
@@ -954,13 +906,13 @@ static char *substituteGetReplacements(subInfo *psubInfo)
|
||||
|
||||
if (gotFirstPattern)
|
||||
catMacroReplacements(psubInfo, ",");
|
||||
gotFirstPattern = 1;
|
||||
gotFirstPattern = true;
|
||||
|
||||
if (ppatternNode) {
|
||||
catMacroReplacements(psubInfo, ppatternNode->var);
|
||||
if (patternIt != patternList.end()) {
|
||||
catMacroReplacements(psubInfo, patternIt->c_str());
|
||||
catMacroReplacements(psubInfo, "=");
|
||||
catMacroReplacements(psubInfo, psubFile->string);
|
||||
ppatternNode = (patternNode *) ellNext(&ppatternNode->node);
|
||||
++patternIt;
|
||||
}
|
||||
else {
|
||||
subFileErrPrint(psubFile, "Warning, too many values given");
|
||||
@@ -969,12 +921,12 @@ static char *substituteGetReplacements(subInfo *psubInfo)
|
||||
while (subGetNextToken(psubFile) == tokenSeparator);
|
||||
}
|
||||
}
|
||||
else while(1) {
|
||||
else while(true) {
|
||||
switch(subGetNextToken(psubFile)) {
|
||||
case tokenRBrace:
|
||||
subGetNextToken(psubFile);
|
||||
EXITS(psubInfo->macroReplacements);
|
||||
return psubInfo->macroReplacements;
|
||||
EXITS(psubInfo->macroReplacements.c_str());
|
||||
return psubInfo->macroReplacements.c_str();
|
||||
|
||||
case tokenSeparator:
|
||||
catMacroReplacements(psubInfo, ",");
|
||||
@@ -1017,11 +969,12 @@ static char *subGetNextLine(subFile *psubFile)
|
||||
return &psubFile->inputBuffer[0];
|
||||
}
|
||||
|
||||
static void subFileErrPrint(subFile *psubFile,char * message)
|
||||
static void subFileErrPrint(subFile *psubFile, const char * message)
|
||||
{
|
||||
fprintf(stderr, "msi: %s\n",message);
|
||||
fprintf(stderr, " in substitution file '%s' at line %d:\n %s",
|
||||
psubFile->substitutionName, psubFile->lineNum, psubFile->inputBuffer);
|
||||
psubFile->substitutionName.c_str(), psubFile->lineNum,
|
||||
psubFile->inputBuffer);
|
||||
}
|
||||
|
||||
|
||||
@@ -1107,32 +1060,8 @@ done:
|
||||
|
||||
static void catMacroReplacements(subInfo *psubInfo, const char *value)
|
||||
{
|
||||
size_t len = strlen(value);
|
||||
|
||||
ENTER;
|
||||
if (psubInfo->size <= (psubInfo->curLength + len)) {
|
||||
size_t newsize = psubInfo->size + MAX_BUFFER_SIZE;
|
||||
char *newbuf;
|
||||
|
||||
STEP("Enlarging buffer");
|
||||
if (newsize <= psubInfo->curLength + len)
|
||||
newsize = psubInfo->curLength + len + 1;
|
||||
newbuf = calloc(1, newsize);
|
||||
if (!newbuf) {
|
||||
fprintf(stderr, "calloc failed for size %lu\n",
|
||||
(unsigned long) newsize);
|
||||
abortExit(1);
|
||||
}
|
||||
if (psubInfo->macroReplacements) {
|
||||
memcpy(newbuf, psubInfo->macroReplacements, psubInfo->curLength);
|
||||
free(psubInfo->macroReplacements);
|
||||
}
|
||||
psubInfo->size = newsize;
|
||||
psubInfo->macroReplacements = newbuf;
|
||||
}
|
||||
|
||||
STEPS("Appending", value);
|
||||
strcat(psubInfo->macroReplacements, value);
|
||||
psubInfo->curLength += len;
|
||||
psubInfo->macroReplacements += value;
|
||||
EXIT;
|
||||
}
|
||||
@@ -83,20 +83,22 @@ static void req_server (void *pParm)
|
||||
|
||||
while (TRUE) {
|
||||
SOCKET clientSock;
|
||||
struct sockaddr sockAddr;
|
||||
osiSockAddr sockAddr;
|
||||
osiSocklen_t addLen = sizeof(sockAddr);
|
||||
|
||||
while (castcp_ctl == ctlPause) {
|
||||
epicsThreadSleep(0.1);
|
||||
}
|
||||
|
||||
clientSock = epicsSocketAccept ( IOC_sock, &sockAddr, &addLen );
|
||||
if ( clientSock == INVALID_SOCKET ) {
|
||||
clientSock = epicsSocketAccept ( IOC_sock, &sockAddr.sa, &addLen );
|
||||
if ( clientSock == INVALID_SOCKET ||
|
||||
sockAddr.sa.sa_family != AF_INET ||
|
||||
addLen < sizeof(sockAddr.ia) ) {
|
||||
char sockErrBuf[64];
|
||||
epicsSocketConvertErrnoToString (
|
||||
sockErrBuf, sizeof ( sockErrBuf ) );
|
||||
errlogPrintf("CAS: Client accept error: %s\n",
|
||||
sockErrBuf );
|
||||
errlogPrintf("CAS: Client accept error: %s (%d)\n",
|
||||
sockErrBuf, (int)addLen );
|
||||
epicsThreadSleep(15.0);
|
||||
continue;
|
||||
}
|
||||
@@ -105,7 +107,7 @@ static void req_server (void *pParm)
|
||||
struct client *pClient;
|
||||
|
||||
/* socket passed in is closed if unsuccessful here */
|
||||
pClient = create_tcp_client ( clientSock );
|
||||
pClient = create_tcp_client ( clientSock, &sockAddr );
|
||||
if ( ! pClient ) {
|
||||
epicsThreadSleep ( 15.0 );
|
||||
continue;
|
||||
@@ -1405,12 +1407,11 @@ void casExpandRecvBuffer ( struct client *pClient, ca_uint32_t size )
|
||||
/*
|
||||
* create_tcp_client ()
|
||||
*/
|
||||
struct client *create_tcp_client ( SOCKET sock )
|
||||
struct client *create_tcp_client (SOCKET sock , const osiSockAddr *peerAddr)
|
||||
{
|
||||
int status;
|
||||
struct client *client;
|
||||
int intTrue = TRUE;
|
||||
osiSocklen_t addrSize;
|
||||
unsigned priorityOfEvents;
|
||||
|
||||
/* socket passed in is destroyed here if unsuccessful */
|
||||
@@ -1419,6 +1420,8 @@ struct client *create_tcp_client ( SOCKET sock )
|
||||
return NULL;
|
||||
}
|
||||
|
||||
client->addr = peerAddr->ia;
|
||||
|
||||
/*
|
||||
* see TCP(4P) this seems to make unsolicited single events much
|
||||
* faster. I take care of queue up as load increases.
|
||||
@@ -1470,15 +1473,6 @@ struct client *create_tcp_client ( SOCKET sock )
|
||||
}
|
||||
#endif
|
||||
|
||||
addrSize = sizeof ( client->addr );
|
||||
status = getpeername ( sock, (struct sockaddr *)&client->addr,
|
||||
&addrSize );
|
||||
if ( status < 0 ) {
|
||||
epicsPrintf ("CAS: peer address fetch failed\n");
|
||||
destroy_tcp_client (client);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
client->evuser = (struct event_user *) db_init_events ();
|
||||
if ( ! client->evuser ) {
|
||||
errlogPrintf ("CAS: unable to init the event facility\n");
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <cantProceed.h>
|
||||
|
||||
#include "addrList.h"
|
||||
#include "dbDefs.h"
|
||||
@@ -45,6 +46,7 @@ void rsrv_online_notify_task(void *pParm)
|
||||
caHdr msg;
|
||||
int status;
|
||||
ca_uint32_t beaconCounter = 0;
|
||||
int *lastError;
|
||||
|
||||
taskwdInsert (epicsThreadGetIdSelf(),NULL,NULL);
|
||||
|
||||
@@ -70,29 +72,45 @@ void rsrv_online_notify_task(void *pParm)
|
||||
msg.m_count = htons (ca_server_port);
|
||||
msg.m_dataType = htons (CA_MINOR_PROTOCOL_REVISION);
|
||||
|
||||
/* beaconAddrList should not change after rsrv_init(), which then starts this thread */
|
||||
lastError = callocMustSucceed(ellCount(&beaconAddrList), sizeof(*lastError), "rsrv_online_notify_task lastError");
|
||||
|
||||
epicsEventSignal(beacon_startStopEvent);
|
||||
|
||||
while (TRUE) {
|
||||
ELLNODE *cur;
|
||||
unsigned i;
|
||||
|
||||
/* send beacon to each interface */
|
||||
for(cur=ellFirst(&beaconAddrList); cur; cur=ellNext(cur))
|
||||
for(i=0, cur=ellFirst(&beaconAddrList); cur; i++, cur=ellNext(cur))
|
||||
{
|
||||
osiSockAddrNode *pAddr = CONTAINER(cur, osiSockAddrNode, node);
|
||||
status = sendto (beaconSocket, (char *)&msg, sizeof(msg), 0,
|
||||
&pAddr->addr.sa, sizeof(pAddr->addr));
|
||||
if (status < 0) {
|
||||
char sockErrBuf[64];
|
||||
char sockDipBuf[22];
|
||||
int err = SOCKERRNO;
|
||||
if(err != lastError[i]) {
|
||||
char sockErrBuf[64];
|
||||
char sockDipBuf[22];
|
||||
|
||||
epicsSocketConvertErrnoToString(sockErrBuf, sizeof(sockErrBuf));
|
||||
ipAddrToDottedIP(&pAddr->addr.ia, sockDipBuf, sizeof(sockDipBuf));
|
||||
errlogPrintf ( "CAS: CA beacon send to %s error: %s\n",
|
||||
sockDipBuf, sockErrBuf);
|
||||
epicsSocketConvertErrorToString(sockErrBuf, sizeof(sockErrBuf), err);
|
||||
ipAddrToDottedIP(&pAddr->addr.ia, sockDipBuf, sizeof(sockDipBuf));
|
||||
errlogPrintf ( "CAS: CA beacon send to %s error: %s\n",
|
||||
sockDipBuf, sockErrBuf);
|
||||
|
||||
lastError[i] = err;
|
||||
}
|
||||
}
|
||||
else {
|
||||
assert (status == sizeof(msg));
|
||||
if(lastError[i]) {
|
||||
char sockDipBuf[22];
|
||||
|
||||
ipAddrToDottedIP(&pAddr->addr.ia, sockDipBuf, sizeof(sockDipBuf));
|
||||
errlogPrintf ( "CAS: CA beacon send to %s ok\n",
|
||||
sockDipBuf);
|
||||
}
|
||||
lastError[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -111,6 +129,8 @@ void rsrv_online_notify_task(void *pParm)
|
||||
delay = 0.02; /* Restart beacon timing if paused */
|
||||
}
|
||||
}
|
||||
|
||||
free(lastError);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -230,7 +230,7 @@ void rsrv_online_notify_task (void *);
|
||||
void cast_server (void *);
|
||||
struct client *create_client ( SOCKET sock, int proto );
|
||||
void destroy_client ( struct client * );
|
||||
struct client *create_tcp_client ( SOCKET sock );
|
||||
struct client *create_tcp_client ( SOCKET sock, const osiSockAddr* peerAddr );
|
||||
void destroy_tcp_client ( struct client * );
|
||||
void casAttachThreadToClient ( struct client * );
|
||||
int camessage ( struct client *client );
|
||||
|
||||
@@ -241,7 +241,7 @@ Assuming there is a system state called "blue", that is being controlled by
|
||||
some other facility such as a timing system, updates could be restricted to
|
||||
periods only when "blue" is true by using
|
||||
|
||||
Hal$ camonitor 'test:channel' 'test:channel.{"while":"blue"}'
|
||||
Hal$ camonitor 'test:channel' 'test:channel.{"sync":{"while":"blue"}}'
|
||||
...
|
||||
|
||||
=cut
|
||||
|
||||
@@ -1,134 +0,0 @@
|
||||
#*************************************************************************
|
||||
# Copyright (c) 2002 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.
|
||||
# EPICS BASE is distributed subject to a Software License Agreement found
|
||||
# in file LICENSE that is included with this distribution.
|
||||
#*************************************************************************
|
||||
recordtype(bi) {
|
||||
include "dbCommon.dbd"
|
||||
field(INP,DBF_INLINK) {
|
||||
prompt("Input Specification")
|
||||
promptgroup("40 - Input")
|
||||
interest(1)
|
||||
}
|
||||
field(VAL,DBF_ENUM) {
|
||||
prompt("Current Value")
|
||||
promptgroup("40 - Input")
|
||||
asl(ASL0)
|
||||
pp(TRUE)
|
||||
}
|
||||
field(ZSV,DBF_MENU) {
|
||||
prompt("Zero Error Severity")
|
||||
promptgroup("70 - Alarm")
|
||||
pp(TRUE)
|
||||
interest(1)
|
||||
menu(menuAlarmSevr)
|
||||
}
|
||||
field(OSV,DBF_MENU) {
|
||||
prompt("One Error Severity")
|
||||
promptgroup("70 - Alarm")
|
||||
pp(TRUE)
|
||||
interest(1)
|
||||
menu(menuAlarmSevr)
|
||||
}
|
||||
field(COSV,DBF_MENU) {
|
||||
prompt("Change of State Svr")
|
||||
promptgroup("70 - Alarm")
|
||||
pp(TRUE)
|
||||
interest(1)
|
||||
menu(menuAlarmSevr)
|
||||
}
|
||||
field(ZNAM,DBF_STRING) {
|
||||
prompt("Zero Name")
|
||||
promptgroup("80 - Display")
|
||||
pp(TRUE)
|
||||
interest(1)
|
||||
size(26)
|
||||
prop(YES)
|
||||
}
|
||||
field(ONAM,DBF_STRING) {
|
||||
prompt("One Name")
|
||||
promptgroup("80 - Display")
|
||||
pp(TRUE)
|
||||
interest(1)
|
||||
size(26)
|
||||
prop(YES)
|
||||
}
|
||||
field(RVAL,DBF_ULONG) {
|
||||
prompt("Raw Value")
|
||||
pp(TRUE)
|
||||
}
|
||||
field(ORAW,DBF_ULONG) {
|
||||
prompt("prev Raw Value")
|
||||
special(SPC_NOMOD)
|
||||
interest(3)
|
||||
}
|
||||
field(MASK,DBF_ULONG) {
|
||||
prompt("Hardware Mask")
|
||||
special(SPC_NOMOD)
|
||||
interest(1)
|
||||
}
|
||||
field(LALM,DBF_USHORT) {
|
||||
prompt("Last Value Alarmed")
|
||||
special(SPC_NOMOD)
|
||||
interest(3)
|
||||
}
|
||||
field(MLST,DBF_USHORT) {
|
||||
prompt("Last Value Monitored")
|
||||
special(SPC_NOMOD)
|
||||
interest(3)
|
||||
}
|
||||
field(SIOL,DBF_INLINK) {
|
||||
prompt("Simulation Input Link")
|
||||
promptgroup("90 - Simulate")
|
||||
interest(1)
|
||||
}
|
||||
field(SVAL,DBF_ULONG) {
|
||||
prompt("Simulation Value")
|
||||
}
|
||||
field(SIML,DBF_INLINK) {
|
||||
prompt("Simulation Mode Link")
|
||||
promptgroup("90 - Simulate")
|
||||
interest(1)
|
||||
}
|
||||
field(SIMM,DBF_MENU) {
|
||||
prompt("Simulation Mode")
|
||||
special(SPC_MOD)
|
||||
interest(1)
|
||||
menu(menuSimm)
|
||||
}
|
||||
field(SIMS,DBF_MENU) {
|
||||
prompt("Simulation Mode Severity")
|
||||
promptgroup("90 - Simulate")
|
||||
interest(2)
|
||||
menu(menuAlarmSevr)
|
||||
}
|
||||
field(OLDSIMM,DBF_MENU) {
|
||||
prompt("Prev. Simulation Mode")
|
||||
special(SPC_NOMOD)
|
||||
interest(4)
|
||||
menu(menuSimm)
|
||||
}
|
||||
field(SSCN,DBF_MENU) {
|
||||
prompt("Sim. Mode Scan")
|
||||
promptgroup("90 - Simulate")
|
||||
interest(1)
|
||||
menu(menuScan)
|
||||
initial("65535")
|
||||
}
|
||||
field(SDLY,DBF_DOUBLE) {
|
||||
prompt("Sim. Mode Async Delay")
|
||||
promptgroup("90 - Simulate")
|
||||
interest(2)
|
||||
initial("-1.0")
|
||||
}
|
||||
%#include "callback.h"
|
||||
field(SIMPVT,DBF_NOACCESS) {
|
||||
prompt("Sim. Mode Private")
|
||||
special(SPC_NOMOD)
|
||||
interest(4)
|
||||
extra("CALLBACK *simpvt")
|
||||
}
|
||||
}
|
||||
491
modules/database/src/std/rec/biRecord.dbd.pod
Normal file
491
modules/database/src/std/rec/biRecord.dbd.pod
Normal file
@@ -0,0 +1,491 @@
|
||||
#*************************************************************************
|
||||
# Copyright (c) 2002 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.
|
||||
# EPICS BASE is distributed subject to a Software License Agreement found
|
||||
# in file LICENSE that is included with this distribution.
|
||||
#*************************************************************************
|
||||
|
||||
=title Binary Input Record (bi)
|
||||
|
||||
This record type is normally used to obtain a binary value of 0 or 1. Most
|
||||
device support modules obtain values from hardware and place the value in
|
||||
RVAL. For these devices, record processing sets VAL = (0,1) if RVAL is (0,
|
||||
not 0). Device support modules may optionally read a value directly from
|
||||
VAL.
|
||||
|
||||
Soft device modules are provided to obtain input via database or channel
|
||||
access links via dbPutField or dbPutLink requests. Two soft device support
|
||||
modules are provided: C<Soft Channel> and C<Raw Soft Channel>. The first
|
||||
allows VAL to be an arbitrary unsigned short integer. The second reads the
|
||||
value into RVAL just like normal hardware modules.
|
||||
|
||||
=head2 Parameter Fields
|
||||
|
||||
The binary input's fields fall into the following categories:
|
||||
|
||||
=over
|
||||
|
||||
=item *
|
||||
scan Parameters
|
||||
|
||||
=item *
|
||||
read and convert parameters
|
||||
|
||||
=item *
|
||||
operator display parameters
|
||||
|
||||
=item *
|
||||
alarm parameters
|
||||
|
||||
=item *
|
||||
run-time parameters
|
||||
|
||||
=back
|
||||
|
||||
=recordtype bi
|
||||
|
||||
=cut
|
||||
|
||||
recordtype(bi) {
|
||||
|
||||
=head3 Scan Parameters
|
||||
|
||||
The binary input record has the standard fields for specifying under what
|
||||
circumstances the record will be processed. These fields are listed in
|
||||
L<Scan Fields>. In addition, L<Scanning Specification> explains how these
|
||||
fields are used. Note that I/O event scanning is only supported for those
|
||||
card types that interrupt.
|
||||
|
||||
=fields SCAN
|
||||
|
||||
=head3 Read and Convert Parameters
|
||||
|
||||
The read and convert fields determine where the binary input gets its
|
||||
input from and how to convert the raw signal to engineering units. The INP
|
||||
field contains the address from where device support retrieves the value.
|
||||
If the binary input record gets its value from hardware, the address of the
|
||||
card must be entered in the INP field, and the name of the device support
|
||||
module must be entered in the DTYP field. See L<Address Specification> for
|
||||
information on the format of the hardware address. Be aware that the format
|
||||
differs between types of cards. You can see a list of device support
|
||||
modules currently supported at the user's local site by using C<dbst>
|
||||
utility (R3.13).
|
||||
|
||||
For records that specify C<Soft Channel> or C<Raw Soft Channel> device
|
||||
support routines, the INP field can be a channel or a database link, or a
|
||||
constant. If a constant, VAL can be changed directly by dbPuts. See
|
||||
L<Address Specification> for information on the format of database and
|
||||
channel access addresses. Also, see L<Device Support for Soft Records> in
|
||||
this chapter for information on soft device support.
|
||||
|
||||
If the record gets its values from hardware or uses the C<Raw Soft Channel>
|
||||
device support, the device support routines place the value in the RVAL
|
||||
field which is then converted using the process described in the next
|
||||
section.
|
||||
|
||||
=fields INP, DTYP, ZNAM, ONAM, RVAL, VAL
|
||||
|
||||
=head3 Conversion Fields
|
||||
|
||||
The VAL field is set equal to (0,1) if the RVAL field is (0, not 0), unless
|
||||
the device support module reads a value directly into VAL or the
|
||||
C<Soft Channel> device support is used. The value can also be fetched as one of
|
||||
the strings specified in the ZNAM or ONAM fields. The ZNAM field has a
|
||||
string that corresponds to the 0 state, so when the value is fetched as
|
||||
this string, C<put_enum_str> will return a 0. The ONAM field hold the
|
||||
string that corresponds to the 1 state, so when the value is fetched as
|
||||
this string, C<put_enum_str> returns a 1.
|
||||
|
||||
=fields ZNAM, ONAM
|
||||
|
||||
=head3 Operator Display Parameters
|
||||
|
||||
These parameters are used to present meaningful data to the operator. The
|
||||
C<get_enum_str> record support routine can retrieve the state string
|
||||
corresponding to the VAL's state. If the value is 1, C<get_enum_str> will
|
||||
return the string in the ONAM field; and if 0, C<get_enum_str> will return
|
||||
the ZNAM string.
|
||||
|
||||
See L<Fields Common to All Record Types> for more on the record name (NAME)
|
||||
and description (DESC) fields.
|
||||
|
||||
=fields ZNAM, ONAM, NAME, DESC
|
||||
|
||||
=head3 Alarm Parameters
|
||||
|
||||
These parameters are used to determine if the binary input is in alarm
|
||||
condition and to determine the severity of that condition. The possible
|
||||
alarm conditions for binary inputs are the SCAN, READ state alarms, and the
|
||||
change of state alarm. The SCAN and READ alarms are called by the device
|
||||
supprt routines.
|
||||
|
||||
The user can choose the severity of each state in the ZSV and OSV fields.
|
||||
The possible values for these fields are C<NO_ALARM>, C<MINOR>, and
|
||||
C<MAJOR>. The ZSV field holds the severity for the zero state; OSV, for
|
||||
the one state. COSV causes an alarm whenever the state changes between
|
||||
0 and 1 and the severity is configured as MINOR or MAJOR.
|
||||
|
||||
See L<Alarm Specification> for a complete explanation of the discrete alarm
|
||||
states. L<Alarm Fields> lists other fields related to alarms that are
|
||||
common to all record types.
|
||||
|
||||
=fields ZSV, OSV, COSV
|
||||
|
||||
=head3 Run-time Parameters and Simulation Mode Parameters
|
||||
|
||||
These parameters are used by the run-time code for processing the binary
|
||||
input. They are not configured using a database configuration tool.
|
||||
|
||||
ORAW is used to determine if monitors should be triggered for RVAL at the same
|
||||
time they are triggered for VAL.
|
||||
|
||||
MASK is given a value by ithe device support routines. This value is used to
|
||||
manipulate the record's value, but is only the concern of the hardware device
|
||||
support routines.
|
||||
|
||||
The LALM fields holds the value of the last occurence of the change of
|
||||
state alarm. It is used to implement the change of state alarm, and thus
|
||||
only has meaning if COSV is MAJOR or MINOR.
|
||||
|
||||
The MSLT field is used by the C<process> record support routine to
|
||||
determine if archive and value change monitors are invoked. They are if MSLT
|
||||
is not equal to VAL.
|
||||
|
||||
=fields ORAW, MASK, LALM, MLST
|
||||
|
||||
The following fields are used to operate the binary input in simulation
|
||||
mode. See L<Fields Common to Many Record Types> for more information on
|
||||
these fields.
|
||||
|
||||
=fields SIOL, SVAL, SIML, SIMM, SIMS
|
||||
|
||||
|
||||
=cut
|
||||
|
||||
include "dbCommon.dbd"
|
||||
field(INP,DBF_INLINK) {
|
||||
prompt("Input Specification")
|
||||
promptgroup("40 - Input")
|
||||
interest(1)
|
||||
}
|
||||
field(VAL,DBF_ENUM) {
|
||||
prompt("Current Value")
|
||||
promptgroup("40 - Input")
|
||||
asl(ASL0)
|
||||
pp(TRUE)
|
||||
}
|
||||
field(ZSV,DBF_MENU) {
|
||||
prompt("Zero Error Severity")
|
||||
promptgroup("70 - Alarm")
|
||||
pp(TRUE)
|
||||
interest(1)
|
||||
menu(menuAlarmSevr)
|
||||
}
|
||||
field(OSV,DBF_MENU) {
|
||||
prompt("One Error Severity")
|
||||
promptgroup("70 - Alarm")
|
||||
pp(TRUE)
|
||||
interest(1)
|
||||
menu(menuAlarmSevr)
|
||||
}
|
||||
field(COSV,DBF_MENU) {
|
||||
prompt("Change of State Svr")
|
||||
promptgroup("70 - Alarm")
|
||||
pp(TRUE)
|
||||
interest(1)
|
||||
menu(menuAlarmSevr)
|
||||
}
|
||||
field(ZNAM,DBF_STRING) {
|
||||
prompt("Zero Name")
|
||||
promptgroup("80 - Display")
|
||||
pp(TRUE)
|
||||
interest(1)
|
||||
size(26)
|
||||
prop(YES)
|
||||
}
|
||||
field(ONAM,DBF_STRING) {
|
||||
prompt("One Name")
|
||||
promptgroup("80 - Display")
|
||||
pp(TRUE)
|
||||
interest(1)
|
||||
size(26)
|
||||
prop(YES)
|
||||
}
|
||||
field(RVAL,DBF_ULONG) {
|
||||
prompt("Raw Value")
|
||||
pp(TRUE)
|
||||
}
|
||||
field(ORAW,DBF_ULONG) {
|
||||
prompt("prev Raw Value")
|
||||
special(SPC_NOMOD)
|
||||
interest(3)
|
||||
}
|
||||
field(MASK,DBF_ULONG) {
|
||||
prompt("Hardware Mask")
|
||||
special(SPC_NOMOD)
|
||||
interest(1)
|
||||
}
|
||||
field(LALM,DBF_USHORT) {
|
||||
prompt("Last Value Alarmed")
|
||||
special(SPC_NOMOD)
|
||||
interest(3)
|
||||
}
|
||||
field(MLST,DBF_USHORT) {
|
||||
prompt("Last Value Monitored")
|
||||
special(SPC_NOMOD)
|
||||
interest(3)
|
||||
}
|
||||
field(SIOL,DBF_INLINK) {
|
||||
prompt("Simulation Input Link")
|
||||
promptgroup("90 - Simulate")
|
||||
interest(1)
|
||||
}
|
||||
field(SVAL,DBF_ULONG) {
|
||||
prompt("Simulation Value")
|
||||
}
|
||||
field(SIML,DBF_INLINK) {
|
||||
prompt("Simulation Mode Link")
|
||||
promptgroup("90 - Simulate")
|
||||
interest(1)
|
||||
}
|
||||
field(SIMM,DBF_MENU) {
|
||||
prompt("Simulation Mode")
|
||||
special(SPC_MOD)
|
||||
interest(1)
|
||||
menu(menuSimm)
|
||||
}
|
||||
field(SIMS,DBF_MENU) {
|
||||
prompt("Simulation Mode Severity")
|
||||
promptgroup("90 - Simulate")
|
||||
interest(2)
|
||||
menu(menuAlarmSevr)
|
||||
}
|
||||
field(OLDSIMM,DBF_MENU) {
|
||||
prompt("Prev. Simulation Mode")
|
||||
special(SPC_NOMOD)
|
||||
interest(4)
|
||||
menu(menuSimm)
|
||||
}
|
||||
field(SSCN,DBF_MENU) {
|
||||
prompt("Sim. Mode Scan")
|
||||
promptgroup("90 - Simulate")
|
||||
interest(1)
|
||||
menu(menuScan)
|
||||
initial("65535")
|
||||
}
|
||||
field(SDLY,DBF_DOUBLE) {
|
||||
prompt("Sim. Mode Async Delay")
|
||||
promptgroup("90 - Simulate")
|
||||
interest(2)
|
||||
initial("-1.0")
|
||||
}
|
||||
%#include "callback.h"
|
||||
field(SIMPVT,DBF_NOACCESS) {
|
||||
prompt("Sim. Mode Private")
|
||||
special(SPC_NOMOD)
|
||||
interest(4)
|
||||
extra("CALLBACK *simpvt")
|
||||
}
|
||||
|
||||
=head2 Record Support
|
||||
|
||||
=head3 Record Support Routines
|
||||
|
||||
=head2 C<init_record>
|
||||
|
||||
This routine initializes SIMM with the value of SIML if SIML type is a
|
||||
CONSTANT link or creates a channel access link if SIML type is PV_LINK.
|
||||
SVAL is likewise initialized if SIOL is a CONSTANT or PV_LINK.
|
||||
|
||||
This routine next checks to see that device support is available and a
|
||||
device support routine is defined. If neither exist, an error is issued and
|
||||
processing is terminated.
|
||||
|
||||
If device support includes C<init_record>, it is called.
|
||||
|
||||
=head2 C<process>
|
||||
|
||||
See next section.
|
||||
|
||||
=head2 C<get_value>
|
||||
|
||||
Fills in the values of struct valueDes so that they refer to VAL.
|
||||
|
||||
=head2 C<get_enum_str>
|
||||
|
||||
Retrieves ASCII string corresponding to VAL.
|
||||
|
||||
=head2 C<get_enum_strs>
|
||||
|
||||
Retrieves ASCII strings for ZNAM and ONAM.
|
||||
|
||||
=head2 C<put_enum_str>
|
||||
|
||||
Check if string matches ZNAM or ONAM, and if it does, sets VAL.
|
||||
|
||||
=head2 Record Processing
|
||||
|
||||
Routine process implements the following algorithm:
|
||||
|
||||
=over 1
|
||||
|
||||
=item 1.
|
||||
Check to see that the appropriate device support module exists. If it
|
||||
doesn't, an error message is issued and processing is terminated with
|
||||
the PACT field still set to TRUE. This ensures that processes will no
|
||||
longer be called for this record. Thus error storms will not occur.
|
||||
|
||||
=item 2.
|
||||
C<readValue> is called. See L<Input Records> for details.
|
||||
|
||||
=item 3.
|
||||
If PACT has been changed to TRUE, the device support read routine has
|
||||
started but has not completed reading a new input value. In this case, the
|
||||
processing routine merely returns, leaving PACT TRUE.
|
||||
|
||||
=item 4.
|
||||
Convert.
|
||||
|
||||
=back
|
||||
|
||||
=over 1
|
||||
|
||||
=item *
|
||||
status = read_bi
|
||||
|
||||
=item *
|
||||
PACT = TRUE
|
||||
|
||||
=item *
|
||||
TIME = tslocaltime
|
||||
|
||||
=item *
|
||||
if status is 0, then set VAL=(0,1) if RVAL is (0, not 0) and UDF = False.
|
||||
|
||||
=item *
|
||||
if status is 2, set status = 0
|
||||
|
||||
=back
|
||||
|
||||
=over 1
|
||||
|
||||
=item 5.
|
||||
Check alarms: This routine checks to see if the new VAL causes the alarm
|
||||
status and severity to change. If so, NSEV, NSTA and LALM are set. Note
|
||||
that if VAL is greater than 1, no checking is performed.
|
||||
|
||||
=item 6.
|
||||
Check if monitors should be invoked:
|
||||
|
||||
=back
|
||||
|
||||
=over 1
|
||||
|
||||
=item *
|
||||
Alarm monitors are invoked if the alarm status or severity has changed.
|
||||
|
||||
=item *
|
||||
Archive and value change monitors are invoked if MSLT is not equal to VAL.
|
||||
|
||||
=item *
|
||||
Monitors for RVAL are checked whenever other monitors are invoked.
|
||||
|
||||
=item *
|
||||
NSEV and NSTA are reset to 0.
|
||||
|
||||
=back
|
||||
|
||||
=over 1
|
||||
|
||||
=item 7.
|
||||
Scan forward link if necessary, set PACT FALSE, and return.
|
||||
|
||||
=back
|
||||
|
||||
=head2 Device Support
|
||||
|
||||
=head3 Fields of Interest to Device Support
|
||||
|
||||
Each binary input record must have an associated set of device support
|
||||
routines. The primary resposibility of the device support routines is to
|
||||
obtain a new raw input value whenever C<read_bi> is called. The device
|
||||
support routines are primarily interested in the following fields:
|
||||
|
||||
=fields PACT, DPVT, UDF, NSEV, NSTA, VAL, INP, RVAL, MASK
|
||||
|
||||
=head3 Device Support routines
|
||||
|
||||
Device support consists of the following routines:
|
||||
|
||||
=head2 C<report(FILE fp, paddr)>
|
||||
|
||||
Not currently used.
|
||||
|
||||
=head2 C<init()>
|
||||
|
||||
This routine is called once during IOC initialization.
|
||||
|
||||
=head2 C<init_record(precord)>
|
||||
|
||||
This routine is optional. If provided, it is called by the record support
|
||||
C<init_record> routine.
|
||||
|
||||
=head2 C<get_ioint_info(int cmd, struct dbCommon *precord, IOSCANPVT *ppvt)>
|
||||
|
||||
This routine is called by the C<ioEventScan> system each time the record is
|
||||
added or deleted from an I/O event scan list. C<cmd> has the value (0,1) if
|
||||
the record is being (added to, deleted from) and I/O event list. It must be
|
||||
provided for any device type that can use the ioEvent scanner.
|
||||
|
||||
=head2 C<read_bi(precord)>
|
||||
|
||||
This routine must provide a new input value. It returns the following
|
||||
values:
|
||||
|
||||
=over
|
||||
|
||||
=item 0:
|
||||
Success. A new raw value is placed in RVAL. The record support module
|
||||
forces VAL to be (0,1) if RVAL is (0, not 0).
|
||||
|
||||
=item 2:
|
||||
Success, but don't modify VAL.
|
||||
|
||||
=item Other:
|
||||
Error.
|
||||
|
||||
=back
|
||||
|
||||
=head3 Device Support for Soft Records
|
||||
|
||||
Two soft device support modules, Soft Channel and Raw Soft Channel, are
|
||||
provided for input records not related to actual hardware devices. The INP
|
||||
link type must be either CONSTANT, DB_LINK, or CA_LINK.
|
||||
|
||||
=head3 Soft Channel
|
||||
|
||||
C<read_bi> always returns a value of 2, which means that no conversion is
|
||||
performed.
|
||||
|
||||
If the INP link type is CONSTANT, then the constant value is stored in VAL
|
||||
by C<init_record>, and the UDF is set to FALSE. VAL can be changed via
|
||||
C<dbPut> requests. If the INP link type is PV_LINK, the C<dbCaAddInlink> is
|
||||
called by C<init_record>.
|
||||
|
||||
C<read_bi> calls C<recGbleGetLinkValue> to read the current value of VAL.
|
||||
See L<Soft Input> for details.
|
||||
|
||||
If the return status of C<recGblGetLinkValue> is zero, then C<read_bi> sets
|
||||
UDF to FALSE. The status of C<recGblGetLinkValue> is returned.
|
||||
|
||||
=head3 Raw Soft Channel
|
||||
|
||||
This module is like the previous except that values are read into RVAL.
|
||||
|
||||
C<read_bi> returns a value of 0. Thus the record processing routine will
|
||||
force VAL to be 0 or 1.
|
||||
|
||||
=cut
|
||||
}
|
||||
@@ -348,9 +348,9 @@ static long get_enum_strs(const DBADDR *paddr,struct dbr_enumStrs *pes)
|
||||
/*SETTING no_str=0 breaks channel access clients*/
|
||||
pes->no_str = 2;
|
||||
memset(pes->strs,'\0',sizeof(pes->strs));
|
||||
strncpy(pes->strs[0],prec->znam,sizeof(prec->znam));
|
||||
strncpy(pes->strs[0],prec->znam,sizeof(pes->strs[0]));
|
||||
if(*prec->znam!=0) pes->no_str=1;
|
||||
strncpy(pes->strs[1],prec->onam,sizeof(prec->onam));
|
||||
strncpy(pes->strs[1],prec->onam,sizeof(pes->strs[1]));
|
||||
if(*prec->onam!=0) pes->no_str=2;
|
||||
return(0);
|
||||
}
|
||||
|
||||
@@ -1,182 +0,0 @@
|
||||
#*************************************************************************
|
||||
# Copyright (c) 2002 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.
|
||||
# EPICS BASE is distributed subject to a Software License Agreement found
|
||||
# in file LICENSE that is included with this distribution.
|
||||
#*************************************************************************
|
||||
recordtype(bo) {
|
||||
include "dbCommon.dbd"
|
||||
field(VAL,DBF_ENUM) {
|
||||
prompt("Current Value")
|
||||
promptgroup("50 - Output")
|
||||
asl(ASL0)
|
||||
pp(TRUE)
|
||||
}
|
||||
field(OMSL,DBF_MENU) {
|
||||
prompt("Output Mode Select")
|
||||
promptgroup("50 - Output")
|
||||
interest(1)
|
||||
menu(menuOmsl)
|
||||
}
|
||||
field(DOL,DBF_INLINK) {
|
||||
prompt("Desired Output Loc")
|
||||
promptgroup("40 - Input")
|
||||
interest(1)
|
||||
}
|
||||
field(OUT,DBF_OUTLINK) {
|
||||
prompt("Output Specification")
|
||||
promptgroup("50 - Output")
|
||||
interest(1)
|
||||
}
|
||||
field(HIGH,DBF_DOUBLE) {
|
||||
prompt("Seconds to Hold High")
|
||||
promptgroup("30 - Action")
|
||||
interest(1)
|
||||
}
|
||||
field(ZNAM,DBF_STRING) {
|
||||
prompt("Zero Name")
|
||||
promptgroup("80 - Display")
|
||||
pp(TRUE)
|
||||
interest(1)
|
||||
size(26)
|
||||
prop(YES)
|
||||
}
|
||||
field(ONAM,DBF_STRING) {
|
||||
prompt("One Name")
|
||||
promptgroup("80 - Display")
|
||||
pp(TRUE)
|
||||
interest(1)
|
||||
size(26)
|
||||
prop(YES)
|
||||
}
|
||||
field(RVAL,DBF_ULONG) {
|
||||
prompt("Raw Value")
|
||||
pp(TRUE)
|
||||
}
|
||||
field(ORAW,DBF_ULONG) {
|
||||
prompt("prev Raw Value")
|
||||
special(SPC_NOMOD)
|
||||
interest(3)
|
||||
}
|
||||
field(MASK,DBF_ULONG) {
|
||||
prompt("Hardware Mask")
|
||||
special(SPC_NOMOD)
|
||||
interest(1)
|
||||
}
|
||||
field(RPVT,DBF_NOACCESS) {
|
||||
prompt("Record Private")
|
||||
special(SPC_NOMOD)
|
||||
interest(4)
|
||||
extra("void * rpvt")
|
||||
}
|
||||
field(WDPT,DBF_NOACCESS) {
|
||||
prompt("Watch Dog Timer ID")
|
||||
special(SPC_NOMOD)
|
||||
interest(4)
|
||||
extra("void * wdpt")
|
||||
}
|
||||
field(ZSV,DBF_MENU) {
|
||||
prompt("Zero Error Severity")
|
||||
promptgroup("70 - Alarm")
|
||||
pp(TRUE)
|
||||
interest(1)
|
||||
menu(menuAlarmSevr)
|
||||
}
|
||||
field(OSV,DBF_MENU) {
|
||||
prompt("One Error Severity")
|
||||
promptgroup("70 - Alarm")
|
||||
pp(TRUE)
|
||||
interest(1)
|
||||
menu(menuAlarmSevr)
|
||||
}
|
||||
field(COSV,DBF_MENU) {
|
||||
prompt("Change of State Sevr")
|
||||
promptgroup("70 - Alarm")
|
||||
pp(TRUE)
|
||||
interest(1)
|
||||
menu(menuAlarmSevr)
|
||||
}
|
||||
field(RBV,DBF_ULONG) {
|
||||
prompt("Readback Value")
|
||||
special(SPC_NOMOD)
|
||||
}
|
||||
field(ORBV,DBF_ULONG) {
|
||||
prompt("Prev Readback Value")
|
||||
special(SPC_NOMOD)
|
||||
interest(3)
|
||||
}
|
||||
field(MLST,DBF_USHORT) {
|
||||
prompt("Last Value Monitored")
|
||||
special(SPC_NOMOD)
|
||||
interest(3)
|
||||
}
|
||||
field(LALM,DBF_USHORT) {
|
||||
prompt("Last Value Alarmed")
|
||||
special(SPC_NOMOD)
|
||||
interest(3)
|
||||
}
|
||||
field(SIOL,DBF_OUTLINK) {
|
||||
prompt("Simulation Output Link")
|
||||
promptgroup("90 - Simulate")
|
||||
interest(1)
|
||||
}
|
||||
field(SIML,DBF_INLINK) {
|
||||
prompt("Simulation Mode Link")
|
||||
promptgroup("90 - Simulate")
|
||||
interest(1)
|
||||
}
|
||||
field(SIMM,DBF_MENU) {
|
||||
prompt("Simulation Mode")
|
||||
special(SPC_MOD)
|
||||
interest(1)
|
||||
menu(menuYesNo)
|
||||
}
|
||||
field(SIMS,DBF_MENU) {
|
||||
prompt("Simulation Mode Severity")
|
||||
promptgroup("90 - Simulate")
|
||||
interest(2)
|
||||
menu(menuAlarmSevr)
|
||||
}
|
||||
field(OLDSIMM,DBF_MENU) {
|
||||
prompt("Prev. Simulation Mode")
|
||||
special(SPC_NOMOD)
|
||||
interest(4)
|
||||
menu(menuSimm)
|
||||
}
|
||||
field(SSCN,DBF_MENU) {
|
||||
prompt("Sim. Mode Scan")
|
||||
promptgroup("90 - Simulate")
|
||||
interest(1)
|
||||
menu(menuScan)
|
||||
initial("65535")
|
||||
}
|
||||
field(SDLY,DBF_DOUBLE) {
|
||||
prompt("Sim. Mode Async Delay")
|
||||
promptgroup("90 - Simulate")
|
||||
interest(2)
|
||||
initial("-1.0")
|
||||
}
|
||||
%#include "callback.h"
|
||||
field(SIMPVT,DBF_NOACCESS) {
|
||||
prompt("Sim. Mode Private")
|
||||
special(SPC_NOMOD)
|
||||
interest(4)
|
||||
extra("CALLBACK *simpvt")
|
||||
}
|
||||
field(IVOA,DBF_MENU) {
|
||||
prompt("INVALID outpt action")
|
||||
promptgroup("50 - Output")
|
||||
interest(2)
|
||||
menu(menuIvoa)
|
||||
}
|
||||
field(IVOV,DBF_USHORT) {
|
||||
prompt("INVALID output value")
|
||||
promptgroup("50 - Output")
|
||||
interest(2)
|
||||
}
|
||||
}
|
||||
|
||||
variable(boHIGHprecision, int)
|
||||
variable(boHIGHlimit, double)
|
||||
611
modules/database/src/std/rec/boRecord.dbd.pod
Normal file
611
modules/database/src/std/rec/boRecord.dbd.pod
Normal file
@@ -0,0 +1,611 @@
|
||||
#*************************************************************************
|
||||
# Copyright (c) 2002 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.
|
||||
# EPICS BASE is distributed subject to a Software License Agreement found
|
||||
# in file LICENSE that is included with this distribution.
|
||||
#*************************************************************************
|
||||
|
||||
=title Binary Output Record (bo)
|
||||
|
||||
The normal use for this record type is to store a simple bit (0 or 1) value
|
||||
to be sent to a Digital Output module. It can also be used to write binary
|
||||
values into other records via database or channel access links. This record
|
||||
can implement both latched and momentary binary outputs depending on how
|
||||
the HIGH field is configured.
|
||||
|
||||
=head2 Parameter Fields
|
||||
|
||||
The binary output's fields fall into the following categories:
|
||||
|
||||
=over 1
|
||||
|
||||
=item *
|
||||
scan parameters
|
||||
|
||||
=item *
|
||||
convert and write parameters
|
||||
|
||||
=item *
|
||||
operator display parameters
|
||||
|
||||
=item *
|
||||
alarm parameters
|
||||
|
||||
=item *
|
||||
run-time parameters
|
||||
|
||||
=back
|
||||
|
||||
=recordtype bo
|
||||
|
||||
=cut
|
||||
|
||||
recordtype(bo) {
|
||||
|
||||
=head3 Scan Parameters
|
||||
|
||||
The binary output record has the standard fields for specifying under what
|
||||
circumstances the record will be processed. The fields are listed in
|
||||
L<Scan Fields>. In addition, L<Scanning Specification> explains how these
|
||||
fields are used. Note that I/O event scanning is only supported for those card
|
||||
types that interrupt.
|
||||
|
||||
=fields SCAN
|
||||
|
||||
=head3 Desired Output Parameters
|
||||
|
||||
The binary output record must specify where its desired output originates.
|
||||
The desired output needs to be in engineering units.
|
||||
|
||||
The first field that determines where the desired output originates is the
|
||||
output mode select (OMSL) field, which can have two possible values:
|
||||
C<losed_loop> or C<supervisory>. If C<supervisory> is specified, the value
|
||||
in the VAL field can be set externally via dbPuts at run-time. If
|
||||
C<closed_loop> is specified, the VAL field's value is obtained from the
|
||||
address specified in the desired output location (DOL) field which can be a
|
||||
database link, a channel access link, or a constant. To achieve continuous
|
||||
control, a database link to a control algorithm record should be entered in
|
||||
the DOL field.
|
||||
|
||||
L<Address Specification> presents more information on database addresses
|
||||
and links. L<Scanning Specification> explaines the effect of database
|
||||
linkage on scanning.
|
||||
|
||||
=fields DOL, OMSL
|
||||
|
||||
=head3 Convert and Write Parameters
|
||||
|
||||
These parameters are used to determine where the binary output writes to
|
||||
and how to convert the engineering units to a raw signal. After VAL is set
|
||||
and forced to be either 1 or 0, as the result of either a dbPut or a new
|
||||
value being retrieved from the link in the DOL field, then what happens
|
||||
next depends on which device support routine is used and how the HIGH field
|
||||
is configured.
|
||||
|
||||
If the C<Soft Channel> device support routine is specified, then the device
|
||||
support routine writes the VAL field's value to the address specified in
|
||||
the OUT field. Otherwise, RVAL is the value written by the device support
|
||||
routines after being converted.
|
||||
|
||||
If VAL is equal to 0, then the record processing routine sets RVAL equal to
|
||||
zero. When VAL is not equal to 0, then RVAL is set equal to the value
|
||||
contained in the MASK field. (MASK is set by the device support routines
|
||||
and is of no concern to the user.) Also, when VAL is not 0 and after RVAL is
|
||||
set equal to MASK, the record processing routine checks to see if the HIGH
|
||||
field is greater than 0. If it is, then the routine will process the record
|
||||
again with VAL set to 0 after the number of seconds specified by HIGH.
|
||||
Thus, HIGH implements a momentary output which changes the state of the
|
||||
device back to 0 after I<N> number of seconds.
|
||||
|
||||
=fields DTYP, OUT, VAL, RVAL, HIGH, ZNAM, ONAM
|
||||
|
||||
=head3 Conversion Parameters
|
||||
|
||||
The ZNAM field has the string that corresponds to the 0 state, and the ONAM
|
||||
field holds the string that corresponds to the 1 state. These fields, other
|
||||
than being used to tell the operator what each state represents, are used
|
||||
to perform conversions if the value fetched by DOL is a string. If it is,
|
||||
VAL is set to the state which corresponds to that string. For instance, if the
|
||||
value fetched is the string "Off" and the ZNAM string is "Off," then VAL is
|
||||
set to 0.
|
||||
|
||||
After VAL is set, if VAL is equal to 0, then the record processing routine
|
||||
sets RVAL equal to zero. When VAL is not equal to 0, then RVAL is set equal
|
||||
to the value contained in the MASK field. (Mask is set by the device
|
||||
support routines and is of no concern to the user.) Also when VAL is equal
|
||||
to 1 and after RVAL is set equal to MASK, the record processing routine checks
|
||||
to see if the HIGH field is greater than 0. If it is, then the routine
|
||||
processes the record again with VAL=0 after the number of seconds specified
|
||||
by HIGH. Thus, HIGH implements a latched output which changes the state of
|
||||
the device or link to 1, then changes it back to 0 after I<N> number of seconds.
|
||||
|
||||
=fields ZNAM, ONAM, HIGH
|
||||
|
||||
=head3 Output Specification
|
||||
|
||||
The OUT field specifies where the binary output record writes its output.
|
||||
It must specify the address of an I/O card if the record sends its output
|
||||
to hardware, and the DTYP field must contain the corresponding device
|
||||
support module. Be aware that the address format differs according to the
|
||||
I/O bus used. See L<Address Specification> for information on the format of
|
||||
hardware addresses. You can see a list of device support modules currently
|
||||
supported at the user's local site by using the C<dbst> utility in R3.13.
|
||||
|
||||
Otherwise, if the record is configured to use the soft device support
|
||||
modules, then it can be either a database link, a channel access link, or a
|
||||
constant. Be aware that nothing will be written when OUT is a constant. See
|
||||
L<Address Specification> for information on the format of the database and
|
||||
channel access addresses. Also, see L<Device Support For Soft Records> in
|
||||
this chapter for more on output to other records.
|
||||
|
||||
=head3 Operator Display Parameters
|
||||
|
||||
These parameters are used to present meaningful data to the operator, The
|
||||
C<get_enum_str> record support routine can retrieve the state string
|
||||
corresponding to the VAL's state. So, if the value is 1, C<get_enum_str>
|
||||
will return the string in the ONAM field: and if 0, C<get_enum_str> will
|
||||
return the ZNAM string.
|
||||
|
||||
See L<Fields Common to All Record Types> for more on the record name (NAME)
|
||||
and description (DESC) fields.
|
||||
|
||||
=fields ZNAM, ONAM, NAME, DESC
|
||||
|
||||
=head3 Alarm Parameters
|
||||
|
||||
These parameters are used to determine the binary output's alarm condition
|
||||
and to determine the severity of that condition. The possible alarm
|
||||
conditions for binary outputs are the SCAN, READ, INVALID and state alarms.
|
||||
The user can configure the state alarm conditions using these fields.
|
||||
|
||||
The possible values for these fields are C<NO_ALARM>, C<MINOR>, and
|
||||
C<MAJOR>. The ZSV holds the severity for the zero state; OSV for the one
|
||||
state. COSV is used to cause an alarm whenever the state changes between
|
||||
states (0-1, 1-0) and its severity is configured as MINOR or MAJOR.
|
||||
|
||||
See L<Invalid Alarm Output Action> for more information on the IVOA and
|
||||
IVOV fields. L<Alarm Fields> lists other fields related to alarms that are
|
||||
common to all record types.
|
||||
|
||||
=fields ZSV, OSV, COSV, IVOA, IVOV
|
||||
|
||||
=head3 Run-Time and Simulation Mode Parameters
|
||||
|
||||
These parameters are used by the run-time code for processiong the binary
|
||||
output. They are not configurable using a configuration tool. They
|
||||
represent the current state of the binary output.
|
||||
|
||||
ORAW is used to determine if monitors should be triggered for RVAL at the
|
||||
same time they are triggered for VAL.
|
||||
|
||||
MASK is given a value by the device support routines and should not concern
|
||||
the user.
|
||||
|
||||
The RBV field is also set by device support. It is the actual read back
|
||||
value obtained from the hardware itself or from the associated device
|
||||
driver.
|
||||
|
||||
The ORBV field is used to decide if monitors should be triggered
|
||||
for RBV at the same time monitors are triggered for changes in VAL.
|
||||
|
||||
The LALM field holds the value of the last occurrence of the change of
|
||||
state alarm. It is used to implement the change of state alarm, and thus
|
||||
only has meaning if COSV is MINOR or MAJOR.
|
||||
|
||||
The MLST is used by the C<process> record support routine to determine if
|
||||
archive and value change monitors are invoked. They are if MLST is not
|
||||
equal to VAL.
|
||||
|
||||
The WPDT field is a private field for honoring seconds to hold HIGH.
|
||||
|
||||
=fields ORAW, MASK, RBV, ORBV, LALM, MLST, RPVT, WDPT
|
||||
|
||||
The following fields are used to operate the binary output in the
|
||||
simulation mode. See L<Fields Common to Many Record Types> for more
|
||||
information on these fields.
|
||||
|
||||
=fields SIOL, SIML, SIMM, SIMS
|
||||
|
||||
=cut
|
||||
|
||||
include "dbCommon.dbd"
|
||||
field(VAL,DBF_ENUM) {
|
||||
prompt("Current Value")
|
||||
promptgroup("50 - Output")
|
||||
asl(ASL0)
|
||||
pp(TRUE)
|
||||
}
|
||||
field(OMSL,DBF_MENU) {
|
||||
prompt("Output Mode Select")
|
||||
promptgroup("50 - Output")
|
||||
interest(1)
|
||||
menu(menuOmsl)
|
||||
}
|
||||
field(DOL,DBF_INLINK) {
|
||||
prompt("Desired Output Loc")
|
||||
promptgroup("40 - Input")
|
||||
interest(1)
|
||||
}
|
||||
field(OUT,DBF_OUTLINK) {
|
||||
prompt("Output Specification")
|
||||
promptgroup("50 - Output")
|
||||
interest(1)
|
||||
}
|
||||
field(HIGH,DBF_DOUBLE) {
|
||||
prompt("Seconds to Hold High")
|
||||
promptgroup("30 - Action")
|
||||
interest(1)
|
||||
}
|
||||
field(ZNAM,DBF_STRING) {
|
||||
prompt("Zero Name")
|
||||
promptgroup("80 - Display")
|
||||
pp(TRUE)
|
||||
interest(1)
|
||||
size(26)
|
||||
prop(YES)
|
||||
}
|
||||
field(ONAM,DBF_STRING) {
|
||||
prompt("One Name")
|
||||
promptgroup("80 - Display")
|
||||
pp(TRUE)
|
||||
interest(1)
|
||||
size(26)
|
||||
prop(YES)
|
||||
}
|
||||
field(RVAL,DBF_ULONG) {
|
||||
prompt("Raw Value")
|
||||
pp(TRUE)
|
||||
}
|
||||
field(ORAW,DBF_ULONG) {
|
||||
prompt("prev Raw Value")
|
||||
special(SPC_NOMOD)
|
||||
interest(3)
|
||||
}
|
||||
field(MASK,DBF_ULONG) {
|
||||
prompt("Hardware Mask")
|
||||
special(SPC_NOMOD)
|
||||
interest(1)
|
||||
}
|
||||
field(RPVT,DBF_NOACCESS) {
|
||||
prompt("Record Private")
|
||||
special(SPC_NOMOD)
|
||||
interest(4)
|
||||
extra("void * rpvt")
|
||||
}
|
||||
field(WDPT,DBF_NOACCESS) {
|
||||
prompt("Watch Dog Timer ID")
|
||||
special(SPC_NOMOD)
|
||||
interest(4)
|
||||
extra("void * wdpt")
|
||||
}
|
||||
field(ZSV,DBF_MENU) {
|
||||
prompt("Zero Error Severity")
|
||||
promptgroup("70 - Alarm")
|
||||
pp(TRUE)
|
||||
interest(1)
|
||||
menu(menuAlarmSevr)
|
||||
}
|
||||
field(OSV,DBF_MENU) {
|
||||
prompt("One Error Severity")
|
||||
promptgroup("70 - Alarm")
|
||||
pp(TRUE)
|
||||
interest(1)
|
||||
menu(menuAlarmSevr)
|
||||
}
|
||||
field(COSV,DBF_MENU) {
|
||||
prompt("Change of State Sevr")
|
||||
promptgroup("70 - Alarm")
|
||||
pp(TRUE)
|
||||
interest(1)
|
||||
menu(menuAlarmSevr)
|
||||
}
|
||||
field(RBV,DBF_ULONG) {
|
||||
prompt("Readback Value")
|
||||
special(SPC_NOMOD)
|
||||
}
|
||||
field(ORBV,DBF_ULONG) {
|
||||
prompt("Prev Readback Value")
|
||||
special(SPC_NOMOD)
|
||||
interest(3)
|
||||
}
|
||||
field(MLST,DBF_USHORT) {
|
||||
prompt("Last Value Monitored")
|
||||
special(SPC_NOMOD)
|
||||
interest(3)
|
||||
}
|
||||
field(LALM,DBF_USHORT) {
|
||||
prompt("Last Value Alarmed")
|
||||
special(SPC_NOMOD)
|
||||
interest(3)
|
||||
}
|
||||
field(SIOL,DBF_OUTLINK) {
|
||||
prompt("Simulation Output Link")
|
||||
promptgroup("90 - Simulate")
|
||||
interest(1)
|
||||
}
|
||||
field(SIML,DBF_INLINK) {
|
||||
prompt("Simulation Mode Link")
|
||||
promptgroup("90 - Simulate")
|
||||
interest(1)
|
||||
}
|
||||
field(SIMM,DBF_MENU) {
|
||||
prompt("Simulation Mode")
|
||||
special(SPC_MOD)
|
||||
interest(1)
|
||||
menu(menuYesNo)
|
||||
}
|
||||
field(SIMS,DBF_MENU) {
|
||||
prompt("Simulation Mode Severity")
|
||||
promptgroup("90 - Simulate")
|
||||
interest(2)
|
||||
menu(menuAlarmSevr)
|
||||
}
|
||||
field(OLDSIMM,DBF_MENU) {
|
||||
prompt("Prev. Simulation Mode")
|
||||
special(SPC_NOMOD)
|
||||
interest(4)
|
||||
menu(menuSimm)
|
||||
}
|
||||
field(SSCN,DBF_MENU) {
|
||||
prompt("Sim. Mode Scan")
|
||||
promptgroup("90 - Simulate")
|
||||
interest(1)
|
||||
menu(menuScan)
|
||||
initial("65535")
|
||||
}
|
||||
field(SDLY,DBF_DOUBLE) {
|
||||
prompt("Sim. Mode Async Delay")
|
||||
promptgroup("90 - Simulate")
|
||||
interest(2)
|
||||
initial("-1.0")
|
||||
}
|
||||
%#include "callback.h"
|
||||
field(SIMPVT,DBF_NOACCESS) {
|
||||
prompt("Sim. Mode Private")
|
||||
special(SPC_NOMOD)
|
||||
interest(4)
|
||||
extra("CALLBACK *simpvt")
|
||||
}
|
||||
field(IVOA,DBF_MENU) {
|
||||
prompt("INVALID outpt action")
|
||||
promptgroup("50 - Output")
|
||||
interest(2)
|
||||
menu(menuIvoa)
|
||||
}
|
||||
field(IVOV,DBF_USHORT) {
|
||||
prompt("INVALID output value")
|
||||
promptgroup("50 - Output")
|
||||
interest(2)
|
||||
}
|
||||
|
||||
|
||||
=head2 Record Support
|
||||
|
||||
=head3 Record Support Routines
|
||||
|
||||
=head2 C<init_record>
|
||||
|
||||
This routine initializes SIMM if SIML is a constant or creates a channel
|
||||
access link if SIML is PV_LINK. If SIOL is a PV_LINK a channel access link
|
||||
is created.
|
||||
|
||||
This routine next checks to see that device support is available. The
|
||||
routine next checks to see if the device support write routine is defined.
|
||||
|
||||
If either device support or the device support write routine does not
|
||||
exist, and error message is issued and processing is terminated.
|
||||
|
||||
If DOL is a constant, then VAL is initialized to 1 if its value is nonzero
|
||||
or initialzed to 0 if DOL is zero, and UDF is set to FALSE.
|
||||
|
||||
If device support includes C<init_record>, it is called. VAL is set using
|
||||
RVAL, and UDF is set to FALSE.
|
||||
|
||||
=head2 C<process>
|
||||
|
||||
See next section.
|
||||
|
||||
=head2 C<get_value>
|
||||
|
||||
Fills in the values of struct valueDes so that they refer to VAL.
|
||||
|
||||
=head2 C<get_enum_str>
|
||||
|
||||
Retrieves ASCII string corresponding to VAL.
|
||||
|
||||
=head2 C<get_enum_strs>
|
||||
|
||||
Retrieves ASCII strings for ZNAM and ONAM.
|
||||
|
||||
=head2 C<put_enum_str>
|
||||
|
||||
Checks if string matches ZNAM or ONAM, and if it does, sets VAL.
|
||||
|
||||
=head2 Record Processing
|
||||
|
||||
Routine process implements the following algorithm:
|
||||
|
||||
=over 1
|
||||
|
||||
=item 1.
|
||||
Check to see that the appropriate device support module exists. If it
|
||||
doesn't, an error message is issued and processing is terminated with
|
||||
the PACT field still set to TRUE. This ensures that processes will no
|
||||
longer be called for this record. Thus error storms will not occur.
|
||||
|
||||
=item 2.
|
||||
If PACT is FALSE
|
||||
|
||||
=back
|
||||
|
||||
=over
|
||||
|
||||
=item *
|
||||
If DOL is DB_LINK and OMSL is CLOSED_LOOP
|
||||
|
||||
=over
|
||||
|
||||
=item *
|
||||
get values from DOL
|
||||
|
||||
=item *
|
||||
check for link alarm
|
||||
|
||||
=item *
|
||||
force VAL to be 0 or 1
|
||||
|
||||
=item *
|
||||
if MASK is defined
|
||||
|
||||
=over
|
||||
|
||||
=item *
|
||||
if VAL is 0 set RVAL = 0
|
||||
|
||||
=back
|
||||
|
||||
=item *
|
||||
else set RVAL = MASK
|
||||
|
||||
=back
|
||||
|
||||
=back
|
||||
|
||||
=over
|
||||
|
||||
=item 3.
|
||||
Check alarms: This routine checks to see if the new VAL causes the alarm
|
||||
status and severity to change. If so, NSEV, NSTA, and LALM are set.
|
||||
|
||||
=item 4.
|
||||
Check severity and write the new value. See L<Invalid Alarm Output Action>
|
||||
for more information on how INVALID alarms affect output.
|
||||
|
||||
=item 5.
|
||||
If PACT has been changed to TRUE, the device support write output routine
|
||||
has started but has not completed writing the new value. in this case, the
|
||||
processing routine merely returns, leaving PACT TRUE.
|
||||
|
||||
=item 6.
|
||||
Check WAIT. If VAL is 1 and WAIT is greater than 0, process again with a
|
||||
VAL=0 after WAIT seconds.
|
||||
|
||||
=item 7.
|
||||
Check to see if monitors should be invoked.
|
||||
|
||||
=back
|
||||
|
||||
=over 1
|
||||
|
||||
=item *
|
||||
Alarm monitors are invoked if the alarm status or severity has changed.
|
||||
|
||||
=item *
|
||||
Archive and value change monitors are invoked if MLST is not equal to VAL.
|
||||
|
||||
=item *
|
||||
Monitors for RVAL and for RBV are checked whenever other monitors are
|
||||
invoked.
|
||||
|
||||
=item *
|
||||
NSEV and NSTA are reset to 0.
|
||||
|
||||
=back
|
||||
|
||||
=over
|
||||
|
||||
=item 8
|
||||
Scan forward link if necessary, set PACT FALSE, and return
|
||||
|
||||
=back
|
||||
|
||||
=head2 Device support
|
||||
|
||||
=head3 Fields Of Interest To Device Support
|
||||
|
||||
Each binary output record must have an associated set of device support
|
||||
routines. The primary responsibility of the device support routines is to
|
||||
write a new value whenever C<write_bo> is called. The device support routines
|
||||
are primarily interested in the following fields:
|
||||
|
||||
=fields PACT, DPVT, NSEV, NSTA, VAL, OUT, RVAL, MASK, RBV
|
||||
|
||||
=head3 Decive Support Routines
|
||||
|
||||
Device support consists of the following routines:
|
||||
|
||||
=head2 C<report(FILE fp, paddr)>
|
||||
|
||||
Not currently used.
|
||||
|
||||
=head2 C<init()>
|
||||
|
||||
This routine is called once during IOC initialization.
|
||||
|
||||
=head2 C<init_record(precord)>
|
||||
|
||||
This routine is optional. If provided, it is called by record support
|
||||
C<init_record> routine. It should determine MASK if it is needed.
|
||||
|
||||
=over
|
||||
|
||||
=item *
|
||||
0: Success. RVAL modified (VAL will be set accordingly)
|
||||
|
||||
=item *
|
||||
2: Success. VAL modified
|
||||
|
||||
=item *
|
||||
other: Error
|
||||
|
||||
=back
|
||||
|
||||
=head2 C<get_ioint_info(int cmd, struct dbCommon *precord, IOSCANPVT *ppvt)>
|
||||
|
||||
This routine is called by the ioEventScan system each time the record is
|
||||
added or deleted from an I/O event scan list. C<cmd> has the value (0,1) if
|
||||
the record is being (added to, deleted from) an I/O event list. It must be
|
||||
provided for any device type that can use the ioEvent scanner.
|
||||
|
||||
=head2 C<write_bo(precord)>
|
||||
|
||||
This routine must output a new value. It returns the following values:
|
||||
|
||||
=over
|
||||
|
||||
=item *
|
||||
0: Success
|
||||
|
||||
=item *
|
||||
other: Error.
|
||||
|
||||
=back
|
||||
|
||||
=head2 Device Support For Soft Records
|
||||
|
||||
Two soft device support modules C<Soft Channel> and C<Raw Soft Channel> are
|
||||
provided for output records not related to actual hardware devices. The OUT
|
||||
link type must be either CONSTANT, DB_LINK, or CA_LINK.
|
||||
|
||||
=head3 Soft Channel
|
||||
|
||||
This module writes the current value of VAL.
|
||||
|
||||
If the OUT link type is PV_LINK, then C<dbCaAddInlink> is called by
|
||||
C<init_record>. C<init_record> always returns a value of 2, which means
|
||||
that no conversion will ever be attempted. C<write_bo> calls
|
||||
C<recGblPutLinkValue> to write the current value of VAL. See L<Soft Output>
|
||||
for details.
|
||||
|
||||
=head3 Raw Soft Channel
|
||||
|
||||
This module is like the previous except that it writes the current value of
|
||||
RVAL
|
||||
|
||||
=cut
|
||||
}
|
||||
|
||||
variable(boHIGHprecision, int)
|
||||
variable(boHIGHlimit, double)
|
||||
@@ -1,324 +0,0 @@
|
||||
#*************************************************************************
|
||||
# Copyright (c) 2007 UChicago Argonne LLC, as Operator of Argonne
|
||||
# National Laboratory.
|
||||
# Copyright (c) 2002 The Regents of the University of California, as
|
||||
# Operator of Los Alamos National Laboratory.
|
||||
# EPICS BASE is distributed subject to a Software License Agreement found
|
||||
# in file LICENSE that is included with this distribution.
|
||||
#*************************************************************************
|
||||
recordtype(calc) {
|
||||
include "dbCommon.dbd"
|
||||
field(VAL,DBF_DOUBLE) {
|
||||
prompt("Result")
|
||||
promptgroup("50 - Output")
|
||||
asl(ASL0)
|
||||
}
|
||||
field(CALC,DBF_STRING) {
|
||||
prompt("Calculation")
|
||||
promptgroup("30 - Action")
|
||||
special(SPC_CALC)
|
||||
pp(TRUE)
|
||||
size(80)
|
||||
initial("0")
|
||||
}
|
||||
field(INPA,DBF_INLINK) {
|
||||
prompt("Input A")
|
||||
promptgroup("41 - Input A-F")
|
||||
interest(1)
|
||||
}
|
||||
field(INPB,DBF_INLINK) {
|
||||
prompt("Input B")
|
||||
promptgroup("41 - Input A-F")
|
||||
interest(1)
|
||||
}
|
||||
field(INPC,DBF_INLINK) {
|
||||
prompt("Input C")
|
||||
promptgroup("41 - Input A-F")
|
||||
interest(1)
|
||||
}
|
||||
field(INPD,DBF_INLINK) {
|
||||
prompt("Input D")
|
||||
promptgroup("41 - Input A-F")
|
||||
interest(1)
|
||||
}
|
||||
field(INPE,DBF_INLINK) {
|
||||
prompt("Input E")
|
||||
promptgroup("41 - Input A-F")
|
||||
interest(1)
|
||||
}
|
||||
field(INPF,DBF_INLINK) {
|
||||
prompt("Input F")
|
||||
promptgroup("41 - Input A-F")
|
||||
interest(1)
|
||||
}
|
||||
field(INPG,DBF_INLINK) {
|
||||
prompt("Input G")
|
||||
promptgroup("42 - Input G-L")
|
||||
interest(1)
|
||||
}
|
||||
field(INPH,DBF_INLINK) {
|
||||
prompt("Input H")
|
||||
promptgroup("42 - Input G-L")
|
||||
interest(1)
|
||||
}
|
||||
field(INPI,DBF_INLINK) {
|
||||
prompt("Input I")
|
||||
promptgroup("42 - Input G-L")
|
||||
interest(1)
|
||||
}
|
||||
field(INPJ,DBF_INLINK) {
|
||||
prompt("Input J")
|
||||
promptgroup("42 - Input G-L")
|
||||
interest(1)
|
||||
}
|
||||
field(INPK,DBF_INLINK) {
|
||||
prompt("Input K")
|
||||
promptgroup("42 - Input G-L")
|
||||
interest(1)
|
||||
}
|
||||
field(INPL,DBF_INLINK) {
|
||||
prompt("Input L")
|
||||
promptgroup("42 - Input G-L")
|
||||
interest(1)
|
||||
}
|
||||
field(EGU,DBF_STRING) {
|
||||
prompt("Engineering Units")
|
||||
promptgroup("80 - Display")
|
||||
interest(1)
|
||||
size(16)
|
||||
prop(YES)
|
||||
}
|
||||
field(PREC,DBF_SHORT) {
|
||||
prompt("Display Precision")
|
||||
promptgroup("80 - Display")
|
||||
interest(1)
|
||||
prop(YES)
|
||||
}
|
||||
field(HOPR,DBF_DOUBLE) {
|
||||
prompt("High Operating Rng")
|
||||
promptgroup("80 - Display")
|
||||
interest(1)
|
||||
prop(YES)
|
||||
}
|
||||
field(LOPR,DBF_DOUBLE) {
|
||||
prompt("Low Operating Range")
|
||||
promptgroup("80 - Display")
|
||||
interest(1)
|
||||
prop(YES)
|
||||
}
|
||||
field(HIHI,DBF_DOUBLE) {
|
||||
prompt("Hihi Alarm Limit")
|
||||
promptgroup("70 - Alarm")
|
||||
pp(TRUE)
|
||||
interest(1)
|
||||
prop(YES)
|
||||
}
|
||||
field(LOLO,DBF_DOUBLE) {
|
||||
prompt("Lolo Alarm Limit")
|
||||
promptgroup("70 - Alarm")
|
||||
pp(TRUE)
|
||||
interest(1)
|
||||
prop(YES)
|
||||
}
|
||||
field(HIGH,DBF_DOUBLE) {
|
||||
prompt("High Alarm Limit")
|
||||
promptgroup("70 - Alarm")
|
||||
pp(TRUE)
|
||||
interest(1)
|
||||
prop(YES)
|
||||
}
|
||||
field(LOW,DBF_DOUBLE) {
|
||||
prompt("Low Alarm Limit")
|
||||
promptgroup("70 - Alarm")
|
||||
pp(TRUE)
|
||||
interest(1)
|
||||
prop(YES)
|
||||
}
|
||||
field(HHSV,DBF_MENU) {
|
||||
prompt("Hihi Severity")
|
||||
promptgroup("70 - Alarm")
|
||||
pp(TRUE)
|
||||
interest(1)
|
||||
prop(YES)
|
||||
menu(menuAlarmSevr)
|
||||
}
|
||||
field(LLSV,DBF_MENU) {
|
||||
prompt("Lolo Severity")
|
||||
promptgroup("70 - Alarm")
|
||||
pp(TRUE)
|
||||
interest(1)
|
||||
prop(YES)
|
||||
menu(menuAlarmSevr)
|
||||
}
|
||||
field(HSV,DBF_MENU) {
|
||||
prompt("High Severity")
|
||||
promptgroup("70 - Alarm")
|
||||
pp(TRUE)
|
||||
interest(1)
|
||||
prop(YES)
|
||||
menu(menuAlarmSevr)
|
||||
}
|
||||
field(LSV,DBF_MENU) {
|
||||
prompt("Low Severity")
|
||||
promptgroup("70 - Alarm")
|
||||
pp(TRUE)
|
||||
interest(1)
|
||||
prop(YES)
|
||||
menu(menuAlarmSevr)
|
||||
}
|
||||
field(AFTC, DBF_DOUBLE) {
|
||||
prompt("Alarm Filter Time Constant")
|
||||
promptgroup("70 - Alarm")
|
||||
interest(1)
|
||||
}
|
||||
field(AFVL, DBF_DOUBLE) {
|
||||
prompt("Alarm Filter Value")
|
||||
special(SPC_NOMOD)
|
||||
interest(3)
|
||||
}
|
||||
field(HYST,DBF_DOUBLE) {
|
||||
prompt("Alarm Deadband")
|
||||
promptgroup("70 - Alarm")
|
||||
interest(1)
|
||||
}
|
||||
field(ADEL,DBF_DOUBLE) {
|
||||
prompt("Archive Deadband")
|
||||
promptgroup("80 - Display")
|
||||
interest(1)
|
||||
}
|
||||
field(MDEL,DBF_DOUBLE) {
|
||||
prompt("Monitor Deadband")
|
||||
promptgroup("80 - Display")
|
||||
interest(1)
|
||||
}
|
||||
field(A,DBF_DOUBLE) {
|
||||
prompt("Value of Input A")
|
||||
pp(TRUE)
|
||||
}
|
||||
field(B,DBF_DOUBLE) {
|
||||
prompt("Value of Input B")
|
||||
pp(TRUE)
|
||||
}
|
||||
field(C,DBF_DOUBLE) {
|
||||
prompt("Value of Input C")
|
||||
pp(TRUE)
|
||||
}
|
||||
field(D,DBF_DOUBLE) {
|
||||
prompt("Value of Input D")
|
||||
pp(TRUE)
|
||||
}
|
||||
field(E,DBF_DOUBLE) {
|
||||
prompt("Value of Input E")
|
||||
pp(TRUE)
|
||||
}
|
||||
field(F,DBF_DOUBLE) {
|
||||
prompt("Value of Input F")
|
||||
pp(TRUE)
|
||||
}
|
||||
field(G,DBF_DOUBLE) {
|
||||
prompt("Value of Input G")
|
||||
pp(TRUE)
|
||||
}
|
||||
field(H,DBF_DOUBLE) {
|
||||
prompt("Value of Input H")
|
||||
pp(TRUE)
|
||||
}
|
||||
field(I,DBF_DOUBLE) {
|
||||
prompt("Value of Input I")
|
||||
pp(TRUE)
|
||||
}
|
||||
field(J,DBF_DOUBLE) {
|
||||
prompt("Value of Input J")
|
||||
pp(TRUE)
|
||||
}
|
||||
field(K,DBF_DOUBLE) {
|
||||
prompt("Value of Input K")
|
||||
pp(TRUE)
|
||||
}
|
||||
field(L,DBF_DOUBLE) {
|
||||
prompt("Value of Input L")
|
||||
pp(TRUE)
|
||||
}
|
||||
field(LA,DBF_DOUBLE) {
|
||||
prompt("Prev Value of A")
|
||||
special(SPC_NOMOD)
|
||||
interest(3)
|
||||
}
|
||||
field(LB,DBF_DOUBLE) {
|
||||
prompt("Prev Value of B")
|
||||
special(SPC_NOMOD)
|
||||
interest(3)
|
||||
}
|
||||
field(LC,DBF_DOUBLE) {
|
||||
prompt("Prev Value of C")
|
||||
special(SPC_NOMOD)
|
||||
interest(3)
|
||||
}
|
||||
field(LD,DBF_DOUBLE) {
|
||||
prompt("Prev Value of D")
|
||||
special(SPC_NOMOD)
|
||||
interest(3)
|
||||
}
|
||||
field(LE,DBF_DOUBLE) {
|
||||
prompt("Prev Value of E")
|
||||
special(SPC_NOMOD)
|
||||
interest(3)
|
||||
}
|
||||
field(LF,DBF_DOUBLE) {
|
||||
prompt("Prev Value of F")
|
||||
special(SPC_NOMOD)
|
||||
interest(3)
|
||||
}
|
||||
field(LG,DBF_DOUBLE) {
|
||||
prompt("Prev Value of G")
|
||||
special(SPC_NOMOD)
|
||||
interest(3)
|
||||
}
|
||||
field(LH,DBF_DOUBLE) {
|
||||
prompt("Prev Value of H")
|
||||
special(SPC_NOMOD)
|
||||
interest(3)
|
||||
}
|
||||
field(LI,DBF_DOUBLE) {
|
||||
prompt("Prev Value of I")
|
||||
special(SPC_NOMOD)
|
||||
interest(3)
|
||||
}
|
||||
field(LJ,DBF_DOUBLE) {
|
||||
prompt("Prev Value of J")
|
||||
special(SPC_NOMOD)
|
||||
interest(3)
|
||||
}
|
||||
field(LK,DBF_DOUBLE) {
|
||||
prompt("Prev Value of K")
|
||||
special(SPC_NOMOD)
|
||||
interest(3)
|
||||
}
|
||||
field(LL,DBF_DOUBLE) {
|
||||
prompt("Prev Value of L")
|
||||
special(SPC_NOMOD)
|
||||
interest(3)
|
||||
}
|
||||
field(LALM,DBF_DOUBLE) {
|
||||
prompt("Last Value Alarmed")
|
||||
special(SPC_NOMOD)
|
||||
interest(3)
|
||||
}
|
||||
field(ALST,DBF_DOUBLE) {
|
||||
prompt("Last Value Archived")
|
||||
special(SPC_NOMOD)
|
||||
interest(3)
|
||||
}
|
||||
field(MLST,DBF_DOUBLE) {
|
||||
prompt("Last Val Monitored")
|
||||
special(SPC_NOMOD)
|
||||
interest(3)
|
||||
}
|
||||
%#include "postfix.h"
|
||||
field(RPCL,DBF_NOACCESS) {
|
||||
prompt("Reverse Polish Calc")
|
||||
special(SPC_NOMOD)
|
||||
interest(4)
|
||||
extra("char rpcl[INFIX_TO_POSTFIX_SIZE(80)]")
|
||||
}
|
||||
}
|
||||
948
modules/database/src/std/rec/calcRecord.dbd.pod
Normal file
948
modules/database/src/std/rec/calcRecord.dbd.pod
Normal file
@@ -0,0 +1,948 @@
|
||||
#*************************************************************************
|
||||
# Copyright (c) 2007 UChicago Argonne LLC, as Operator of Argonne
|
||||
# National Laboratory.
|
||||
# Copyright (c) 2002 The Regents of the University of California, as
|
||||
# Operator of Los Alamos National Laboratory.
|
||||
# EPICS BASE is distributed subject to a Software License Agreement found
|
||||
# in file LICENSE that is included with this distribution.
|
||||
#*************************************************************************
|
||||
|
||||
=title Calculation Record (calc)
|
||||
|
||||
The calculation or "Calc" record is used to perform algebraic, relational,
|
||||
and logical operations on values retrieved from other records. The result
|
||||
of its operations can then be accessed by another record so that it can
|
||||
then be used.
|
||||
|
||||
=head2 Parameter Fields
|
||||
|
||||
The fields in the record fall into the following categories:
|
||||
|
||||
=over 1
|
||||
|
||||
=item *
|
||||
scan parameters
|
||||
|
||||
=item *
|
||||
read parameters
|
||||
|
||||
=item *
|
||||
expression parameters
|
||||
|
||||
=item *
|
||||
operator display parameters
|
||||
|
||||
=item *
|
||||
alarm parameters
|
||||
|
||||
=item *
|
||||
monitor parameters
|
||||
|
||||
=item *
|
||||
run-time parameters
|
||||
|
||||
=back
|
||||
|
||||
=recordtype calc
|
||||
|
||||
=cut
|
||||
|
||||
recordtype(calc) {
|
||||
|
||||
=head3 Scan Parameters
|
||||
|
||||
The Calc record has the standard fields for specifying under what
|
||||
circumstances the record will be processed. These fields are listed in
|
||||
L<Scan Fields>. In addition, L<Scanning Specification> explains how these
|
||||
fields are used. Since the Calc record supports no direct interfaces to
|
||||
hardware, it cannot be scanned on I/O interrupt, so its SCAN field cannot
|
||||
be C<I/O Intr>.
|
||||
|
||||
=fields SCAN
|
||||
|
||||
|
||||
=head3 Read Parameters
|
||||
|
||||
The read parameters for the Calc record consist of 12 input links INPA,
|
||||
INPB, ... INPL. The fields can be database links, channel access links, or
|
||||
constants. If they are links, they must specify another record's field or a
|
||||
channel access link. If they are constants, they will be initialized with
|
||||
the value they are configured with and can be changed via C<dbPuts>. They
|
||||
cannot be hardware addresses.
|
||||
|
||||
See L<Address Specification> for information on how to specify database
|
||||
links.
|
||||
|
||||
=fields INPA, INPB, INPC, INPD, INPE, INPF, INPG, INPH, INPI, INPJ, INPK, INPL
|
||||
|
||||
=head3 Expression
|
||||
|
||||
At the core of the Calc record lies the CALC and RPCL fields. The CALC field
|
||||
contains the infix expresion which the record routine will use when it
|
||||
processes the record. The resulting value is placed in the VAL field and
|
||||
can be accessed from there. The CALC expression is actually converted to
|
||||
opcode and stored as Reverse Polish Notation in the RPCL field. It is this
|
||||
expression which is actually used to calculate VAL. The Reverse Polish
|
||||
expression is evaluated more efficiently during run-time than an infix
|
||||
expression. CALC can be changed at run-time, and a special record routine
|
||||
calls a function to convert it to Reverse Polish Notation.
|
||||
|
||||
The infix expressions that can be used are very similar to the C expression
|
||||
syntax, but with some additions and subtle differences in operator meaning
|
||||
and precedence. The string may contain a series of expressions separated by
|
||||
a semi-colon character ";" any one of which may actually provide the
|
||||
calculation result; however, all of the other expressions included must
|
||||
assign their result to a variable. All alphabetic elements described below
|
||||
are case independent, so upper and lower case letters may be used and mixed
|
||||
in the variable and function names as desired. Spaces may be used anywhere
|
||||
within an expression except between characters that make up a single
|
||||
expression element.
|
||||
|
||||
The range of expressions supported by the calculation record are separated
|
||||
into literals, constants, operands, algebraic operators, trigonometric operators,
|
||||
relational operators, logical operators, the assignment operator,
|
||||
parentheses and commas, and the question mark or '?:' operator.
|
||||
|
||||
=fields CALC, RPCL
|
||||
|
||||
=head3 Literals
|
||||
|
||||
=over 1
|
||||
|
||||
=item *
|
||||
Standard double precision floating point numbers
|
||||
|
||||
=item *
|
||||
Inf: Infinity
|
||||
|
||||
=item *
|
||||
Nan: Not a Number
|
||||
|
||||
=back
|
||||
|
||||
=head3 Constants
|
||||
|
||||
=over 1
|
||||
|
||||
=item *
|
||||
PI: returns the mathematical constant E<pi>
|
||||
|
||||
=item *
|
||||
D2R: evaluates to E<pi>/180 which, when used as a multiplier, converts an
|
||||
angle from degrees to radians
|
||||
|
||||
=item *
|
||||
R2D: evaluates to 180/E<pi> which as a multiplier converts an angle from
|
||||
radians to degrees
|
||||
|
||||
=back
|
||||
|
||||
=head3 Operands
|
||||
|
||||
The expression uses the values retrieved from the INPx links as operands,
|
||||
though constants can be used as operands too. These values retrieved from
|
||||
the input links are stored in the A-L fields. The values to be used in the
|
||||
expression are simply referenced by the field letter. For instance, the
|
||||
value obtained from INPA link is stored in the field A, and the value
|
||||
obtained from INPB is stored in field B. The field names can be included in
|
||||
the expression which will operate on their respective values, as in A+B.
|
||||
Also, the RNDM nullary function can be included as an operand in the
|
||||
expression in order to generate a random number between 0 and 1.
|
||||
|
||||
=fields A, B, C, D, E, F, G, H, I, J, K, L
|
||||
|
||||
The keyword VAL returns the current contents of the VAL field (which can be
|
||||
written to by a CA put, so it might I<not> be the result from the last time
|
||||
the expression was evaluated).
|
||||
|
||||
=head3 Algebraic Operators
|
||||
|
||||
=over 1
|
||||
|
||||
=item *
|
||||
ABS: Absolute value (unary)
|
||||
|
||||
=item *
|
||||
SQR: Square root (unary)
|
||||
|
||||
=item *
|
||||
MIN: Minimum (any number of args)
|
||||
|
||||
=item *
|
||||
MAX: Maximum (any number of args)
|
||||
|
||||
=item *
|
||||
FINITE: returns non-zero if none of the arguments are NaN or Inf (any
|
||||
number of args)
|
||||
|
||||
=item *
|
||||
ISNAN: returns non-zero if any of the arguments is NaN or Inf (any number
|
||||
of args)
|
||||
|
||||
=item *
|
||||
CEIL: Ceiling (unary)
|
||||
|
||||
=item *
|
||||
FLOOR: Floor (unary)
|
||||
|
||||
=item *
|
||||
LOG: Log base 10 (unary)
|
||||
|
||||
=item *
|
||||
LOGE: Natural log (unary)
|
||||
|
||||
=item *
|
||||
LN: Natural log (unary)
|
||||
|
||||
=item *
|
||||
EXP: Exponential function (unary)
|
||||
|
||||
=item *
|
||||
^ : Exponential (binary)
|
||||
|
||||
=item *
|
||||
** : Exponential (binary)
|
||||
|
||||
=item *
|
||||
+ : Addition (binary)
|
||||
|
||||
=item *
|
||||
- : Subtraction (binary)
|
||||
|
||||
=item *
|
||||
* : Multiplication (binary)
|
||||
|
||||
=item *
|
||||
/ : Division (binary)
|
||||
|
||||
=item *
|
||||
% : Modulo (binary)
|
||||
|
||||
=item *
|
||||
NOT: Negate (unary)
|
||||
|
||||
=back
|
||||
|
||||
=head3 Trigonometric Operators
|
||||
|
||||
=over 1
|
||||
|
||||
=item *
|
||||
SIN: Sine
|
||||
|
||||
=item *
|
||||
SINH: Hyperbolic sine
|
||||
|
||||
=item *
|
||||
ASIN: Arc sine
|
||||
|
||||
=item *
|
||||
COS: Cosine
|
||||
|
||||
=item *
|
||||
COSH: Hyperbolic cosine
|
||||
|
||||
=item *
|
||||
ACOS: Arc cosine
|
||||
|
||||
=item *
|
||||
TAN: Tangent
|
||||
|
||||
=item *
|
||||
TANH: Hyperbolic tangent
|
||||
|
||||
=item *
|
||||
ATAN: Arc tangent
|
||||
|
||||
=back
|
||||
|
||||
=head3 Relational Operators
|
||||
|
||||
=over 1
|
||||
|
||||
=item *
|
||||
>= : Greater than or equal to
|
||||
|
||||
=item *
|
||||
> : Greater than
|
||||
|
||||
=item *
|
||||
<= : Less than or equal to
|
||||
|
||||
=item *
|
||||
< : Less than
|
||||
|
||||
=item *
|
||||
# : Not equal to
|
||||
|
||||
=item *
|
||||
= : Equal to
|
||||
|
||||
=back
|
||||
|
||||
=head3 Logical Operators
|
||||
|
||||
=over 1
|
||||
|
||||
=item *
|
||||
&& : And
|
||||
|
||||
=item *
|
||||
|| : Or
|
||||
|
||||
=item *
|
||||
! : Not
|
||||
|
||||
=back
|
||||
|
||||
=head3 Bitwise Operators
|
||||
|
||||
=over 1
|
||||
|
||||
=item *
|
||||
| : Bitwise Or
|
||||
|
||||
=item *
|
||||
& : Bitwise And
|
||||
|
||||
=item *
|
||||
OR : Bitwise Or
|
||||
|
||||
=item *
|
||||
AND : Bitwise And
|
||||
|
||||
=item *
|
||||
XOR : Bitwise Exclusive Or
|
||||
|
||||
=item *
|
||||
~ : One's Complement
|
||||
|
||||
=item *
|
||||
<< : Left shift
|
||||
|
||||
=item *
|
||||
>> : Right shift
|
||||
|
||||
=back
|
||||
|
||||
=head3 Assignment Operator
|
||||
|
||||
=over 1
|
||||
|
||||
=item *
|
||||
:= : assigns a value (right hand side) to a variable (i.e. field)
|
||||
|
||||
=back
|
||||
|
||||
=head3 Parantheses, Comma, and Semicolon
|
||||
|
||||
The open and close parentheses are supported. Nested parentheses are
|
||||
supported.
|
||||
|
||||
The comma is supported when used to separate the arguments of a binary
|
||||
function.
|
||||
|
||||
The semicolon is used to separate expressions. Although only one
|
||||
traditional calculation expression is allowed, multiple assignment
|
||||
expressions are allowed.
|
||||
|
||||
=head3 Conditional Expression
|
||||
|
||||
The C language's question mark operator is supported. The format is:
|
||||
C<condition ? True result : False result>
|
||||
|
||||
=head3 Expression Examples
|
||||
|
||||
=head3 Algebraic
|
||||
|
||||
C<A + B + 10>
|
||||
|
||||
=over 1
|
||||
|
||||
=item *
|
||||
Result is A + B + 10
|
||||
|
||||
=back
|
||||
|
||||
=head3 Relational
|
||||
|
||||
C<(A + B) < (C + D)>
|
||||
|
||||
=over 1
|
||||
|
||||
=item *
|
||||
Result is 1 if (A + B) < (C + D)
|
||||
|
||||
=item *
|
||||
Result is 0 if (A + B) >= (C + D)
|
||||
|
||||
=back
|
||||
|
||||
=head3 Question Mark
|
||||
|
||||
C<(A + B) < (C + D) ? E : F + L + 10>
|
||||
|
||||
=over 1
|
||||
|
||||
=item *
|
||||
Result is E if (A + B) < (C + D)
|
||||
|
||||
=item *
|
||||
Result is F + L + 10 if (A + B) >= (C + D)
|
||||
|
||||
=back
|
||||
|
||||
Prior to Base 3.14.9 it was legal to omit the : and the second (else) part
|
||||
of the conditional, like this:
|
||||
|
||||
C<(A + B)<(C + D) ? E>
|
||||
|
||||
=over 1
|
||||
|
||||
=item
|
||||
Result is E if (A + B)<(C + D)
|
||||
|
||||
=item
|
||||
Result is unchanged if (A + B)>=(C + D)
|
||||
|
||||
From 3.14.9 onwards, this expresion must be written as
|
||||
C<(A + B) < (C + D) ? E : VAL>
|
||||
|
||||
=back
|
||||
|
||||
=head3 Logical
|
||||
|
||||
C<A&B>
|
||||
|
||||
=over 1
|
||||
|
||||
=item *
|
||||
Causes the following to occur:
|
||||
|
||||
=over 1
|
||||
|
||||
=item *
|
||||
Convert A to integer
|
||||
|
||||
=item *
|
||||
Convert B to integer
|
||||
|
||||
=item *
|
||||
Bitwise And A and B
|
||||
|
||||
=item *
|
||||
Convert result to floating point
|
||||
|
||||
=back
|
||||
|
||||
=back
|
||||
|
||||
=head3 Assignment
|
||||
|
||||
C<sin(a); a:=a+D2R>
|
||||
|
||||
=over 1
|
||||
|
||||
=item *
|
||||
Causes the Calc record to output the successive values of a sine curve in
|
||||
1 degree intervals.
|
||||
|
||||
=back
|
||||
|
||||
=head3 Operator Display Parameters
|
||||
|
||||
These parameters are used to present meaningful data to the operator. These
|
||||
fields are used to display VAL and other parameters of the calculation
|
||||
record either textually or graphically.
|
||||
|
||||
The EGU field contains a string of up to 16 characters which is supplied by
|
||||
the user and which describes the values being operated upon. The string is
|
||||
retrieved whenever the routine C<get_units> is called. The EGU string is
|
||||
solely for an operator's sake and does not have to be used.
|
||||
|
||||
The HOPR and LOPR fields only refer to the limits of the VAL, HIHI, HIGH,
|
||||
LOW and LOLO fields. PREC controls the precision of the VAL field.
|
||||
|
||||
See L<Fields Common to All Record Types> for more on the record name (NAME)
|
||||
and description (DESC) fields.
|
||||
|
||||
=fields EGU, PREC, HOPR, LOPR, NAME, DESC
|
||||
|
||||
=head3 Alarm Parameters
|
||||
|
||||
The possible alarm conditions for the Calc record are the SCAN, READ,
|
||||
Calculation, and limit alarms. The SCAN and READ alarms are called by the
|
||||
record support routines. The Calculation alarm is called by the record
|
||||
processing routine when the CALC expression is an invalid one, upon which
|
||||
an error message is generated.
|
||||
|
||||
The following alarm parameters which are configured by the user, define the
|
||||
limit alarms for the VAL field and the severity corresponding to those
|
||||
conditions.
|
||||
|
||||
The HYST field defines an alarm deadband for each limit. See L<Alarm Specification>
|
||||
for a complete explanation of alarms of these fields. L<Alarm Fields>
|
||||
lists other fields related to alarms that are common to all record types.
|
||||
|
||||
=fields HIHI, HIGH, LOW, LOLO, HHSV, HSV, LSV, LLSV, HYST
|
||||
|
||||
=head3 Monitor Parameters
|
||||
|
||||
These paramaeters are used to determine when to send monitors for the value
|
||||
fields. These monitors are sent when the value field exceeds the last
|
||||
monitored field by the appropriate deadband, the ADEL for archiver monitors
|
||||
and the MDEL field for all other types of monitors. If these fields have a
|
||||
value of zero, everytime the value changes, monitors are triggered; if they have a
|
||||
value of -1, everytime the record is scanned, monitors are triggered. See
|
||||
L<Monitor Specification> for a complete explanation of monitors.
|
||||
|
||||
=fields ADEL, MDEL
|
||||
|
||||
=head3 Run-time Parameters
|
||||
|
||||
These fields are not configurable using a configuration tool and none are
|
||||
modifiable at run-time. They are used to process the record.
|
||||
|
||||
The LALM field is used to implement the hysteresis factor for the alarm
|
||||
limits.
|
||||
|
||||
The LA-LL fields are used to decide when to trigger monitors for the
|
||||
corresponding fields. For instance, if LA does not equal the value A,
|
||||
monitors for A are triggered. The MLST and ALST fields are used in the same
|
||||
manner for the VAL field.
|
||||
|
||||
=fields LALM, ALST, MLST, LA, LB, LC, LD, LE, LF, LG, LH, LI, LJ, LK, LL
|
||||
|
||||
=cut
|
||||
|
||||
include "dbCommon.dbd"
|
||||
field(VAL,DBF_DOUBLE) {
|
||||
prompt("Result")
|
||||
promptgroup("50 - Output")
|
||||
asl(ASL0)
|
||||
}
|
||||
field(CALC,DBF_STRING) {
|
||||
prompt("Calculation")
|
||||
promptgroup("30 - Action")
|
||||
special(SPC_CALC)
|
||||
pp(TRUE)
|
||||
size(80)
|
||||
initial("0")
|
||||
}
|
||||
field(INPA,DBF_INLINK) {
|
||||
prompt("Input A")
|
||||
promptgroup("41 - Input A-F")
|
||||
interest(1)
|
||||
}
|
||||
field(INPB,DBF_INLINK) {
|
||||
prompt("Input B")
|
||||
promptgroup("41 - Input A-F")
|
||||
interest(1)
|
||||
}
|
||||
field(INPC,DBF_INLINK) {
|
||||
prompt("Input C")
|
||||
promptgroup("41 - Input A-F")
|
||||
interest(1)
|
||||
}
|
||||
field(INPD,DBF_INLINK) {
|
||||
prompt("Input D")
|
||||
promptgroup("41 - Input A-F")
|
||||
interest(1)
|
||||
}
|
||||
field(INPE,DBF_INLINK) {
|
||||
prompt("Input E")
|
||||
promptgroup("41 - Input A-F")
|
||||
interest(1)
|
||||
}
|
||||
field(INPF,DBF_INLINK) {
|
||||
prompt("Input F")
|
||||
promptgroup("41 - Input A-F")
|
||||
interest(1)
|
||||
}
|
||||
field(INPG,DBF_INLINK) {
|
||||
prompt("Input G")
|
||||
promptgroup("42 - Input G-L")
|
||||
interest(1)
|
||||
}
|
||||
field(INPH,DBF_INLINK) {
|
||||
prompt("Input H")
|
||||
promptgroup("42 - Input G-L")
|
||||
interest(1)
|
||||
}
|
||||
field(INPI,DBF_INLINK) {
|
||||
prompt("Input I")
|
||||
promptgroup("42 - Input G-L")
|
||||
interest(1)
|
||||
}
|
||||
field(INPJ,DBF_INLINK) {
|
||||
prompt("Input J")
|
||||
promptgroup("42 - Input G-L")
|
||||
interest(1)
|
||||
}
|
||||
field(INPK,DBF_INLINK) {
|
||||
prompt("Input K")
|
||||
promptgroup("42 - Input G-L")
|
||||
interest(1)
|
||||
}
|
||||
field(INPL,DBF_INLINK) {
|
||||
prompt("Input L")
|
||||
promptgroup("42 - Input G-L")
|
||||
interest(1)
|
||||
}
|
||||
field(EGU,DBF_STRING) {
|
||||
prompt("Engineering Units")
|
||||
promptgroup("80 - Display")
|
||||
interest(1)
|
||||
size(16)
|
||||
prop(YES)
|
||||
}
|
||||
field(PREC,DBF_SHORT) {
|
||||
prompt("Display Precision")
|
||||
promptgroup("80 - Display")
|
||||
interest(1)
|
||||
prop(YES)
|
||||
}
|
||||
field(HOPR,DBF_DOUBLE) {
|
||||
prompt("High Operating Rng")
|
||||
promptgroup("80 - Display")
|
||||
interest(1)
|
||||
prop(YES)
|
||||
}
|
||||
field(LOPR,DBF_DOUBLE) {
|
||||
prompt("Low Operating Range")
|
||||
promptgroup("80 - Display")
|
||||
interest(1)
|
||||
prop(YES)
|
||||
}
|
||||
field(HIHI,DBF_DOUBLE) {
|
||||
prompt("Hihi Alarm Limit")
|
||||
promptgroup("70 - Alarm")
|
||||
pp(TRUE)
|
||||
interest(1)
|
||||
prop(YES)
|
||||
}
|
||||
field(LOLO,DBF_DOUBLE) {
|
||||
prompt("Lolo Alarm Limit")
|
||||
promptgroup("70 - Alarm")
|
||||
pp(TRUE)
|
||||
interest(1)
|
||||
prop(YES)
|
||||
}
|
||||
field(HIGH,DBF_DOUBLE) {
|
||||
prompt("High Alarm Limit")
|
||||
promptgroup("70 - Alarm")
|
||||
pp(TRUE)
|
||||
interest(1)
|
||||
prop(YES)
|
||||
}
|
||||
field(LOW,DBF_DOUBLE) {
|
||||
prompt("Low Alarm Limit")
|
||||
promptgroup("70 - Alarm")
|
||||
pp(TRUE)
|
||||
interest(1)
|
||||
prop(YES)
|
||||
}
|
||||
field(HHSV,DBF_MENU) {
|
||||
prompt("Hihi Severity")
|
||||
promptgroup("70 - Alarm")
|
||||
pp(TRUE)
|
||||
interest(1)
|
||||
prop(YES)
|
||||
menu(menuAlarmSevr)
|
||||
}
|
||||
field(LLSV,DBF_MENU) {
|
||||
prompt("Lolo Severity")
|
||||
promptgroup("70 - Alarm")
|
||||
pp(TRUE)
|
||||
interest(1)
|
||||
prop(YES)
|
||||
menu(menuAlarmSevr)
|
||||
}
|
||||
field(HSV,DBF_MENU) {
|
||||
prompt("High Severity")
|
||||
promptgroup("70 - Alarm")
|
||||
pp(TRUE)
|
||||
interest(1)
|
||||
prop(YES)
|
||||
menu(menuAlarmSevr)
|
||||
}
|
||||
field(LSV,DBF_MENU) {
|
||||
prompt("Low Severity")
|
||||
promptgroup("70 - Alarm")
|
||||
pp(TRUE)
|
||||
interest(1)
|
||||
prop(YES)
|
||||
menu(menuAlarmSevr)
|
||||
}
|
||||
field(AFTC, DBF_DOUBLE) {
|
||||
prompt("Alarm Filter Time Constant")
|
||||
promptgroup("70 - Alarm")
|
||||
interest(1)
|
||||
}
|
||||
field(AFVL, DBF_DOUBLE) {
|
||||
prompt("Alarm Filter Value")
|
||||
special(SPC_NOMOD)
|
||||
interest(3)
|
||||
}
|
||||
field(HYST,DBF_DOUBLE) {
|
||||
prompt("Alarm Deadband")
|
||||
promptgroup("70 - Alarm")
|
||||
interest(1)
|
||||
}
|
||||
field(ADEL,DBF_DOUBLE) {
|
||||
prompt("Archive Deadband")
|
||||
promptgroup("80 - Display")
|
||||
interest(1)
|
||||
}
|
||||
field(MDEL,DBF_DOUBLE) {
|
||||
prompt("Monitor Deadband")
|
||||
promptgroup("80 - Display")
|
||||
interest(1)
|
||||
}
|
||||
field(A,DBF_DOUBLE) {
|
||||
prompt("Value of Input A")
|
||||
pp(TRUE)
|
||||
}
|
||||
field(B,DBF_DOUBLE) {
|
||||
prompt("Value of Input B")
|
||||
pp(TRUE)
|
||||
}
|
||||
field(C,DBF_DOUBLE) {
|
||||
prompt("Value of Input C")
|
||||
pp(TRUE)
|
||||
}
|
||||
field(D,DBF_DOUBLE) {
|
||||
prompt("Value of Input D")
|
||||
pp(TRUE)
|
||||
}
|
||||
field(E,DBF_DOUBLE) {
|
||||
prompt("Value of Input E")
|
||||
pp(TRUE)
|
||||
}
|
||||
field(F,DBF_DOUBLE) {
|
||||
prompt("Value of Input F")
|
||||
pp(TRUE)
|
||||
}
|
||||
field(G,DBF_DOUBLE) {
|
||||
prompt("Value of Input G")
|
||||
pp(TRUE)
|
||||
}
|
||||
field(H,DBF_DOUBLE) {
|
||||
prompt("Value of Input H")
|
||||
pp(TRUE)
|
||||
}
|
||||
field(I,DBF_DOUBLE) {
|
||||
prompt("Value of Input I")
|
||||
pp(TRUE)
|
||||
}
|
||||
field(J,DBF_DOUBLE) {
|
||||
prompt("Value of Input J")
|
||||
pp(TRUE)
|
||||
}
|
||||
field(K,DBF_DOUBLE) {
|
||||
prompt("Value of Input K")
|
||||
pp(TRUE)
|
||||
}
|
||||
field(L,DBF_DOUBLE) {
|
||||
prompt("Value of Input L")
|
||||
pp(TRUE)
|
||||
}
|
||||
field(LA,DBF_DOUBLE) {
|
||||
prompt("Prev Value of A")
|
||||
special(SPC_NOMOD)
|
||||
interest(3)
|
||||
}
|
||||
field(LB,DBF_DOUBLE) {
|
||||
prompt("Prev Value of B")
|
||||
special(SPC_NOMOD)
|
||||
interest(3)
|
||||
}
|
||||
field(LC,DBF_DOUBLE) {
|
||||
prompt("Prev Value of C")
|
||||
special(SPC_NOMOD)
|
||||
interest(3)
|
||||
}
|
||||
field(LD,DBF_DOUBLE) {
|
||||
prompt("Prev Value of D")
|
||||
special(SPC_NOMOD)
|
||||
interest(3)
|
||||
}
|
||||
field(LE,DBF_DOUBLE) {
|
||||
prompt("Prev Value of E")
|
||||
special(SPC_NOMOD)
|
||||
interest(3)
|
||||
}
|
||||
field(LF,DBF_DOUBLE) {
|
||||
prompt("Prev Value of F")
|
||||
special(SPC_NOMOD)
|
||||
interest(3)
|
||||
}
|
||||
field(LG,DBF_DOUBLE) {
|
||||
prompt("Prev Value of G")
|
||||
special(SPC_NOMOD)
|
||||
interest(3)
|
||||
}
|
||||
field(LH,DBF_DOUBLE) {
|
||||
prompt("Prev Value of H")
|
||||
special(SPC_NOMOD)
|
||||
interest(3)
|
||||
}
|
||||
field(LI,DBF_DOUBLE) {
|
||||
prompt("Prev Value of I")
|
||||
special(SPC_NOMOD)
|
||||
interest(3)
|
||||
}
|
||||
field(LJ,DBF_DOUBLE) {
|
||||
prompt("Prev Value of J")
|
||||
special(SPC_NOMOD)
|
||||
interest(3)
|
||||
}
|
||||
field(LK,DBF_DOUBLE) {
|
||||
prompt("Prev Value of K")
|
||||
special(SPC_NOMOD)
|
||||
interest(3)
|
||||
}
|
||||
field(LL,DBF_DOUBLE) {
|
||||
prompt("Prev Value of L")
|
||||
special(SPC_NOMOD)
|
||||
interest(3)
|
||||
}
|
||||
field(LALM,DBF_DOUBLE) {
|
||||
prompt("Last Value Alarmed")
|
||||
special(SPC_NOMOD)
|
||||
interest(3)
|
||||
}
|
||||
field(ALST,DBF_DOUBLE) {
|
||||
prompt("Last Value Archived")
|
||||
special(SPC_NOMOD)
|
||||
interest(3)
|
||||
}
|
||||
field(MLST,DBF_DOUBLE) {
|
||||
prompt("Last Val Monitored")
|
||||
special(SPC_NOMOD)
|
||||
interest(3)
|
||||
}
|
||||
%#include "postfix.h"
|
||||
field(RPCL,DBF_NOACCESS) {
|
||||
prompt("Reverse Polish Calc")
|
||||
special(SPC_NOMOD)
|
||||
interest(4)
|
||||
extra("char rpcl[INFIX_TO_POSTFIX_SIZE(80)]")
|
||||
}
|
||||
|
||||
=head2 Record Support
|
||||
|
||||
=head3 Record Support Routines
|
||||
|
||||
=head2 C<init_record>
|
||||
|
||||
For each constant input link, the corresponding value field is initialized
|
||||
with the constant value if the input link is CONSTANT or a channel access
|
||||
link is created if the input link is a PV_LINK.
|
||||
|
||||
A routine postfix is called to convert the infix expression in CALC to
|
||||
Reverse Polish Notation. The result is stored in RPCL.
|
||||
|
||||
=head2 C<process>
|
||||
|
||||
See next section.
|
||||
|
||||
=head2 C<special>
|
||||
|
||||
This is called if CALC is changed. C<special> calls postfix.
|
||||
|
||||
=head2 C<get_value>
|
||||
|
||||
Fills in the values of struct valueDes so that the refer to VAL.
|
||||
|
||||
=head2 C<get_units>
|
||||
|
||||
Retrieves EGU.
|
||||
|
||||
=head2 C<get_precision>
|
||||
|
||||
Retrieves PREC.
|
||||
|
||||
=head2 C<get_graphic_double>
|
||||
|
||||
Sets the upper display and lower display limits for a field. If the field
|
||||
is VAL, HIHI, HIGH, LOW, or LOLO, the limits are set to HOPR and LOPR, else
|
||||
if the field has upper and lower limits defined they will be used, else the
|
||||
upper and lower maximum values for the field will be used.
|
||||
|
||||
=head2 C<get_control_double>
|
||||
|
||||
Sets the upper control and the lower control limits for a field. If the
|
||||
field is VAL, HIHI, HIGH, LOW, or LOLO, the limits are set to HOPR and
|
||||
LOPR, else if the field has upper and lower limits defined they will be
|
||||
used, else the upper and lower maximum values for the field type will be
|
||||
used.
|
||||
|
||||
=head2 C<get_alarm_double>
|
||||
|
||||
Sets the following values:
|
||||
|
||||
=over 1
|
||||
|
||||
upper_alarm_limit = HIHI
|
||||
|
||||
upper_warning_limit = HIGH
|
||||
|
||||
lower_warning_limit = LOW
|
||||
|
||||
lower_alarm_limit = LOLO
|
||||
|
||||
=back
|
||||
|
||||
=head3 Record Processing
|
||||
|
||||
Routine process implements the following algorithm:
|
||||
|
||||
=over 1
|
||||
|
||||
=item 1.
|
||||
Fetch all arguments.
|
||||
|
||||
=item 2.
|
||||
Call routine C<calcPerform>, which calculates VAL from the postfix version of
|
||||
the expression given in CALC. If C<calcPerform> returns success UDF is set to
|
||||
FALSE.
|
||||
|
||||
=item 3.
|
||||
Check alarms. This routine checks to see if the new VAL causes the alarm
|
||||
status and severity to change. If so, NSEV, NSTA, and LALM are set. It also
|
||||
honors the alarm hysteresis factor (HYST). Thus the value must change by
|
||||
at least HYST before the alarm status and severity changes.
|
||||
|
||||
=item 4.
|
||||
Check to see if monitors should be invoked.
|
||||
|
||||
=back
|
||||
|
||||
=over 1
|
||||
|
||||
=item *
|
||||
Alarm monitors are invoked if the alarm status or severity has changed.
|
||||
|
||||
=item *
|
||||
Archive and values change monitors are invoked if ADEL and MDEL conditions
|
||||
are met.
|
||||
|
||||
=item *
|
||||
Monitors for A-L are checked whenever other monitors are invoked.
|
||||
|
||||
=item *
|
||||
NSEV and NSTA are reset to 0.
|
||||
|
||||
=back
|
||||
|
||||
=over
|
||||
|
||||
=item 5.
|
||||
Scan forward link if necessary, set PACT FALSE, and return.
|
||||
|
||||
=back
|
||||
|
||||
=cut
|
||||
|
||||
}
|
||||
@@ -1,530 +0,0 @@
|
||||
#*************************************************************************
|
||||
# Copyright (c) 2007 UChicago Argonne LLC, as Operator of Argonne
|
||||
# National Laboratory.
|
||||
# Copyright (c) 2002 The Regents of the University of California, as
|
||||
# Operator of Los Alamos National Laboratory.
|
||||
# EPICS BASE is distributed subject to a Software License Agreement found
|
||||
# in file LICENSE that is included with this distribution.
|
||||
#*************************************************************************
|
||||
menu(calcoutOOPT) {
|
||||
choice(calcoutOOPT_Every_Time,"Every Time")
|
||||
choice(calcoutOOPT_On_Change,"On Change")
|
||||
choice(calcoutOOPT_When_Zero,"When Zero")
|
||||
choice(calcoutOOPT_When_Non_zero,"When Non-zero")
|
||||
choice(calcoutOOPT_Transition_To_Zero,"Transition To Zero")
|
||||
choice(calcoutOOPT_Transition_To_Non_zero,"Transition To Non-zero")
|
||||
}
|
||||
menu(calcoutDOPT) {
|
||||
choice(calcoutDOPT_Use_VAL,"Use CALC")
|
||||
choice(calcoutDOPT_Use_OVAL,"Use OCAL")
|
||||
}
|
||||
menu(calcoutINAV) {
|
||||
choice(calcoutINAV_EXT_NC,"Ext PV NC")
|
||||
choice(calcoutINAV_EXT,"Ext PV OK")
|
||||
choice(calcoutINAV_LOC,"Local PV")
|
||||
choice(calcoutINAV_CON,"Constant")
|
||||
}
|
||||
recordtype(calcout) {
|
||||
include "dbCommon.dbd"
|
||||
field(RPVT,DBF_NOACCESS) {
|
||||
prompt("Record Private")
|
||||
special(SPC_NOMOD)
|
||||
interest(4)
|
||||
extra("struct rpvtStruct *rpvt")
|
||||
}
|
||||
field(VAL,DBF_DOUBLE) {
|
||||
prompt("Result")
|
||||
promptgroup("50 - Output")
|
||||
asl(ASL0)
|
||||
}
|
||||
field(PVAL,DBF_DOUBLE) {
|
||||
prompt("Previous Value")
|
||||
}
|
||||
field(CALC,DBF_STRING) {
|
||||
prompt("Calculation")
|
||||
promptgroup("30 - Action")
|
||||
special(SPC_CALC)
|
||||
pp(TRUE)
|
||||
size(80)
|
||||
initial("0")
|
||||
}
|
||||
field(CLCV,DBF_LONG) {
|
||||
prompt("CALC Valid")
|
||||
interest(1)
|
||||
}
|
||||
field(INPA,DBF_INLINK) {
|
||||
prompt("Input A")
|
||||
special(SPC_MOD)
|
||||
promptgroup("41 - Input A-F")
|
||||
interest(1)
|
||||
}
|
||||
field(INPB,DBF_INLINK) {
|
||||
prompt("Input B")
|
||||
special(SPC_MOD)
|
||||
promptgroup("41 - Input A-F")
|
||||
interest(1)
|
||||
}
|
||||
field(INPC,DBF_INLINK) {
|
||||
prompt("Input C")
|
||||
special(SPC_MOD)
|
||||
promptgroup("41 - Input A-F")
|
||||
interest(1)
|
||||
}
|
||||
field(INPD,DBF_INLINK) {
|
||||
prompt("Input D")
|
||||
special(SPC_MOD)
|
||||
promptgroup("41 - Input A-F")
|
||||
interest(1)
|
||||
}
|
||||
field(INPE,DBF_INLINK) {
|
||||
prompt("Input E")
|
||||
special(SPC_MOD)
|
||||
promptgroup("41 - Input A-F")
|
||||
interest(1)
|
||||
}
|
||||
field(INPF,DBF_INLINK) {
|
||||
prompt("Input F")
|
||||
special(SPC_MOD)
|
||||
promptgroup("41 - Input A-F")
|
||||
interest(1)
|
||||
}
|
||||
field(INPG,DBF_INLINK) {
|
||||
prompt("Input G")
|
||||
special(SPC_MOD)
|
||||
promptgroup("42 - Input G-L")
|
||||
interest(1)
|
||||
}
|
||||
field(INPH,DBF_INLINK) {
|
||||
prompt("Input H")
|
||||
special(SPC_MOD)
|
||||
promptgroup("42 - Input G-L")
|
||||
interest(1)
|
||||
}
|
||||
field(INPI,DBF_INLINK) {
|
||||
prompt("Input I")
|
||||
special(SPC_MOD)
|
||||
promptgroup("42 - Input G-L")
|
||||
interest(1)
|
||||
}
|
||||
field(INPJ,DBF_INLINK) {
|
||||
prompt("Input J")
|
||||
special(SPC_MOD)
|
||||
promptgroup("42 - Input G-L")
|
||||
interest(1)
|
||||
}
|
||||
field(INPK,DBF_INLINK) {
|
||||
prompt("Input K")
|
||||
special(SPC_MOD)
|
||||
promptgroup("42 - Input G-L")
|
||||
interest(1)
|
||||
}
|
||||
field(INPL,DBF_INLINK) {
|
||||
prompt("Input L")
|
||||
special(SPC_MOD)
|
||||
promptgroup("42 - Input G-L")
|
||||
interest(1)
|
||||
}
|
||||
field(OUT,DBF_OUTLINK) {
|
||||
prompt("Output Specification")
|
||||
special(SPC_MOD)
|
||||
promptgroup("50 - Output")
|
||||
interest(1)
|
||||
}
|
||||
field(INAV,DBF_MENU) {
|
||||
prompt("INPA PV Status")
|
||||
special(SPC_NOMOD)
|
||||
interest(1)
|
||||
menu(calcoutINAV)
|
||||
initial("1")
|
||||
}
|
||||
field(INBV,DBF_MENU) {
|
||||
prompt("INPB PV Status")
|
||||
special(SPC_NOMOD)
|
||||
interest(1)
|
||||
menu(calcoutINAV)
|
||||
initial("1")
|
||||
}
|
||||
field(INCV,DBF_MENU) {
|
||||
prompt("INPC PV Status")
|
||||
special(SPC_NOMOD)
|
||||
interest(1)
|
||||
menu(calcoutINAV)
|
||||
initial("1")
|
||||
}
|
||||
field(INDV,DBF_MENU) {
|
||||
prompt("INPD PV Status")
|
||||
special(SPC_NOMOD)
|
||||
interest(1)
|
||||
menu(calcoutINAV)
|
||||
initial("1")
|
||||
}
|
||||
field(INEV,DBF_MENU) {
|
||||
prompt("INPE PV Status")
|
||||
special(SPC_NOMOD)
|
||||
interest(1)
|
||||
menu(calcoutINAV)
|
||||
initial("1")
|
||||
}
|
||||
field(INFV,DBF_MENU) {
|
||||
prompt("INPF PV Status")
|
||||
special(SPC_NOMOD)
|
||||
interest(1)
|
||||
menu(calcoutINAV)
|
||||
initial("1")
|
||||
}
|
||||
field(INGV,DBF_MENU) {
|
||||
prompt("INPG PV Status")
|
||||
special(SPC_NOMOD)
|
||||
interest(1)
|
||||
menu(calcoutINAV)
|
||||
initial("1")
|
||||
}
|
||||
field(INHV,DBF_MENU) {
|
||||
prompt("INPH PV Status")
|
||||
special(SPC_NOMOD)
|
||||
interest(1)
|
||||
menu(calcoutINAV)
|
||||
initial("1")
|
||||
}
|
||||
field(INIV,DBF_MENU) {
|
||||
prompt("INPI PV Status")
|
||||
special(SPC_NOMOD)
|
||||
interest(1)
|
||||
menu(calcoutINAV)
|
||||
initial("1")
|
||||
}
|
||||
field(INJV,DBF_MENU) {
|
||||
prompt("INPJ PV Status")
|
||||
special(SPC_NOMOD)
|
||||
interest(1)
|
||||
menu(calcoutINAV)
|
||||
initial("1")
|
||||
}
|
||||
field(INKV,DBF_MENU) {
|
||||
prompt("INPK PV Status")
|
||||
special(SPC_NOMOD)
|
||||
interest(1)
|
||||
menu(calcoutINAV)
|
||||
initial("1")
|
||||
}
|
||||
field(INLV,DBF_MENU) {
|
||||
prompt("INPL PV Status")
|
||||
special(SPC_NOMOD)
|
||||
interest(1)
|
||||
menu(calcoutINAV)
|
||||
initial("1")
|
||||
}
|
||||
field(OUTV,DBF_MENU) {
|
||||
prompt("OUT PV Status")
|
||||
special(SPC_NOMOD)
|
||||
interest(1)
|
||||
menu(calcoutINAV)
|
||||
}
|
||||
field(OOPT,DBF_MENU) {
|
||||
prompt("Output Execute Opt")
|
||||
promptgroup("50 - Output")
|
||||
interest(1)
|
||||
menu(calcoutOOPT)
|
||||
}
|
||||
field(ODLY,DBF_DOUBLE) {
|
||||
prompt("Output Execute Delay")
|
||||
promptgroup("50 - Output")
|
||||
asl(ASL0)
|
||||
interest(1)
|
||||
}
|
||||
field(DLYA,DBF_USHORT) {
|
||||
prompt("Output Delay Active")
|
||||
special(SPC_NOMOD)
|
||||
asl(ASL0)
|
||||
}
|
||||
field(DOPT,DBF_MENU) {
|
||||
prompt("Output Data Opt")
|
||||
promptgroup("30 - Action")
|
||||
interest(1)
|
||||
menu(calcoutDOPT)
|
||||
}
|
||||
field(OCAL,DBF_STRING) {
|
||||
prompt("Output Calculation")
|
||||
promptgroup("30 - Action")
|
||||
special(SPC_CALC)
|
||||
pp(TRUE)
|
||||
size(80)
|
||||
initial("0")
|
||||
}
|
||||
field(OCLV,DBF_LONG) {
|
||||
prompt("OCAL Valid")
|
||||
interest(1)
|
||||
}
|
||||
field(OEVT,DBF_STRING) {
|
||||
prompt("Event To Issue")
|
||||
promptgroup("30 - Action")
|
||||
special(SPC_MOD)
|
||||
asl(ASL0)
|
||||
size(40)
|
||||
}
|
||||
%#include "dbScan.h"
|
||||
field(EPVT, DBF_NOACCESS) {
|
||||
prompt("Event private")
|
||||
special(SPC_NOMOD)
|
||||
interest(4)
|
||||
extra("EVENTPVT epvt")
|
||||
}
|
||||
field(IVOA,DBF_MENU) {
|
||||
prompt("INVALID output action")
|
||||
promptgroup("50 - Output")
|
||||
interest(2)
|
||||
menu(menuIvoa)
|
||||
}
|
||||
field(IVOV,DBF_DOUBLE) {
|
||||
prompt("INVALID output value")
|
||||
promptgroup("50 - Output")
|
||||
interest(2)
|
||||
}
|
||||
field(EGU,DBF_STRING) {
|
||||
prompt("Engineering Units")
|
||||
promptgroup("80 - Display")
|
||||
interest(1)
|
||||
size(16)
|
||||
prop(YES)
|
||||
}
|
||||
field(PREC,DBF_SHORT) {
|
||||
prompt("Display Precision")
|
||||
promptgroup("80 - Display")
|
||||
interest(1)
|
||||
prop(YES)
|
||||
}
|
||||
field(HOPR,DBF_DOUBLE) {
|
||||
prompt("High Operating Rng")
|
||||
promptgroup("80 - Display")
|
||||
interest(1)
|
||||
prop(YES)
|
||||
}
|
||||
field(LOPR,DBF_DOUBLE) {
|
||||
prompt("Low Operating Range")
|
||||
promptgroup("80 - Display")
|
||||
interest(1)
|
||||
prop(YES)
|
||||
}
|
||||
field(HIHI,DBF_DOUBLE) {
|
||||
prompt("Hihi Alarm Limit")
|
||||
promptgroup("70 - Alarm")
|
||||
pp(TRUE)
|
||||
interest(1)
|
||||
prop(YES)
|
||||
}
|
||||
field(LOLO,DBF_DOUBLE) {
|
||||
prompt("Lolo Alarm Limit")
|
||||
promptgroup("70 - Alarm")
|
||||
pp(TRUE)
|
||||
interest(1)
|
||||
prop(YES)
|
||||
}
|
||||
field(HIGH,DBF_DOUBLE) {
|
||||
prompt("High Alarm Limit")
|
||||
promptgroup("70 - Alarm")
|
||||
pp(TRUE)
|
||||
interest(1)
|
||||
prop(YES)
|
||||
}
|
||||
field(LOW,DBF_DOUBLE) {
|
||||
prompt("Low Alarm Limit")
|
||||
promptgroup("70 - Alarm")
|
||||
pp(TRUE)
|
||||
interest(1)
|
||||
prop(YES)
|
||||
}
|
||||
field(HHSV,DBF_MENU) {
|
||||
prompt("Hihi Severity")
|
||||
promptgroup("70 - Alarm")
|
||||
pp(TRUE)
|
||||
interest(1)
|
||||
prop(YES)
|
||||
menu(menuAlarmSevr)
|
||||
}
|
||||
field(LLSV,DBF_MENU) {
|
||||
prompt("Lolo Severity")
|
||||
promptgroup("70 - Alarm")
|
||||
pp(TRUE)
|
||||
interest(1)
|
||||
prop(YES)
|
||||
menu(menuAlarmSevr)
|
||||
}
|
||||
field(HSV,DBF_MENU) {
|
||||
prompt("High Severity")
|
||||
promptgroup("70 - Alarm")
|
||||
pp(TRUE)
|
||||
interest(1)
|
||||
prop(YES)
|
||||
menu(menuAlarmSevr)
|
||||
}
|
||||
field(LSV,DBF_MENU) {
|
||||
prompt("Low Severity")
|
||||
promptgroup("70 - Alarm")
|
||||
pp(TRUE)
|
||||
interest(1)
|
||||
prop(YES)
|
||||
menu(menuAlarmSevr)
|
||||
}
|
||||
field(HYST,DBF_DOUBLE) {
|
||||
prompt("Alarm Deadband")
|
||||
promptgroup("70 - Alarm")
|
||||
interest(1)
|
||||
}
|
||||
field(ADEL,DBF_DOUBLE) {
|
||||
prompt("Archive Deadband")
|
||||
promptgroup("80 - Display")
|
||||
interest(1)
|
||||
}
|
||||
field(MDEL,DBF_DOUBLE) {
|
||||
prompt("Monitor Deadband")
|
||||
promptgroup("80 - Display")
|
||||
interest(1)
|
||||
}
|
||||
field(A,DBF_DOUBLE) {
|
||||
prompt("Value of Input A")
|
||||
pp(TRUE)
|
||||
}
|
||||
field(B,DBF_DOUBLE) {
|
||||
prompt("Value of Input B")
|
||||
pp(TRUE)
|
||||
}
|
||||
field(C,DBF_DOUBLE) {
|
||||
prompt("Value of Input C")
|
||||
pp(TRUE)
|
||||
}
|
||||
field(D,DBF_DOUBLE) {
|
||||
prompt("Value of Input D")
|
||||
pp(TRUE)
|
||||
}
|
||||
field(E,DBF_DOUBLE) {
|
||||
prompt("Value of Input E")
|
||||
pp(TRUE)
|
||||
}
|
||||
field(F,DBF_DOUBLE) {
|
||||
prompt("Value of Input F")
|
||||
pp(TRUE)
|
||||
}
|
||||
field(G,DBF_DOUBLE) {
|
||||
prompt("Value of Input G")
|
||||
pp(TRUE)
|
||||
}
|
||||
field(H,DBF_DOUBLE) {
|
||||
prompt("Value of Input H")
|
||||
pp(TRUE)
|
||||
}
|
||||
field(I,DBF_DOUBLE) {
|
||||
prompt("Value of Input I")
|
||||
pp(TRUE)
|
||||
}
|
||||
field(J,DBF_DOUBLE) {
|
||||
prompt("Value of Input J")
|
||||
pp(TRUE)
|
||||
}
|
||||
field(K,DBF_DOUBLE) {
|
||||
prompt("Value of Input K")
|
||||
pp(TRUE)
|
||||
}
|
||||
field(L,DBF_DOUBLE) {
|
||||
prompt("Value of Input L")
|
||||
pp(TRUE)
|
||||
}
|
||||
field(OVAL,DBF_DOUBLE) {
|
||||
prompt("Output Value")
|
||||
asl(ASL0)
|
||||
}
|
||||
field(LA,DBF_DOUBLE) {
|
||||
prompt("Prev Value of A")
|
||||
special(SPC_NOMOD)
|
||||
interest(3)
|
||||
}
|
||||
field(LB,DBF_DOUBLE) {
|
||||
prompt("Prev Value of B")
|
||||
special(SPC_NOMOD)
|
||||
interest(3)
|
||||
}
|
||||
field(LC,DBF_DOUBLE) {
|
||||
prompt("Prev Value of C")
|
||||
special(SPC_NOMOD)
|
||||
interest(3)
|
||||
}
|
||||
field(LD,DBF_DOUBLE) {
|
||||
prompt("Prev Value of D")
|
||||
special(SPC_NOMOD)
|
||||
interest(3)
|
||||
}
|
||||
field(LE,DBF_DOUBLE) {
|
||||
prompt("Prev Value of E")
|
||||
special(SPC_NOMOD)
|
||||
interest(3)
|
||||
}
|
||||
field(LF,DBF_DOUBLE) {
|
||||
prompt("Prev Value of F")
|
||||
special(SPC_NOMOD)
|
||||
interest(3)
|
||||
}
|
||||
field(LG,DBF_DOUBLE) {
|
||||
prompt("Prev Value of G")
|
||||
special(SPC_NOMOD)
|
||||
interest(3)
|
||||
}
|
||||
field(LH,DBF_DOUBLE) {
|
||||
prompt("Prev Value of H")
|
||||
special(SPC_NOMOD)
|
||||
interest(3)
|
||||
}
|
||||
field(LI,DBF_DOUBLE) {
|
||||
prompt("Prev Value of I")
|
||||
special(SPC_NOMOD)
|
||||
interest(3)
|
||||
}
|
||||
field(LJ,DBF_DOUBLE) {
|
||||
prompt("Prev Value of J")
|
||||
special(SPC_NOMOD)
|
||||
interest(3)
|
||||
}
|
||||
field(LK,DBF_DOUBLE) {
|
||||
prompt("Prev Value of K")
|
||||
special(SPC_NOMOD)
|
||||
interest(3)
|
||||
}
|
||||
field(LL,DBF_DOUBLE) {
|
||||
prompt("Prev Value of L")
|
||||
special(SPC_NOMOD)
|
||||
interest(3)
|
||||
}
|
||||
field(POVL,DBF_DOUBLE) {
|
||||
prompt("Prev Value of OVAL")
|
||||
asl(ASL0)
|
||||
}
|
||||
field(LALM,DBF_DOUBLE) {
|
||||
prompt("Last Value Alarmed")
|
||||
special(SPC_NOMOD)
|
||||
interest(3)
|
||||
}
|
||||
field(ALST,DBF_DOUBLE) {
|
||||
prompt("Last Value Archived")
|
||||
special(SPC_NOMOD)
|
||||
interest(3)
|
||||
}
|
||||
field(MLST,DBF_DOUBLE) {
|
||||
prompt("Last Val Monitored")
|
||||
special(SPC_NOMOD)
|
||||
interest(3)
|
||||
}
|
||||
%#include "postfix.h"
|
||||
field(RPCL,DBF_NOACCESS) {
|
||||
prompt("Reverse Polish Calc")
|
||||
special(SPC_NOMOD)
|
||||
interest(4)
|
||||
extra("char rpcl[INFIX_TO_POSTFIX_SIZE(80)]")
|
||||
}
|
||||
field(ORPC,DBF_NOACCESS) {
|
||||
prompt("Reverse Polish OCalc")
|
||||
special(SPC_NOMOD)
|
||||
interest(4)
|
||||
extra("char orpc[INFIX_TO_POSTFIX_SIZE(80)]")
|
||||
}
|
||||
}
|
||||
|
||||
variable(calcoutODLYprecision, int)
|
||||
variable(calcoutODLYlimit, double)
|
||||
1271
modules/database/src/std/rec/calcoutRecord.dbd.pod
Normal file
1271
modules/database/src/std/rec/calcoutRecord.dbd.pod
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,204 +0,0 @@
|
||||
#*************************************************************************
|
||||
# Copyright (c) 2002 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.
|
||||
# EPICS BASE is distributed subject to a Software License Agreement found
|
||||
# in file LICENSE that is included with this distribution.
|
||||
#*************************************************************************
|
||||
menu(dfanoutSELM) {
|
||||
choice(dfanoutSELM_All,"All")
|
||||
choice(dfanoutSELM_Specified,"Specified")
|
||||
choice(dfanoutSELM_Mask,"Mask")
|
||||
}
|
||||
recordtype(dfanout) {
|
||||
include "dbCommon.dbd"
|
||||
field(VAL,DBF_DOUBLE) {
|
||||
prompt("Desired Output")
|
||||
promptgroup("40 - Input")
|
||||
asl(ASL0)
|
||||
pp(TRUE)
|
||||
}
|
||||
field(SELM,DBF_MENU) {
|
||||
prompt("Select Mechanism")
|
||||
promptgroup("30 - Action")
|
||||
interest(1)
|
||||
menu(dfanoutSELM)
|
||||
}
|
||||
field(SELN,DBF_USHORT) {
|
||||
prompt("Link Selection")
|
||||
interest(1)
|
||||
initial("1")
|
||||
}
|
||||
field(SELL,DBF_INLINK) {
|
||||
prompt("Link Selection Loc")
|
||||
promptgroup("30 - Action")
|
||||
interest(1)
|
||||
}
|
||||
field(OUTA,DBF_OUTLINK) {
|
||||
prompt("Output Spec A")
|
||||
promptgroup("50 - Output")
|
||||
interest(1)
|
||||
}
|
||||
field(OUTB,DBF_OUTLINK) {
|
||||
prompt("Output Spec B")
|
||||
promptgroup("50 - Output")
|
||||
interest(1)
|
||||
}
|
||||
field(OUTC,DBF_OUTLINK) {
|
||||
prompt("Output Spec C")
|
||||
promptgroup("50 - Output")
|
||||
interest(1)
|
||||
}
|
||||
field(OUTD,DBF_OUTLINK) {
|
||||
prompt("Output Spec D")
|
||||
promptgroup("50 - Output")
|
||||
interest(1)
|
||||
}
|
||||
field(OUTE,DBF_OUTLINK) {
|
||||
prompt("Output Spec E")
|
||||
promptgroup("50 - Output")
|
||||
interest(1)
|
||||
}
|
||||
field(OUTF,DBF_OUTLINK) {
|
||||
prompt("Output Spec F")
|
||||
promptgroup("50 - Output")
|
||||
interest(1)
|
||||
}
|
||||
field(OUTG,DBF_OUTLINK) {
|
||||
prompt("Output Spec G")
|
||||
promptgroup("50 - Output")
|
||||
interest(1)
|
||||
}
|
||||
field(OUTH,DBF_OUTLINK) {
|
||||
prompt("Output Spec H")
|
||||
promptgroup("50 - Output")
|
||||
interest(1)
|
||||
}
|
||||
field(DOL,DBF_INLINK) {
|
||||
prompt("Desired Output Loc")
|
||||
promptgroup("40 - Input")
|
||||
interest(1)
|
||||
}
|
||||
field(OMSL,DBF_MENU) {
|
||||
prompt("Output Mode Select")
|
||||
promptgroup("50 - Output")
|
||||
interest(1)
|
||||
menu(menuOmsl)
|
||||
}
|
||||
field(EGU,DBF_STRING) {
|
||||
prompt("Engineering Units")
|
||||
promptgroup("80 - Display")
|
||||
interest(1)
|
||||
size(16)
|
||||
prop(YES)
|
||||
}
|
||||
field(PREC,DBF_SHORT) {
|
||||
prompt("Display Precision")
|
||||
promptgroup("80 - Display")
|
||||
interest(1)
|
||||
prop(YES)
|
||||
}
|
||||
field(HOPR,DBF_DOUBLE) {
|
||||
prompt("High Operating Range")
|
||||
promptgroup("80 - Display")
|
||||
interest(1)
|
||||
prop(YES)
|
||||
}
|
||||
field(LOPR,DBF_DOUBLE) {
|
||||
prompt("Low Operating Range")
|
||||
promptgroup("80 - Display")
|
||||
interest(1)
|
||||
prop(YES)
|
||||
}
|
||||
field(HIHI,DBF_DOUBLE) {
|
||||
prompt("Hihi Alarm Limit")
|
||||
promptgroup("70 - Alarm")
|
||||
pp(TRUE)
|
||||
interest(1)
|
||||
prop(YES)
|
||||
}
|
||||
field(LOLO,DBF_DOUBLE) {
|
||||
prompt("Lolo Alarm Limit")
|
||||
promptgroup("70 - Alarm")
|
||||
pp(TRUE)
|
||||
interest(1)
|
||||
prop(YES)
|
||||
}
|
||||
field(HIGH,DBF_DOUBLE) {
|
||||
prompt("High Alarm Limit")
|
||||
promptgroup("70 - Alarm")
|
||||
pp(TRUE)
|
||||
interest(1)
|
||||
prop(YES)
|
||||
}
|
||||
field(LOW,DBF_DOUBLE) {
|
||||
prompt("Low Alarm Limit")
|
||||
promptgroup("70 - Alarm")
|
||||
pp(TRUE)
|
||||
interest(1)
|
||||
prop(YES)
|
||||
}
|
||||
field(HHSV,DBF_MENU) {
|
||||
prompt("Hihi Severity")
|
||||
promptgroup("70 - Alarm")
|
||||
pp(TRUE)
|
||||
interest(1)
|
||||
prop(YES)
|
||||
menu(menuAlarmSevr)
|
||||
}
|
||||
field(LLSV,DBF_MENU) {
|
||||
prompt("Lolo Severity")
|
||||
promptgroup("70 - Alarm")
|
||||
pp(TRUE)
|
||||
interest(1)
|
||||
prop(YES)
|
||||
menu(menuAlarmSevr)
|
||||
}
|
||||
field(HSV,DBF_MENU) {
|
||||
prompt("High Severity")
|
||||
promptgroup("70 - Alarm")
|
||||
pp(TRUE)
|
||||
interest(1)
|
||||
prop(YES)
|
||||
menu(menuAlarmSevr)
|
||||
}
|
||||
field(LSV,DBF_MENU) {
|
||||
prompt("Low Severity")
|
||||
promptgroup("70 - Alarm")
|
||||
pp(TRUE)
|
||||
interest(1)
|
||||
prop(YES)
|
||||
menu(menuAlarmSevr)
|
||||
}
|
||||
field(HYST,DBF_DOUBLE) {
|
||||
prompt("Alarm Deadband")
|
||||
promptgroup("70 - Alarm")
|
||||
interest(1)
|
||||
}
|
||||
field(ADEL,DBF_DOUBLE) {
|
||||
prompt("Archive Deadband")
|
||||
promptgroup("80 - Display")
|
||||
interest(1)
|
||||
}
|
||||
field(MDEL,DBF_DOUBLE) {
|
||||
prompt("Monitor Deadband")
|
||||
promptgroup("80 - Display")
|
||||
interest(1)
|
||||
}
|
||||
field(LALM,DBF_DOUBLE) {
|
||||
prompt("Last Value Alarmed")
|
||||
special(SPC_NOMOD)
|
||||
interest(3)
|
||||
}
|
||||
field(ALST,DBF_DOUBLE) {
|
||||
prompt("Last Value Archived")
|
||||
special(SPC_NOMOD)
|
||||
interest(3)
|
||||
}
|
||||
field(MLST,DBF_DOUBLE) {
|
||||
prompt("Last Val Monitored")
|
||||
special(SPC_NOMOD)
|
||||
interest(3)
|
||||
}
|
||||
}
|
||||
454
modules/database/src/std/rec/dfanoutRecord.dbd.pod
Normal file
454
modules/database/src/std/rec/dfanoutRecord.dbd.pod
Normal file
@@ -0,0 +1,454 @@
|
||||
#*************************************************************************
|
||||
# Copyright (c) 2002 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.
|
||||
# EPICS BASE is distributed subject to a Software License Agreement found
|
||||
# in file LICENSE that is included with this distribution.
|
||||
#*************************************************************************
|
||||
|
||||
=title Data Fanout Record (dfanout)
|
||||
|
||||
The Data Fanout or "dfanout" record is used to forward data to up to
|
||||
eight other records. It's similar to the fanout record except that the
|
||||
capability to forward data has been added to it. If has no associated
|
||||
device support.
|
||||
|
||||
=head2 Parameter Fields
|
||||
|
||||
The fields in this record can be classified into the following categories:
|
||||
|
||||
=over
|
||||
|
||||
=item *
|
||||
scan parameters
|
||||
|
||||
=item *
|
||||
desired output parameters
|
||||
|
||||
=item *
|
||||
write parameters
|
||||
|
||||
=item *
|
||||
operator display parameters
|
||||
|
||||
=item *
|
||||
alarm parameters
|
||||
|
||||
=item *
|
||||
monitor parameters
|
||||
|
||||
=item *
|
||||
run-time and simulation mode parameters
|
||||
|
||||
=back
|
||||
|
||||
=recordtype dfanout
|
||||
|
||||
=cut
|
||||
|
||||
menu(dfanoutSELM) {
|
||||
choice(dfanoutSELM_All,"All")
|
||||
choice(dfanoutSELM_Specified,"Specified")
|
||||
choice(dfanoutSELM_Mask,"Mask")
|
||||
}
|
||||
|
||||
recordtype(dfanout) {
|
||||
|
||||
=head3 Scan Parameters
|
||||
|
||||
The data fanout record has the standard fields for specifying under what
|
||||
circumstances it will be processed. These fields are listed in
|
||||
L<Scan Fields>. In addition, L<Scanning Specification> explains how these
|
||||
fields are used. Since the data fanout record supports no direct interfaces
|
||||
to hardware, it cannot be scanned on I/O interrupt, so its SCAN field
|
||||
cannot be C<I/O Intr>.
|
||||
|
||||
=head3 Desired Output Parameters
|
||||
|
||||
The data fanout record must specify where the desired output value
|
||||
originates, i.e., the data which is to be fowarded to the records in its
|
||||
output links. The output mode select (OMSL) field determines whether the
|
||||
output originates from another record or from run-time database access.
|
||||
When set to C<closed_loop>, the desired output is retrieved from the link
|
||||
specified in the desired output (DOL) field, which can specify either a
|
||||
database or a channel access link, and placed into the VAL field. When set
|
||||
to C<supervisory>, the desired output can be written to the VAL field via
|
||||
dbPuts at run-time.
|
||||
|
||||
The DOL field can also be a constant in which case the VAL field is
|
||||
initialized to the constant value.
|
||||
|
||||
Note that there are no conversion parameters, so the desired output value
|
||||
undergoes no conversions before it is sent out to the output links.
|
||||
|
||||
=fields DOL, OMSL, VAL
|
||||
|
||||
=head3 Write Parameters
|
||||
|
||||
The OUTA-OUTH fields specify where VAL is to be sent. Each field that is to
|
||||
forward data must specify an address to another record. See
|
||||
L<Address Specification> for information on specifying links.
|
||||
|
||||
The SELL, SELM, and SELN fields specify which output links are to be
|
||||
used.
|
||||
|
||||
=head4 Menu dfanoutSELM
|
||||
|
||||
SELM is a menu, with three choices:
|
||||
|
||||
=menu dfanoutSELM
|
||||
|
||||
If SELM=="All", then all output links are used, and the values of
|
||||
SELL and SELN are ignored.
|
||||
|
||||
If SELM=="Specified", then the value of SELN is used to specify a single
|
||||
link which will be used. If SELN==0, then no link will be used; if SELN==1,
|
||||
then OUTA will be used, and so on.
|
||||
|
||||
SELN can either have its value set directly, or have its values retrieved
|
||||
from another EPICS PV. If SELL is a valid PV link, then SELN will be set to
|
||||
the values of the linked PV.
|
||||
|
||||
If SELM=="Mask", then SELN will be treated as a bit mask. If bit one of
|
||||
SELN is set, then OUTA will be used, if bit two is set, OUTB will be used.
|
||||
Thus if SELN==5, OUTC and OUTA will be used.
|
||||
|
||||
=fields OUTA, OUTB, OUTC, OUTD, OUTE, OUTF, OUTG, OUTH
|
||||
|
||||
=head3 Operator Display Parameters
|
||||
|
||||
These parameters are used to present meaningful data to the operator. They
|
||||
display the value and other parameters of the data fanout record either
|
||||
textually or graphically.
|
||||
|
||||
The EGU field can contain a string of up to 16 characters describing the
|
||||
value on the VAL field.
|
||||
|
||||
The HOPR and LOPR fields determine the upper and lower display limits for
|
||||
graphic displays and the upper and lower control limits for control
|
||||
displays. They apply to the VAL, HIHI, HIGH, LOW, and LOLO fields. The
|
||||
record support routines C<get_graphic_double> or C<get_control_double>
|
||||
retrieve HOPR and LOPR.
|
||||
|
||||
See L<Fields Common to All Record Types> for more on the record name (NAME)
|
||||
and description (DESC) fields.
|
||||
|
||||
=fields EGU, HOPR, LOPR, NAME, DESC
|
||||
|
||||
=head3 Alarm Parameters
|
||||
|
||||
The possible alarm conditions for data fanouts are the SCAN, READ, INVALID,
|
||||
and limit alarms. The SCAN and READ alarms are called by the record
|
||||
routines. The limit alarms are configured by the user in the HIHI, LOLO,
|
||||
HIGH, and LOW fields using floating point values. The limit alarms apply
|
||||
only to the VAL field. The severity for each of these limits is specified
|
||||
in the corresponding field (HHSV, LLSV, HSV, LSV) and can be either
|
||||
NO_ALARM, MINOR, or MAJOR. In the hysteresis field (HYST) can be entered a
|
||||
number which serves as the deadband on the limit alarms.
|
||||
|
||||
See L<Alarm Specification> for a complete explanation of alarms and these
|
||||
fields. L<Alarm Fields> lists other fields related to alarms that are
|
||||
common to all record types.
|
||||
|
||||
=fields HIHI, HIGH, LOW, LOLO, HHSV, HSV, LSV, LLSV, HYST
|
||||
|
||||
=head3 Monitor Parameters
|
||||
|
||||
These parameters are used to determine when to send monitors placed on the
|
||||
VAL field. These monitors are sent when the value field exceeds the last
|
||||
monitored fields by the specified deadband, ADEL for archivers monitors and
|
||||
MDEL for all other types of monitors. If these fields have a value of zero,
|
||||
everytime the value changes, a monitor will be triggered; if they have a
|
||||
value of -1, everytime the record is scanned, monitors are triggered. See
|
||||
L<Monitor Specification> for a complete explanation of monitors.
|
||||
|
||||
=fields ADEL, MDEL
|
||||
|
||||
=head3 Run-Time Parameters and Simulation Mode Parameters
|
||||
|
||||
These parameters are used by the run-time code for processing the data
|
||||
fanout record. Ther are not configurable. They are used to implement the
|
||||
hysteresis factors for monitor callbacks.
|
||||
|
||||
=fields LALM, ALST, MLST
|
||||
|
||||
=cut
|
||||
|
||||
include "dbCommon.dbd"
|
||||
field(VAL,DBF_DOUBLE) {
|
||||
prompt("Desired Output")
|
||||
promptgroup("40 - Input")
|
||||
asl(ASL0)
|
||||
pp(TRUE)
|
||||
}
|
||||
field(SELM,DBF_MENU) {
|
||||
prompt("Select Mechanism")
|
||||
promptgroup("30 - Action")
|
||||
interest(1)
|
||||
menu(dfanoutSELM)
|
||||
}
|
||||
field(SELN,DBF_USHORT) {
|
||||
prompt("Link Selection")
|
||||
interest(1)
|
||||
initial("1")
|
||||
}
|
||||
field(SELL,DBF_INLINK) {
|
||||
prompt("Link Selection Loc")
|
||||
promptgroup("30 - Action")
|
||||
interest(1)
|
||||
}
|
||||
field(OUTA,DBF_OUTLINK) {
|
||||
prompt("Output Spec A")
|
||||
promptgroup("50 - Output")
|
||||
interest(1)
|
||||
}
|
||||
field(OUTB,DBF_OUTLINK) {
|
||||
prompt("Output Spec B")
|
||||
promptgroup("50 - Output")
|
||||
interest(1)
|
||||
}
|
||||
field(OUTC,DBF_OUTLINK) {
|
||||
prompt("Output Spec C")
|
||||
promptgroup("50 - Output")
|
||||
interest(1)
|
||||
}
|
||||
field(OUTD,DBF_OUTLINK) {
|
||||
prompt("Output Spec D")
|
||||
promptgroup("50 - Output")
|
||||
interest(1)
|
||||
}
|
||||
field(OUTE,DBF_OUTLINK) {
|
||||
prompt("Output Spec E")
|
||||
promptgroup("50 - Output")
|
||||
interest(1)
|
||||
}
|
||||
field(OUTF,DBF_OUTLINK) {
|
||||
prompt("Output Spec F")
|
||||
promptgroup("50 - Output")
|
||||
interest(1)
|
||||
}
|
||||
field(OUTG,DBF_OUTLINK) {
|
||||
prompt("Output Spec G")
|
||||
promptgroup("50 - Output")
|
||||
interest(1)
|
||||
}
|
||||
field(OUTH,DBF_OUTLINK) {
|
||||
prompt("Output Spec H")
|
||||
promptgroup("50 - Output")
|
||||
interest(1)
|
||||
}
|
||||
field(DOL,DBF_INLINK) {
|
||||
prompt("Desired Output Loc")
|
||||
promptgroup("40 - Input")
|
||||
interest(1)
|
||||
}
|
||||
field(OMSL,DBF_MENU) {
|
||||
prompt("Output Mode Select")
|
||||
promptgroup("50 - Output")
|
||||
interest(1)
|
||||
menu(menuOmsl)
|
||||
}
|
||||
field(EGU,DBF_STRING) {
|
||||
prompt("Engineering Units")
|
||||
promptgroup("80 - Display")
|
||||
interest(1)
|
||||
size(16)
|
||||
prop(YES)
|
||||
}
|
||||
field(PREC,DBF_SHORT) {
|
||||
prompt("Display Precision")
|
||||
promptgroup("80 - Display")
|
||||
interest(1)
|
||||
prop(YES)
|
||||
}
|
||||
field(HOPR,DBF_DOUBLE) {
|
||||
prompt("High Operating Range")
|
||||
promptgroup("80 - Display")
|
||||
interest(1)
|
||||
prop(YES)
|
||||
}
|
||||
field(LOPR,DBF_DOUBLE) {
|
||||
prompt("Low Operating Range")
|
||||
promptgroup("80 - Display")
|
||||
interest(1)
|
||||
prop(YES)
|
||||
}
|
||||
field(HIHI,DBF_DOUBLE) {
|
||||
prompt("Hihi Alarm Limit")
|
||||
promptgroup("70 - Alarm")
|
||||
pp(TRUE)
|
||||
interest(1)
|
||||
prop(YES)
|
||||
}
|
||||
field(LOLO,DBF_DOUBLE) {
|
||||
prompt("Lolo Alarm Limit")
|
||||
promptgroup("70 - Alarm")
|
||||
pp(TRUE)
|
||||
interest(1)
|
||||
prop(YES)
|
||||
}
|
||||
field(HIGH,DBF_DOUBLE) {
|
||||
prompt("High Alarm Limit")
|
||||
promptgroup("70 - Alarm")
|
||||
pp(TRUE)
|
||||
interest(1)
|
||||
prop(YES)
|
||||
}
|
||||
field(LOW,DBF_DOUBLE) {
|
||||
prompt("Low Alarm Limit")
|
||||
promptgroup("70 - Alarm")
|
||||
pp(TRUE)
|
||||
interest(1)
|
||||
prop(YES)
|
||||
}
|
||||
field(HHSV,DBF_MENU) {
|
||||
prompt("Hihi Severity")
|
||||
promptgroup("70 - Alarm")
|
||||
pp(TRUE)
|
||||
interest(1)
|
||||
prop(YES)
|
||||
menu(menuAlarmSevr)
|
||||
}
|
||||
field(LLSV,DBF_MENU) {
|
||||
prompt("Lolo Severity")
|
||||
promptgroup("70 - Alarm")
|
||||
pp(TRUE)
|
||||
interest(1)
|
||||
prop(YES)
|
||||
menu(menuAlarmSevr)
|
||||
}
|
||||
field(HSV,DBF_MENU) {
|
||||
prompt("High Severity")
|
||||
promptgroup("70 - Alarm")
|
||||
pp(TRUE)
|
||||
interest(1)
|
||||
prop(YES)
|
||||
menu(menuAlarmSevr)
|
||||
}
|
||||
field(LSV,DBF_MENU) {
|
||||
prompt("Low Severity")
|
||||
promptgroup("70 - Alarm")
|
||||
pp(TRUE)
|
||||
interest(1)
|
||||
prop(YES)
|
||||
menu(menuAlarmSevr)
|
||||
}
|
||||
field(HYST,DBF_DOUBLE) {
|
||||
prompt("Alarm Deadband")
|
||||
promptgroup("70 - Alarm")
|
||||
interest(1)
|
||||
}
|
||||
field(ADEL,DBF_DOUBLE) {
|
||||
prompt("Archive Deadband")
|
||||
promptgroup("80 - Display")
|
||||
interest(1)
|
||||
}
|
||||
field(MDEL,DBF_DOUBLE) {
|
||||
prompt("Monitor Deadband")
|
||||
promptgroup("80 - Display")
|
||||
interest(1)
|
||||
}
|
||||
field(LALM,DBF_DOUBLE) {
|
||||
prompt("Last Value Alarmed")
|
||||
special(SPC_NOMOD)
|
||||
interest(3)
|
||||
}
|
||||
field(ALST,DBF_DOUBLE) {
|
||||
prompt("Last Value Archived")
|
||||
special(SPC_NOMOD)
|
||||
interest(3)
|
||||
}
|
||||
field(MLST,DBF_DOUBLE) {
|
||||
prompt("Last Val Monitored")
|
||||
special(SPC_NOMOD)
|
||||
interest(3)
|
||||
}
|
||||
|
||||
=head2 Record Support
|
||||
|
||||
=head3 Record Support Routines
|
||||
|
||||
=head2 C<init_record()>
|
||||
|
||||
This routine initializes all output links that are defined. Then it initializes
|
||||
DOL if DOL is a constant or a PV_LINK. When initializing the output links
|
||||
and the DOL link, a non-zero value is returned if an error occurs.
|
||||
|
||||
=head2 C<process()>
|
||||
|
||||
See next section.
|
||||
|
||||
=head2 C<get_value()>
|
||||
|
||||
This routine fills in the members of C<struct valueDes> with the VAL fields
|
||||
value and characteristics.
|
||||
|
||||
=head2 C<get_units()>
|
||||
|
||||
The routine copies the string specified in the EGU field to the location
|
||||
specified by a pointer which is passed to the routine.
|
||||
|
||||
=head2 C<get_graphic_double()>
|
||||
|
||||
If the referenced field is VAL, HIHI, HIGH, LOW, or LOLO, this routine sets
|
||||
the C<upper_disp_limit> member of the C<dbr_grDouble> structure to the
|
||||
HOPR and the C<lower_disp_limit> member to the LOPR. If the referenced
|
||||
field is not one of the above fields, then C<recGblGetControlDouble()>
|
||||
routine is called.
|
||||
|
||||
=head2 C<get_control_double()>
|
||||
|
||||
Same as the C<get_graphic_double> routine except that it uses the
|
||||
C<dbr_ctrlDouble> structure.
|
||||
|
||||
=head2 C<get_alarm_double()>
|
||||
|
||||
This sets the members of the C<dbr_alDouble> structure to the specified
|
||||
alarm limits if the referenced field is VAL:
|
||||
|
||||
=over
|
||||
|
||||
upper_alarm_limit = HIHI
|
||||
|
||||
upper_warning_limit = HIGH
|
||||
|
||||
lower_warning_limit = LOW
|
||||
|
||||
lower_alarm_limit = LOLO
|
||||
|
||||
=back
|
||||
|
||||
If the referenced field is not VAL, the C<recGblGetAlarmDouble()> routine
|
||||
is called.
|
||||
|
||||
=head3 Record Processing
|
||||
|
||||
=over
|
||||
|
||||
=item 1.
|
||||
The C<process()> routine first retrieves a value for DOL and places it in
|
||||
VAL if OMSL is set to colsed loop mode. If an error occurs, then UDF is set
|
||||
to FALSE.
|
||||
|
||||
=item 2.
|
||||
PACT is set TRUE
|
||||
|
||||
=item 3.
|
||||
VAL is then sent to all the records specified in the OUTA-OUTH fields by
|
||||
calling C<recGblePutLinkValue()> for each link.
|
||||
|
||||
=item 4.
|
||||
Alarms are checked and monitors are called if conditions apply.
|
||||
|
||||
=item 5.
|
||||
The data fanout's own forward link is then processed.
|
||||
|
||||
=item 6.
|
||||
PACT is set FALSE, and the C<process()> routine returns. A -1 is returned
|
||||
if there was an error writing values to one of the output links.
|
||||
|
||||
=back
|
||||
|
||||
=cut
|
||||
}
|
||||
@@ -183,17 +183,16 @@ static long special(DBADDR *paddr, int after)
|
||||
|
||||
#define indexof(field) int64inRecord##field
|
||||
|
||||
static long get_units(DBADDR *paddr,char *units)
|
||||
static long get_units(DBADDR *paddr, char *units)
|
||||
{
|
||||
int64inRecord *prec=(int64inRecord *)paddr->precord;
|
||||
int64inRecord *prec = (int64inRecord *) paddr->precord;
|
||||
|
||||
if(paddr->pfldDes->field_type == DBF_LONG) {
|
||||
strncpy(units,prec->egu,DB_UNITS_SIZE);
|
||||
if (paddr->pfldDes->field_type == DBF_INT64) {
|
||||
strncpy(units, prec->egu, DB_UNITS_SIZE);
|
||||
}
|
||||
return(0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static long get_graphic_double(DBADDR *paddr, struct dbr_grDouble *pgd)
|
||||
{
|
||||
int64inRecord *prec=(int64inRecord *)paddr->precord;
|
||||
@@ -255,7 +254,7 @@ static long get_alarm_double(DBADDR *paddr, struct dbr_alDouble *pad)
|
||||
|
||||
static void checkAlarms(int64inRecord *prec, epicsTimeStamp *timeLast)
|
||||
{
|
||||
enum {
|
||||
enum {
|
||||
range_Lolo = 1,
|
||||
range_Low,
|
||||
range_Normal,
|
||||
|
||||
@@ -51,7 +51,7 @@ static long special(DBADDR *, int);
|
||||
#define get_array_info NULL
|
||||
#define put_array_info NULL
|
||||
#define get_units NULL
|
||||
#define get_precision NULL
|
||||
static long get_precision(const DBADDR *, long *);
|
||||
#define get_enum_str NULL
|
||||
#define get_enum_strs NULL
|
||||
#define put_enum_str NULL
|
||||
@@ -204,6 +204,16 @@ static long special(DBADDR *paddr, int after)
|
||||
}
|
||||
}
|
||||
|
||||
static long get_precision(const DBADDR *paddr,long *precision)
|
||||
{
|
||||
mbbiDirectRecord *prec=(mbbiDirectRecord *)paddr->precord;
|
||||
if(dbGetFieldIndex(paddr)==mbbiDirectRecordVAL)
|
||||
*precision = prec->nobt;
|
||||
else
|
||||
recGblGetPrec(paddr,precision);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void monitor(mbbiDirectRecord *prec)
|
||||
{
|
||||
epicsUInt16 events = recGblResetAlarms(prec);
|
||||
|
||||
@@ -51,7 +51,7 @@ static long special(DBADDR *, int);
|
||||
#define get_array_info NULL
|
||||
#define put_array_info NULL
|
||||
#define get_units NULL
|
||||
#define get_precision NULL
|
||||
static long get_precision(const DBADDR *, long *);
|
||||
#define get_enum_str NULL
|
||||
#define get_enum_strs NULL
|
||||
#define put_enum_str NULL
|
||||
@@ -313,6 +313,16 @@ static long special(DBADDR *paddr, int after)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long get_precision(const DBADDR *paddr,long *precision)
|
||||
{
|
||||
mbboDirectRecord *prec=(mbboDirectRecord *)paddr->precord;
|
||||
if(dbGetFieldIndex(paddr)==mbboDirectRecordVAL)
|
||||
*precision = prec->nobt;
|
||||
else
|
||||
recGblGetPrec(paddr,precision);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void monitor(mbboDirectRecord *prec)
|
||||
{
|
||||
epicsUInt16 events = recGblResetAlarms(prec);
|
||||
|
||||
@@ -99,7 +99,7 @@ static void monitor(stateRecord *prec)
|
||||
monitor_mask = recGblResetAlarms(prec);
|
||||
if(strncmp(prec->oval,prec->val,sizeof(prec->val))) {
|
||||
db_post_events(prec,&(prec->val[0]),monitor_mask|DBE_VALUE|DBE_LOG);
|
||||
strncpy(prec->oval,prec->val,sizeof(prec->val));
|
||||
strncpy(prec->oval,prec->val,sizeof(prec->oval));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -121,8 +121,7 @@ static long init_record(struct dbCommon *pcommon, int pass)
|
||||
if (status)
|
||||
return status;
|
||||
}
|
||||
|
||||
strncpy(prec->oval, prec->val, sizeof(prec->val));
|
||||
strncpy(prec->oval, prec->val, sizeof(prec->oval));
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -183,7 +182,7 @@ static void monitor(stringinRecord *prec)
|
||||
|
||||
if (strncmp(prec->oval, prec->val, sizeof(prec->val))) {
|
||||
monitor_mask |= DBE_VALUE | DBE_LOG;
|
||||
strncpy(prec->oval, prec->val, sizeof(prec->val));
|
||||
strncpy(prec->oval, prec->val, sizeof(prec->oval));
|
||||
}
|
||||
|
||||
if (prec->mpst == stringinPOST_Always)
|
||||
|
||||
@@ -126,8 +126,7 @@ static long init_record(struct dbCommon *pcommon, int pass)
|
||||
if(status)
|
||||
return status;
|
||||
}
|
||||
|
||||
strncpy(prec->oval, prec->val, sizeof(prec->val));
|
||||
strncpy(prec->oval, prec->val, sizeof(prec->oval));
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -215,7 +214,7 @@ static void monitor(stringoutRecord *prec)
|
||||
|
||||
if (strncmp(prec->oval, prec->val, sizeof(prec->val))) {
|
||||
monitor_mask |= DBE_VALUE | DBE_LOG;
|
||||
strncpy(prec->oval, prec->val, sizeof(prec->val));
|
||||
strncpy(prec->oval, prec->val, sizeof(prec->oval));
|
||||
}
|
||||
|
||||
if (prec->mpst == stringoutPOST_Always)
|
||||
|
||||
@@ -97,7 +97,7 @@ sub ParseDBD {
|
||||
my $rtyp = $dbd->recordtype($record_type);
|
||||
if (!defined $rtyp) {
|
||||
$rtyp = DBD::Recordtype->new($record_type);
|
||||
warn "Device using undefined record type '$record_type', place-holder created\n";
|
||||
warn "Device using unknown record type '$record_type', declaration created\n";
|
||||
$dbd->add($rtyp);
|
||||
}
|
||||
$rtyp->add_device(DBD::Device->new($link_type, $dset, $choice));
|
||||
@@ -218,7 +218,12 @@ sub parse_breaktable {
|
||||
sub parse_recordtype {
|
||||
my ($dbd, $record_type) = @_;
|
||||
pushContext("recordtype($record_type)");
|
||||
my $rtyp = DBD::Recordtype->new($record_type);
|
||||
# Re-use a matching declaration record type if one exists
|
||||
my $rtyp = $dbd->recordtype($record_type);
|
||||
if (!defined($rtyp) || $rtyp->fields) {
|
||||
# Earlier record type is not a declaration, don't re-use it
|
||||
$rtyp = DBD::Recordtype->new($record_type);
|
||||
}
|
||||
while(1) {
|
||||
parseCommon($rtyp);
|
||||
if (m/\G field \s* \( \s* $RXstr \s* , \s* $RXstr \s* \) \s* \{/xgc) {
|
||||
|
||||
@@ -130,7 +130,18 @@ sub attribute {
|
||||
}
|
||||
|
||||
sub equals {
|
||||
dieContext("Record field objects are not comparable");
|
||||
my ($l, $r) = @_;
|
||||
return 1 if $l eq $r;
|
||||
return 0 if
|
||||
$l->{NAME} ne $r->{NAME} ||
|
||||
$l->{DBF_TYPE} ne $r->{DBF_TYPE};
|
||||
my ($la, $ra) = ($l->{ATTR_INDEX}, $r->{ATTR_INDEX});
|
||||
my @keys = sort keys %$la;
|
||||
return 0 if join(',', @keys) ne join(',', sort keys %$ra);
|
||||
foreach my $k (@keys) {
|
||||
return 0 if $la->{$k} ne $ra->{$k};
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
sub check_valid {
|
||||
|
||||
@@ -104,9 +104,19 @@ sub pod {
|
||||
|
||||
sub equals {
|
||||
my ($new, $known) = @_;
|
||||
return 0 if ! $known->fields;
|
||||
return 1 if ! $new->fields;
|
||||
dieContext("Duplicate definition of record type '$known->{NAME}'");
|
||||
return 1 if $new eq $known;
|
||||
return 0 if $new->{NAME} ne $known->{NAME};
|
||||
return 1 if ! $new->fields; # Later declarations always match
|
||||
# NB: Definition after declaration is handled in parse_recordtype()
|
||||
my @nf = @{$new->{FIELD_LIST}};
|
||||
my @kf = @{$known->{FIELD_LIST}};
|
||||
return 0 if scalar @nf != scalar @kf;
|
||||
while (@nf) {
|
||||
my $nf = shift @nf;
|
||||
my $kf = shift @kf;
|
||||
return 0 if ! $nf->equals($kf);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
sub toDeclaration {
|
||||
|
||||
@@ -212,7 +212,7 @@ sub _getline {
|
||||
|
||||
my $line = readline $self->{stdout};
|
||||
if (defined $line) {
|
||||
chomp $line;
|
||||
$line =~ s/[\r\n]+ $//x; # chomp broken on Windows?
|
||||
printf "#%d >> %s\n", $self->{pid}, $line if $self->{debug};
|
||||
}
|
||||
elsif (eof($self->{stdout})) {
|
||||
|
||||
@@ -40,4 +40,27 @@ PERL_SCRIPTS += registerRecordDeviceDriver.pl
|
||||
|
||||
HTMLS += dbdToHtml.html
|
||||
|
||||
# Build Package Config Files
|
||||
|
||||
FINAL_LOCATION ?= $(shell $(PERL) $(TOOLS)/fullPathName.pl $(INSTALL_LOCATION))
|
||||
C_CFLAGS += $(filter-out -g,$(filter-out -O%,$(filter-out -W%,$(CPPFLAGS))))
|
||||
C_CFLAGS += $(filter-out -g,$(filter-out -O%,$(filter-out -W%,$(CFLAGS))))
|
||||
PKGVARS += FINAL_LOCATION OS_CLASS CMPLR_CLASS C_CFLAGS LDFLAGS LDLIBS
|
||||
PKGVARS += EPICS_VERSION EPICS_REVISION EPICS_MODIFICATION EPICS_PATCH_LEVEL
|
||||
PKGVARS += CC CCC CPP AR LD
|
||||
PKGVARS += EPICS_BASE_HOST_LIBS EPICS_BASE_IOC_LIBS
|
||||
|
||||
EXPANDFLAGS += $(foreach var,$(PKGVARS),-D$(var)="$(strip $($(var)))")
|
||||
PKGCONFIG += epics-base-$(T_A).pc
|
||||
ifeq ($(T_A),$(EPICS_HOST_ARCH))
|
||||
PKGCONFIG += epics-base.pc
|
||||
endif
|
||||
|
||||
EXPAND += $(PKGCONFIG:%=%@)
|
||||
CLEANS += epics-base-$(T_A).pc@
|
||||
|
||||
include $(TOP)/configure/RULES
|
||||
|
||||
epics-base-$(T_A).pc@: ../epics-base-arch.pc@
|
||||
@$(RM) $@
|
||||
@$(CP) $< $@
|
||||
|
||||
@@ -127,6 +127,33 @@ if ($opt_D) { # Output dependencies only
|
||||
open my $out, '>', $opt_o or
|
||||
die "Can't create $opt_o: $!\n";
|
||||
|
||||
my $podHtml;
|
||||
my $idify;
|
||||
|
||||
if ($::XHTML) {
|
||||
$podHtml = Pod::Simple::XHTML->new();
|
||||
$podHtml->html_doctype(<< '__END_DOCTYPE');
|
||||
<?xml version='1.0' encoding='iso-8859-1'?>
|
||||
<!DOCTYPE html PUBLIC '-//W3C//DTD XHTML 1.0 Transitional//EN'
|
||||
'http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd'>
|
||||
__END_DOCTYPE
|
||||
$podHtml->html_header_tags($podHtml->html_header_tags .
|
||||
"\n<link rel='stylesheet' href='style.css' type='text/css'>");
|
||||
|
||||
$idify = sub {
|
||||
my $title = shift;
|
||||
return $podHtml->idify($title, 1);
|
||||
}
|
||||
} else { # Fall back to HTML
|
||||
$podHtml = Pod::Simple::HTML->new();
|
||||
$podHtml->html_css('style.css');
|
||||
|
||||
$idify = sub {
|
||||
my $title = shift;
|
||||
return Pod::Simple::HTML::esc($podHtml->section_escape($title));
|
||||
}
|
||||
}
|
||||
|
||||
# Parse the Pod text from the root DBD object
|
||||
my $pod = join "\n", '=for html <div class="pod">', '',
|
||||
map {
|
||||
@@ -156,22 +183,6 @@ my $pod = join "\n", '=for html <div class="pod">', '',
|
||||
} $dbd->pod,
|
||||
'=for html </div>', '';
|
||||
|
||||
my $podHtml;
|
||||
|
||||
if ($::XHTML) {
|
||||
$podHtml = Pod::Simple::XHTML->new();
|
||||
$podHtml->html_doctype(<< '__END_DOCTYPE');
|
||||
<?xml version='1.0' encoding='iso-8859-1'?>
|
||||
<!DOCTYPE html PUBLIC '-//W3C//DTD XHTML 1.0 Transitional//EN'
|
||||
'http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd'>
|
||||
__END_DOCTYPE
|
||||
$podHtml->html_header_tags($podHtml->html_header_tags .
|
||||
"\n<link rel='stylesheet' href='style.css' type='text/css'>");
|
||||
} else { # Fall back to HTML
|
||||
$podHtml = Pod::Simple::HTML->new();
|
||||
$podHtml->html_css('style.css');
|
||||
}
|
||||
|
||||
$podHtml->force_title(encode_entities($title));
|
||||
$podHtml->perldoc_url_prefix('');
|
||||
$podHtml->perldoc_url_postfix('.html');
|
||||
@@ -249,7 +260,7 @@ sub fieldTableRow {
|
||||
if ($type eq 'MENU') {
|
||||
my $mn = $fld->attribute('menu');
|
||||
my $menu = $dbd->menu($mn);
|
||||
my $url = $menu ? "#Menu_$mn" : "${mn}.html";
|
||||
my $url = $menu ? '#' . &$idify("Menu $mn") : "${mn}.html";
|
||||
$html .= " (<a href='$url'>$mn</a>)";
|
||||
}
|
||||
$html .= '</td><td class="cell">';
|
||||
@@ -377,6 +388,8 @@ can be found in the aai and aSub record types.
|
||||
|
||||
If you look at the L<aoRecord.dbd.pod> file you'll see that the POD there starts
|
||||
by documenting a record-specific menu definition. The "menu" keyword generates a
|
||||
table that lists all the choices found in the named menu.
|
||||
table that lists all the choices found in the named menu. Any MENU fields in the
|
||||
field tables that refer to a locally-defined menu will generate a link to a
|
||||
document section which must be titled "Menu [menuName]".
|
||||
|
||||
=cut
|
||||
|
||||
@@ -12,6 +12,7 @@ ARCH=@ARCH@
|
||||
OS_CLASS=@OS_CLASS@
|
||||
CMPLR_CLASS=@CMPLR_CLASS@
|
||||
|
||||
EPICS_BASE_HOST_LIBS=@EPICS_BASE_HOST_LIBS@
|
||||
EPICS_BASE_IOC_LIBS=@EPICS_BASE_IOC_LIBS@
|
||||
|
||||
# Directories
|
||||
@@ -14,6 +14,7 @@ DIRS += ioc/db
|
||||
DIRS += ioc/dbtemplate
|
||||
|
||||
DIRS += std/rec
|
||||
DIRS += std/link
|
||||
DIRS += std/filters
|
||||
|
||||
DIRS += tools
|
||||
|
||||
@@ -79,6 +79,11 @@ dbServer one = {
|
||||
oneInit, oneRun, onePause, oneStop
|
||||
};
|
||||
|
||||
dbServer one2 = {
|
||||
ELLNODE_INIT, "one",
|
||||
oneReport, oneStats, oneClient,
|
||||
oneInit, oneRun, onePause, oneStop
|
||||
};
|
||||
|
||||
/* Server layer for testing NULL methods */
|
||||
|
||||
@@ -123,7 +128,7 @@ MAIN(dbServerTest)
|
||||
char *theName = "The One";
|
||||
int status;
|
||||
|
||||
testPlan(24);
|
||||
testPlan(25);
|
||||
|
||||
/* Prove that we handle substring names properly */
|
||||
epicsEnvSet("EPICS_IOC_IGNORE_SERVERS", "none ones");
|
||||
@@ -132,7 +137,8 @@ MAIN(dbServerTest)
|
||||
testOk(dbRegisterServer(&one) == 0, "Registered 'one'");
|
||||
testOk1(oneState == NOTHING_CALLED);
|
||||
|
||||
testOk(dbRegisterServer(&one) != 0, "Duplicate registration rejected");
|
||||
testOk(dbRegisterServer(&one) == 0, "Duplicate registration accepted");
|
||||
testOk(dbRegisterServer(&one2) != 0, "change registration rejected");
|
||||
testOk(dbRegisterServer(&illegal) != 0, "Illegal registration rejected");
|
||||
|
||||
testDiag("Registering dbServer 'no-routines'");
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
use strict;
|
||||
use Test;
|
||||
|
||||
BEGIN {plan tests => 9}
|
||||
BEGIN {plan tests => 12}
|
||||
|
||||
# Check include/substitute command model
|
||||
ok(msi('-I .. ../t1-template.txt'), slurp('../t1-result.txt'));
|
||||
@@ -33,16 +33,9 @@ ok(msi('-S../t6-substitute.txt ../t6-template.txt'), slurp('../t6-result.txt'));
|
||||
|
||||
# Output option -o and verbose option -V
|
||||
my $out = 't7-output.txt';
|
||||
my $count = 5; # Try up to 5 times...
|
||||
my $result;
|
||||
do {
|
||||
unlink $out;
|
||||
msi("-I.. -V -o $out ../t1-template.txt");
|
||||
$result = slurp($out);
|
||||
print "# msi output file empty, retrying\n"
|
||||
if $result eq '';
|
||||
} while ($result eq '') && (--$count > 0);
|
||||
ok($result, slurp('../t7-result.txt'));
|
||||
unlink $out;
|
||||
msi("-I.. -V -o $out ../t1-template.txt");
|
||||
ok(slurp($out), slurp('../t7-result.txt'));
|
||||
|
||||
# Dependency generation, include/substitute model
|
||||
ok(msi('-I.. -D -o t8.txt ../t1-template.txt'), slurp('../t8-result.txt'));
|
||||
@@ -50,6 +43,17 @@ ok(msi('-I.. -D -o t8.txt ../t1-template.txt'), slurp('../t8-result.txt'));
|
||||
# Dependency generation, dbLoadTemplate format
|
||||
ok(msi('-I.. -D -ot9.txt -S ../t2-substitution.txt'), slurp('../t9-result.txt'));
|
||||
|
||||
# Substitution file, variable format, with 0 variable definitions
|
||||
ok(msi('-I. -I.. -S ../t10-substitute.txt'), slurp('../t10-result.txt'));
|
||||
|
||||
# Substitution file, pattern format, with 0 pattern definitions
|
||||
ok(msi('-I. -I.. -S ../t11-substitute.txt'), slurp('../t11-result.txt'));
|
||||
|
||||
# Substitution file, environment variable macros in template filename
|
||||
my %envs = (TEST_NO => 12, PREFIX => 't');
|
||||
@ENV{ keys %envs } = values %envs;
|
||||
ok(msi('-I. -I.. -S ../t12-substitute.txt'), slurp('../t12-result.txt'));
|
||||
delete @ENV{ keys %envs }; # Not really needed
|
||||
|
||||
# Test support routines
|
||||
|
||||
@@ -63,21 +67,8 @@ sub slurp {
|
||||
|
||||
sub msi {
|
||||
my ($args) = @_;
|
||||
my $nul = $^O eq 'MSWin32' ? 'NUL' : '/dev/null';
|
||||
my $msi = '@TOP@/bin/@ARCH@/msi';
|
||||
$msi .= '.exe' if ($^O eq 'MSWin32') || ($^O eq 'cygwin');
|
||||
my $result;
|
||||
if ($args =~ m/-o / && $args !~ m/-D/) {
|
||||
# An empty result is expected
|
||||
$result = `$msi $args`;
|
||||
}
|
||||
else {
|
||||
# Try up to 5 times, sometimes msi fails on Windows
|
||||
my $count = 5;
|
||||
do {
|
||||
$result = `$msi $args`;
|
||||
print "# result of '$msi $args' empty, retrying\n"
|
||||
if $result eq '';
|
||||
} while ($result eq '') && (--$count > 0);
|
||||
}
|
||||
return $result;
|
||||
$msi =~ tr(/)(\\) if $^O eq 'MSWin32';
|
||||
return `$msi $args 2>$nul`;
|
||||
}
|
||||
|
||||
4
modules/database/test/ioc/dbtemplate/t10-result.txt
Normal file
4
modules/database/test/ioc/dbtemplate/t10-result.txt
Normal file
@@ -0,0 +1,4 @@
|
||||
# comment line
|
||||
a=$(a)
|
||||
# comment line
|
||||
a=gbl
|
||||
8
modules/database/test/ioc/dbtemplate/t10-substitute.txt
Normal file
8
modules/database/test/ioc/dbtemplate/t10-substitute.txt
Normal file
@@ -0,0 +1,8 @@
|
||||
file t10-template.txt {
|
||||
{}
|
||||
}
|
||||
|
||||
global { a=gbl }
|
||||
file t10-template.txt {
|
||||
{}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user