Compare commits
315 Commits
libcom-3.1
...
R3.16.2-rc
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8a7442e878 | ||
|
|
31870b4c41 | ||
|
|
ae38fb2c1c | ||
|
|
a3ace1f260 | ||
|
|
728bb556cf | ||
|
|
b7afb287d5 | ||
|
|
275d36b09d | ||
|
|
a81c3503d2 | ||
|
|
03b8257d71 | ||
|
|
46370302f6 | ||
|
|
ab59c97f4b | ||
|
|
77bdea22f8 | ||
|
|
00ee7bf7d3 | ||
|
|
3c607d9034 | ||
|
|
06f522b253 | ||
|
|
c6476fbbdc | ||
|
|
b336545853 | ||
|
|
63994839d0 | ||
|
|
1564f87bd6 | ||
|
|
6eb88b16a0 | ||
|
|
27ee078bc8 | ||
|
|
a62c357e99 | ||
|
|
ea0556e471 | ||
|
|
891caa5933 | ||
|
|
b18478077a | ||
|
|
dd78ab0888 | ||
|
|
f1e55ef240 | ||
|
|
2c07e5fbb9 | ||
|
|
11ba48232c | ||
|
|
531ab6fc36 | ||
|
|
e7e9e66651 | ||
|
|
453ad41c48 | ||
|
|
eed208afaa | ||
|
|
ae63854dff | ||
|
|
ce7943fb44 | ||
|
|
4e865a03d8 | ||
|
|
9a4febd3bc | ||
|
|
dc5d373b57 | ||
|
|
47c361f135 | ||
|
|
9943796f7f | ||
| 3cb72ec209 | |||
| 701ef5b936 | |||
| c3995a9d63 | |||
| ce3eadde34 | |||
|
|
62929fcbd1 | ||
|
|
456a68eb96 | ||
|
|
b319b4722f | ||
|
|
b4cc5fdf4b | ||
|
|
d35835659c | ||
| 65714033ea | |||
| 7151fbe498 | |||
|
|
cbb13bf6b1 | ||
|
|
150d764d28 | ||
|
|
7dd1ea4cab | ||
|
|
922ed30136 | ||
|
|
9f9f119e7e | ||
|
|
d8214a4531 | ||
|
|
120b100e7e | ||
|
|
949e9d788a | ||
|
|
27c6e6a385 | ||
|
|
4b59476170 | ||
|
|
526b565c6b | ||
|
|
1b7b2bcceb | ||
|
|
49c925d064 | ||
|
|
46b5d6006e | ||
|
|
6a2ed4b333 | ||
|
|
73b81ad139 | ||
|
|
7c00cc8045 | ||
|
|
b2bb14c654 | ||
|
|
ae5122759d | ||
|
|
d3bcf5737f | ||
|
|
6c5505ad3e | ||
|
|
4247d98b08 | ||
|
|
13735a8088 | ||
|
|
58d4242b68 | ||
|
|
8e42f516b0 | ||
|
|
9051cdbb34 | ||
|
|
8ffea9de27 | ||
|
|
2548a37267 | ||
|
|
592a83385d | ||
|
|
1ffd30c6d4 | ||
|
|
6e85a407da | ||
|
|
76a4a20698 | ||
|
|
7626856a20 | ||
|
|
1dc1b25aaa | ||
|
|
fb31dd784b | ||
|
|
fe7260e263 | ||
|
|
67e2b74758 | ||
|
|
c09b6e2f1b | ||
|
|
45be2306bd | ||
|
|
6027f906c3 | ||
|
|
ec351c5e2f | ||
| 89870e2817 | |||
| 4e9cf72d71 | |||
| 80869a0868 | |||
| 998fa984ba | |||
| 31844af88e | |||
| 8f161f9463 | |||
| e0399478ad | |||
|
|
2a2a1e54ac | ||
|
|
20d2cff501 | ||
|
|
860ce156a2 | ||
|
|
27431facb8 | ||
|
|
fe4b5d7d72 | ||
|
|
a447ed8bd0 | ||
|
|
7ef9ea7193 | ||
|
|
0f21196670 | ||
|
|
31fc35fbe8 | ||
|
|
f892731b3f | ||
|
|
d20ce9e6bc | ||
|
|
e82f59a2d7 | ||
|
|
6761726e95 | ||
|
|
4e24acebfe | ||
| ab493264b2 | |||
| 68779943eb | |||
| 7a5ff26984 | |||
| eae59183cc | |||
|
|
8144d2ea01 | ||
|
|
b32629c3bf | ||
|
|
220e404203 | ||
|
|
83b17d5061 | ||
|
|
65dec97f9e | ||
|
|
dcb494b494 | ||
|
|
8f55a1307d | ||
|
|
e459e8bdd4 | ||
|
|
b558bd9b16 | ||
|
|
3c16c3c0da | ||
|
|
7d28ae3732 | ||
|
|
a9d7f7be13 | ||
|
|
c0a7ab976c | ||
|
|
b029448059 | ||
|
|
3b77d9be8c | ||
|
|
6e3aa77c42 | ||
|
|
2fb46fc541 | ||
|
|
36f23f3aec | ||
|
|
ffe6fceffa | ||
|
|
bcfdc8d368 | ||
|
|
c6c25ab43d | ||
|
|
5796f717ef | ||
|
|
89b9e240b0 | ||
|
|
8cdcaf5a87 | ||
|
|
f2ceb3bbbf | ||
|
|
fd30989f63 | ||
|
|
55db6525ee | ||
|
|
fe1ec6ed31 | ||
|
|
8fb6c6d610 | ||
|
|
23c4eb42a3 | ||
|
|
6d7f70f200 | ||
|
|
67844bacc3 | ||
|
|
7e7d230d8c | ||
|
|
c0cbf8e985 | ||
|
|
498b248811 | ||
|
|
c1ece40f41 | ||
|
|
a732539eee | ||
|
|
3bc0805a89 | ||
|
|
c72e35c769 | ||
|
|
7e293e60a6 | ||
|
|
c80783dfa9 | ||
|
|
3b6a4ad5a6 | ||
| ccc8f75ec7 | |||
| d9742d5240 | |||
| 7aa2ae2094 | |||
| ab517a9392 | |||
| 4df39bb425 | |||
| 6ff271527b | |||
|
|
2d9c5e99a1 | ||
|
|
ca22d50831 | ||
|
|
3b89515664 | ||
|
|
1893cb4f54 | ||
|
|
2b1d5ae4e3 | ||
|
|
1ca8535266 | ||
|
|
c0cbbd8bee | ||
|
|
fef15d6c91 | ||
|
|
805e62b29c | ||
|
|
d94c8d1e37 | ||
|
|
a4fcd2296a | ||
|
|
4972803ce2 | ||
|
|
3b7e348a8c | ||
|
|
00a974ce52 | ||
|
|
490c504736 | ||
|
|
35ad28dde1 | ||
|
|
ba4c609506 | ||
|
|
49371cfe00 | ||
|
|
06ad4a0d70 | ||
|
|
a2ae07dfcd | ||
|
|
b539ced6d5 | ||
|
|
1b332361e7 | ||
|
|
5cb91d9f6d | ||
|
|
116c90c2ea | ||
|
|
3f3696fb91 | ||
|
|
57eea6a153 | ||
|
|
877d38e79a | ||
|
|
92f0f65d2c | ||
|
|
7cef334b64 | ||
|
|
b8a0792fae | ||
|
|
91ce807e8b | ||
|
|
c2c32e5876 | ||
|
|
ca2003bb63 | ||
|
|
3d88316eab | ||
|
|
b9443f8813 | ||
|
|
086bc961a4 | ||
|
|
58dc1ced9b | ||
|
|
d8802c8b24 | ||
|
|
b7d4609e57 | ||
|
|
7b5b23f6d3 | ||
|
|
c8a7e1597d | ||
|
|
0f7a7902e4 | ||
|
|
42403232e9 | ||
|
|
8333338f99 | ||
|
|
ceaff61c09 | ||
|
|
12bb8969ad | ||
|
|
ea408578e0 | ||
|
|
2307e94d1c | ||
|
|
05a3699b49 | ||
|
|
97ea68d40c | ||
|
|
f44da65942 | ||
|
|
be8f35d782 | ||
|
|
6cc623a7b4 | ||
| 2b4a9632b7 | |||
| 396cf4ee3f | |||
|
|
d7e416e76a | ||
|
|
c05101bb3f | ||
|
|
958c81db89 | ||
|
|
9020c2ce1a | ||
|
|
8f64af96fd | ||
|
|
54c47f02de | ||
|
|
d87ac0319b | ||
|
|
20404003bf | ||
|
|
1fa5d9d3b6 | ||
|
|
5e394e4928 | ||
|
|
c0d4835e66 | ||
|
|
8ae34ba01d | ||
|
|
98f656fc96 | ||
|
|
1458f8640e | ||
|
|
a9764c8f62 | ||
|
|
98d9ea4545 | ||
|
|
8eb4eec7d2 | ||
|
|
98930eebc4 | ||
| e50c468512 | |||
|
|
292141458c | ||
|
|
c18b6f2ccf | ||
|
|
e41f8bf518 | ||
|
|
ae548d3400 | ||
| 428a8f57e9 | |||
| 29795656e6 | |||
| b2d6b67b06 | |||
| 1e9826d187 | |||
| 0691fc5f57 | |||
| 8a3080c16f | |||
| d19afc73af | |||
| adf5375616 | |||
|
|
85c6e9bdfb | ||
|
|
550beeab9f | ||
|
|
bf91275200 | ||
|
|
ac4d5c95ac | ||
| 7d836d9554 | |||
| 51e492fbb1 | |||
|
|
cd8fd8a08f | ||
|
|
61296b8cff | ||
|
|
de442e9584 | ||
|
|
ac367398b3 | ||
|
|
713c2d5080 | ||
|
|
eef6f3afbb | ||
|
|
af07016464 | ||
|
|
734d16291f | ||
|
|
f1e5e9689b | ||
|
|
1454f42a27 | ||
|
|
506be838af | ||
|
|
ddbdcf9462 | ||
|
|
729e6fda4d | ||
|
|
0315e90e6e | ||
|
|
00f30ac53a | ||
|
|
66c6aaa44f | ||
|
|
c830a3a4ee | ||
|
|
1daab5fb35 | ||
|
|
f5cd555383 | ||
|
|
f527e5939e | ||
|
|
d41f2e6806 | ||
|
|
692b971e06 | ||
|
|
8766ce05aa | ||
|
|
84e0220852 | ||
|
|
8f62940265 | ||
|
|
8a1477ecab | ||
|
|
5c97e54cf7 | ||
|
|
3646493014 | ||
|
|
89cbb95c2c | ||
|
|
006ce1a240 | ||
|
|
276dee2c3e | ||
|
|
98a2871727 | ||
|
|
5ca1bb3bd5 | ||
|
|
f6be3c7f70 | ||
|
|
00924dcba0 | ||
|
|
8e2b782b7c | ||
|
|
3b0f34e0be | ||
|
|
2d1f1ed2fa | ||
|
|
db005ac3fe | ||
|
|
c28a360357 | ||
|
|
0f0deb8903 | ||
|
|
baf997193e | ||
|
|
5278799575 | ||
|
|
aebfe587d8 | ||
|
|
031e755303 | ||
|
|
e794639e31 | ||
|
|
2af98c33c9 | ||
|
|
e8b4f448ea | ||
|
|
d5f74fe006 | ||
|
|
6923ca9fda | ||
|
|
aace975de1 | ||
|
|
c8b60e0f1b | ||
|
|
1255cdc9ee | ||
|
|
12da38a7ca | ||
|
|
f78e1f39d7 | ||
|
|
684fe10d8c | ||
|
|
15b97f65cb | ||
|
|
c1b0c1bac1 |
@@ -11,8 +11,8 @@ env:
|
||||
- CMPLR=clang STATIC=YES
|
||||
- WINE=32 TEST=NO STATIC=YES
|
||||
- WINE=32 TEST=NO STATIC=NO
|
||||
- RTEMS=4.10 TEST=NO
|
||||
- RTEMS=4.9 TEST=NO
|
||||
- RTEMS=4.10 TEST=YES
|
||||
- RTEMS=4.9 TEST=YES
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
@@ -25,8 +25,8 @@ addons:
|
||||
- flex
|
||||
- texinfo
|
||||
- install-info
|
||||
- qemu-system-x86
|
||||
cache:
|
||||
directories:
|
||||
- $HOME/.cache
|
||||
install: sh ci/travis-prepare.sh </dev/null
|
||||
script: sh ci/travis-build.sh </dev/null
|
||||
|
||||
2
README
2
README
@@ -20,5 +20,5 @@ Additional information about EPICS including mailing list
|
||||
archives and subscription instructions, documentation and
|
||||
training materials, additional components, links to other
|
||||
websites etc. is available on the EPICS home page at
|
||||
http://www.aps.anl.gov/epics/
|
||||
https://epics.anl.gov/
|
||||
|
||||
|
||||
@@ -40,11 +40,12 @@ configuration:
|
||||
# Environment variables: compiler toolchain
|
||||
environment:
|
||||
matrix:
|
||||
- TOOLCHAIN: 9.0
|
||||
- TOOLCHAIN: 10.0
|
||||
- TOOLCHAIN: 11.0
|
||||
- TOOLCHAIN: 12.0
|
||||
- TOOLCHAIN: 14.0
|
||||
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
|
||||
TOOLCHAIN: 2017
|
||||
- TOOLCHAIN: cygwin
|
||||
- TOOLCHAIN: mingw
|
||||
|
||||
@@ -57,8 +58,6 @@ platform:
|
||||
matrix:
|
||||
exclude:
|
||||
# VS Express installs don't have the 64 bit compiler
|
||||
- platform: x64
|
||||
TOOLCHAIN: 9.0
|
||||
- platform: x64
|
||||
TOOLCHAIN: 10.0
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
:: Universal build script for AppVeyor (https://ci.appveyor.com/)
|
||||
:: Environment:
|
||||
:: TOOLCHAIN - toolchain version [9.0/10.0/11.0/12.0/14.0/cygwin/mingw]
|
||||
:: TOOLCHAIN - toolchain version [10.0/11.0/12.0/14.0/2017/cygwin/mingw]
|
||||
:: CONFIGURATION - determines EPICS build [dynamic/static]
|
||||
:: PLATFORM - architecture [x86/x64]
|
||||
::
|
||||
@@ -57,10 +57,22 @@ if "%TOOLCHAIN%"=="mingw" (
|
||||
)
|
||||
|
||||
set "VSINSTALL=C:\Program Files (x86)\Microsoft Visual Studio %TOOLCHAIN%"
|
||||
if not exist "%VSINSTALL%\" set "VSINSTALL=C:\Program Files (x86)\Microsoft Visual Studio\%TOOLCHAIN%\Community"
|
||||
if not exist "%VSINSTALL%\" goto MSMissing
|
||||
|
||||
set "MAKE=C:\tools\make"
|
||||
|
||||
echo [INFO] APPVEYOR_BUILD_WORKER_IMAGE=%APPVEYOR_BUILD_WORKER_IMAGE%
|
||||
|
||||
if "%OS%"=="64BIT" (
|
||||
set EPICS_HOST_ARCH=windows-x64%ST%
|
||||
:: VS 2017
|
||||
if exist "%VSINSTALL%\VC\Auxiliary\Build\vcvars64.bat" (
|
||||
call "%VSINSTALL%\VC\Auxiliary\Build\vcvars64.bat"
|
||||
where cl
|
||||
if !ERRORLEVEL! NEQ 0 goto MSMissing
|
||||
goto MSFound
|
||||
)
|
||||
if exist "%VSINSTALL%\VC\vcvarsall.bat" (
|
||||
call "%VSINSTALL%\VC\vcvarsall.bat" amd64
|
||||
where cl
|
||||
@@ -79,12 +91,19 @@ if "%OS%"=="64BIT" (
|
||||
)
|
||||
) else (
|
||||
set EPICS_HOST_ARCH=win32-x86%ST%
|
||||
:: VS 2017
|
||||
if exist "%VSINSTALL%\VC\Auxiliary\Build\vcvars32.bat" (
|
||||
call "%VSINSTALL%\VC\Auxiliary\Build\vcvars32.bat"
|
||||
where cl
|
||||
if !ERRORLEVEL! NEQ 0 goto MSMissing
|
||||
goto MSFound
|
||||
)
|
||||
if exist "%VSINSTALL%\VC\vcvarsall.bat" (
|
||||
call "%VSINSTALL%\VC\vcvarsall.bat" x86
|
||||
where cl
|
||||
if !ERRORLEVEL! NEQ 0 goto MSMissing
|
||||
goto MSFound
|
||||
)
|
||||
)
|
||||
if exist "%VSINSTALL%\VC\bin\vcvars32.bat" (
|
||||
call "%VSINSTALL%\VC\bin\vcvars32.bat"
|
||||
where cl
|
||||
|
||||
@@ -65,6 +65,6 @@ if "%TOOLCHAIN%"=="mingw" (
|
||||
)
|
||||
|
||||
echo [INFO] Installing Make 4.1
|
||||
@powershell -Command "(new-object net.webclient).DownloadFile('https://www.aps.anl.gov/epics/download/tools/make-4.1-win64.zip', 'C:\tools\make-4.1.zip')"
|
||||
curl -fsS --retry 3 -o C:\tools\make-4.1.zip https://epics.anl.gov/download/tools/make-4.1-win64.zip
|
||||
cd \tools
|
||||
"C:\Program Files\7-Zip\7z" e make-4.1.zip
|
||||
|
||||
@@ -6,18 +6,9 @@ die() {
|
||||
exit 1
|
||||
}
|
||||
|
||||
ticker() {
|
||||
while true
|
||||
do
|
||||
sleep 60
|
||||
date -R
|
||||
[ -r "$1" ] && tail -n10 "$1"
|
||||
done
|
||||
}
|
||||
|
||||
CACHEKEY=1
|
||||
|
||||
EPICS_HOST_ARCH=`sh startup/EpicsHostArch`
|
||||
EPICS_HOST_ARCH=`perl src/tools/EpicsHostArch.pl`
|
||||
|
||||
[ -e configure/os/CONFIG_SITE.Common.linux-x86 ] || die "Wrong location: $PWD"
|
||||
|
||||
@@ -62,23 +53,22 @@ if [ -n "$RTEMS" ]
|
||||
then
|
||||
echo "Cross RTEMS${RTEMS} for pc386"
|
||||
install -d /home/travis/.cache
|
||||
curl -L "https://github.com/mdavidsaver/rsb/releases/download/travis-20160306-2/rtems${RTEMS}-i386-trusty-20190306-2.tar.gz" \
|
||||
| tar -C /home/travis/.cache -xj
|
||||
curl -L "https://github.com/mdavidsaver/rsb/releases/download/20171203-${RTEMS}/i386-rtems${RTEMS}-trusty-20171203-${RTEMS}.tar.bz2" \
|
||||
| tar -C / -xmj
|
||||
|
||||
sed -i -e '/^RTEMS_VERSION/d' -e '/^RTEMS_BASE/d' configure/os/CONFIG_SITE.Common.RTEMS
|
||||
cat << EOF >> configure/os/CONFIG_SITE.Common.RTEMS
|
||||
RTEMS_VERSION=$RTEMS
|
||||
RTEMS_BASE=/home/travis/.cache/rtems${RTEMS}-i386
|
||||
RTEMS_BASE=/home/travis/.rtems
|
||||
EOF
|
||||
cat << EOF >> configure/CONFIG_SITE
|
||||
CROSS_COMPILER_TARGET_ARCHS+=RTEMS-pc386
|
||||
CROSS_COMPILER_TARGET_ARCHS += RTEMS-pc386-qemu
|
||||
CROSS_COMPILER_RUNTEST_ARCHS += RTEMS-pc386-qemu
|
||||
EOF
|
||||
|
||||
# find local qemu-system-i386
|
||||
export PATH="$HOME/.cache/qemu/usr/bin:$PATH"
|
||||
echo -n "Using QEMU: "
|
||||
type qemu-system-i386 || echo "Missing qemu"
|
||||
EXTRA=RTEMS_QEMU_FIXUPS=YES
|
||||
fi
|
||||
|
||||
make -j2 $EXTRA
|
||||
|
||||
@@ -1,40 +0,0 @@
|
||||
#!/bin/sh
|
||||
set -e -x
|
||||
|
||||
die() {
|
||||
echo "$1" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
CURDIR="$PWD"
|
||||
|
||||
QDIR="$HOME/.cache/qemu"
|
||||
|
||||
if [ -n "$RTEMS" -a "$TEST" = "YES" ]
|
||||
then
|
||||
git clone --quiet --branch vme --depth 10 https://github.com/mdavidsaver/qemu.git "$HOME/.build/qemu"
|
||||
cd "$HOME/.build/qemu"
|
||||
|
||||
HEAD=`git log -n1 --pretty=format:%H`
|
||||
echo "HEAD revision $HEAD"
|
||||
|
||||
[ -e "$HOME/.cache/qemu/built" ] && BUILT=`cat "$HOME/.cache/qemu/built"`
|
||||
echo "Cached revision $BUILT"
|
||||
|
||||
if [ "$HEAD" != "$BUILT" ]
|
||||
then
|
||||
echo "Building QEMU"
|
||||
git submodule --quiet update --init
|
||||
|
||||
install -d "$HOME/.build/qemu/build"
|
||||
cd "$HOME/.build/qemu/build"
|
||||
|
||||
"$HOME/.build/qemu/configure" --prefix="$HOME/.cache/qemu/usr" --target-list=i386-softmmu --disable-werror
|
||||
make -j2
|
||||
make install
|
||||
|
||||
echo "$HEAD" > "$HOME/.cache/qemu/built"
|
||||
fi
|
||||
fi
|
||||
|
||||
cd "$CURDIR"
|
||||
@@ -20,11 +20,15 @@ else
|
||||
endif
|
||||
|
||||
# Provide a default if the user hasn't set EPICS_HOST_ARCH
|
||||
ifeq ($(origin EPICS_HOST_ARCH), undefined)
|
||||
# NB: We use a simply expanded variable here for performance:
|
||||
EPICS_HOST_ARCH := $(shell $(CONFIG)/../startup/EpicsHostArch.pl)
|
||||
endif
|
||||
#
|
||||
ifeq ($(origin EPICS_HOST_ARCH), undefined)
|
||||
# Bootstrapping ...
|
||||
EHA := $(firstword $(wildcard $(EPICS_BASE)/lib/perl/EpicsHostArch.pl \
|
||||
$(TOP)/src/tools/EpicsHostArch.pl))
|
||||
# NB: We use a simply expanded variable here for performance:
|
||||
export EPICS_HOST_ARCH := $(shell perl $(EHA))
|
||||
EHA :=
|
||||
endif
|
||||
|
||||
-include $(CONFIG)/RELEASE
|
||||
-include $(CONFIG)/RELEASE.$(EPICS_HOST_ARCH)
|
||||
|
||||
@@ -67,7 +67,7 @@ REGISTERRECORDDEVICEDRIVER = $(PERL) $(TOOLS)/registerRecordDeviceDriver.pl
|
||||
CONVERTRELEASE = $(PERL) $(call FIND_TOOL,convertRelease.pl)
|
||||
FULLPATHNAME = $(PERL) $(TOOLS)/fullPathName.pl
|
||||
TAPTOJUNIT = $(PERL) $(TOOLS)/tap-to-junit-xml.pl
|
||||
GENVERSIONHEADER = $(PERL) $(TOOLS)/genVersionHeader.pl $(QUIET_FLAG)
|
||||
GENVERSIONHEADER = $(PERL) $(TOOLS)/genVersionHeader.pl $(QUIET_FLAG) $(QUESTION_FLAG)
|
||||
|
||||
#---------------------------------------------------------------
|
||||
# tools for installing libraries and products
|
||||
|
||||
@@ -38,19 +38,19 @@ EPICS_VERSION = 3
|
||||
EPICS_REVISION = 16
|
||||
|
||||
# EPICS_MODIFICATION must be a number >=0 and <256
|
||||
EPICS_MODIFICATION = 1
|
||||
EPICS_MODIFICATION = 2
|
||||
|
||||
# EPICS_PATCH_LEVEL must be a number (win32 resource file requirement)
|
||||
# Not included if zero
|
||||
EPICS_PATCH_LEVEL = 0
|
||||
|
||||
# This will end in -DEV between official releases
|
||||
EPICS_DEV_SNAPSHOT=-DEV
|
||||
#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
|
||||
|
||||
@@ -80,6 +80,7 @@ IOCS_APPL_TOP = $(shell $(FULLPATHNAME) $(INSTALL_LOCATION))
|
||||
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,)
|
||||
|
||||
#-------------------------------------------------------
|
||||
ifdef T_A
|
||||
|
||||
@@ -34,35 +34,30 @@
|
||||
# The future dates below assume the rules don't get changed;
|
||||
# see http://www.timeanddate.com/time/dst/2018.html to check.
|
||||
#
|
||||
# DST for 2017 US: Mar 12 - Nov 05
|
||||
# EU: Mar 26 - Oct 29
|
||||
EPICS_TIMEZONE = CUS::360:031202:110502
|
||||
#EPICS_TIMEZONE = MET::-60:032602:102902
|
||||
#
|
||||
# DST for 2018 US: Mar 11 - Nov 04
|
||||
# EU: Mar 25 - Oct 28
|
||||
#EPICS_TIMEZONE = CUS::360:031102:110402
|
||||
#EPICS_TIMEZONE = MET::-60:032502:102802
|
||||
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 = MET::-60:033102:102702
|
||||
#EPICS_TIMEZONE = MET::-60:033102:102703
|
||||
#
|
||||
# DST for 2020 US: Mar 08 - Nov 01
|
||||
# EU: Mar 29 - Oct 25
|
||||
#EPICS_TIMEZONE = CUS::360:030802:110102
|
||||
#EPICS_TIMEZONE = MET::-60:032902:102502
|
||||
#EPICS_TIMEZONE = MET::-60:032902:102503
|
||||
#
|
||||
# DST for 2021 US: Mar 14 - Nov 07
|
||||
# EU: Mar 28 - Oct 31
|
||||
#EPICS_TIMEZONE = CUS::360:031402:110702
|
||||
#EPICS_TIMEZONE = MET::-60:032802:103102
|
||||
#EPICS_TIMEZONE = MET::-60:032802:103103
|
||||
#
|
||||
# DST for 2022 US: Mar 13 - Nov 06
|
||||
# EU: Mar 27 - Oct 30
|
||||
#EPICS_TIMEZONE = CUS::360:031302:110602
|
||||
#EPICS_TIMEZONE = MET::-60:032702:103002
|
||||
#EPICS_TIMEZONE = MET::-60:032702:103003
|
||||
|
||||
# EPICS_TS_NTP_INET
|
||||
# NTP time server ip address for VxWorks and RTEMS.
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
#*************************************************************************
|
||||
# Copyright (c) 2002 The University of Chicago, as Operator of Argonne
|
||||
# Copyright (c) 2006 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 Versions 3.13.7
|
||||
# and higher are distributed subject to a Software License Agreement found
|
||||
# in file LICENSE that is included with this distribution.
|
||||
# EPICS BASE is distributed subject to a Software License Agreement found
|
||||
# in the file LICENSE that is included with this distribution.
|
||||
#*************************************************************************
|
||||
|
||||
ifndef T_A
|
||||
|
||||
@@ -4,9 +4,10 @@
|
||||
# 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.
|
||||
# in the file LICENSE that is included with this distribution.
|
||||
#*************************************************************************
|
||||
#RULES.Db
|
||||
|
||||
# RULES.Db
|
||||
|
||||
# Set db substitutions and template file suffixes
|
||||
SUBST_SUFFIX ?= .substitutions
|
||||
|
||||
@@ -4,9 +4,10 @@
|
||||
# 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.
|
||||
# in the file LICENSE that is included with this distribution.
|
||||
#*************************************************************************
|
||||
#RULES.ioc
|
||||
|
||||
# RULES.ioc
|
||||
|
||||
include $(CONFIG)/RULES_DIRS
|
||||
|
||||
|
||||
@@ -90,6 +90,4 @@ realclean:
|
||||
.PHONY : $(BUILD_ARCHS) rebuild archsCommonClean
|
||||
.PHONY : $(ACTIONS) clean realclean archclean host all
|
||||
|
||||
# User specific rules
|
||||
#
|
||||
-include $(HOME)/configure/RULES_USER
|
||||
include $(CONFIG)/RULES_COMMON
|
||||
|
||||
@@ -6,11 +6,12 @@
|
||||
# EPICS BASE is distributed subject to a Software License Agreement found
|
||||
# in the file LICENSE that is included with this distribution.
|
||||
#*************************************************************************
|
||||
|
||||
# RULES_BUILD
|
||||
|
||||
# Rules for making things specified in a Makefile
|
||||
#
|
||||
# Rules for making things specified in Makefile
|
||||
#
|
||||
# we are in O.$(T_A), but most sources are elsewhere
|
||||
#
|
||||
# CWD is O.$(T_A), but most sources are elsewhere
|
||||
|
||||
ifndef BASE_RULES_BUILD
|
||||
BASE_RULES_BUILD=1
|
||||
@@ -79,9 +80,9 @@ else
|
||||
host:
|
||||
endif
|
||||
|
||||
-include $(CONFIG)/RULES_FILE_TYPE
|
||||
include $(CONFIG)/RULES_FILE_TYPE
|
||||
|
||||
-include $(CONFIG)/RULES.Db
|
||||
include $(CONFIG)/RULES.Db
|
||||
|
||||
#---------------------------------------------------------------
|
||||
# Include defines and rules for prod, library and test* targets
|
||||
@@ -102,6 +103,7 @@ endif
|
||||
# Products and Object libraries
|
||||
#
|
||||
PRODTARGETS += $(PRODNAME) $(MUNCHNAME) $(CTDT_SRCS) $(CTDT_OBJS) $(NMS)
|
||||
TESTPRODTARGETS += $(TESTPRODNAME) $(TESTMUNCHNAME)
|
||||
|
||||
#---------------------------------------------------------------
|
||||
# Test specifications and test result files
|
||||
@@ -140,7 +142,7 @@ rebuild: clean install
|
||||
|
||||
build: inc
|
||||
|
||||
build: $(OBJSNAME) $(LIBTARGETS) $(PRODTARGETS) $(TESTPRODNAME) \
|
||||
build: $(OBJSNAME) $(LIBTARGETS) $(PRODTARGETS) $(TESTPRODTARGETS) \
|
||||
$(TARGETS) $(TESTSCRIPTS) $(INSTALL_LIB_INSTALLS)
|
||||
|
||||
inc : $(COMMON_INC) $(INSTALL_INC) $(INSTALL_CONFIGS)
|
||||
@@ -158,20 +160,21 @@ clean: build_clean
|
||||
|
||||
build_clean:
|
||||
$(ECHO) "Cleaning"
|
||||
@$(RM) *.i *$(OBJ) *.a $(TESTPRODNAME) \
|
||||
@$(RM) *.i *$(OBJ) *.a \
|
||||
$(LIBNAME) $(TESTLIBNAME) $(SHRLIBNAME) $(TESTSHRLIBNAME) \
|
||||
$(DLLSTUB_LIBNAME) $(TESTDLLSTUB_LIBNAME) \
|
||||
$(LOADABLE_SHRLIBNAME) \
|
||||
$(INC) $(TARGETS) $(TDS) $(CLEANS) \
|
||||
*.out MakefileInclude *.manifest *.exp \
|
||||
$(COMMON_INC) $(HDEPENDS_FILES) $(PRODTARGETS) \
|
||||
$(COMMON_INC) $(HDEPENDS_FILES) $(PRODTARGETS) $(TESTPRODTARGETS) \
|
||||
$(TESTSCRIPTS) $(TAPFILES) $(JUNITFILES)
|
||||
ifdef RES
|
||||
@$(RM) *$(RES)
|
||||
endif
|
||||
|
||||
$(DIRECTORY_TARGETS) :
|
||||
$(MKDIR) $@
|
||||
# Sort mkdir targets to remove duplicates & make parents first
|
||||
$(DIRECTORY_TARGETS):
|
||||
$(MKDIR) $(sort $@)
|
||||
|
||||
# Install LIB_INSTALLS libraries before linking executables
|
||||
$(TESTPRODNAME) $(PRODNAME): | $(INSTALL_LIB_INSTALLS)
|
||||
@@ -183,7 +186,7 @@ endif
|
||||
|
||||
# RELEASE file consistency checking
|
||||
checkRelease:
|
||||
$(CONVERTRELEASE) checkRelease
|
||||
+$(CONVERTRELEASE) checkRelease
|
||||
warnRelease:
|
||||
-$(CONVERTRELEASE) checkRelease
|
||||
noCheckRelease:
|
||||
@@ -254,15 +257,13 @@ YACCOPT ?= $($*_YACCOPT)
|
||||
$(MV) $*.tab.c $*.c
|
||||
$(if $(findstring -d, $(YACCOPT)),$(MV) $*.tab.h $*.h,)
|
||||
|
||||
# must be a seperate rule since when not using '-d' the
|
||||
# must be a separate rule since when not using '-d' the
|
||||
# prefix for .h will be different then .c
|
||||
%.h : %.c %.y
|
||||
|
||||
%.c: %.l
|
||||
@$(RM) $*.yy.c
|
||||
$(LEX) $(LEXOPT) -t $< > $*.yy.c
|
||||
@$(RM) $@
|
||||
$(MV) $*.yy.c $@
|
||||
$(LEX) $(LEXOPT) -o$@ $<
|
||||
|
||||
#---------------------------------------------------------------
|
||||
# Libraries, shared/DLL and stubs
|
||||
@@ -321,6 +322,10 @@ $(MUNCHNAME): %$(MUNCH_SUFFIX): $(MUNCH_DEPENDS) %$(EXE)
|
||||
@$(RM) $@
|
||||
$(MUNCH_CMD)
|
||||
|
||||
$(TESTMUNCHNAME): %$(MUNCH_SUFFIX): $(MUNCH_DEPENDS) %$(EXE)
|
||||
@$(RM) $@
|
||||
$(MUNCH_CMD)
|
||||
|
||||
#---------------------------------------------------------------
|
||||
# GeSys modules for RTEMS
|
||||
$(MODNAME): %$(MODEXT): %$(EXE)
|
||||
@@ -380,7 +385,7 @@ endif
|
||||
# Generate a perl program to exec the real test binary.
|
||||
%.t: %$(EXE) $(TOOLS)/makeTestfile.pl
|
||||
@$(RM) $@
|
||||
$(PERL) $(TOOLS)/makeTestfile.pl $@ $<
|
||||
$(PERL) $(TOOLS)/makeTestfile.pl $(T_A) $(EPICS_HOST_ARCH) $@ $<
|
||||
|
||||
#---------------------------------------------------------------
|
||||
# Generate header with version number from VCS
|
||||
@@ -517,7 +522,7 @@ $(INSTALL_TEMPLATES_SUBDIR)/%: %
|
||||
$(ECHO) "Installing $@"
|
||||
@$(INSTALL) -d -m $(INSTALL_PERMISSIONS) $< $(@D)
|
||||
|
||||
-include $(CONFIG)/RULES_EXPAND
|
||||
include $(CONFIG)/RULES_EXPAND
|
||||
|
||||
.PRECIOUS: %.i %.o %.c %.nm %.cpp %.cc
|
||||
.PRECIOUS: $(COMMON_INC)
|
||||
@@ -526,5 +531,9 @@ $(INSTALL_TEMPLATES_SUBDIR)/%: %
|
||||
.PHONY: runtests tapfiles clean-tests test-results junitfiles
|
||||
.PHONY: checkRelease warnRelease noCheckRelease FORCE
|
||||
|
||||
include $(CONFIG)/RULES_COMMON
|
||||
|
||||
else
|
||||
$(warning RULES_BUILD included more than once. \
|
||||
Use 'make show-makefiles' to work out why.)
|
||||
endif # BASE_RULES_BUILD
|
||||
# EOF RULES_BUILD
|
||||
|
||||
35
configure/RULES_COMMON
Normal file
35
configure/RULES_COMMON
Normal file
@@ -0,0 +1,35 @@
|
||||
#*************************************************************************
|
||||
# Copyright (c) 2018 UChicago Argonne LLC, as Operator of Argonne
|
||||
# National Laboratory.
|
||||
# EPICS BASE is distributed subject to a Software License Agreement found
|
||||
# in the file LICENSE that is included with this distribution.
|
||||
#*************************************************************************
|
||||
|
||||
# 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)
|
||||
|
||||
# The sort prevents warnings about duplicate targets:
|
||||
$(sort $(SHOW_MAKEFILES)): show-makefile.%:
|
||||
@echo " $(subst __colon__,:,$(@:show-makefile.%=%))"
|
||||
|
||||
.PHONY: show-makefiles show-makefile.%
|
||||
|
||||
# These rules support printing a Makefile variable values.
|
||||
# Many variables are only set inside an O.<arch> build directory.
|
||||
# make PRINT.T_A
|
||||
|
||||
PRINT_Var = $(@:PRINT.%=%)
|
||||
PRINT.%:
|
||||
@echo $(PRINT_Var) = '$($(PRINT_Var))'
|
||||
|
||||
.PHONY: PRINT PRINT.%
|
||||
|
||||
|
||||
# User specific rules
|
||||
#
|
||||
-include $(HOME)/configure/RULES_USER
|
||||
@@ -92,7 +92,4 @@ $(ARCHS) $(ACTIONS) $(actionArchTargets) :%: \
|
||||
.PHONY : $(dirActionArchTargets)
|
||||
.PHONY : $(actionArchTargets)
|
||||
|
||||
|
||||
# User specific rules
|
||||
#
|
||||
-include $(HOME)/configure/RULES_USER
|
||||
include $(CONFIG)/RULES_COMMON
|
||||
|
||||
@@ -1,4 +1,13 @@
|
||||
# <top>/configure/RULES_EXPAND
|
||||
#*************************************************************************
|
||||
# Copyright (c) 2011 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 the file LICENSE that is included with this distribution.
|
||||
#*************************************************************************
|
||||
|
||||
# RULES_EXPAND
|
||||
|
||||
vpath %@ $(USR_VPATH) $(ALL_SRC_DIRS)
|
||||
|
||||
|
||||
@@ -7,9 +7,11 @@
|
||||
# in the file LICENSE that is included with this distribution.
|
||||
#*************************************************************************
|
||||
|
||||
# Include <top>/configure/RULES_BUILD from tops defined in RELEASE* files
|
||||
# Include <top>/configure/RULES_BUILD from tops defined in RELEASE* files,
|
||||
# excluding EPICS_BASE
|
||||
#
|
||||
RELEASE_RULES_BUILDS = $(foreach top, $(RELEASE_TOPS), \
|
||||
RELEASE_RULES_BUILDS = $(foreach top, \
|
||||
$(filter-out EPICS_BASE, $(RELEASE_TOPS)), \
|
||||
$(wildcard $($(top))/configure/RULES_BUILD))
|
||||
ifneq ($(RELEASE_RULES_BUILDS),)
|
||||
include $(RELEASE_RULES_BUILDS)
|
||||
@@ -23,7 +25,7 @@ ifneq ($(RELEASE_CFG_RULES),)
|
||||
include $(RELEASE_CFG_RULES)
|
||||
endif
|
||||
|
||||
# If this is not BASE then include <TOP>/configure/RULES_BUILD
|
||||
# If this is not BASE then include <top>/configure/RULES_BUILD
|
||||
#
|
||||
ifeq ($(wildcard $(TOP)/configure/CONFIG_BASE_VERSION),)
|
||||
TOP_RULES_BUILDS = $(wildcard $(TOP)/configure/RULES_BUILD)
|
||||
@@ -67,7 +69,3 @@ file_type_clean:
|
||||
@$(RM) $(foreach type, $(FILE_TYPE), $($(type)))
|
||||
|
||||
.PHONY : file_type_clean
|
||||
|
||||
# User specific rules
|
||||
#
|
||||
-include $(HOME)/configure/RULES_USER
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
#*************************************************************************
|
||||
# Copyright (c) 2002 The University of Chicago, as Operator of Argonne
|
||||
# Copyright (c) 2006 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 Versions 3.13.7
|
||||
# and higher are distributed subject to a Software License Agreement found
|
||||
# in file LICENSE that is included with this distribution.
|
||||
# EPICS BASE is distributed subject to a Software License Agreement found
|
||||
# in the file LICENSE that is included with this distribution.
|
||||
#*************************************************************************
|
||||
|
||||
# Octave definitions and rules
|
||||
|
||||
@@ -1,18 +1,13 @@
|
||||
#*************************************************************************
|
||||
# Copyright (c) 2002 The University of Chicago, as Operator of Argonne
|
||||
# Copyright (c) 2011 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 Versions 3.13.7
|
||||
# and higher are distributed subject to a Software License Agreement found
|
||||
# in file LICENSE that is included with this distribution.
|
||||
# EPICS BASE is distributed subject to a Software License Agreement found
|
||||
# in the file LICENSE that is included with this distribution.
|
||||
#*************************************************************************
|
||||
#
|
||||
# RULES_TARGET
|
||||
#
|
||||
# This file is to be maintained by the community.
|
||||
#
|
||||
#-----------------------------------------------------------------------
|
||||
|
||||
# RULES_TARGET
|
||||
|
||||
define TARGET_template
|
||||
$(1)_$(2) += $$(if $$(strip $$($(1)_$(2)_$$(OS_CLASS))), \
|
||||
@@ -38,6 +33,17 @@ $(foreach target, $(PROD) $(TESTPROD), \
|
||||
|
||||
#-----------------------------------------------------------------------
|
||||
|
||||
# These must be done before PROD2_template
|
||||
define TESTLIBRARY_template
|
||||
$(1)_DIR = .
|
||||
TESTBUILD_LIBRARY += $$(if $$(strip $$($(1)_OBJSNAME) $$(LIBRARY_OBJS)),$(1),)
|
||||
endef
|
||||
|
||||
$(foreach target, $(TESTLIBRARY), \
|
||||
$(eval $(call TESTLIBRARY_template,$(strip $(target)))))
|
||||
|
||||
#-----------------------------------------------------------------------
|
||||
|
||||
define TARGET2_template
|
||||
$(1)_LDLIBS += $$($(1)_LIBS)
|
||||
$(1)_LDLIBS += $$(if $$(strip $$($(1)_LIBS_$(OS_CLASS))), \
|
||||
@@ -123,16 +129,6 @@ $(foreach target, $(LIBRARY), \
|
||||
|
||||
#-----------------------------------------------------------------------
|
||||
|
||||
define LIBRARY3_template
|
||||
$(1)_DIR = .
|
||||
TESTBUILD_LIBRARY += $$(if $$(strip $$($(1)_OBJSNAME) $$(LIBRARY_OBJS)),$(1),)
|
||||
endef
|
||||
|
||||
$(foreach target, $(TESTLIBRARY), \
|
||||
$(eval $(call LIBRARY3_template,$(strip $(target)))))
|
||||
|
||||
#-----------------------------------------------------------------------
|
||||
|
||||
define LOADABLE_LIBRARY_template
|
||||
LOADABLE_BUILD_LIBRARY += $$(if $$(strip $$($(1)_OBJSNAME) $$(LIBRARY_OBJS)),$(1),)
|
||||
|
||||
|
||||
@@ -27,9 +27,13 @@ ifneq ($(CONFIG),$(TOP)/configure)
|
||||
-include $(TOP)/configure/CONFIG_SITE.Common.RTEMS
|
||||
endif
|
||||
|
||||
#--------------------------------------------------
|
||||
# Set RTEMS_BSP from T_A if not already done
|
||||
RTEMS_BSP ?= $(subst RTEMS-,,$(T_A))
|
||||
|
||||
#-------------------------------------------------------
|
||||
# Pick up the RTEMS tool/path definitions from the RTEMS BSP directory.
|
||||
include $(RTEMS_BASE)/$(RTEMS_TARGET_CPU)-rtems$(RTEMS_VERSION)/$(subst RTEMS-,,$(T_A))/Makefile.inc
|
||||
include $(RTEMS_BASE)/$(RTEMS_TARGET_CPU)-rtems$(RTEMS_VERSION)/$(RTEMS_BSP)/Makefile.inc
|
||||
include $(RTEMS_CUSTOM)
|
||||
include $(CONFIG.CC)
|
||||
|
||||
@@ -136,6 +140,13 @@ MOD_LDFLAGS = $(OPT_LDFLAGS) $(TARGET_LDFLAGS) $(USR_LDFLAGS) $(POSIX_LDFLAGS) \
|
||||
LINK.mod = $(CCC) -o $@ $(PRODDIR_LDFLAGS) $(MOD_LDFLAGS)
|
||||
LINK.mod += $(PROD_LDFLAGS) $(PROD_LD_OBJS) $(PROD_LD_RESS) $(MOD_LDLIBS)
|
||||
|
||||
#--------------------------------------------------
|
||||
# Here munching means creating a bootable object binary
|
||||
ifdef MUNCH_SUFFIX
|
||||
MUNCHNAME = $(PRODNAME:%$(EXE)=%$(MUNCH_SUFFIX))
|
||||
TESTMUNCHNAME = $(TESTPRODNAME:%$(EXE)=%$(MUNCH_SUFFIX))
|
||||
endif
|
||||
|
||||
#--------------------------------------------------
|
||||
# RTEMS has neither shared libraries nor dynamic loading
|
||||
STATIC_BUILD=YES
|
||||
|
||||
@@ -9,5 +9,6 @@
|
||||
#
|
||||
# All RTEMS targets use the same Makefile fragment
|
||||
#
|
||||
RTEMS_TARGET_CPU=arm
|
||||
RTEMS_BSP = at91rm9200ek
|
||||
RTEMS_TARGET_CPU = arm
|
||||
include $(CONFIG)/os/CONFIG.Common.RTEMS
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
# All RTEMS targets use the same Makefile fragment
|
||||
#
|
||||
EXE = .elf
|
||||
RTEMS_BSP = beatnik
|
||||
RTEMS_TARGET_CPU = powerpc
|
||||
GNU_TARGET = powerpc-rtems
|
||||
ARCH_DEP_CFLAGS += -DMY_DO_BOOTP=NULL
|
||||
@@ -15,7 +16,6 @@ ARCH_DEP_CFLAGS += -DRTEMS_NETWORK_CONFIG_CLUSTER_SPACE=5120
|
||||
OP_SYS_LDLIBS += -lbspExt
|
||||
|
||||
MUNCH_SUFFIX = .boot
|
||||
MUNCHNAME = $(PRODNAME:%$(EXE)=%$(MUNCH_SUFFIX))
|
||||
define MUNCH_CMD
|
||||
$(RTEMS_TOOLS)/bin/$(OBJCOPY_FOR_TARGET) -O binary $< $@
|
||||
endef
|
||||
|
||||
@@ -5,5 +5,6 @@
|
||||
#
|
||||
# All RTEMS targets use the same Makefile fragment
|
||||
#
|
||||
RTEMS_TARGET_CPU=m68k
|
||||
RTEMS_BSP = gen68360
|
||||
RTEMS_TARGET_CPU = m68k
|
||||
include $(CONFIG)/os/CONFIG.Common.RTEMS
|
||||
|
||||
@@ -5,5 +5,6 @@
|
||||
#
|
||||
# All RTEMS targets use the same Makefile fragment
|
||||
#
|
||||
RTEMS_TARGET_CPU=ppc
|
||||
RTEMS_BSP = mcp750
|
||||
RTEMS_TARGET_CPU = ppc
|
||||
include $(CONFIG)/os/CONFIG.Common.RTEMS
|
||||
|
||||
@@ -5,5 +5,6 @@
|
||||
#
|
||||
# All RTEMS targets use the same Makefile fragment
|
||||
#
|
||||
RTEMS_TARGET_CPU=m68k
|
||||
RTEMS_BSP = mvme167
|
||||
RTEMS_TARGET_CPU = m68k
|
||||
include $(CONFIG)/os/CONFIG.Common.RTEMS
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
# All RTEMS targets use the same Makefile fragment
|
||||
#
|
||||
EXE = .elf
|
||||
RTEMS_BSP = mvme2100
|
||||
RTEMS_TARGET_CPU = powerpc
|
||||
GNU_TARGET = powerpc-rtems
|
||||
ARCH_DEP_CFLAGS += -DMY_DO_BOOTP=NULL
|
||||
@@ -13,7 +14,6 @@ ARCH_DEP_CFLAGS += -DHAVE_PPCBUG
|
||||
OP_SYS_LDLIBS += -lbspExt
|
||||
|
||||
MUNCH_SUFFIX = .boot
|
||||
MUNCHNAME = $(PRODNAME:%$(EXE)=%$(MUNCH_SUFFIX))
|
||||
define MUNCH_CMD
|
||||
$(RTEMS_TOOLS)/bin/$(OBJCOPY_FOR_TARGET) -O binary -R .comment -S $< rtems
|
||||
gzip -f9 rtems
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
#
|
||||
# Author: Matt Rippa
|
||||
#
|
||||
RTEMS_BSP = mvme2700
|
||||
RTEMS_TARGET_CPU = powerpc
|
||||
ARCH_DEP_CFLAGS += -DMY_DO_BOOTP=NULL
|
||||
ARCH_DEP_CFLAGS += -DHAVE_PPCBUG
|
||||
ARCH_DEP_CFLAGS += -DNVRAM_INDIRECT
|
||||
|
||||
MUNCH_SUFFIX = .boot
|
||||
MUNCHNAME = $(PRODNAME:%$(EXE)=%$(MUNCH_SUFFIX))
|
||||
define MUNCH_CMD
|
||||
$(RTEMS_TOOLS)/bin/$(OBJCOPY_FOR_TARGET) -O binary -R .comment -S $< rtems
|
||||
gzip -f9 rtems
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
# All RTEMS targets use the same Makefile fragment
|
||||
#
|
||||
EXE = .elf
|
||||
RTEMS_BSP = mvme3100
|
||||
RTEMS_TARGET_CPU = powerpc
|
||||
GNU_TARGET = powerpc-rtems
|
||||
ARCH_DEP_CFLAGS += -DMY_DO_BOOTP=NULL
|
||||
@@ -15,7 +16,6 @@ ARCH_DEP_CFLAGS += -DRTEMS_NETWORK_CONFIG_CLUSTER_SPACE=5120
|
||||
OP_SYS_LDLIBS += -lbspExt
|
||||
|
||||
MUNCH_SUFFIX = .boot
|
||||
MUNCHNAME = $(PRODNAME:%$(EXE)=%$(MUNCH_SUFFIX))
|
||||
define MUNCH_CMD
|
||||
$(RTEMS_TOOLS)/bin/$(OBJCOPY_FOR_TARGET) -O binary $< $@
|
||||
endef
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
# All RTEMS targets use the same Makefile fragment
|
||||
#
|
||||
EXE = .elf
|
||||
RTEMS_BSP = mvme5500
|
||||
RTEMS_TARGET_CPU = powerpc
|
||||
GNU_TARGET = powerpc-rtems
|
||||
ARCH_DEP_CFLAGS += -DMY_DO_BOOTP=NULL
|
||||
@@ -16,7 +17,6 @@ ARCH_DEP_CFLAGS += -DBSP_NVRAM_BASE_ADDR=0xf1110000
|
||||
OP_SYS_LDLIBS += -lbspExt
|
||||
|
||||
MUNCH_SUFFIX = .boot
|
||||
MUNCHNAME = $(PRODNAME:%$(EXE)=%$(MUNCH_SUFFIX))
|
||||
define MUNCH_CMD
|
||||
$(RTEMS_TOOLS)/bin/$(OBJCOPY_FOR_TARGET) -O binary $< $@
|
||||
endef
|
||||
|
||||
@@ -5,15 +5,15 @@
|
||||
#
|
||||
# All RTEMS targets use the same Makefile fragment
|
||||
#
|
||||
RTEMS_TARGET_CPU=i386
|
||||
RTEMS_BSP = pc386
|
||||
RTEMS_TARGET_CPU = i386
|
||||
|
||||
MUNCH_SUFFIX = .boot
|
||||
MUNCHNAME = $(PRODNAME:%$(EXE)=%$(MUNCH_SUFFIX))
|
||||
define MUNCH_CMD
|
||||
$(RTEMS_TOOLS)/bin/$(OBJCOPY_FOR_TARGET) -O binary -R .comment -S $< temp.bin
|
||||
$(RM) $*.bin
|
||||
$(RTEMS_TOOLS)/bin/$(OBJCOPY_FOR_TARGET) -O binary -R .comment -S $< $*.bin
|
||||
$(BIN2BOOT) $@ 0x00097E00 \
|
||||
$(PROJECT_RELEASE)/lib/start16.bin 0x00097C00 0 temp.bin 0x00100000 0
|
||||
rm -f temp.bin
|
||||
$(PROJECT_RELEASE)/lib/start16.bin 0x00097C00 0 $*.bin 0x00100000 0
|
||||
endef
|
||||
|
||||
include $(CONFIG)/os/CONFIG.Common.RTEMS
|
||||
|
||||
11
configure/os/CONFIG.Common.RTEMS-pc386-qemu
Normal file
11
configure/os/CONFIG.Common.RTEMS-pc386-qemu
Normal file
@@ -0,0 +1,11 @@
|
||||
# CONFIG.Common.RTEMS-pc386-qemu
|
||||
#
|
||||
# Definitions for the RTEMS-pc386-qemu target
|
||||
# Site-specific overrides go in CONFIG_SITE.Common.RTEMS-pc386-qemu
|
||||
#
|
||||
#-------------------------------------------------------
|
||||
|
||||
# Include definitions from RTEMS-pc386
|
||||
include $(CONFIG)/os/CONFIG.Common.RTEMS-pc386
|
||||
|
||||
RTEMS_QEMU_FIXUPS = YES
|
||||
@@ -5,5 +5,6 @@
|
||||
#
|
||||
# All RTEMS targets use the same Makefile fragment
|
||||
#
|
||||
RTEMS_TARGET_CPU=ppc
|
||||
RTEMS_BSP = psim
|
||||
RTEMS_TARGET_CPU = ppc
|
||||
include $(CONFIG)/os/CONFIG.Common.RTEMS
|
||||
|
||||
@@ -5,11 +5,11 @@
|
||||
#
|
||||
# All RTEMS targets use the same Makefile fragment
|
||||
#
|
||||
RTEMS_BSP = uC5282
|
||||
RTEMS_TARGET_CPU = m68k
|
||||
ARCH_DEP_CFLAGS += -DMY_DO_BOOTP=NULL
|
||||
|
||||
MUNCH_SUFFIX = .boot
|
||||
MUNCHNAME = $(PRODNAME:%$(EXE)=%$(MUNCH_SUFFIX))
|
||||
define MUNCH_CMD
|
||||
$(RTEMS_TOOLS)/bin/$(OBJCOPY_FOR_TARGET) -O binary -R .comment -S $< $@
|
||||
endef
|
||||
|
||||
@@ -170,6 +170,10 @@ COMPILE.ctdt = $(CC) -c $(CPPFLAGS) $(CFLAGS_ctdt) $(INCLUDES) $(SOURCE_FLAG)
|
||||
VXCPPFLAGS = $(filter-out $(OP_SYS_INCLUDE_CPPFLAGS),$(CPPFLAGS))
|
||||
PREPROCESS.cpp = $(CPP) $(VXCPPFLAGS) $(INCLUDES) $< > $@
|
||||
|
||||
#--------------------------------------------------
|
||||
# Use LEDLIB for command-line editing
|
||||
COMMANDLINE_LIBRARY = LEDLIB
|
||||
|
||||
#--------------------------------------------------
|
||||
# Allow site overrides
|
||||
-include $(CONFIG)/os/CONFIG_SITE.Common.vxWorksCommon
|
||||
|
||||
@@ -65,14 +65,14 @@ GNU = NO
|
||||
#
|
||||
# Darwin shared libraries
|
||||
#
|
||||
SHRLIB_LDFLAGS = -dynamiclib -flat_namespace -undefined suppress \
|
||||
SHRLIB_LDFLAGS = -dynamiclib -undefined dynamic_lookup \
|
||||
-install_name $(shell $(FULLPATHNAME) $(INSTALL_LIB))/$@ \
|
||||
$(addprefix -compatibility_version , $(SHRLIB_VERSION)) \
|
||||
$(addprefix -current_version , $(SHRLIB_VERSION))
|
||||
SHRLIB_SUFFIX_BASE = .dylib
|
||||
SHRLIB_SUFFIX = $(addprefix ., $(SHRLIB_VERSION))$(SHRLIB_SUFFIX_BASE)
|
||||
|
||||
LOADABLE_SHRLIB_LDFLAGS = -bundle -flat_namespace -undefined suppress
|
||||
LOADABLE_SHRLIB_LDFLAGS = -bundle -undefined dynamic_lookup
|
||||
|
||||
#
|
||||
# Position-independent code is the default on Darwin.
|
||||
|
||||
@@ -79,16 +79,20 @@ CPP = cl -nologo -C -E
|
||||
|
||||
# Configure OS vendor C++ compiler
|
||||
#
|
||||
# __STDC__=0 gives us both:
|
||||
# 1) define STDC for code (pretend ANSI conformance)
|
||||
# 2) set it to 0 to use MS C "extensions" (open for _open etc.)
|
||||
# because MS uses: if __STDC__ ... disable many nice things
|
||||
#
|
||||
# -EHsc - generate code for exceptions
|
||||
# -GR - generate code for run time type identification
|
||||
#
|
||||
CCC = cl -EHsc -GR
|
||||
CODE_CPPFLAGS += -nologo -D__STDC__=0
|
||||
|
||||
# Other compiler flags, used for CPP, C and C++
|
||||
#
|
||||
# -FC - Show absolute path of source file in diagnostics
|
||||
# -D__STDC__=0 gives us both:
|
||||
# 1) define STDC for code (pretend ANSI conformance)
|
||||
# 2) set it to 0 to use MS C "extensions" (open for _open etc.)
|
||||
# because MS uses: if __STDC__ ... disable many nice things
|
||||
#
|
||||
CODE_CPPFLAGS += -nologo -FC -D__STDC__=0
|
||||
CODE_CPPFLAGS += -D_CRT_SECURE_NO_DEPRECATE -D_CRT_NONSTDC_NO_DEPRECATE
|
||||
|
||||
|
||||
@@ -136,6 +140,16 @@ STATIC_LDLIBS_NO=
|
||||
STATIC_LDFLAGS=
|
||||
RANLIB=
|
||||
|
||||
#
|
||||
# option needed for parallel builds with Visual Studio 2015 onward
|
||||
#
|
||||
# -FS Force Synchronous PDB Writes
|
||||
ifneq ($(VisualStudioVersion),)
|
||||
OPT_CXXFLAGS_NO += -FS
|
||||
OPT_CFLAGS_NO += -FS
|
||||
endif
|
||||
|
||||
|
||||
#
|
||||
# add -profile here to run the ms profiler
|
||||
# -LTCG whole program optimization
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
#
|
||||
# Site-specific overrides for RTEMS-pc386 target
|
||||
#
|
||||
9
configure/os/CONFIG_SITE.Common.RTEMS-pc386-qemu
Normal file
9
configure/os/CONFIG_SITE.Common.RTEMS-pc386-qemu
Normal file
@@ -0,0 +1,9 @@
|
||||
# CONFIG_SITE.Common.RTEMS-pc386-qemu
|
||||
#
|
||||
# Site-specific overrides for the RTEMS-pc386-qemu target
|
||||
#
|
||||
|
||||
# If you're building this architecture you _probably_ want to
|
||||
# run the tests for it under QEMU, but if not you can turn
|
||||
# them off here by commenting out this line:
|
||||
CROSS_COMPILER_RUNTEST_ARCHS += RTEMS-pc386-qemu
|
||||
@@ -67,8 +67,9 @@
|
||||
Software requirements
|
||||
|
||||
GNU make
|
||||
You must use GNU make, gnumake, for any EPICS builds. Set your path so
|
||||
that a gnumake version 3.81 or later is available.
|
||||
You must use the GNU version of make for EPICS builds, and we now
|
||||
recommend version 4.1 or later (version 3.82 may work on Linux, but
|
||||
doesn't on Windows).
|
||||
|
||||
Perl
|
||||
You must have Perl version 5.8.1 or later installed. The EPICS
|
||||
@@ -99,20 +100,17 @@
|
||||
|
||||
RTEMS
|
||||
For RTEMS targets, you need RTEMS core and toolset version 4.9.2 or
|
||||
later.
|
||||
4.10. The newer 4.11 or 5.x releases are not supported yet.
|
||||
|
||||
GNU readline or Tecla library
|
||||
GNU readline and Tecla libraries can be used by the IOC shell to provide
|
||||
command line editing and command line history recall and edit. GNU
|
||||
readline (or Tecla library) must be installed on your target system when
|
||||
COMMANDLINE_LIBRARY is set to READLINE (or TECLA) for that target. EPICS
|
||||
(EPICS shell) is the default specified in CONFIG_COMMON. A READLINE
|
||||
override is defined for linux-x86 in the EPICS distribution. Comment out
|
||||
COMMANDLINE_LIBRARY=READLINE in
|
||||
configure/os/CONFIG_SITE.Common.linux-x86 if readline is not installed
|
||||
on linux-x86. Command-line editing and history will then be those
|
||||
supplied by the os. On vxWorks the ledLib command-line input library is
|
||||
used instead.
|
||||
Command-line editing libraries
|
||||
GNU readline or other OS-specific libraries can be used by the IOC shell
|
||||
to provide command line editing and history recall. The default setting
|
||||
is different for each OS. On Linux the default is to use READLINE since
|
||||
most distributions include it. On MacOS the default is also READLINE
|
||||
since Apple provides a compatible library, although it isn't GNU. On
|
||||
RTEMS we support GNU readline and Tecla, although the default is to use
|
||||
neither since these have to be added to the RTEMS installation
|
||||
separately. On vxWorks we support the built-in ledLib library.
|
||||
|
||||
Host system storage requirements
|
||||
|
||||
@@ -166,12 +164,11 @@
|
||||
|
||||
base/startup directory - contains scripts to set environment and path
|
||||
|
||||
EpicsHostArch C shell script to set EPICS_HOST_ARCH env variable
|
||||
EpicsHostArch.pl Perl script to set EPICS_HOST_ARCH env variable
|
||||
Site.profile bourne shell script to set path and env variables
|
||||
Site.cshrc c shell script to set path and env variables
|
||||
cygwin.bat WIN32 bat file to set cygwin path and env variables
|
||||
win32.bat WIN32 bat file to set path and env variables
|
||||
EpicsHostArch Shell script to set EPICS_HOST_ARCH env variable
|
||||
unix.csh C shell script to set path and env variables
|
||||
unix.sh Bourne shell script to set path and env variables
|
||||
win32.bat Bat file example to configure win32-x86 target
|
||||
windows.bat Bat file example to configure windows-x64 target
|
||||
|
||||
base/configure directory - contains build definitions and rules
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ of my Bash login script (~/.bash_login):
|
||||
#
|
||||
EPICS_BASE="${HOME}/src/EPICS/base"
|
||||
EPICS_EXTENSIONS="${HOME}/src/EPICS/extensions"
|
||||
<strong>.</strong> "${EPICS_BASE}"/startup/Site.profile
|
||||
<strong>.</strong> "${EPICS_BASE}"/startup/unix.sh
|
||||
</pre>
|
||||
</li>
|
||||
<li>
|
||||
|
||||
@@ -72,8 +72,8 @@
|
||||
<H3><A NAME="0_0_6"> Software requirements</A></H3>
|
||||
|
||||
<BLOCKQUOTE><B>GNU make</B><BR>
|
||||
You must use GNU make, gnumake, for any EPICS builds. Set your path
|
||||
so that a gnumake version 3.81 or later is available.
|
||||
You must use the GNU version of make for EPICS builds, and we now recommend
|
||||
version 4.1 or later (version 3.82 may work on Linux, but doesn't on Windows).
|
||||
|
||||
<P><B>Perl</B><BR>
|
||||
You must have Perl version 5.8.1 or later installed. The EPICS configuration
|
||||
@@ -98,25 +98,25 @@
|
||||
base/configure/os/CONFIG_SITE.Common.vxWorksCommon file or in one of its
|
||||
target-specific overrides.</P>
|
||||
|
||||
<P>Consult the <a href="http://www.aps.anl.gov/epics/base/vxWorks6.php">vxWorks
|
||||
<P>Consult the <a href="https://epics.anl.gov/base/vxWorks6.php">vxWorks
|
||||
6.x</a> EPICS web pages and the vxWorks documentation for information
|
||||
about configuring your vxWorks operating system for use with EPICS.</P>
|
||||
|
||||
<P><B>RTEMS</B><BR>
|
||||
For RTEMS targets, you need RTEMS core and toolset version 4.9.2 or later.</P>
|
||||
For RTEMS targets, you need RTEMS core and toolset version 4.9.2 or 4.10. The
|
||||
newer 4.11 or 5.x releases are not supported yet.</P>
|
||||
|
||||
<P><B>Command-line editing libraries</B><BR>
|
||||
|
||||
GNU readline or other OS-specific libraries can be used by the IOC shell to
|
||||
provide command line editing and history recall. The default setting is
|
||||
different for each OS. On Linux the default is to use READLINE since most
|
||||
distributions include it. On MacOS the default is also READLINE since Apple
|
||||
provides a compatible library, although it isn't GNU. On RTEMS we support GNU
|
||||
readline and Tecla, although the default is to use neither since these have to
|
||||
be added to the RTEMS installation separately. On vxWorks we support the
|
||||
built-in ledLib library.</P>
|
||||
|
||||
<P><B>GNU readline or Tecla library</B><BR>
|
||||
GNU readline and Tecla libraries can be used by the IOC shell to
|
||||
provide command line editing and command line history recall and edit.
|
||||
GNU readline (or Tecla library) must be installed on your target system
|
||||
when COMMANDLINE_LIBRARY is set to READLINE (or TECLA) for that target.
|
||||
EPICS (EPICS shell) is the default specified in CONFIG_COMMON. A
|
||||
READLINE override is defined for linux-x86 in the EPICS distribution.
|
||||
Comment out COMMANDLINE_LIBRARY=READLINE in
|
||||
configure/os/CONFIG_SITE.Common.linux-x86 if readline is not installed
|
||||
on linux-x86. Command-line editing and history will then be those
|
||||
supplied by the os. On vxWorks the ledLib command-line input library is
|
||||
used instead.</P>
|
||||
</BLOCKQUOTE>
|
||||
|
||||
<H3><A NAME="0_0_7"> Host system storage requirements</A></H3>
|
||||
@@ -128,7 +128,7 @@
|
||||
|
||||
<H3><A NAME="0_0_8"> Documentation</A></H3>
|
||||
<BLOCKQUOTE>EPICS documentation is available through the
|
||||
<a href="http://www.aps.anl.gov/epics/">EPICS website</a> at Argonne.
|
||||
<a href="https://epics.anl.gov/">EPICS website</a> at Argonne.
|
||||
<P>Release specific documentation can also be found in the base/documentation
|
||||
directory of the distribution.</BLOCKQUOTE>
|
||||
|
||||
@@ -176,12 +176,11 @@
|
||||
|
||||
<H4>base/startup directory - contains scripts to set environment and path</H4>
|
||||
<PRE>
|
||||
EpicsHostArch C shell script to set EPICS_HOST_ARCH env variable
|
||||
EpicsHostArch.pl Perl script to set EPICS_HOST_ARCH env variable
|
||||
Site.profile bourne shell script to set path and env variables
|
||||
Site.cshrc c shell script to set path and env variables
|
||||
cygwin.bat WIN32 bat file to set cygwin path and env variables
|
||||
win32.bat WIN32 bat file to set path and env variables
|
||||
EpicsHostArch Shell script to set EPICS_HOST_ARCH env variable
|
||||
unix.csh C shell script to set path and env variables
|
||||
unix.sh Bourne shell script to set path and env variables
|
||||
win32.bat Bat file example to configure win32-x86 target
|
||||
windows.bat Bat file example to configure windows-x64 target
|
||||
</PRE>
|
||||
|
||||
<H4>base/configure directory - contains build definitions and rules</H4>
|
||||
|
||||
@@ -3,11 +3,13 @@
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="content-type" content="text/html; charset=iso-8859-1">
|
||||
<title>EPICS Base R3.16.1 Release Notes</title>
|
||||
<title>EPICS Base R3.16.2 Release Notes</title>
|
||||
</head>
|
||||
|
||||
<body lang="en">
|
||||
<h1 align="center">EPICS Base Release 3.16.1</h1>
|
||||
<h1 align="center">EPICS Base Release 3.16.2</h1>
|
||||
|
||||
<h2 align="center">Changes made between 3.16.1 and 3.16.2</h2>
|
||||
|
||||
<!-- Insert new items immediately below this template ...
|
||||
|
||||
@@ -17,6 +19,201 @@
|
||||
|
||||
-->
|
||||
|
||||
<h3>Type-safe Device and Driver Support Tables</h3>
|
||||
|
||||
<p>Type-safe versions of the device and driver support structures <tt>dset</tt>
|
||||
and <tt>drvet</tt> have been added to the devSup.h and drvSup.h headers
|
||||
respectively. The original structure definitions have not been changed so
|
||||
existing support modules will still build normally, but older modules can be
|
||||
modified and new code written to be compatible with both.</p>
|
||||
|
||||
<p>The old structure definitions will be replaced by the new ones if the macros
|
||||
<tt>USE_TYPED_DSET</tt> and/or <tt>USE_TYPED_DRVET</tt> are defined when the
|
||||
appropriate header is included. The best place to define these is in the
|
||||
Makefile, as with the <tt>USE_TYPED_RSET</tt> macro that was introduced in
|
||||
Base-3.16.1 and described below. See the comments in devSup.h for a brief usage
|
||||
example, or look at <a href="https://github.com/epics-modules/ipac/commit/a7e0ff4089b9aa39108bc8569e95ba7fcf07cee9">
|
||||
this commit</a> to the ipac module to see a module conversion.</p>
|
||||
|
||||
<p>A helper function <tt>DBLINK* dbGetDevLink(dbCommon *prec)</tt> has also been
|
||||
added to devSup.h which fetches a pointer to the INP or OUT field of the
|
||||
record.</p>
|
||||
|
||||
<h3>RTEMS build configuration update, running tests under QEMU</h3>
|
||||
|
||||
<p>This release includes the ability to run the EPICS unit tests built for a
|
||||
special version of the RTEMS-pc386 target architecture on systems that have an
|
||||
appropriate QEMU emulator installed (<tt>qemu-system-i386</tt>). It is also now
|
||||
possible to create sub-architectures of RTEMS targets, whereas previously the
|
||||
EPICS target architecture name had to be <tt>RTEMS-$(RTEMS_BSP)</tt>.</p>
|
||||
|
||||
<p>The new target <tt>RTEMS-pc386-qemu</tt> builds binaries that can be run in
|
||||
the <tt>qemu-system-i386</tt> PC System emulator. This target is a derivative of
|
||||
the original <tt>RTEMS-pc386</tt> target but with additional software to build
|
||||
an in-memory file-system, and some minor modifications to allow the unit tests
|
||||
to work properly under QEMU. When this target is enabled, building any of the
|
||||
make targets that cause the built-in self-tests to be run (such as
|
||||
<tt>make runtests</tt>) will also run the tests for RTEMS using QEMU.</p>
|
||||
|
||||
<p>To allow the new 3-component RTEMS target name, the EPICS build system for
|
||||
RTEMS was modified to allow a <tt>configure/os/CONFIG.Common.<arch></tt>
|
||||
file to set the <tt>RTEMS_BSP</tt> variable to inform the build what RTEMS BSP
|
||||
to use. Previously this was inferred from the value of the <tt>T_A</tt> make
|
||||
variable, but that prevents having multiple EPICS targets that build against the
|
||||
same BSP. All the included RTEMS target configuration files have been updated;
|
||||
build configuration files for out-of-tree RTEMS targets will continue to work as
|
||||
the original rules are used to set <tt>RTEMS_BSP</tt> if it hasn't been set when
|
||||
needed.</p>
|
||||
|
||||
<h3>Link type enhancements</h3>
|
||||
|
||||
<p>This release adds three new link types: "state", "debug" and "trace". The
|
||||
"state" link type gets and puts boolean values from/to the dbState library that
|
||||
was added in the 3.15.1 release. The "debug" link type sets the
|
||||
<code>jlink::debug</code> flag in its child link, while the "trace" link type
|
||||
also causes the arguments and return values for all calls to the child link's
|
||||
jlif and lset routines to be printed on stdout. The debug flag can no longer be
|
||||
set using an info tag. The addition of the "trace" link type has allowed over
|
||||
200 lines of conditional diagnostic printf() calls to be removed from the other
|
||||
link types.</p>
|
||||
|
||||
<p>The "calc" link type can now be used for output links as well as input links.
|
||||
This allows modification of the output value and even combining it with values
|
||||
from other input links. See the separate JSON Link types document for
|
||||
details.</p>
|
||||
|
||||
<p>A new <code>start_child()</code> method was added to the end of the jlif
|
||||
interface table.</p>
|
||||
|
||||
<p>The <code>lset</code> methods have now been properly documented in the
|
||||
dbLink.h header file using Doxygen annotations, although we do not run Doxygen
|
||||
on the source tree yet to generate API documentation.</p>
|
||||
|
||||
<p>Link types that utilize child links must now indicate whether the child will
|
||||
be used for input, output or forward linking by the return value from its
|
||||
<code>parse_start_map()</code> method. The <code>jlif_key_result</code> enum now
|
||||
contains 3 values <code>jlif_key_child_inlink</code>,
|
||||
<code>jlif_key_child_outlink</code> and <code>jlif_key_child_fwdlink</code>
|
||||
instead of the single <code>jlif_key_child_link</code> that was previously used
|
||||
for this.</p>
|
||||
|
||||
<h3>GNUmake targets for debugging</h3>
|
||||
|
||||
<p>Some additional build rules have been added to help debug configuration
|
||||
problems with the build system. Run <tt>make show-makefiles</tt> to get a sorted
|
||||
list of all the files that the build system includes when building in the
|
||||
current directory.</p>
|
||||
|
||||
<p>A new pattern rule for <tt>PRINT.%</tt> can be used to show the value of any
|
||||
GNUmake variable for the current build directory (make sure you are in the right
|
||||
directory though, many variables are only set when inside the
|
||||
<tt>O.<i>arch</i></tt> build directory). For example <tt>make PRINT.T_A</tt>
|
||||
will display the build target architecture name from inside a
|
||||
<tt>O.<i>arch</i></tt> directory but the variable will be empty from an
|
||||
application top or src directory. <tt>make PRINT.EPICS_BASE</tt> will show the
|
||||
path to Base from any EPICS application directory though.</p>
|
||||
|
||||
<h3>Propagate PUTF across Asynchronous record processing</h3>
|
||||
|
||||
<p>The IOC contains a mechanism involving the PUTF and RPRO fields of each
|
||||
record to ensure that if a record is busy when it receives a put to one of its
|
||||
fields, the record will be processed again to ensure that the new field value
|
||||
has been correctly acted on. Until now that mechanism only worked if the put was
|
||||
to the asynchronous record itself, so puts that were chained from some other
|
||||
record via a DB link did not cause reprocessing.</p>
|
||||
|
||||
<p>In this release the mechanism has been extended to propagate the PUTF state
|
||||
across DB links until all downstream records have been reprocessed. Some
|
||||
additional information about the record state can be shown by setting the TPRO
|
||||
field of an upstream record, and even more trace data is displayed if the
|
||||
debugging variable <tt>dbAccessDebugPUTF</tt> is set in addition to TPRO.</p>
|
||||
|
||||
<h3>Finding info fields</h3>
|
||||
|
||||
<p>A new iocsh command <code>dbli</code> lists the info fields defined in the
|
||||
database, and can take a glob pattern to limit output to specific info names.
|
||||
The newly added dbStaticLib function <code>dbNextMatchingInfo()</code> iterates
|
||||
through the info fields defined in the current record, and is used to implement
|
||||
the new command.</p>
|
||||
|
||||
<h3>Output from <tt>dbpr</tt> command enhanced</h3>
|
||||
|
||||
<p>The "DataBase Print Record" command <tt>dbpr</tt> now generates slightly
|
||||
better output, with more field types having their own display methods. This
|
||||
release also includes additional protection against buffer overflows while
|
||||
printing long links in <tt>dbpr</tt>, and corrects the output of long strings
|
||||
from the <tt>dbgf</tt> command.</p>
|
||||
|
||||
<h3>Record types mbbiDirect and mbboDirect upgraded to 32 bit</h3>
|
||||
|
||||
<p>The VAL fields and related fields of these records are now <tt>DBF_LONG</tt>.
|
||||
(Not <tt>DBF_ULONG</tt> in order to prevent Channel Access from promoting them
|
||||
to <tt>DBF_DOUBLE</tt>.) Additional bit fields <tt>B10</tt>...<tt>B1F</tt> have
|
||||
been added.</p>
|
||||
|
||||
<p>Device support that accesses <tt>VAL</tt> or the bit fields directly (most
|
||||
don't) and aims for compatibility with old and new versions of these records
|
||||
should use at least 32 bit integer types to avoid bit loss. The number of bit
|
||||
fields can be calculated using <code>8 * sizeof(prec->val)</code>
|
||||
which is correct in both versions.</p>
|
||||
|
||||
<h3>Restore use of ledlib for VxWorks command editing</h3>
|
||||
|
||||
<p>The epicsReadline refactoring work described below unfortunately disabled the
|
||||
VxWorks implementation of the osdReadline.c API that uses ledlib for command
|
||||
editing and history. This functionality has now been restored, see Launchpad
|
||||
<a href="https://bugs.launchpad.net/bugs/1741578">bug #1741578</a>.</p>
|
||||
|
||||
<h3>Constant link types</h3>
|
||||
|
||||
<p>Constant links can now hold 64-bit integer values, either as scalars or
|
||||
arrays. Only base 10 is supported by the JSON parser though, the JSON standard
|
||||
doesn't allow for hexadecimal numbers.</p>
|
||||
|
||||
<h3>Upgraded the YAJL JSON Library</h3>
|
||||
|
||||
<p>The third-party YAJL library that has been included in libCom for several
|
||||
years has been upgraded to version 2.1.0 and several bugs fixed. This has an
|
||||
updated API, requiring any code that uses it to parse its own JSON files to be
|
||||
modified to match. The changes are mainly that it uses <tt>size_t</tt> instead
|
||||
<tt>unsigned int</tt> for string lengths, but it also uses <tt>long long</tt>
|
||||
instead of <tt>long</tt> for JSON integer values, which was the main motivation
|
||||
for the upgrade.</p>
|
||||
|
||||
<p>The self-tests that YAJL comes with have been imported and are now run as an
|
||||
EPICS Unit Test program, and the JSON syntax accepted by the parser was extended
|
||||
to permit trailing commas in both arrays and maps. The difference between the
|
||||
old and new YAJL APIs can be detected at compile time by looking for the macro
|
||||
<tt>EPICS_YAJL_VERSION</tt> which is defined in the yajl_common.h header file
|
||||
along with a brief description of the API changes.</p>
|
||||
|
||||
<h3>Timestamp support for the calc link type</h3>
|
||||
|
||||
<p>A new optional parameter can be given when specifying a calc JSON link. The
|
||||
<tt>time</tt> parameter is a string containing a single letter <tt>A..L</tt>
|
||||
that selects one of the input links to be used for the timestamp of calculation
|
||||
if requested. The timestamp will be fetched atomically with the value from the
|
||||
chosen input link (providing that input link type supports the readLocked()
|
||||
method).</p>
|
||||
|
||||
<h3>Silence errors from puts to constant link types</h3>
|
||||
|
||||
<p>A soft channel output record with the OUT link unset uses the CONSTANT link
|
||||
type. The new link type code was causing some soft channel device supports to
|
||||
return an error status from the write method of that link type, which would
|
||||
cause a ca_put() operation to such a record to generate an exception. This has
|
||||
been silenced by giving the constant link types a dummy putValue method. A new
|
||||
test program has been added to prevent regressions of this behaviour.</p>
|
||||
|
||||
<h3>RSRV expanding large buffer causes crash</h3>
|
||||
|
||||
<p>In the 3.16.1 release a crash can occur in the IOC's RSRV server when a large
|
||||
array is made even larger; the previous array buffer was not being released
|
||||
correctly. See Launchpad
|
||||
<a href="https://bugs.launchpad.net/epics-base/+bug/1706703">bug
|
||||
#1706703</a>.</p>
|
||||
|
||||
|
||||
<h2 align="center">Changes made between 3.16.0.1 and 3.16.1</h2>
|
||||
|
||||
<h3>IOC Database Support for 64-bit integers</h3>
|
||||
@@ -50,6 +247,27 @@ preprocessor macro):</p>
|
||||
#endif
|
||||
</pre></blockquote>
|
||||
|
||||
<p>If the code uses the old db_access.h types (probably because it's calling
|
||||
Channel Access APIs) then it will have to test against the EPICS version number
|
||||
instead, like this:</p>
|
||||
|
||||
<blockquote><pre>
|
||||
#include <epicsVersion.h>
|
||||
|
||||
#ifndef VERSION_INT
|
||||
# define VERSION_INT(V,R,M,P) ( ((V)<<24) | ((R)<<16) | ((M)<<8) | (P))
|
||||
#endif
|
||||
#ifndef EPICS_VERSION_INT
|
||||
# define EPICS_VERSION_INT VERSION_INT(EPICS_VERSION, EPICS_REVISION, EPICS_MODIFICATION, EPICS_PATCH_LEVEL)
|
||||
#endif
|
||||
|
||||
#if EPICS_VERSION_INT >= VERSION_INT(3,16,1,0)
|
||||
/* Code where Base has INT64 support */
|
||||
#else
|
||||
/* Code for older versions */
|
||||
#endif
|
||||
</pre></blockquote>
|
||||
|
||||
<p>Channel Access does not (and probably never will) directly support 64-bit
|
||||
integer types, so the new field types are presented to the CA server as
|
||||
<tt>DBF_DOUBLE</tt> values. This means that field values larger than 2^52
|
||||
@@ -217,15 +435,15 @@ to implement the link APIs, so will work properly after these conversions:</p>
|
||||
link type, i.e. change this code:
|
||||
|
||||
<pre>
|
||||
if (prec->siml.type == CONSTANT) {
|
||||
recGblInitConstantLink(&prec->siml, DBF_USHORT, &prec->simm);
|
||||
if (prec->siml.type == CONSTANT) {
|
||||
recGblInitConstantLink(&prec->siml, DBF_USHORT, &prec->simm);
|
||||
}
|
||||
</pre>
|
||||
|
||||
into this:
|
||||
|
||||
<pre>
|
||||
recGblInitConstantLink(&prec->siml, DBF_USHORT, &prec->simm);
|
||||
recGblInitConstantLink(&prec->siml, DBF_USHORT, &prec->simm);
|
||||
</pre>
|
||||
|
||||
Note that <tt>recGblInitConstantLink()</tt> still returns TRUE if the field was
|
||||
@@ -238,20 +456,20 @@ or undefined links, FALSE for links whose <tt>dbGetLink()</tt> routine may
|
||||
return different values on different calls. For example this:
|
||||
|
||||
<pre>
|
||||
if (prec->dol.type != CONSTANT)
|
||||
if (prec->dol.type != CONSTANT)
|
||||
</pre>
|
||||
|
||||
should become this:
|
||||
|
||||
<pre>
|
||||
if (!dbLinkIsConstant(&prec->dol))
|
||||
if (!dbLinkIsConstant(&prec->dol))
|
||||
</pre>
|
||||
|
||||
When the converted software is also required to build against older versions of
|
||||
Base, this macro definition may be useful:
|
||||
|
||||
<pre>
|
||||
#define dbLinkIsConstant(lnk) ((lnk)->type == CONSTANT)
|
||||
#define dbLinkIsConstant(lnk) ((lnk)->type == CONSTANT)
|
||||
</pre>
|
||||
</li>
|
||||
|
||||
@@ -259,7 +477,7 @@ Base, this macro definition may be useful:
|
||||
link has been resolved as a CA link using code such as
|
||||
|
||||
<pre>
|
||||
if (prec->inp.type == CA_LINK)
|
||||
if (prec->inp.type == CA_LINK)
|
||||
</pre>
|
||||
|
||||
will still compile and run, but will only work properly with the old CA link
|
||||
@@ -269,7 +487,7 @@ examine or modify data inside the link. After conversion the above line would
|
||||
probably become:
|
||||
|
||||
<pre>
|
||||
if (dbLinkIsVolatile(&prec->inp))
|
||||
if (dbLinkIsVolatile(&prec->inp))
|
||||
</pre>
|
||||
|
||||
A volatile link is one like a Channel Access link which may disconnect and
|
||||
@@ -279,7 +497,7 @@ same state they started in. For compatibility when building against older
|
||||
versions of Base, this macro definition may be useful:
|
||||
|
||||
<pre>
|
||||
#define dbLinkIsVolatile(lnk) ((lnk)->type == CA_LINK)
|
||||
#define dbLinkIsVolatile(lnk) ((lnk)->type == CA_LINK)
|
||||
</pre>
|
||||
</li>
|
||||
|
||||
@@ -579,11 +797,62 @@ the stdout stream, making it hard to parse.</p>
|
||||
callback.h header and removed the need for dbScan.c to reach into the internals
|
||||
of its CALLBACK objects.</p>
|
||||
|
||||
|
||||
<h2 align="center">Changes from the 3.15 branch since 3.15.5</h2>
|
||||
<h2 align="center">Changes from the 3.15 branch since 3.15.6</h2>
|
||||
|
||||
<!-- Insert inherited items immediately below here ... -->
|
||||
|
||||
|
||||
<h2 align="center">Changes made between 3.15.5 and 3.15.6</h2>
|
||||
|
||||
<h3>Unsetting environment variables</h3>
|
||||
|
||||
<p>The new command <code>epicsEnvUnset <i>varname</i></code> can be used to
|
||||
unset an environment variable.</p>
|
||||
|
||||
<h3>Warning indicators in msi (and macLib) output</h3>
|
||||
|
||||
<p>The libCom macro expansion library has been modified so that when the
|
||||
SUPPRESS_WARNINGS flag is set it will no longer include any <tt>,undefined</tt>
|
||||
or <tt>,recursive</tt> indicators in its output when undefined or recursive
|
||||
macros are encountered. These indicators were harmless when the output was fed
|
||||
into an IOC along with a definition for the macro, but when the <tt>msi</tt>
|
||||
tool was used to generate other kinds of files they caused problems. If the
|
||||
<tt>msi -V</tt> flag is used the markers will still be present in the output
|
||||
whenever the appropriate condition is seen.</p>
|
||||
|
||||
<h3>Improvements to msi</h3>
|
||||
|
||||
<p>In addition to fixing its response to discovering parsing errors in its
|
||||
substitution input file (reported as Launchpad
|
||||
<a href="https://bugs.launchpad.net/epics-base/+bug/1503661">bug #1503661</a>)
|
||||
so it now deletes the incomplete output file, the msi program has been cleaned
|
||||
up a little bit internally.</p>
|
||||
|
||||
<h3>All array records now post monitors on their array-length fields</h3>
|
||||
|
||||
<p>The waveform record has been posting monitors on its NORD field since Base
|
||||
3.15.0.1; we finally got around to doing the equivalent in all the other
|
||||
built-in record types, which even required modifying device support in some
|
||||
cases. This fixes <a href="https://bugs.launchpad.net/epics-base/+bug/1730727">
|
||||
Launchpad bug #1730727</a>.</p>
|
||||
|
||||
<h3>HOWTO: Converting Wiki Record Reference to POD</h3>
|
||||
|
||||
<p>Some documentation has been added to the <tt>dbdToHtml.pl</tt> script
|
||||
explaining how Perl POD (Plain Old Documentation) markup can be added to
|
||||
<tt>.dbd</tt> files to generate HTML documentation for the record types. To see
|
||||
these instructions, run <tt>perl bin/<host>/dbdToHtml.pl -H</tt>
|
||||
or <tt>perldoc bin/<host>/dbdToHtml.pl</tt>.</p>
|
||||
|
||||
<h3>Fix problem with numeric soft events</h3>
|
||||
|
||||
<p>Changing from numeric to named soft events introduced an incompatibility
|
||||
when a numeric event 1-255 is converted from a DOUBLE, e.g. from a calc record.
|
||||
The <tt>post_event()</tt> API is not marked deprecated any more.
|
||||
|
||||
<p>Also <code>scanpel</code> has been modified to accept a glob pattern for
|
||||
event name filtering and to show events with no connected records as well.</p>
|
||||
|
||||
<h3>Add osiSockOptMcastLoop_t and osiSockTest</h3>
|
||||
|
||||
<p>Added a new OS-independent typedef for multicast socket options, and a test
|
||||
@@ -598,9 +867,115 @@ of having go modify or replace the original. A new .gitignore pattern
|
||||
tells git to ignore all configure/*.local files.</p>
|
||||
|
||||
|
||||
<h2 align="center">Changes from the 3.14 branch since 3.15.5</h2>
|
||||
<h2 align="center">Changes from the 3.14 branch between 3.15.5 and 3.15.6</h2>
|
||||
|
||||
<!-- Insert inherited items immediately below here ... -->
|
||||
<h3>Fix broken <tt>EPICS_IOC_LOG_FILE_LIMIT=0</tt> setting</h3>
|
||||
|
||||
<p>The Application Developers' Guide says this is allowed and disables the
|
||||
limit on the log-file, but it hasn't actually worked for some time (if ever).
|
||||
Note that the iocLogServer will be removed from newer Base release sometime
|
||||
soon as its functionality can be implemented by other dedicated log servers
|
||||
such as logstash or syslog-ng.</p>
|
||||
|
||||
<p>Fixes <a href="https://bugs.launchpad.net/bugs/1786858">lp:1786858</a>
|
||||
and part of <a href="https://bugs.launchpad.net/bugs/1786966">lp:1786966</a>.
|
||||
</p>
|
||||
|
||||
<h3>Cleanup of startup directory</h3>
|
||||
|
||||
<p>The files in the startup directory have not been maintained in recent years
|
||||
and have grown crufty (technical term). This release includes the following
|
||||
updates to these files:</p>
|
||||
|
||||
<ul>
|
||||
|
||||
<li>The Perl <tt>EpicsHostArch.pl</tt> script has been rewritten, and support
|
||||
for a few previously missing host architectures has been added to it.</li>
|
||||
|
||||
<li>The <tt>EpicsHostArch.pl</tt> script has also been moved into the standard
|
||||
<tt>src/tools</tt> directory, from where it will be installed into
|
||||
<tt>lib/perl</tt>. In this new location it is no longer executable, so it must
|
||||
be run by the <tt>perl</tt> executable.</li>
|
||||
|
||||
<li>The build system has been adjusted to look for <tt>EpicsHostArch.pl</tt> in
|
||||
both places if the <tt>EPICS_HOST_ARCH</tt> environment variable has not been
|
||||
set at build-time.</li>
|
||||
|
||||
<li>Sites that used the original Perl script to set <tt>EPICS_HOST_ARCH</tt> as
|
||||
part of their standard environment will need to adjust their scripts when they
|
||||
upgrade to this release.</li>
|
||||
|
||||
<li>The <tt>EpicsHostArch</tt> shell script has been replaced with a wrapper
|
||||
routine that calls the Perl <tt>EpicsHostArch.pl</tt> script. Sites that rely on
|
||||
this script to set <tt>EPICS_HOST_ARCH</tt> should consider switching to the
|
||||
Perl script instead.</li>
|
||||
|
||||
<li>The <tt>Site.cshrc</tt> and <tt>Site.profile</tt> files have been renamed to
|
||||
<tt>unix.csh</tt> and <tt>unix.sh</tt>, respectively.</li>
|
||||
|
||||
<li>The existing <tt>win32.bat</tt> file has been cleaned up and a new
|
||||
<tt>windows.bat</tt> file added for 64-bit targets. The contents of these files
|
||||
should be seen as examples, don't uncomment or install parts for software that
|
||||
you don't explicitly know that you need.</li>
|
||||
|
||||
</ul>
|
||||
|
||||
<h3>Recent Apple XCode Build Issues</h3>
|
||||
|
||||
<p>The latest version of XCode will not compile calls to <tt>system()</tt> or
|
||||
<tt>clock_settime()</tt> for iOS targets. There were several places in Base
|
||||
where these were being compiled, although there were probably never called. The
|
||||
code has now been modified to permit iOS builds to complete again.</p>
|
||||
|
||||
<h3>Prevent illegal alarm severities</h3>
|
||||
|
||||
<p>A check has been added to <tt>recGblResetAlarms()</tt> that prevents records
|
||||
from getting an alarm severity higher than INVALID_ALARM. It is still possible
|
||||
for a field like HSV to get set to a value that is not a legal alarm severity,
|
||||
but the core IOC code should never copy such a value into a record's SEVR or
|
||||
ACKS fields. With this fix the record's alarm severity will be limited to
|
||||
INVALID_ALARM.</p>
|
||||
|
||||
<h3>Fixes for Launchpad bugs</h3>
|
||||
|
||||
<p>The following launchpad bugs have fixes included:</p>
|
||||
|
||||
<ul>
|
||||
<li><a href="https://bugs.launchpad.net/epics-base/+bug/1786320">
|
||||
lp: #1786320</a>, dbCa subscribes twice to ENUM</li>
|
||||
<li><a href="https://bugs.launchpad.net/epics-base/+bug/541221">
|
||||
lp: #541221</a>, 'assert (pca->pgetNative)' failed in ../dbCa.c</li>
|
||||
<li><a href="https://bugs.launchpad.net/epics-base/+bug/1747091">
|
||||
lp: #1747091</a>, epicsTimeGetEvent() / generalTime bug</li>
|
||||
<li><a href="https://bugs.launchpad.net/epics-base/+bug/1743076">
|
||||
lp: #1743076</a>, Segfault in ca_attach_context() during exits</li>
|
||||
<li><a href="https://bugs.launchpad.net/epics-base/+bug/1751380">
|
||||
lp: #1751380</a>, Deadlock in ca_clear_subscription()</li>
|
||||
<li><a href="https://bugs.launchpad.net/epics-base/+bug/1597809">
|
||||
lp: #1597809</a>, Setting NAME field in DB file may break IOC</li>
|
||||
<li><a href="https://bugs.launchpad.net/epics-base/+bug/1770292">
|
||||
lp: #1770292</a>, get_alarm_double() inconsistent across record types</li>
|
||||
<li><a href="https://bugs.launchpad.net/epics-base/+bug/1771298">
|
||||
lp: #1771298</a>, Conversion of NaN to integer relies on undefined
|
||||
behavior</li>
|
||||
</ul>
|
||||
|
||||
<h3>Updated VxWorks Timezone settings</h3>
|
||||
|
||||
<p>Removed the settings for 2017; fixed the hour of the change for MET.</p>
|
||||
|
||||
<h3>Fixed camonitor server side relative timestamps bug</h3>
|
||||
|
||||
<p>Initialize the first time-stamp from the first monitor, not the client-side
|
||||
current time in this configuration.</p>
|
||||
|
||||
<h3>Build changes for MSVC</h3>
|
||||
|
||||
<p>Windows builds using Visual Studio 2015 and later now use the <tt>-FS</tt>
|
||||
compiler option to allow parallel builds to work properly.</p>
|
||||
|
||||
<p>We now give the <tt>-FC</tt> option to tell the compiler to print absolute
|
||||
paths for source files in diagnostic messages.</p>
|
||||
|
||||
<h3>Extend maximum Posix epicsEventWaitWithTimeout() delay</h3>
|
||||
|
||||
|
||||
@@ -131,7 +131,7 @@ the final release version.</p>
|
||||
<tr>
|
||||
<td> </td>
|
||||
<td>Release Manager</td>
|
||||
<td>Tag the module in Git using these tag conventions:
|
||||
<td>Tag the module in Git, using these tag conventions:
|
||||
<ul>
|
||||
<li>
|
||||
<tt>R3.16.1-pre<i>n</i></tt>
|
||||
@@ -141,7 +141,7 @@ the final release version.</p>
|
||||
<tt>R3.16.1-rc<i>n</i></tt>
|
||||
— release candidate tag
|
||||
</li>
|
||||
</ul>
|
||||
</ul>
|
||||
<blockquote><tt>
|
||||
cd base-3.16<br />
|
||||
git tag -m 'ANJ: Tagged for 3.16.1-rc1' R3.16.1-rc1
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
# 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 the file LICENSE that is included with this distribution.
|
||||
# in the file LICENSE that is included with this distribution.
|
||||
#*************************************************************************
|
||||
|
||||
TOP = ..
|
||||
@@ -72,9 +72,11 @@ std_DEPEND_DIRS = ioc libCom/RTEMS
|
||||
DIRS += std/filters/test
|
||||
std/filters/test_DEPEND_DIRS = std
|
||||
|
||||
DIRS += std/link/test
|
||||
std/link/test_DEPEND_DIRS = std
|
||||
|
||||
DIRS += std/rec/test
|
||||
std/rec/test_DEPEND_DIRS = std
|
||||
|
||||
|
||||
include $(TOP)/configure/RULES_DIRS
|
||||
|
||||
|
||||
@@ -1021,7 +1021,7 @@ d:/user/epics/base-3.15/lib/win32-x86/Com.lib</code></p>
|
||||
<h2><a name="CommandUtils">Command Line Utilities</a></h2>
|
||||
|
||||
<h3><a name="acctst">acctst</a></h3>
|
||||
<pre>acctst <PV name> [progress logging level] [channel duplication count]
|
||||
<pre>acctst <PV name> [progress logging level] [channel duplication count]
|
||||
[test repetition count] [enable preemptive callback]</pre>
|
||||
|
||||
<h4>Description</h4>
|
||||
@@ -2091,7 +2091,7 @@ example, be beneficial when tuning an archiver installation.</p>
|
||||
<p>Significant performance gains can be realized when the CA client library
|
||||
doesn't wait for a response to return from the server after each request. All
|
||||
requests which require interaction with a CA server are accumulated (buffered)
|
||||
and not forwarded to the IOC until one of <code>ca_flush_io()</code>,
|
||||
and not forwarded to the IOC until one of <code>ca_flush_io()</code>,
|
||||
<code>ca_pend_io()</code>, <code>ca_pend_event()</code>, or
|
||||
<code>ca_sg_block()</code> are called allowing several operations to be
|
||||
efficiently sent over the network together. Any process variable values written
|
||||
@@ -2115,16 +2115,16 @@ shouldn't test the success of a CA function call by checking to see if the
|
||||
returned value is zero as is the UNIX convention. Below are several methods to
|
||||
test CA function returns. See <a href="#ca_signal"><code>ca_signal()</code> and
|
||||
<code>SEVCHK()</code></a> for more information on this topic.</p>
|
||||
<pre>status = ca_XXXX();
|
||||
SEVCHK( status, "ca_XXXX() returned failure status");
|
||||
<pre>status = ca_XXXX();
|
||||
SEVCHK( status, "ca_XXXX() returned failure status");
|
||||
|
||||
if ( status & CA_M_SUCCESS ) {
|
||||
printf ( "The requested ca_XXXX() operation didn't complete successfully");
|
||||
}
|
||||
if ( status & CA_M_SUCCESS ) {
|
||||
printf ( "The requested ca_XXXX() operation didn't complete successfully");
|
||||
}
|
||||
|
||||
if ( status != ECA_NORMAL ) {
|
||||
if ( status != ECA_NORMAL ) {
|
||||
printf("The requested ca_XXXX() operation didn't complete successfully because \"%s\"\n",
|
||||
ca_message ( status ) );
|
||||
ca_message ( status ) );
|
||||
}</pre>
|
||||
|
||||
<h3><a name="Channel">Channel Access Data Types</a></h3>
|
||||
@@ -2297,7 +2297,7 @@ int main ( int argc, char ** argv )
|
||||
unsigned nBytes;
|
||||
unsigned elementCount;
|
||||
char timeString[32];
|
||||
unsigned i;
|
||||
unsigned i;
|
||||
chid chan;
|
||||
double sum;
|
||||
int status;
|
||||
@@ -2340,7 +2340,7 @@ int main ( int argc, char ** argv )
|
||||
epicsTimeToStrftime ( timeString, sizeof ( timeString ),
|
||||
"%a %b %d %Y %H:%M:%S.%f", & pTD->stamp );
|
||||
|
||||
printf ( "The sum of elements in %s at %s was %f\n",
|
||||
printf ( "The sum of elements in %s at %s was %f\n",
|
||||
argv[1], timeString, sum );
|
||||
|
||||
ca_clear_channel ( chan );
|
||||
@@ -2371,7 +2371,7 @@ executing within the user's callback function.</p>
|
||||
<pre>typedef struct event_handler_args {
|
||||
void *usr; /* user argument supplied with request */
|
||||
chanId chid; /* channel id */
|
||||
long type; /* the type of the item returned */
|
||||
long type; /* the type of the item returned */
|
||||
long count; /* the element count of the item returned */
|
||||
const void *dbr; /* a pointer to the item returned */
|
||||
int status; /* ECA_XXX status of the requested op from the server */
|
||||
@@ -2394,7 +2394,7 @@ attached to the request, an exception handler is executed in the client. The
|
||||
default exception handler prints a message on the console and exits if the
|
||||
exception condition is severe. Certain internal exceptions within the CA client
|
||||
library, and failures detected by the SEVCHK macro may also cause the exception
|
||||
handler to be invoked. To modify this behavior see
|
||||
handler to be invoked. To modify this behavior see
|
||||
<code><a href="#ca_add_exception_event">ca_add_exception_event</a>()</code>.</p>
|
||||
|
||||
<h3><a name="Server">Server and Client Share the Same Address Space on The Same
|
||||
@@ -2686,6 +2686,14 @@ automatically released by the system when the process exits and
|
||||
vxWorks or RTEMS no cleanup occurs unless the application calls
|
||||
<code>ca_context_destroy()</code>.</p>
|
||||
|
||||
<p>Note: This operation blocks until any user callbacks for any channel
|
||||
created in the current context have run to completion. If callbacks take a
|
||||
lock (mutex) then it is the user's responsibility to ensure that this lock
|
||||
is not held when <code>ca_clear_context()</code> is called, otherwise a
|
||||
deadlock may ensue. (See also
|
||||
<code><a href="#ca_clear_channel">ca_clear_channel</a>()</code> and
|
||||
<code><a href="#ca_clear_event">ca_clear_subscription</a>()</code>.)</p>
|
||||
|
||||
<h4>Returns</h4>
|
||||
|
||||
<p>ECA_NORMAL - Normal successful completion</p>
|
||||
@@ -2831,6 +2839,12 @@ efficiently sent over the network in one message.</p>
|
||||
clearing a channel does shutdown and reclaim any channel state change event
|
||||
subscriptions (monitors) registered with the channel.</p>
|
||||
|
||||
<p>Note: This operation blocks until any user callbacks for this channel
|
||||
have run to completion. If callbacks take a lock (mutex) then it is the
|
||||
user's responsibility to ensure that this lock is not held when
|
||||
<code>ca_clear_channel()</code> is called, otherwise a deadlock may ensue.
|
||||
(See also <code><a href="#ca_clear_event">ca_clear_subscription</a>()</code>.)</p>
|
||||
|
||||
<h4>Arguments</h4>
|
||||
<dl>
|
||||
<dt><code>CHID</code></dt>
|
||||
@@ -2845,16 +2859,16 @@ subscriptions (monitors) registered with the channel.</p>
|
||||
|
||||
<h3><code><a name="ca_put">ca_put()</a></code></h3>
|
||||
<pre>#include <cadef.h>
|
||||
int ca_put ( chtype TYPE,
|
||||
chid CHID, void *PVALUE );
|
||||
int ca_array_put ( chtype TYPE, unsigned long COUNT,
|
||||
int ca_put ( chtype TYPE,
|
||||
chid CHID, void *PVALUE );
|
||||
int ca_array_put ( chtype TYPE, unsigned long COUNT,
|
||||
chid CHID, const void *PVALUE);
|
||||
typedef void ( caEventCallBackFunc ) (struct event_handler_args);
|
||||
int ca_put_callback ( chtype TYPE,
|
||||
chid CHID, const void *PVALUE,
|
||||
caEventCallBackFunc PFUNC, void *USERARG );
|
||||
int ca_array_put_callback ( chtype TYPE, unsigned long COUNT,
|
||||
chid CHID, const void *PVALUE,
|
||||
int ca_put_callback ( chtype TYPE,
|
||||
chid CHID, const void *PVALUE,
|
||||
caEventCallBackFunc PFUNC, void *USERARG );
|
||||
int ca_array_put_callback ( chtype TYPE, unsigned long COUNT,
|
||||
chid CHID, const void *PVALUE,
|
||||
caEventCallBackFunc PFUNC, void *USERARG );</pre>
|
||||
|
||||
<h4>Description</h4>
|
||||
@@ -3081,7 +3095,7 @@ when a CA get request is initiated.</p>
|
||||
typedef void ( caEventCallBackFunc ) (struct event_handler_args);
|
||||
int ca_create_subscription ( chtype TYPE, unsigned long COUNT,
|
||||
chid CHID, unsigned long MASK,
|
||||
caEventCallBackFunc USERFUNC, void *USERARG,
|
||||
caEventCallBackFunc USERFUNC, void *USERARG,
|
||||
evid *PEVID );</pre>
|
||||
|
||||
<h4>Description</h4>
|
||||
@@ -3165,7 +3179,7 @@ indicating the current state of the channel.</p>
|
||||
<dt><code>MASK</code></dt>
|
||||
<dd>A mask with bits set for each of the event trigger types requested. The
|
||||
event trigger mask must be a <em>bitwise or</em> of one or more of the
|
||||
following constants.
|
||||
following constants.
|
||||
<ul>
|
||||
<li>DBE_VALUE - Trigger events when the channel value exceeds the
|
||||
monitor dead band</li>
|
||||
@@ -3212,6 +3226,13 @@ and not forwarded to the server until one of <code>ca_flush_io()</code>, <code>c
|
||||
<code>ca_pend_event()</code>, or <code>ca_sg_block()</code> are called. This allows several requests to be
|
||||
efficiently sent together in one message.</p>
|
||||
|
||||
<p>Note: This operation blocks until any user callbacks for this channel
|
||||
have run to completion. If callbacks take a lock (mutex) then it is the
|
||||
user's responsibility to ensure that this lock is not held when
|
||||
<code>ca_clear_subscription()</code> is called, otherwise a deadlock may
|
||||
ensue. (See also <code><a
|
||||
href="#ca_clear_channel">ca_clear_channel</a>()</code>.)</p>
|
||||
|
||||
<h4>Arguments</h4>
|
||||
<dl>
|
||||
<dt>EVID</dt>
|
||||
@@ -3376,7 +3397,7 @@ becomes full.</p>
|
||||
|
||||
<h3><code><a name="ca_signal">ca_signal()</a></code></h3>
|
||||
<pre>#include <cadef.h>
|
||||
int ca_signal ( long CA_STATUS, const char * CONTEXT_STRING );
|
||||
int ca_signal ( long CA_STATUS, const char * CONTEXT_STRING );
|
||||
void SEVCHK( CA_STATUS, CONTEXT_STRING );</pre>
|
||||
|
||||
<h4>Description</h4>
|
||||
@@ -3393,7 +3414,7 @@ recommended error handler for simple applications which do not wish to write
|
||||
code testing the status returned from each channel access call.</p>
|
||||
|
||||
<h4>Examples</h4>
|
||||
<pre>status = ca_context_create (...);
|
||||
<pre>status = ca_context_create (...);
|
||||
SEVCHK ( status, "Unable to create a CA client context" );</pre>
|
||||
|
||||
<p>If the application only wishes to print the message associated with an error
|
||||
@@ -3417,7 +3438,7 @@ this purpose.</p>
|
||||
|
||||
<h3><code><a
|
||||
name="ca_add_exception_event">ca_add_exception_event()</a></code></h3>
|
||||
<pre>#include <cadef.h>
|
||||
<pre>#include <cadef.h>
|
||||
typedef void (*pCallback) ( struct exception_handler_args HANDLERARGS );
|
||||
int ca_add_exception_event ( pCallback USERFUNC, void *USERARG );</pre>
|
||||
|
||||
@@ -3626,7 +3647,7 @@ specified channel.</p>
|
||||
<dt><code>PFUNC</code></dt>
|
||||
<dd>Pointer to a user supplied callback function. A null pointer uninstalls
|
||||
the current handler. The following arguments are passed <em>by value</em>
|
||||
to the supplied callback handler.
|
||||
to the supplied callback handler.
|
||||
<pre>typedef struct ca_access_rights {
|
||||
unsigned read_access:1;
|
||||
unsigned write_access:1;
|
||||
@@ -3966,8 +3987,8 @@ type.</p>
|
||||
prints diagnostics to standard out.</p>
|
||||
|
||||
<h4>Examples</h4>
|
||||
<pre>void ca_test_event ();
|
||||
status = ca_create_subscription ( type, chid, ca_test_event, NULL, NULL );
|
||||
<pre>void ca_test_event ();
|
||||
status = ca_create_subscription ( type, chid, ca_test_event, NULL, NULL );
|
||||
SEVCHK ( status, .... );</pre>
|
||||
|
||||
<h4>See Also</h4>
|
||||
@@ -4001,8 +4022,8 @@ outstanding within them at any given time.</p>
|
||||
</dl>
|
||||
|
||||
<h4>Examples</h4>
|
||||
<pre>CA_SYNC_GID gid;
|
||||
status = ca_sg_create ( &gid );
|
||||
<pre>CA_SYNC_GID gid;
|
||||
status = ca_sg_create ( &gid );
|
||||
SEVCHK ( status, Sync group create failed );</pre>
|
||||
|
||||
<h4>Returns</h4>
|
||||
@@ -4040,8 +4061,8 @@ int ca_sg_delete ( CA_SYNC_GID GID );</pre>
|
||||
</dl>
|
||||
|
||||
<h4>Examples</h4>
|
||||
<pre>CA_SYNC_GID gid;
|
||||
status = ca_sg_delete ( gid );
|
||||
<pre>CA_SYNC_GID gid;
|
||||
status = ca_sg_delete ( gid );
|
||||
SEVCHK ( status, Sync group delete failed );</pre>
|
||||
|
||||
<h4>Returns</h4>
|
||||
@@ -4152,7 +4173,7 @@ will not block unless additional subsequent requests are made.</p>
|
||||
</dl>
|
||||
|
||||
<h4>Examples</h4>
|
||||
<pre>CA_SYNC_GID gid;
|
||||
<pre>CA_SYNC_GID gid;
|
||||
status = ca_sg_reset(gid);</pre>
|
||||
|
||||
<h4>Returns</h4>
|
||||
@@ -4165,7 +4186,7 @@ status = ca_sg_reset(gid);</pre>
|
||||
<pre>#include <cadef.h>
|
||||
int ca_sg_put ( CA_SYNC_GID GID, chtype TYPE,
|
||||
chid CHID, void *PVALUE );
|
||||
int ca_sg_array_put ( CA_SYNC_GID GID, chtype TYPE,
|
||||
int ca_sg_array_put ( CA_SYNC_GID GID, chtype TYPE,
|
||||
unsigned long COUNT, chid CHID, void *PVALUE );</pre>
|
||||
|
||||
<p>Write a value, or array of values, to a channel and increment the outstanding
|
||||
@@ -4306,7 +4327,7 @@ reissued.</p>
|
||||
|
||||
<h3><code><a name="ca_client_status">ca_client_status()</a></code></h3>
|
||||
<pre>int ca_client_status ( unsigned level );
|
||||
int ca_context_status ( struct ca_client_context *CONTEXT,
|
||||
int ca_context_status ( struct ca_client_context *CONTEXT,
|
||||
unsigned LEVEL );</pre>
|
||||
|
||||
<h4>Description</h4>
|
||||
|
||||
@@ -127,21 +127,10 @@ const char * ca_message_text []
|
||||
|
||||
static epicsThreadOnceId caClientContextIdOnce = EPICS_THREAD_ONCE_INIT;
|
||||
|
||||
extern "C" void ca_client_exit_handler (void *)
|
||||
{
|
||||
if ( caClientContextId ) {
|
||||
epicsThreadPrivateDelete ( caClientContextId );
|
||||
caClientContextId = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// runs once only for each process
|
||||
extern "C" void ca_init_client_context ( void * )
|
||||
{
|
||||
caClientContextId = epicsThreadPrivateCreate ();
|
||||
if ( caClientContextId ) {
|
||||
epicsAtExit ( ca_client_exit_handler,0 );
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -390,7 +390,7 @@ int epicsShareAPI ca_array_get_callback ( chtype type,
|
||||
{
|
||||
caStatus = ECA_ALLOCMEM;
|
||||
}
|
||||
catch ( cacChannel::msgBodyCacheTooSmall ) {
|
||||
catch ( cacChannel::msgBodyCacheTooSmall & ) {
|
||||
caStatus = ECA_TOLARGE;
|
||||
}
|
||||
catch ( ... )
|
||||
|
||||
@@ -652,7 +652,7 @@ not follow this pattern, but are still printable strings.
|
||||
|
||||
=item [1] R3.15 Channel Access Reference Manual by Jeffrey O. Hill
|
||||
|
||||
L<http://www.aps.anl.gov/epics/base/R3-15/2-docs/CAref.html>
|
||||
L<https://epics.anl.gov/base/R3-15/5-docs/CAref.html>
|
||||
|
||||
=back
|
||||
|
||||
|
||||
@@ -58,7 +58,7 @@ Cap5_LIBS = ca Com
|
||||
Cap5_INCLUDES = -I$(shell $(PERL) ../perlConfig.pl archlib)/CORE
|
||||
Cap5_CFLAGS = $(shell $(PERL) ../perlConfig.pl ccflags)
|
||||
|
||||
CLEANS += Cap5.c pod2htmd.tmp pod2htmi.tmp
|
||||
CLEANS += Cap5.c
|
||||
|
||||
include $(TOP)/configure/RULES
|
||||
|
||||
|
||||
@@ -140,6 +140,10 @@ sub display {
|
||||
printf " Lo ctrl limit: %g\n", $data->{lower_ctrl_limit};
|
||||
printf " Hi ctrl limit: %g\n", $data->{upper_ctrl_limit};
|
||||
}
|
||||
if (exists $data->{ackt}) {
|
||||
printf " Ack transients: %s\n", $data->{ackt} ? 'YES' : 'NO';
|
||||
printf " Ack severity: %s\n", $data->{acks};
|
||||
}
|
||||
} else {
|
||||
my $value = format_number($data, $type);
|
||||
if ($opt_t) {
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
* Copyright (c) 2002 Berliner Elektronenspeicherringgesellschaft fuer
|
||||
* Synchrotronstrahlung.
|
||||
* EPICS BASE is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
* in file LICENSE that is included with this distribution.
|
||||
\*************************************************************************/
|
||||
|
||||
/*
|
||||
@@ -419,7 +419,7 @@ int main (int argc, char *argv[])
|
||||
if (argc > optind+1) {
|
||||
for (i = optind + 1; i < argc; i++) {
|
||||
strcat(cbuf, " ");
|
||||
strcat(cbuf, argv[i]);
|
||||
strcat(cbuf, argv[i]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -530,6 +530,11 @@ int main (int argc, char *argv[])
|
||||
/* Use standard put with defined timeout */
|
||||
result = ca_array_put (dbrType, count, pvs[0].chid, pbuf);
|
||||
}
|
||||
if (result != ECA_NORMAL) {
|
||||
fprintf(stderr, "Error from put operation: %s\n", ca_message(result));
|
||||
return 1;
|
||||
}
|
||||
|
||||
result = ca_pend_io(caTimeout);
|
||||
if (result == ECA_TIMEOUT) {
|
||||
fprintf(stderr, "Write operation timed out: Data was not written.\n");
|
||||
@@ -545,7 +550,7 @@ int main (int argc, char *argv[])
|
||||
}
|
||||
|
||||
if (result != ECA_NORMAL) {
|
||||
fprintf(stderr, "Error occured writing data.\n");
|
||||
fprintf(stderr, "Error occured writing data: %s\n", ca_message(result));
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
@@ -414,6 +414,12 @@ char *dbr2str (const void *value, unsigned type)
|
||||
ptsNewS = &((struct TYPE *)value)->stamp; \
|
||||
ptsNewC = &tsNow; \
|
||||
\
|
||||
if (!tsInitS) \
|
||||
{ \
|
||||
tsFirst = *ptsNewS; \
|
||||
tsInitS = 1; \
|
||||
} \
|
||||
\
|
||||
switch (tsType) { \
|
||||
case relative: \
|
||||
ptsRefC = &tsStart; \
|
||||
@@ -506,12 +512,6 @@ void print_time_val_sts (pv* pv, unsigned long reqElems)
|
||||
epicsTimeGetCurrent(&tsNow);
|
||||
epicsTimeToStrftime(timeText, TIMETEXTLEN, timeFormatStr, &tsNow);
|
||||
|
||||
if (!tsInitS)
|
||||
{
|
||||
tsFirst = tsNow;
|
||||
tsInitS = 1;
|
||||
}
|
||||
|
||||
if (pv->nElems <= 1 && fieldSeparator == ' ') printf("%-30s", pv->name);
|
||||
else printf("%s", pv->name);
|
||||
printf("%c", fieldSeparator);
|
||||
|
||||
@@ -193,7 +193,7 @@ udpiiu::udpiiu (
|
||||
|
||||
#ifdef IP_MULTICAST_TTL
|
||||
{
|
||||
int ttl;
|
||||
osiSockOptMcastTTL_t ttl;
|
||||
long val;
|
||||
if(envGetLongConfigParam(&EPICS_CA_MCAST_TTL, &val))
|
||||
val =1;
|
||||
|
||||
@@ -665,6 +665,11 @@ caStatus casDGClient::processDG ()
|
||||
if ( status != S_cas_success ) {
|
||||
break;
|
||||
}
|
||||
|
||||
if ( this->in.bytesPresent () > 0 && dgInBytesConsumed == 0 && status == S_cas_success ) {
|
||||
this->in.removeMsg ( this->in.bytesPresent() );
|
||||
}
|
||||
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
* 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.
|
||||
* in file LICENSE that is included with this distribution.
|
||||
\*************************************************************************/
|
||||
/*
|
||||
* Author Jeffrey O. Hill
|
||||
@@ -26,33 +26,39 @@
|
||||
#include "casAsyncIOI.h"
|
||||
#include "casMonitor.h"
|
||||
|
||||
casPVI::casPVI ( casPV & intf ) :
|
||||
pCAS ( NULL ), pPV ( & intf ), nMonAttached ( 0u ),
|
||||
|
||||
// Use casErrMessage instead of errMessage to show PV name
|
||||
#define casErrMessage(S, PM) \
|
||||
errPrintf(S, __FILE__, __LINE__, ", %s, %s", getName(), PM)
|
||||
|
||||
casPVI::casPVI ( casPV & intf ) :
|
||||
pCAS ( NULL ), pPV ( & intf ), nMonAttached ( 0u ),
|
||||
nIOAttached ( 0u ), deletePending ( false ) {}
|
||||
|
||||
casPVI::~casPVI ()
|
||||
{
|
||||
//
|
||||
// all channels should have been destroyed
|
||||
// (otherwise the server tool is yanking the
|
||||
//
|
||||
// all channels should have been destroyed
|
||||
// (otherwise the server tool is yanking the
|
||||
// PV out from under the server)
|
||||
//
|
||||
casVerify ( this->chanList.count() == 0u );
|
||||
//
|
||||
casVerify ( this->chanList.count() == 0u );
|
||||
|
||||
//
|
||||
// all outstanding IO should have been deleted
|
||||
// when we destroyed the channels
|
||||
//
|
||||
casVerify ( this->nIOAttached == 0u );
|
||||
//
|
||||
// all outstanding IO should have been deleted
|
||||
// when we destroyed the channels
|
||||
//
|
||||
casVerify ( this->nIOAttached == 0u );
|
||||
if ( this->nIOAttached ) {
|
||||
errlogPrintf ( "The number of IO objected attached is %u\n", this->nIOAttached );
|
||||
errlogPrintf ( "%u IO objects still attached in destructor\n",
|
||||
this->nIOAttached );
|
||||
}
|
||||
|
||||
//
|
||||
// all monitors should have been deleted
|
||||
// when we destroyed the channels
|
||||
//
|
||||
casVerify ( this->nMonAttached == 0u );
|
||||
//
|
||||
// all monitors should have been deleted
|
||||
// when we destroyed the channels
|
||||
//
|
||||
casVerify ( this->nMonAttached == 0u );
|
||||
|
||||
{
|
||||
epicsGuard < epicsMutex > guard ( this->mutex );
|
||||
@@ -139,26 +145,26 @@ caStatus casPVI::attachToServer ( caServerI & cas )
|
||||
caStatus casPVI::updateEnumStringTable ( casCtx & ctxIn )
|
||||
{
|
||||
epicsGuard < epicsMutex > guard ( this->mutex );
|
||||
|
||||
|
||||
//
|
||||
// create a gdd with the "enum string table" application type
|
||||
//
|
||||
// gddArray(int app, aitEnum prim, int dimen, ...);
|
||||
gdd * pTmp = new gddScalar ( gddAppType_enums );
|
||||
if ( pTmp == NULL ) {
|
||||
errMessage ( S_cas_noMemory,
|
||||
casErrMessage ( S_cas_noMemory,
|
||||
"unable to create gdd for read of application type \"enums\" string"
|
||||
" conversion table for enumerated PV" );
|
||||
return S_cas_noMemory;
|
||||
}
|
||||
|
||||
caStatus status = convertContainerMemberToAtomic ( *pTmp,
|
||||
caStatus status = convertContainerMemberToAtomic ( *pTmp,
|
||||
gddAppType_enums, MAX_ENUM_STATES );
|
||||
if ( status != S_cas_success ) {
|
||||
pTmp->unreference ();
|
||||
errMessage ( status,
|
||||
"unable to to config gdd for read of application type \"enums\" string"
|
||||
" conversion table for enumerated PV");
|
||||
casErrMessage ( status,
|
||||
"unable to config gdd for read of application type \"enums\" string"
|
||||
" conversion table for enumerated PV" );
|
||||
return status;
|
||||
}
|
||||
|
||||
@@ -169,12 +175,11 @@ caStatus casPVI::updateEnumStringTable ( casCtx & ctxIn )
|
||||
if ( status == S_cas_success ) {
|
||||
updateEnumStringTableAsyncCompletion ( *pTmp );
|
||||
}
|
||||
else if ( status != S_casApp_asyncCompletion &&
|
||||
else if ( status != S_casApp_asyncCompletion &&
|
||||
status != S_casApp_postponeAsyncIO ) {
|
||||
errPrintf ( status, __FILE__, __LINE__,
|
||||
"- unable to read application type \"enums\" "
|
||||
" (string conversion table) from enumerated native type PV \"%s\"",
|
||||
this->getName() );
|
||||
casErrMessage ( status,
|
||||
"unable to read application type \"enums\" "
|
||||
" (string conversion table) from enumerated native type PV" );
|
||||
}
|
||||
|
||||
pTmp->unreference ();
|
||||
@@ -185,40 +190,39 @@ caStatus casPVI::updateEnumStringTable ( casCtx & ctxIn )
|
||||
void casPVI::updateEnumStringTableAsyncCompletion ( const gdd & resp )
|
||||
{
|
||||
epicsGuard < epicsMutex > guard ( this->mutex );
|
||||
|
||||
|
||||
if ( resp.isContainer() ) {
|
||||
errMessage ( S_cas_badType,
|
||||
"application type \"enums\" string conversion table for"
|
||||
" enumerated PV was a container (expected vector of strings)" );
|
||||
casErrMessage ( S_cas_badType,
|
||||
"Invalid \"enums\" string conversion table for"
|
||||
" enumerated PV (container instead of vector of strings)" );
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if ( resp.dimension() == 0 ) {
|
||||
if ( resp.primitiveType() == aitEnumString ) {
|
||||
aitString *pStr = (aitString *) resp.dataVoid ();
|
||||
if ( ! this->enumStrTbl.setString ( 0, pStr->string() ) ) {
|
||||
errMessage ( S_cas_noMemory,
|
||||
casErrMessage ( S_cas_noMemory,
|
||||
"no memory to set enumerated PV string cache" );
|
||||
}
|
||||
}
|
||||
else if ( resp.primitiveType() == aitEnumFixedString ) {
|
||||
aitFixedString *pStr = (aitFixedString *) resp.dataVoid ();
|
||||
if ( ! this->enumStrTbl.setString ( 0, pStr->fixed_string ) ) {
|
||||
errMessage ( S_cas_noMemory,
|
||||
casErrMessage ( S_cas_noMemory,
|
||||
"no memory to set enumerated PV string cache" );
|
||||
}
|
||||
}
|
||||
else {
|
||||
errPrintf ( S_cas_badType, __FILE__, __LINE__,
|
||||
casErrMessage ( S_cas_badType,
|
||||
"application type \"enums\" string conversion"
|
||||
" table for enumerated PV \"%s\" isnt a string type?",
|
||||
getName() );
|
||||
" table for enumerated PV isnt a string type?" );
|
||||
}
|
||||
}
|
||||
else if ( resp.dimension() == 1 ) {
|
||||
gddStatus gdd_status;
|
||||
aitIndex index, first, count;
|
||||
|
||||
|
||||
gdd_status = resp.getBound ( 0, first, count );
|
||||
assert ( gdd_status == 0 );
|
||||
|
||||
@@ -232,7 +236,7 @@ void casPVI::updateEnumStringTableAsyncCompletion ( const gdd & resp )
|
||||
aitString *pStr = (aitString *) resp.dataVoid ();
|
||||
for ( index = 0; index<count; index++ ) {
|
||||
if ( ! this->enumStrTbl.setString ( index, pStr[index].string() ) ) {
|
||||
errMessage ( S_cas_noMemory,
|
||||
casErrMessage ( S_cas_noMemory,
|
||||
"no memory to set enumerated PV string cache" );
|
||||
}
|
||||
}
|
||||
@@ -241,19 +245,18 @@ void casPVI::updateEnumStringTableAsyncCompletion ( const gdd & resp )
|
||||
aitFixedString *pStr = (aitFixedString *) resp.dataVoid ();
|
||||
for ( index = 0; index < count; index++ ) {
|
||||
if ( ! this->enumStrTbl.setString ( index, pStr[index].fixed_string ) ) {
|
||||
errMessage ( S_cas_noMemory,
|
||||
casErrMessage ( S_cas_noMemory,
|
||||
"no memory to set enumerated PV string cache" );
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
errMessage ( S_cas_badType,
|
||||
"application type \"enums\" string conversion"
|
||||
" table for enumerated PV isnt a string type?" );
|
||||
casErrMessage( S_cas_badType,
|
||||
"bad \"enums\" string conversion table for enumerated PV" );
|
||||
}
|
||||
}
|
||||
else {
|
||||
errMessage ( S_cas_badType,
|
||||
casErrMessage ( S_cas_badType,
|
||||
"application type \"enums\" string conversion table"
|
||||
" for enumerated PV was multi-dimensional"
|
||||
" (expected vector of strings)" );
|
||||
@@ -287,7 +290,7 @@ void casPVI::postEvent ( const casEventMask & select, const gdd & event )
|
||||
}
|
||||
}
|
||||
|
||||
caStatus casPVI::installMonitor (
|
||||
caStatus casPVI::installMonitor (
|
||||
casMonitor & mon, tsDLList < casMonitor > & monitorList )
|
||||
{
|
||||
epicsGuard < epicsMutex > guard ( this->mutex );
|
||||
@@ -303,14 +306,14 @@ caStatus casPVI::installMonitor (
|
||||
}
|
||||
}
|
||||
|
||||
casMonitor * casPVI::removeMonitor (
|
||||
casMonitor * casPVI::removeMonitor (
|
||||
tsDLList < casMonitor > & list, ca_uint32_t clientIdIn )
|
||||
{
|
||||
epicsGuard < epicsMutex > guard ( this->mutex );
|
||||
casMonitor * pMon = 0;
|
||||
//
|
||||
// (it is reasonable to do a linear search here because
|
||||
// sane clients will require only one or two monitors
|
||||
// sane clients will require only one or two monitors
|
||||
// per channel)
|
||||
//
|
||||
tsDLIter < casMonitor > iter = list.firstIter ();
|
||||
@@ -359,9 +362,9 @@ void casPVI::installChannel ( chanIntfForPV & chan )
|
||||
epicsGuard < epicsMutex > guard ( this->mutex );
|
||||
this->chanList.add ( chan );
|
||||
}
|
||||
|
||||
void casPVI::removeChannel (
|
||||
chanIntfForPV & chan, tsDLList < casMonitor > & src,
|
||||
|
||||
void casPVI::removeChannel (
|
||||
chanIntfForPV & chan, tsDLList < casMonitor > & src,
|
||||
tsDLList < casMonitor > & dest )
|
||||
{
|
||||
epicsGuard < epicsMutex > guard ( this->mutex );
|
||||
@@ -380,8 +383,8 @@ void casPVI::clearOutstandingReads ( tsDLList < casAsyncIOI > & ioList )
|
||||
{
|
||||
epicsGuard < epicsMutex > guard ( this->mutex );
|
||||
|
||||
// cancel any pending asynchronous IO
|
||||
tsDLIter < casAsyncIOI > iterIO =
|
||||
// cancel any pending asynchronous IO
|
||||
tsDLIter < casAsyncIOI > iterIO =
|
||||
ioList.firstIter ();
|
||||
while ( iterIO.valid () ) {
|
||||
tsDLIter < casAsyncIOI > tmp = iterIO;
|
||||
@@ -407,7 +410,7 @@ void casPVI::destroyAllIO ( tsDLList < casAsyncIOI > & ioList )
|
||||
}
|
||||
}
|
||||
|
||||
void casPVI::installIO (
|
||||
void casPVI::installIO (
|
||||
tsDLList < casAsyncIOI > & ioList, casAsyncIOI & io )
|
||||
{
|
||||
epicsGuard < epicsMutex > guard ( this->mutex );
|
||||
@@ -416,7 +419,7 @@ void casPVI::installIO (
|
||||
this->nIOAttached++;
|
||||
}
|
||||
|
||||
void casPVI::uninstallIO (
|
||||
void casPVI::uninstallIO (
|
||||
tsDLList < casAsyncIOI > & ioList, casAsyncIOI & io )
|
||||
{
|
||||
{
|
||||
@@ -517,8 +520,8 @@ aitEnum casPVI::bestExternalType () const
|
||||
}
|
||||
}
|
||||
|
||||
// CA only does 1D arrays for now
|
||||
aitIndex casPVI::nativeCount ()
|
||||
// CA only does 1D arrays for now
|
||||
aitIndex casPVI::nativeCount ()
|
||||
{
|
||||
epicsGuard < epicsMutex > guard ( this->mutex );
|
||||
if ( this->pPV ) {
|
||||
@@ -542,4 +545,3 @@ const char * casPVI::getName () const
|
||||
return "<disconnected>";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -45,6 +45,7 @@
|
||||
#include "epicsExport.h"
|
||||
#include "link.h"
|
||||
#include "recSup.h"
|
||||
#include "dbUnitTest.h" /* for testSyncCallback() */
|
||||
|
||||
|
||||
static int callbackQueueSize = 2000;
|
||||
@@ -352,3 +353,86 @@ void callbackRequestProcessCallbackDelayed(CALLBACK *pcallback,
|
||||
callbackSetProcess(pcallback, Priority, pRec);
|
||||
callbackRequestDelayed(pcallback, seconds);
|
||||
}
|
||||
|
||||
/* Sync. process of testSyncCallback()
|
||||
*
|
||||
* 1. For each priority, make a call to callbackRequest() for each worker.
|
||||
* 2. Wait until all callbacks are concurrently being executed
|
||||
* 3. Last worker to begin executing signals success and begins waking up other workers
|
||||
* 4. Last worker to wake signals testSyncCallback() to complete
|
||||
*/
|
||||
typedef struct {
|
||||
epicsEventId wait_phase2, wait_phase4;
|
||||
int nphase2, nphase3;
|
||||
epicsCallback cb;
|
||||
} sync_helper;
|
||||
|
||||
static void sync_callback(epicsCallback *cb)
|
||||
{
|
||||
sync_helper *helper;
|
||||
callbackGetUser(helper, cb);
|
||||
|
||||
testGlobalLock();
|
||||
|
||||
assert(helper->nphase2 > 0);
|
||||
if(--helper->nphase2!=0) {
|
||||
/* we are _not_ the last to start. */
|
||||
testGlobalUnlock();
|
||||
epicsEventMustWait(helper->wait_phase2);
|
||||
testGlobalLock();
|
||||
}
|
||||
|
||||
/* we are either the last to start, or have been
|
||||
* woken by the same and must pass the wakeup along
|
||||
*/
|
||||
epicsEventMustTrigger(helper->wait_phase2);
|
||||
|
||||
assert(helper->nphase2 == 0);
|
||||
assert(helper->nphase3 > 0);
|
||||
|
||||
if(--helper->nphase3==0) {
|
||||
/* we are the last to wake up. wake up testSyncCallback() */
|
||||
epicsEventMustTrigger(helper->wait_phase4);
|
||||
}
|
||||
|
||||
testGlobalUnlock();
|
||||
}
|
||||
|
||||
void testSyncCallback(void)
|
||||
{
|
||||
sync_helper helper[NUM_CALLBACK_PRIORITIES];
|
||||
unsigned i;
|
||||
|
||||
testDiag("Begin testSyncCallback()");
|
||||
|
||||
for(i=0; i<NUM_CALLBACK_PRIORITIES; i++) {
|
||||
helper[i].wait_phase2 = epicsEventMustCreate(epicsEventEmpty);
|
||||
helper[i].wait_phase4 = epicsEventMustCreate(epicsEventEmpty);
|
||||
|
||||
/* no real need to lock here, but do so anyway so that valgrind can establish
|
||||
* the locking requirements for sync_helper.
|
||||
*/
|
||||
testGlobalLock();
|
||||
helper[i].nphase2 = helper[i].nphase3 = callbackQueue[i].threadsRunning;
|
||||
testGlobalUnlock();
|
||||
|
||||
callbackSetUser(&helper[i], &helper[i].cb);
|
||||
callbackSetPriority(i, &helper[i].cb);
|
||||
callbackSetCallback(sync_callback, &helper[i].cb);
|
||||
|
||||
callbackRequest(&helper[i].cb);
|
||||
}
|
||||
|
||||
for(i=0; i<NUM_CALLBACK_PRIORITIES; i++) {
|
||||
epicsEventMustWait(helper[i].wait_phase4);
|
||||
}
|
||||
|
||||
for(i=0; i<NUM_CALLBACK_PRIORITIES; i++) {
|
||||
testGlobalLock();
|
||||
epicsEventDestroy(helper[i].wait_phase2);
|
||||
epicsEventDestroy(helper[i].wait_phase4);
|
||||
testGlobalUnlock();
|
||||
}
|
||||
|
||||
testDiag("Complete testSyncCallback()");
|
||||
}
|
||||
|
||||
@@ -42,7 +42,9 @@ typedef struct callbackPvt {
|
||||
int priority;
|
||||
void *user; /*for use by callback user*/
|
||||
void *timer; /*for use by callback itself*/
|
||||
}CALLBACK;
|
||||
}epicsCallback;
|
||||
|
||||
typedef epicsCallback CALLBACK;
|
||||
|
||||
typedef void (*CALLBACKFUNC)(struct callbackPvt*);
|
||||
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
#include "errlog.h"
|
||||
#include "errMdef.h"
|
||||
|
||||
#define epicsExportSharedSymbols
|
||||
#include "epicsExport.h" /* #define epicsExportSharedSymbols */
|
||||
#include "caeventmask.h"
|
||||
#include "callback.h"
|
||||
#include "dbAccessDefs.h"
|
||||
@@ -65,6 +65,9 @@
|
||||
epicsShareDef struct dbBase *pdbbase = 0;
|
||||
epicsShareDef volatile int interruptAccept=FALSE;
|
||||
|
||||
epicsShareDef int dbAccessDebugPUTF = 0;
|
||||
epicsExportAddress(int, dbAccessDebugPUTF);
|
||||
|
||||
/* Hook Routines */
|
||||
|
||||
epicsShareDef DB_LOAD_RECORDS_HOOK_ROUTINE dbLoadRecordsHook = NULL;
|
||||
@@ -293,10 +296,14 @@ static void get_alarm(DBADDR *paddr, char **ppbuffer,
|
||||
if (*options & DBR_AL_LONG) {
|
||||
struct dbr_alLong *pal = (struct dbr_alLong*) pbuffer;
|
||||
|
||||
pal->upper_alarm_limit = (epicsInt32) ald.upper_alarm_limit;
|
||||
pal->upper_warning_limit = (epicsInt32) ald.upper_warning_limit;
|
||||
pal->lower_warning_limit = (epicsInt32) ald.lower_warning_limit;
|
||||
pal->lower_alarm_limit = (epicsInt32) ald.lower_alarm_limit;
|
||||
pal->upper_alarm_limit = finite(ald.upper_alarm_limit) ?
|
||||
(epicsInt32) ald.upper_alarm_limit : 0;
|
||||
pal->upper_warning_limit = finite(ald.upper_warning_limit) ?
|
||||
(epicsInt32) ald.upper_warning_limit : 0;
|
||||
pal->lower_warning_limit = finite(ald.lower_warning_limit) ?
|
||||
(epicsInt32) ald.lower_warning_limit : 0;
|
||||
pal->lower_alarm_limit = finite(ald.lower_alarm_limit) ?
|
||||
(epicsInt32) ald.lower_alarm_limit : 0;
|
||||
|
||||
if (no_data)
|
||||
*options ^= DBR_AL_LONG; /*Turn off option*/
|
||||
@@ -442,22 +449,6 @@ int dbGetFieldIndex(const struct dbAddr *paddr)
|
||||
return paddr->pfldDes->indRecordType;
|
||||
}
|
||||
|
||||
/*
|
||||
* Process a record if its scan field is passive.
|
||||
* Will notify if processing is complete by callback.
|
||||
* (only if you are interested in completion)
|
||||
*/
|
||||
long dbScanPassive(dbCommon *pfrom, dbCommon *pto)
|
||||
{
|
||||
/* if not passive just return success */
|
||||
if (pto->scan != 0)
|
||||
return 0;
|
||||
|
||||
if (pfrom && pfrom->ppn)
|
||||
dbNotifyAdd(pfrom,pto);
|
||||
return dbProcess(pto);
|
||||
}
|
||||
|
||||
/*
|
||||
* Process the record.
|
||||
* 1. Check for breakpoints.
|
||||
@@ -523,7 +514,8 @@ long dbProcess(dbCommon *precord)
|
||||
unsigned short monitor_mask;
|
||||
|
||||
if (*ptrace)
|
||||
printf("%s: Active %s\n", context, precord->name);
|
||||
printf("%s: dbProcess of Active '%s' with RPRO=%d\n",
|
||||
context, precord->name, precord->rpro);
|
||||
|
||||
/* raise scan alarm after MAX_LOCK times */
|
||||
if ((precord->stat == SCAN_ALARM) ||
|
||||
@@ -552,7 +544,8 @@ long dbProcess(dbCommon *precord)
|
||||
/* if disabled check disable alarm severity and return success */
|
||||
if (precord->disa == precord->disv) {
|
||||
if (*ptrace)
|
||||
printf("%s: Disabled %s\n", context, precord->name);
|
||||
printf("%s: dbProcess of Disabled '%s'\n",
|
||||
context, precord->name);
|
||||
|
||||
/*take care of caching and notifyCompletion*/
|
||||
precord->rpro = FALSE;
|
||||
@@ -589,7 +582,7 @@ long dbProcess(dbCommon *precord)
|
||||
}
|
||||
|
||||
if (*ptrace)
|
||||
printf("%s: Process %s\n", context, precord->name);
|
||||
printf("%s: dbProcess of '%s'\n", context, precord->name);
|
||||
|
||||
/* process record */
|
||||
status = prset->process(precord);
|
||||
@@ -709,6 +702,18 @@ void dbInitEntryFromRecord(struct dbCommon *prec, DBENTRY *pdbentry)
|
||||
pdbentry->precnode = ppvt->recnode;
|
||||
}
|
||||
|
||||
struct link* dbGetDevLink(struct dbCommon* prec)
|
||||
{
|
||||
DBLINK *plink = 0;
|
||||
DBENTRY entry;
|
||||
dbInitEntryFromRecord(prec, &entry);
|
||||
if(dbFindField(&entry, "INP")==0 || dbFindField(&entry, "OUT")==0) {
|
||||
plink = (DBLINK*)entry.pfield;
|
||||
}
|
||||
dbFinishEntry(&entry);
|
||||
return plink;
|
||||
}
|
||||
|
||||
long dbValueSize(short dbr_type)
|
||||
{
|
||||
/* sizes for value associated with each DBR request type */
|
||||
@@ -1036,7 +1041,7 @@ static long dbPutFieldLink(DBADDR *paddr,
|
||||
return S_db_badDbrtype;
|
||||
}
|
||||
|
||||
status = dbParseLink(pstring, pfldDes->field_type, &link_info, 0);
|
||||
status = dbParseLink(pstring, pfldDes->field_type, &link_info);
|
||||
if (status)
|
||||
return status;
|
||||
|
||||
@@ -1209,8 +1214,8 @@ long dbPutField(DBADDR *paddr, short dbrType,
|
||||
precord->scan == 0 &&
|
||||
dbrType < DBR_PUT_ACKT)) {
|
||||
if (precord->pact) {
|
||||
if (precord->tpro)
|
||||
printf("%s: Active %s\n",
|
||||
if (dbAccessDebugPUTF && precord->tpro)
|
||||
printf("%s: dbPutField to Active '%s', setting RPRO=1\n",
|
||||
epicsThreadGetNameSelf(), precord->name);
|
||||
precord->rpro = TRUE;
|
||||
} else {
|
||||
@@ -1343,4 +1348,3 @@ done:
|
||||
paddr->pfield = pfieldsave;
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
@@ -34,6 +34,7 @@ extern "C" {
|
||||
|
||||
epicsShareExtern struct dbBase *pdbbase;
|
||||
epicsShareExtern volatile int interruptAccept;
|
||||
epicsShareExtern int dbAccessDebugPUTF;
|
||||
|
||||
/* The database field and request types are defined in dbFldTypes.h*/
|
||||
/* Data Base Request Options */
|
||||
|
||||
@@ -410,7 +410,7 @@ long dbd(const char *record_name)
|
||||
|
||||
precord = addr.precord;
|
||||
|
||||
if (! (precord->bkpt & BKPT_ON_MASK)) {
|
||||
if (!(precord->bkpt & BKPT_ON_MASK)) {
|
||||
printf(" BKPT> No breakpoint set in this record\n");
|
||||
return(S_db_bkptNotSet);
|
||||
}
|
||||
|
||||
@@ -784,11 +784,16 @@ static void connectionCallback(struct connection_handler_args arg)
|
||||
if (pca->gotFirstConnection) {
|
||||
if (pca->nelements != ca_element_count(arg.chid) ||
|
||||
pca->dbrType != ca_field_type(arg.chid)) {
|
||||
/* BUG: We have no way to clear any old subscription with the
|
||||
* originally chosen data type/size. That will continue
|
||||
* to send us data and will result in an assert() fail.
|
||||
*/
|
||||
/* Let next dbCaGetLink and/or dbCaPutLink determine options */
|
||||
/* Size or type changed, clear everything and let the next call
|
||||
to dbCaGetLink() and/or dbCaPutLink() reset everything */
|
||||
if (pca->evidNative) {
|
||||
ca_clear_event(pca->evidNative);
|
||||
pca->evidNative = 0;
|
||||
}
|
||||
if (pca->evidString) {
|
||||
ca_clear_event(pca->evidString);
|
||||
pca->evidString = 0;
|
||||
}
|
||||
plink->value.pv_link.pvlMask &=
|
||||
~(pvlOptInpNative | pvlOptInpString |
|
||||
pvlOptOutNative | pvlOptOutString);
|
||||
@@ -837,6 +842,7 @@ static void eventCallback(struct event_handler_args arg)
|
||||
struct dbr_time_double *pdbr_time_double;
|
||||
dbCaCallback monitor = 0;
|
||||
void *userPvt = 0;
|
||||
int doScan = 1;
|
||||
|
||||
assert(pca);
|
||||
epicsMutexMustLock(pca->lock);
|
||||
@@ -867,10 +873,13 @@ static void eventCallback(struct event_handler_args arg)
|
||||
memcpy(pca->pgetString, dbr_value_ptr(arg.dbr, arg.type), size);
|
||||
pca->gotInString = TRUE;
|
||||
} else switch (arg.type){
|
||||
case DBR_TIME_ENUM:
|
||||
/* Disable the record scan if we also have a string monitor */
|
||||
doScan = !(plink->value.pv_link.pvlMask & pvlOptInpString);
|
||||
/* fall through */
|
||||
case DBR_TIME_STRING:
|
||||
case DBR_TIME_SHORT:
|
||||
case DBR_TIME_FLOAT:
|
||||
case DBR_TIME_ENUM:
|
||||
case DBR_TIME_CHAR:
|
||||
case DBR_TIME_LONG:
|
||||
case DBR_TIME_DOUBLE:
|
||||
@@ -888,7 +897,7 @@ static void eventCallback(struct event_handler_args arg)
|
||||
pca->sevr = pdbr_time_double->severity;
|
||||
pca->stat = pdbr_time_double->status;
|
||||
memcpy(&pca->timeStamp, &pdbr_time_double->stamp, sizeof(epicsTimeStamp));
|
||||
if (precord) {
|
||||
if (doScan && precord) {
|
||||
struct pv_link *ppv_link = &plink->value.pv_link;
|
||||
|
||||
if ((ppv_link->pvlMask & pvlOptCP) ||
|
||||
@@ -1149,7 +1158,8 @@ static void dbCaTask(void *arg)
|
||||
status = ca_add_array_event(
|
||||
dbf_type_to_DBR_TIME(ca_field_type(pca->chid)),
|
||||
0, /* dynamic size */
|
||||
pca->chid, eventCallback, pca, 0.0, 0.0, 0.0, 0);
|
||||
pca->chid, eventCallback, pca, 0.0, 0.0, 0.0,
|
||||
&pca->evidNative);
|
||||
if (status != ECA_NORMAL) {
|
||||
errlogPrintf("dbCaTask ca_add_array_event %s\n",
|
||||
ca_message(status));
|
||||
@@ -1161,7 +1171,8 @@ static void dbCaTask(void *arg)
|
||||
pca->pgetString = dbCalloc(1, MAX_STRING_SIZE);
|
||||
epicsMutexUnlock(pca->lock);
|
||||
status = ca_add_array_event(DBR_TIME_STRING, 1,
|
||||
pca->chid, eventCallback, pca, 0.0, 0.0, 0.0, 0);
|
||||
pca->chid, eventCallback, pca, 0.0, 0.0, 0.0,
|
||||
&pca->evidString);
|
||||
if (status != ECA_NORMAL) {
|
||||
errlogPrintf("dbCaTask ca_add_array_event %s\n",
|
||||
ca_message(status));
|
||||
|
||||
@@ -80,6 +80,8 @@ typedef struct caLink
|
||||
char *pgetString;
|
||||
void *pputNative;
|
||||
char *pputString;
|
||||
evid evidNative;
|
||||
evid evidString;
|
||||
char gotInNative;
|
||||
char gotInString;
|
||||
char gotOutNative;
|
||||
|
||||
@@ -12,6 +12,29 @@
|
||||
* Current Author: Andrew Johnson
|
||||
*/
|
||||
|
||||
/* The PUTF and RPRO fields in dbCommon are flags that indicate when a record
|
||||
* is being processed as a result of an external put (i.e. some server process
|
||||
* calling dbPutField()), ensuring that the record and its successors will
|
||||
* eventually get processed even if they happen to be busy at the time of the
|
||||
* put. From Base-3.16.2 and 7.0.2 the code ensures that all records downstream
|
||||
* from the original are processed even if a busy asynchronous device appears
|
||||
* in the processing chain (this breaks the chain in older versions).
|
||||
*
|
||||
* PUTF - This field is set in dbPutField() prior to it calling dbProcess().
|
||||
* It is normally cleared at the end of processing in recGblFwdLink().
|
||||
* It may also be cleared in dbProcess() if DISA==DISV (scan disabled),
|
||||
* or by the processTarget() function below.
|
||||
*
|
||||
* If PUTF is TRUE before a call to dbProcess(prec), then after it returns
|
||||
* either PACT is TRUE, or PUTF will be FALSE.
|
||||
*
|
||||
* RPRO - This field is set by dbPutField() or by the processTarget() function
|
||||
* below when a record to be processed is found to be busy (PACT==1).
|
||||
* It is normally cleared in recGblFwdLink() when the record is queued
|
||||
* for re-processing, or in dbProcess() if DISA==DISV (scan disabled).
|
||||
*/
|
||||
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
@@ -43,17 +66,22 @@
|
||||
#include "dbNotify.h"
|
||||
#include "dbScan.h"
|
||||
#include "dbStaticLib.h"
|
||||
#include "dbServer.h"
|
||||
#include "devSup.h"
|
||||
#include "link.h"
|
||||
#include "recGbl.h"
|
||||
#include "recSup.h"
|
||||
#include "special.h"
|
||||
#include "dbDbLink.h"
|
||||
|
||||
|
||||
/***************************** Database Links *****************************/
|
||||
|
||||
/* Forward definition */
|
||||
/* Forward definitions */
|
||||
static lset dbDb_lset;
|
||||
|
||||
static long processTarget(dbCommon *psrc, dbCommon *pdst);
|
||||
|
||||
long dbDbInitLink(struct link *plink, short dbfType)
|
||||
{
|
||||
DBADDR dbaddr;
|
||||
@@ -138,11 +166,7 @@ static long dbDbGetValue(struct link *plink, short dbrType, void *pbuffer,
|
||||
|
||||
/* scan passive records if link is process passive */
|
||||
if (ppv_link->pvlMask & pvlOptPP) {
|
||||
unsigned char pact = precord->pact;
|
||||
|
||||
precord->pact = TRUE;
|
||||
status = dbScanPassive(precord, paddr->precord);
|
||||
precord->pact = pact;
|
||||
if (status)
|
||||
return status;
|
||||
}
|
||||
@@ -311,22 +335,10 @@ static long dbDbPutValue(struct link *plink, short dbrType,
|
||||
return status;
|
||||
|
||||
if (paddr->pfield == (void *) &pdest->proc ||
|
||||
(ppv_link->pvlMask & pvlOptPP && pdest->scan == 0)) {
|
||||
/* if dbPutField caused asyn record to process */
|
||||
/* ask for reprocessing*/
|
||||
if (pdest->putf) {
|
||||
pdest->rpro = TRUE;
|
||||
} else { /* process dest record with source's PACT true */
|
||||
unsigned char pact;
|
||||
|
||||
if (psrce && psrce->ppn)
|
||||
dbNotifyAdd(psrce, pdest);
|
||||
pact = psrce->pact;
|
||||
psrce->pact = TRUE;
|
||||
status = dbProcess(pdest);
|
||||
psrce->pact = pact;
|
||||
}
|
||||
(ppv_link->pvlMask & pvlOptPP && pdest->scan == 0)) {
|
||||
status = processTarget(psrce, pdest);
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
@@ -356,3 +368,75 @@ static lset dbDb_lset = {
|
||||
dbDbPutValue, NULL,
|
||||
dbDbScanFwdLink, doLocked
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* Process a record if its scan field is passive.
|
||||
*/
|
||||
long dbScanPassive(dbCommon *pfrom, dbCommon *pto)
|
||||
{
|
||||
/* if not passive we're done */
|
||||
if (pto->scan != 0)
|
||||
return 0;
|
||||
|
||||
return processTarget(pfrom, pto);
|
||||
}
|
||||
|
||||
static long processTarget(dbCommon *psrc, dbCommon *pdst)
|
||||
{
|
||||
char context[40] = "";
|
||||
int trace = dbAccessDebugPUTF && *dbLockSetAddrTrace(psrc);
|
||||
long status;
|
||||
epicsUInt8 pact = psrc->pact;
|
||||
|
||||
psrc->pact = TRUE;
|
||||
|
||||
if (psrc && psrc->ppn)
|
||||
dbNotifyAdd(psrc, pdst);
|
||||
|
||||
if (trace && dbServerClient(context, sizeof(context))) {
|
||||
/* No client, use thread name */
|
||||
strncpy(context, epicsThreadGetNameSelf(), sizeof(context));
|
||||
context[sizeof(context) - 1] = 0;
|
||||
}
|
||||
|
||||
if (!pdst->pact) {
|
||||
/* Normal propagation of PUTF from src to dst */
|
||||
if (trace)
|
||||
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
|
||||
* we were originally triggered by a call to dbPutField(),
|
||||
* so we mark the dst record for reprocessing once the async
|
||||
* completion is over.
|
||||
*/
|
||||
if (trace)
|
||||
printf("%s: '%s' -> Active '%s', setting RPRO=1\n",
|
||||
context, psrc->name, pdst->name);
|
||||
|
||||
pdst->putf = FALSE;
|
||||
pdst->rpro = TRUE;
|
||||
}
|
||||
else {
|
||||
/* The dst record is busy, but we weren't triggered by a call
|
||||
* to dbPutField(). Do nothing.
|
||||
*/
|
||||
if (trace)
|
||||
printf("%s: '%s' -> Active '%s', done\n",
|
||||
context, psrc->name, pdst->name);
|
||||
}
|
||||
|
||||
status = dbProcess(pdst);
|
||||
|
||||
psrc->pact = pact;
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
@@ -117,6 +117,8 @@ static char *EVENT_PEND_NAME = "eventTask";
|
||||
|
||||
static struct evSubscrip canceledEvent;
|
||||
|
||||
static epicsMutexId stopSync;
|
||||
|
||||
static unsigned short ringSpace ( const struct event_que *pevq )
|
||||
{
|
||||
if ( pevq->evque[pevq->putix] == EVENTQEMPTY ) {
|
||||
@@ -258,6 +260,10 @@ dbEventCtx db_init_events (void)
|
||||
{
|
||||
struct event_user * evUser;
|
||||
|
||||
if (!stopSync) {
|
||||
stopSync = epicsMutexMustCreate();
|
||||
}
|
||||
|
||||
if (!dbevEventUserFreeList) {
|
||||
freeListInitPvt(&dbevEventUserFreeList,
|
||||
sizeof(struct event_user),8);
|
||||
@@ -337,6 +343,7 @@ epicsShareFunc void db_cleanup_events(void)
|
||||
dbevFieldLogFreeList = NULL;
|
||||
}
|
||||
|
||||
/* intentionally leak stopSync to avoid possible shutdown races */
|
||||
/*
|
||||
* DB_CLOSE_EVENTS()
|
||||
*
|
||||
@@ -372,11 +379,15 @@ void db_close_events (dbEventCtx ctx)
|
||||
|
||||
epicsMutexUnlock ( evUser->lock );
|
||||
|
||||
epicsMutexMustLock (stopSync);
|
||||
|
||||
epicsEventDestroy(evUser->pexitsem);
|
||||
epicsEventDestroy(evUser->ppendsem);
|
||||
epicsEventDestroy(evUser->pflush_sem);
|
||||
epicsMutexDestroy(evUser->lock);
|
||||
|
||||
epicsMutexUnlock (stopSync);
|
||||
|
||||
freeListFree(dbevEventUserFreeList, evUser);
|
||||
}
|
||||
|
||||
@@ -1059,8 +1070,15 @@ static void event_task (void *pParm)
|
||||
|
||||
taskwdRemove(epicsThreadGetIdSelf());
|
||||
|
||||
/* use stopSync to ensure pexitsem is not destroy'd
|
||||
* until epicsEventSignal() has returned.
|
||||
*/
|
||||
epicsMutexMustLock (stopSync);
|
||||
|
||||
epicsEventSignal(evUser->pexitsem);
|
||||
|
||||
epicsMutexUnlock(stopSync);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -152,6 +152,12 @@ static const iocshArg * const dbnrArgs[1] = {&dbnrArg0};
|
||||
static const iocshFuncDef dbnrFuncDef = {"dbnr",1,dbnrArgs};
|
||||
static void dbnrCallFunc(const iocshArgBuf *args) { dbnr(args[0].ival);}
|
||||
|
||||
/* dbli */
|
||||
static const iocshArg dbliArg0 = { "pattern",iocshArgString};
|
||||
static const iocshArg * const dbliArgs[1] = {&dbliArg0};
|
||||
static const iocshFuncDef dbliFuncDef = {"dbli",1,dbliArgs};
|
||||
static void dbliCallFunc(const iocshArgBuf *args) { dbli(args[0].sval);}
|
||||
|
||||
/* dbla */
|
||||
static const iocshArg dblaArg0 = { "pattern",iocshArgString};
|
||||
static const iocshArg * const dblaArgs[1] = {&dblaArg0};
|
||||
@@ -415,6 +421,7 @@ void dbIocRegister(void)
|
||||
iocshRegister(&dblFuncDef,dblCallFunc);
|
||||
iocshRegister(&dbnrFuncDef,dbnrCallFunc);
|
||||
iocshRegister(&dblaFuncDef,dblaCallFunc);
|
||||
iocshRegister(&dbliFuncDef,dbliCallFunc);
|
||||
iocshRegister(&dbgrepFuncDef,dbgrepCallFunc);
|
||||
iocshRegister(&dbgfFuncDef,dbgfCallFunc);
|
||||
iocshRegister(&dbpfFuncDef,dbpfCallFunc);
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* Copyright (c) 2016 UChicago Argonne LLC, as Operator of Argonne
|
||||
* National Laboratory.
|
||||
* EPICS BASE is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
* in file LICENSE that is included with this distribution.
|
||||
\*************************************************************************/
|
||||
/* dbJLink.c */
|
||||
|
||||
@@ -25,19 +25,33 @@
|
||||
#include "dbLock.h"
|
||||
#include "dbStaticLib.h"
|
||||
#include "link.h"
|
||||
#include "epicsExport.h"
|
||||
|
||||
#define IFDEBUG(n) if(parser->parse_debug)
|
||||
epicsShareDef int dbJLinkDebug = 0;
|
||||
epicsExportAddress(int, dbJLinkDebug);
|
||||
|
||||
#define IFDEBUG(n) if (dbJLinkDebug >= (n))
|
||||
|
||||
typedef struct parseContext {
|
||||
jlink *pjlink;
|
||||
jlink *product;
|
||||
short dbfType;
|
||||
short jsonDepth;
|
||||
unsigned key_is_link:1;
|
||||
unsigned parse_debug:1;
|
||||
unsigned lset_debug:1;
|
||||
} parseContext;
|
||||
|
||||
epicsShareDef const char *jlif_result_name[2] = {
|
||||
"jlif_stop",
|
||||
"jlif_continue",
|
||||
};
|
||||
|
||||
epicsShareDef const char *jlif_key_result_name[5] = {
|
||||
"jlif_key_stop",
|
||||
"jlif_key_continue",
|
||||
"jlif_key_child_inlink",
|
||||
"jlif_key_child_outlink",
|
||||
"jlif_key_child_fwdlink"
|
||||
};
|
||||
|
||||
#define CALL_OR_STOP(routine) !(routine) ? jlif_stop : (routine)
|
||||
|
||||
static int dbjl_return(parseContext *parser, jlif_result result) {
|
||||
@@ -45,8 +59,8 @@ static int dbjl_return(parseContext *parser, jlif_result result) {
|
||||
|
||||
IFDEBUG(10) {
|
||||
printf("dbjl_return(%s@%p, %d)\t", pjlink ? pjlink->pif->name : "", pjlink, result);
|
||||
printf(" jsonDepth=%d, parseDepth=%d, key_is_link=%d\n",
|
||||
parser->jsonDepth, pjlink ? pjlink->parseDepth : 0, parser->key_is_link);
|
||||
printf(" jsonDepth=%d, parseDepth=%d, dbfType=%d\n",
|
||||
parser->jsonDepth, pjlink ? pjlink->parseDepth : 0, parser->dbfType);
|
||||
}
|
||||
|
||||
if (result == jlif_stop && pjlink) {
|
||||
@@ -59,6 +73,9 @@ static int dbjl_return(parseContext *parser, jlif_result result) {
|
||||
pjlink->pif->free_jlink(pjlink);
|
||||
}
|
||||
|
||||
IFDEBUG(10)
|
||||
printf(" returning %d %s\n", result,
|
||||
result == jlif_stop ? "*** STOP ***" : "Continue");
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -68,8 +85,8 @@ static int dbjl_value(parseContext *parser, jlif_result result) {
|
||||
|
||||
IFDEBUG(10) {
|
||||
printf("dbjl_value(%s@%p, %d)\t", pjlink ? pjlink->pif->name : "", pjlink, result);
|
||||
printf(" jsonDepth=%d, parseDepth=%d, key_is_link=%d\n",
|
||||
parser->jsonDepth, pjlink ? pjlink->parseDepth : 0, parser->key_is_link);
|
||||
printf(" jsonDepth=%d, parseDepth=%d, dbfType=%d\n",
|
||||
parser->jsonDepth, pjlink ? pjlink->parseDepth : 0, parser->dbfType);
|
||||
}
|
||||
|
||||
if (result == jlif_stop || pjlink->parseDepth > 0)
|
||||
@@ -81,7 +98,6 @@ static int dbjl_value(parseContext *parser, jlif_result result) {
|
||||
} else if (parent->pif->end_child) {
|
||||
parent->pif->end_child(parent, pjlink);
|
||||
}
|
||||
pjlink->debug = 0;
|
||||
|
||||
parser->pjlink = parent;
|
||||
|
||||
@@ -159,29 +175,46 @@ static int dbjl_start_map(void *ctx) {
|
||||
if (!pjlink) {
|
||||
IFDEBUG(10) {
|
||||
printf("dbjl_start_map(NULL)\t");
|
||||
printf(" jsonDepth=%d, parseDepth=00, key_is_link=%d\n",
|
||||
parser->jsonDepth, parser->key_is_link);
|
||||
printf(" jsonDepth=%d, parseDepth=00, dbfType=%d\n",
|
||||
parser->jsonDepth, parser->dbfType);
|
||||
}
|
||||
|
||||
assert(parser->jsonDepth == 0);
|
||||
parser->jsonDepth++;
|
||||
parser->key_is_link = 1;
|
||||
return jlif_continue; /* Opening '{' */
|
||||
}
|
||||
|
||||
IFDEBUG(10) {
|
||||
printf("dbjl_start_map(%s@%p)\t", pjlink ? pjlink->pif->name : "", pjlink);
|
||||
printf(" jsonDepth=%d, parseDepth=%d, key_is_link=%d\n",
|
||||
parser->jsonDepth, pjlink ? pjlink->parseDepth : 0, parser->key_is_link);
|
||||
printf(" jsonDepth=%d, parseDepth=%d, dbfType=%d\n",
|
||||
parser->jsonDepth, pjlink ? pjlink->parseDepth : 0, parser->dbfType);
|
||||
}
|
||||
|
||||
pjlink->parseDepth++;
|
||||
parser->jsonDepth++;
|
||||
|
||||
result = CALL_OR_STOP(pjlink->pif->parse_start_map)(pjlink);
|
||||
if (result == jlif_key_child_link) {
|
||||
parser->key_is_link = 1;
|
||||
switch (result) {
|
||||
case jlif_key_child_inlink:
|
||||
parser->dbfType = DBF_INLINK;
|
||||
result = jlif_continue;
|
||||
break;
|
||||
case jlif_key_child_outlink:
|
||||
parser->dbfType = DBF_OUTLINK;
|
||||
result = jlif_continue;
|
||||
break;
|
||||
case jlif_key_child_fwdlink:
|
||||
parser->dbfType = DBF_FWDLINK;
|
||||
result = jlif_continue;
|
||||
break;
|
||||
case jlif_key_stop:
|
||||
case jlif_key_continue:
|
||||
break;
|
||||
default:
|
||||
errlogPrintf("dbJLinkInit: Bad return %d from '%s'::parse_start_map()\n",
|
||||
result, pjlink->pif->name);
|
||||
result = jlif_stop;
|
||||
break;
|
||||
}
|
||||
|
||||
IFDEBUG(10)
|
||||
@@ -196,8 +229,9 @@ static int dbjl_map_key(void *ctx, const unsigned char *key, size_t len) {
|
||||
char *link_name;
|
||||
linkSup *linkSup;
|
||||
jlif *pjlif;
|
||||
jlink *child;
|
||||
|
||||
if (!parser->key_is_link) {
|
||||
if (parser->dbfType == 0) {
|
||||
if (!pjlink) {
|
||||
errlogPrintf("dbJLinkInit: Illegal second link key '%.*s'\n",
|
||||
(int) len, key);
|
||||
@@ -207,8 +241,8 @@ static int dbjl_map_key(void *ctx, const unsigned char *key, size_t len) {
|
||||
IFDEBUG(10) {
|
||||
printf("dbjl_map_key(%s@%p, \"%.*s\")\t",
|
||||
pjlink->pif->name, pjlink, (int) len, key);
|
||||
printf(" jsonDepth=%d, parseDepth=%d, key_is_link=%d\n",
|
||||
parser->jsonDepth, pjlink ? pjlink->parseDepth : 0, parser->key_is_link);
|
||||
printf(" jsonDepth=%d, parseDepth=%d, dbfType=%d\n",
|
||||
parser->jsonDepth, pjlink ? pjlink->parseDepth : 0, parser->dbfType);
|
||||
}
|
||||
|
||||
assert(pjlink->parseDepth > 0);
|
||||
@@ -219,8 +253,8 @@ static int dbjl_map_key(void *ctx, const unsigned char *key, size_t len) {
|
||||
|
||||
IFDEBUG(10) {
|
||||
printf("dbjl_map_key(NULL, \"%.*s\")\t", (int) len, key);
|
||||
printf(" jsonDepth=%d, parseDepth=00, key_is_link=%d\n",
|
||||
parser->jsonDepth, parser->key_is_link);
|
||||
printf(" jsonDepth=%d, parseDepth=00, dbfType=%d\n",
|
||||
parser->jsonDepth, parser->dbfType);
|
||||
}
|
||||
|
||||
link_name = dbmfStrndup((const char *) key, len);
|
||||
@@ -241,27 +275,35 @@ static int dbjl_map_key(void *ctx, const unsigned char *key, size_t len) {
|
||||
return dbjl_return(parser, jlif_stop);
|
||||
}
|
||||
|
||||
dbmfFree(link_name);
|
||||
|
||||
pjlink = pjlif->alloc_jlink(parser->dbfType);
|
||||
if (!pjlink) {
|
||||
errlogPrintf("dbJLinkInit: Out of memory\n");
|
||||
child = pjlif->alloc_jlink(parser->dbfType);
|
||||
if (!child) {
|
||||
errlogPrintf("dbJLinkInit: Link type '%s' allocation failed. \n",
|
||||
link_name);
|
||||
dbmfFree(link_name);
|
||||
return dbjl_return(parser, jlif_stop);
|
||||
}
|
||||
pjlink->pif = pjlif;
|
||||
pjlink->parent = NULL;
|
||||
pjlink->parseDepth = 0;
|
||||
pjlink->debug = !!parser->lset_debug;
|
||||
|
||||
child->pif = pjlif;
|
||||
child->parseDepth = 0;
|
||||
child->debug = 0;
|
||||
|
||||
if (parser->pjlink) {
|
||||
/* We're starting a child link, save its parent */
|
||||
pjlink->parent = parser->pjlink;
|
||||
child->parent = pjlink;
|
||||
|
||||
if (pjlink->pif->start_child)
|
||||
pjlink->pif->start_child(pjlink, child);
|
||||
}
|
||||
parser->pjlink = pjlink;
|
||||
parser->key_is_link = 0;
|
||||
else
|
||||
child->parent = NULL;
|
||||
|
||||
parser->pjlink = child;
|
||||
parser->dbfType = 0;
|
||||
|
||||
dbmfFree(link_name);
|
||||
|
||||
IFDEBUG(8)
|
||||
printf("dbjl_map_key: New %s@%p\n", pjlink ? pjlink->pif->name : "", pjlink);
|
||||
printf("dbjl_map_key: New %s@%p\n", child ? child->pif->name : "", child);
|
||||
|
||||
return jlif_continue;
|
||||
}
|
||||
@@ -274,9 +316,9 @@ static int dbjl_end_map(void *ctx) {
|
||||
IFDEBUG(10) {
|
||||
printf("dbjl_end_map(%s@%p)\t",
|
||||
pjlink ? pjlink->pif->name : "NULL", pjlink);
|
||||
printf(" jsonDepth=%d, parseDepth=%d, key_is_link=%d\n",
|
||||
printf(" jsonDepth=%d, parseDepth=%d, dbfType=%d\n",
|
||||
parser->jsonDepth, pjlink ? pjlink->parseDepth : 0,
|
||||
parser->key_is_link);
|
||||
parser->dbfType);
|
||||
}
|
||||
|
||||
parser->jsonDepth--;
|
||||
@@ -298,8 +340,8 @@ static int dbjl_start_array(void *ctx) {
|
||||
|
||||
IFDEBUG(10) {
|
||||
printf("dbjl_start_array(%s@%p)\t", pjlink ? pjlink->pif->name : "", pjlink);
|
||||
printf(" jsonDepth=%d, parseDepth=%d, key_is_link=%d\n",
|
||||
parser->jsonDepth, pjlink ? pjlink->parseDepth : 0, parser->key_is_link);
|
||||
printf(" jsonDepth=%d, parseDepth=%d, dbfType=%d\n",
|
||||
parser->jsonDepth, pjlink ? pjlink->parseDepth : 0, parser->dbfType);
|
||||
}
|
||||
|
||||
assert(pjlink);
|
||||
@@ -316,8 +358,8 @@ static int dbjl_end_array(void *ctx) {
|
||||
|
||||
IFDEBUG(10) {
|
||||
printf("dbjl_end_array(%s@%p)\t", pjlink ? pjlink->pif->name : "", pjlink);
|
||||
printf(" jsonDepth=%d, parseDepth=%d, key_is_link=%d\n",
|
||||
parser->jsonDepth, pjlink ? pjlink->parseDepth : 0, parser->key_is_link);
|
||||
printf(" jsonDepth=%d, parseDepth=%d, dbfType=%d\n",
|
||||
parser->jsonDepth, pjlink ? pjlink->parseDepth : 0, parser->dbfType);
|
||||
}
|
||||
|
||||
assert(pjlink);
|
||||
@@ -335,7 +377,7 @@ static yajl_callbacks dbjl_callbacks = {
|
||||
};
|
||||
|
||||
long dbJLinkParse(const char *json, size_t jlen, short dbfType,
|
||||
jlink **ppjlink, unsigned opts)
|
||||
jlink **ppjlink)
|
||||
{
|
||||
parseContext context, *parser = &context;
|
||||
yajl_alloc_funcs dbjl_allocs;
|
||||
@@ -347,17 +389,14 @@ long dbJLinkParse(const char *json, size_t jlen, short dbfType,
|
||||
parser->product = NULL;
|
||||
parser->dbfType = dbfType;
|
||||
parser->jsonDepth = 0;
|
||||
parser->key_is_link = 0;
|
||||
parser->parse_debug = !!(opts&LINK_DEBUG_JPARSE);
|
||||
parser->lset_debug = !!(opts&LINK_DEBUG_LSET);
|
||||
|
||||
IFDEBUG(10)
|
||||
printf("dbJLinkInit(\"%.*s\", %d, %p)\n",
|
||||
(int) jlen, json, dbfType, ppjlink);
|
||||
|
||||
IFDEBUG(10)
|
||||
printf("dbJLinkInit: jsonDepth=%d, key_is_link=%d\n",
|
||||
parser->jsonDepth, parser->key_is_link);
|
||||
printf("dbJLinkInit: jsonDepth=%d, dbfType=%d\n",
|
||||
parser->jsonDepth, parser->dbfType);
|
||||
|
||||
yajl_set_default_alloc_funcs(&dbjl_allocs);
|
||||
yh = yajl_alloc(&dbjl_callbacks, &dbjl_allocs, parser);
|
||||
@@ -365,8 +404,14 @@ long dbJLinkParse(const char *json, size_t jlen, short dbfType,
|
||||
return S_db_noMemory;
|
||||
|
||||
ys = yajl_parse(yh, (const unsigned char *) json, jlen);
|
||||
if (ys == yajl_status_ok)
|
||||
IFDEBUG(10)
|
||||
printf("dbJLinkInit: yajl_parse() returned %d\n", ys);
|
||||
|
||||
if (ys == yajl_status_ok) {
|
||||
ys = yajl_complete_parse(yh);
|
||||
IFDEBUG(10)
|
||||
printf("dbJLinkInit: yajl_complete_parse() returned %d\n", ys);
|
||||
}
|
||||
|
||||
switch (ys) {
|
||||
unsigned char *err;
|
||||
@@ -378,6 +423,9 @@ long dbJLinkParse(const char *json, size_t jlen, short dbfType,
|
||||
break;
|
||||
|
||||
case yajl_status_error:
|
||||
IFDEBUG(10)
|
||||
printf(" jsonDepth=%d, product=%p, pjlink=%p\n",
|
||||
parser->jsonDepth, parser->product, parser->pjlink);
|
||||
err = yajl_get_error(yh, 1, (const unsigned char *) json, jlen);
|
||||
errlogPrintf("dbJLinkInit: %s\n", err);
|
||||
yajl_free_error(yh, err);
|
||||
@@ -389,18 +437,24 @@ long dbJLinkParse(const char *json, size_t jlen, short dbfType,
|
||||
}
|
||||
|
||||
yajl_free(yh);
|
||||
|
||||
IFDEBUG(10)
|
||||
printf("dbJLinkInit: returning status=0x%lx\n\n",
|
||||
status);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
long dbJLinkInit(struct link *plink)
|
||||
{
|
||||
jlink *pjlink;
|
||||
|
||||
assert(plink);
|
||||
pjlink = plink->value.json.jlink;
|
||||
|
||||
if (pjlink)
|
||||
plink->lset = pjlink->pif->get_lset(pjlink);
|
||||
if (plink->type == JSON_LINK) {
|
||||
jlink *pjlink = plink->value.json.jlink;
|
||||
|
||||
if (pjlink)
|
||||
plink->lset = pjlink->pif->get_lset(pjlink);
|
||||
}
|
||||
|
||||
dbLinkOpen(plink);
|
||||
return 0;
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* Copyright (c) 2016 UChicago Argonne LLC, as Operator of Argonne
|
||||
* National Laboratory.
|
||||
* EPICS BASE is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
* in file LICENSE that is included with this distribution.
|
||||
\*************************************************************************/
|
||||
/* dbJLink.h */
|
||||
|
||||
@@ -21,12 +21,16 @@ typedef enum {
|
||||
jlif_continue = 1
|
||||
} jlif_result;
|
||||
|
||||
epicsShareExtern const char *jlif_result_name[2];
|
||||
|
||||
typedef enum {
|
||||
jlif_key_stop = jlif_stop,
|
||||
jlif_key_continue = jlif_continue,
|
||||
jlif_key_child_link
|
||||
jlif_key_child_inlink, jlif_key_child_outlink, jlif_key_child_fwdlink
|
||||
} jlif_key_result;
|
||||
|
||||
epicsShareExtern const char *jlif_key_result_name[5];
|
||||
|
||||
struct link;
|
||||
struct lset;
|
||||
struct jlif;
|
||||
@@ -35,7 +39,7 @@ typedef struct jlink {
|
||||
struct jlif *pif; /* Link methods */
|
||||
struct jlink *parent; /* NULL for top-level links */
|
||||
int parseDepth; /* Used by parser, unused afterwards */
|
||||
unsigned debug:1; /* set by caller of jlif operations to request debug output to console */
|
||||
unsigned debug:1; /* Set to request debug output to console */
|
||||
/* Link types extend or embed this structure for private storage */
|
||||
} jlink;
|
||||
|
||||
@@ -72,8 +76,9 @@ typedef struct jlif {
|
||||
/* Optional, parser saw a string value */
|
||||
|
||||
jlif_key_result (*parse_start_map)(jlink *);
|
||||
/* Optional, parser saw an open-brace '{'. Return jlif_key_child_link
|
||||
* to expect a child link next (extra key/value pairs may follow).
|
||||
/* Optional, parser saw an open-brace '{'. Return jlif_key_child_inlink,
|
||||
* jlif_key_child_outlink, or jlif_key_child_fwdlink to expect a child
|
||||
* link next (extra key/value pairs may follow)
|
||||
*/
|
||||
|
||||
jlif_result (*parse_map_key)(jlink *, const char *key, size_t len);
|
||||
@@ -90,7 +95,8 @@ typedef struct jlif {
|
||||
|
||||
void (*end_child)(jlink *parent, jlink *child);
|
||||
/* Optional, called with pointer to the new child link after
|
||||
* parse_start_map() returned jlif_key_child_link */
|
||||
* the child link has finished parsing successfully
|
||||
*/
|
||||
|
||||
struct lset* (*get_lset)(const jlink *);
|
||||
/* Required, return lset for this link instance */
|
||||
@@ -98,7 +104,7 @@ typedef struct jlif {
|
||||
void (*report)(const jlink *, int level, int indent);
|
||||
/* Optional, print status information about this link instance, then
|
||||
* if (level > 0) print a link identifier (at indent+2) and call
|
||||
* dbJLinkReport(child, level-1, indent+4)
|
||||
* dbJLinkReport(child, level-1, indent+4)
|
||||
* for each child.
|
||||
*/
|
||||
|
||||
@@ -107,13 +113,19 @@ typedef struct jlif {
|
||||
* Stop immediately and return status if non-zero.
|
||||
*/
|
||||
|
||||
void (*start_child)(jlink *parent, jlink *child);
|
||||
/* Optional, called with pointer to the new child link after
|
||||
* parse_start_map() returned a jlif_key_child_link value and
|
||||
* the child link has been allocated (but not parsed yet)
|
||||
*/
|
||||
|
||||
/* Link types must NOT extend this table with their own routines,
|
||||
* this space is reserved for extensions to the jlink interface.
|
||||
*/
|
||||
} jlif;
|
||||
|
||||
epicsShareFunc long dbJLinkParse(const char *json, size_t len, short dbfType,
|
||||
jlink **ppjlink, unsigned opts);
|
||||
jlink **ppjlink);
|
||||
epicsShareFunc long dbJLinkInit(struct link *plink);
|
||||
|
||||
epicsShareFunc void dbJLinkFree(jlink *);
|
||||
@@ -130,4 +142,3 @@ epicsShareFunc long dbJLinkMapAll(char *recname, jlink_map_fn rtn, void *ctx);
|
||||
#endif
|
||||
|
||||
#endif /* INC_dbJLink_H */
|
||||
|
||||
|
||||
@@ -49,7 +49,7 @@
|
||||
#include "special.h"
|
||||
|
||||
/* How to identify links in error messages */
|
||||
static const char * link_field_name(const struct link *plink)
|
||||
const char * dbLinkFieldName(const struct link *plink)
|
||||
{
|
||||
const struct dbCommon *precord = plink->precord;
|
||||
const dbRecordType *pdbRecordType = precord->rdes;
|
||||
@@ -136,7 +136,7 @@ void dbInitLink(struct link *plink, short dbfType)
|
||||
errlogPrintf("Forward-link uses Channel Access "
|
||||
"without pointing to PROC field\n"
|
||||
" %s.%s => %s\n",
|
||||
precord->name, link_field_name(plink),
|
||||
precord->name, dbLinkFieldName(plink),
|
||||
plink->value.pv_link.pvname);
|
||||
}
|
||||
}
|
||||
@@ -265,8 +265,19 @@ int dbIsLinkConnected(const struct link *plink)
|
||||
{
|
||||
lset *plset = plink->lset;
|
||||
|
||||
if (!plset || !plset->isConnected)
|
||||
if (!plset)
|
||||
return FALSE;
|
||||
if (!plset->isVolatile)
|
||||
return TRUE;
|
||||
|
||||
if (!plset->isConnected) {
|
||||
struct dbCommon *precord = plink->precord;
|
||||
|
||||
errlogPrintf("dbLink: Link type for '%s.%s' is volatile but has no"
|
||||
" lset::isConnected() method\n",
|
||||
precord->name, dbLinkFieldName(plink));
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return plset->isConnected(plink);
|
||||
}
|
||||
|
||||
@@ -27,60 +27,345 @@ extern "C" {
|
||||
|
||||
struct dbLocker;
|
||||
|
||||
/** @file dbLink.h
|
||||
* @brief Link Support API
|
||||
*
|
||||
* Link support run-time API, all link types provide an lset which is used by
|
||||
* the IOC database to control and operate the link. This file also declares the
|
||||
* dbLink routines that IOC, record and device code can call to perform link
|
||||
* operations.
|
||||
*/
|
||||
|
||||
/** @brief callback routine for locked link operations
|
||||
*
|
||||
* Called by the lset::doLocked method to permit multiple link operations
|
||||
* while the link instance is locked.
|
||||
*
|
||||
* @param plink the link
|
||||
* @param priv context for the callback routine
|
||||
*/
|
||||
typedef long (*dbLinkUserCallback)(struct link *plink, void *priv);
|
||||
|
||||
/** @brief Link Support Entry Table
|
||||
*
|
||||
* This structure provides information about and methods for an individual link
|
||||
* type. A pointer to this structure is included in every link's lset field, and
|
||||
* is used to perform operations on the link. For JSON links the pointer is
|
||||
* obtained by calling pjlink->pif->get_lset() at link initialization time,
|
||||
* immediately before calling dbLinkOpen() to activate the link.
|
||||
*/
|
||||
typedef struct lset {
|
||||
/* Characteristics of the link type */
|
||||
|
||||
/** @brief link constancy
|
||||
*
|
||||
* 1 means this is a constant link type whose value doesn't change.
|
||||
* The link's value will be obtained using one of the methods loadScalar,
|
||||
* loadLS or loadArray.
|
||||
*/
|
||||
const unsigned isConstant:1;
|
||||
|
||||
/** @brief link volatility
|
||||
*
|
||||
* 0 means the link is always connected.
|
||||
*/
|
||||
const unsigned isVolatile:1;
|
||||
|
||||
/* Activation */
|
||||
/** @brief activate link
|
||||
*
|
||||
* Optional, called whenever a JSON link is initialized or added at runtime.
|
||||
*
|
||||
* @param plink the link
|
||||
*/
|
||||
void (*openLink)(struct link *plink);
|
||||
|
||||
/* Destructor */
|
||||
/** @brief deactivate link
|
||||
*
|
||||
* Optional, called whenever a link address is changed at runtime, or the
|
||||
* IOC is shutting down.
|
||||
*
|
||||
* @param locker
|
||||
* @param plink the link
|
||||
*/
|
||||
void (*removeLink)(struct dbLocker *locker, struct link *plink);
|
||||
|
||||
/* Const init, data type hinting */
|
||||
/* Constant link initialization and data type hinting */
|
||||
|
||||
/** @brief load constant scalar from link type
|
||||
*
|
||||
* Usually called during IOC initialization, constant link types must copy a
|
||||
* scalar value of the indicated data type to the buffer provided and return
|
||||
* 0. A non-constant link type can use this method call as an early hint
|
||||
* that subsequent calls to dbGetLink() will request scalar data of the
|
||||
* indicated type, although the type might change.
|
||||
*
|
||||
* @param plink the link
|
||||
* @param dbrType data type code
|
||||
* @param pbuffer where to put the value
|
||||
* @returns 0 if a value was loaded, non-zero otherwise
|
||||
*/
|
||||
long (*loadScalar)(struct link *plink, short dbrType, void *pbuffer);
|
||||
|
||||
/** @brief load constant long string from link type
|
||||
*
|
||||
* Usually called during IOC initialization, constant link types must copy a
|
||||
* nil-terminated string up to size characters long to the buffer provided,
|
||||
* and write the length of that string to the plen location. A non-constant
|
||||
* link type can use this as an early hint that subsequent calls to
|
||||
* dbGetLink() will request long string data, although this might change.
|
||||
*
|
||||
* @param plink the link
|
||||
* @param pbuffer where to put the string
|
||||
* @param size length of pbuffer in chars
|
||||
* @param plen set to number of chars written
|
||||
* @returns status value
|
||||
*/
|
||||
long (*loadLS)(struct link *plink, char *pbuffer, epicsUInt32 size,
|
||||
epicsUInt32 *plen);
|
||||
|
||||
/** @brief load constant array from link type
|
||||
*
|
||||
* Usually called during IOC initialization, constant link types must copy
|
||||
* an array value of the indicated data type to the buffer provided, update
|
||||
* the pnRequest location to indicate how many elements were loaded, and
|
||||
* return 0. A non-constant link type can use this method call as an early
|
||||
* hint that subsequent calls to dbGetLink() will request array data of the
|
||||
* indicated type and max size, although the request might change.
|
||||
*
|
||||
* @param plink the link
|
||||
* @param dbrType data type code
|
||||
* @param pbuffer where to put the value
|
||||
* @param pnRequest Max elements on entry, actual on exit
|
||||
* @returns 0 if elements were loaded, non-zero otherwise
|
||||
*/
|
||||
long (*loadArray)(struct link *plink, short dbrType, void *pbuffer,
|
||||
long *pnRequest);
|
||||
|
||||
/* Metadata */
|
||||
|
||||
/** @brief return link connection status
|
||||
*
|
||||
* Return an indication whether this link is connected or not. This routine
|
||||
* is polled by the calcout and some external record types. Not required for
|
||||
* non-volatile link types, which are by definition always connected.
|
||||
*
|
||||
* @param plink the link
|
||||
* @returns 1 if connected, 0 if disconnected
|
||||
*/
|
||||
int (*isConnected)(const struct link *plink);
|
||||
|
||||
/** @brief get data type of link destination
|
||||
*
|
||||
* Called on both input and output links by long string support code to
|
||||
* decide whether to use DBR_CHAR/DBR_UCHAR or DBR_STRING for a subsequent
|
||||
* dbPutLink() or dbGetLink() call. Optional, but if not provided long
|
||||
* strings cannot be transported over this link type, and no warning or
|
||||
* error will appear to explain why. Not required for constant link types.
|
||||
*
|
||||
* @param plink the link
|
||||
* @returns DBF_* type code, or -1 on error/disconnected link
|
||||
*/
|
||||
int (*getDBFtype)(const struct link *plink);
|
||||
long (*getElements)(const struct link *plink, long *nelements);
|
||||
|
||||
/* Get data */
|
||||
|
||||
/** @brief get array size of an input link
|
||||
*
|
||||
* Called on input links by the compress record type for memory allocation
|
||||
* purposes, before using the dbGetLink() routine to fetch the actual
|
||||
* array data.
|
||||
*
|
||||
* @param plink the link
|
||||
* @param pnElements where to put the answer
|
||||
* @returns status value
|
||||
*/
|
||||
long (*getElements)(const struct link *plink, long *pnElements);
|
||||
|
||||
/** @brief get value from an input link
|
||||
*
|
||||
* Called to fetch data from the link, which must be converted into the
|
||||
* given data type and placed in the buffer indicated. The actual number of
|
||||
* elements retrieved should be updated in the pnRequest location. If this
|
||||
* method returns an error status value, the link's record will be placed
|
||||
* into an Invalid severity / Link Alarm state by the dbGetLink() routine
|
||||
* that calls this method.
|
||||
*
|
||||
* @param plink the link
|
||||
* @param dbrType data type code
|
||||
* @param pbuffer where to put the value
|
||||
* @param pnRequest max elements on entry, actual on exit
|
||||
* @returns status value
|
||||
*/
|
||||
long (*getValue)(struct link *plink, short dbrType, void *pbuffer,
|
||||
long *pnRequest);
|
||||
|
||||
/** @brief get the control range for an output link
|
||||
*
|
||||
* Called to fetch the control range for the link target, as a pair of
|
||||
* double values for the lowest and highest values that the target will
|
||||
* accept. This method is not used at all by the IOC or built-in record
|
||||
* types, although external record types may require it.
|
||||
*
|
||||
* @param plink the link
|
||||
* @param lo lowest accepted value
|
||||
* @param hi highest accepted value
|
||||
* @returns status value
|
||||
*/
|
||||
long (*getControlLimits)(const struct link *plink, double *lo, double *hi);
|
||||
|
||||
/** @brief get the display range from an input link
|
||||
*
|
||||
* Called to fetch the display range for an input link target, as a pair of
|
||||
* double values for the lowest and highest values that the PV expects to
|
||||
* return. This method is used by several built-in record types to obtain
|
||||
* the display range for their generic input links.
|
||||
*
|
||||
* @param plink the link
|
||||
* @param lo lowest accepted value
|
||||
* @param hi highest accepted value
|
||||
* @returns status value
|
||||
*/
|
||||
long (*getGraphicLimits)(const struct link *plink, double *lo, double *hi);
|
||||
|
||||
/** @brief get the alarm limits from an input link
|
||||
*
|
||||
* Called to fetch the alarm limits for an input link target, as four
|
||||
* double values for the warning and alarm levels that the PV checks its
|
||||
* value against. This method is used by several built-in record types to
|
||||
* obtain the alarm limits for their generic input links.
|
||||
*
|
||||
* @param plink the link
|
||||
* @param lolo low alarm value
|
||||
* @param lo low warning value
|
||||
* @param hi high warning value
|
||||
* @param hihi high alarm value
|
||||
* @returns status value
|
||||
*/
|
||||
long (*getAlarmLimits)(const struct link *plink, double *lolo, double *lo,
|
||||
double *hi, double *hihi);
|
||||
|
||||
/** @brief get the precision from an input link
|
||||
*
|
||||
* Called to fetch the precision for an input link target. This method is
|
||||
* used by several built-in record types to obtain the precision for their
|
||||
* generic input links.
|
||||
*
|
||||
* @param plink the link
|
||||
* @param precision where to put the answer
|
||||
* @returns status value
|
||||
*/
|
||||
long (*getPrecision)(const struct link *plink, short *precision);
|
||||
|
||||
/** @brief get the units string from an input link
|
||||
*
|
||||
* Called to fetch the units string for an input link target. This method is
|
||||
* used by several built-in record types to obtain the units string for
|
||||
* their generic input links.
|
||||
*
|
||||
* @param plink the link
|
||||
* @param units where to put the answer
|
||||
* @param unitsSize buffer size for the answer
|
||||
* @returns status value
|
||||
*/
|
||||
long (*getUnits)(const struct link *plink, char *units, int unitsSize);
|
||||
|
||||
/** @brief get the alarm condition from an input link
|
||||
*
|
||||
* Called to fetch the alarm status and severity for an input link target.
|
||||
* Either status or severity pointers may be NULL when that value is not
|
||||
* needed by the calling code. This method is used by several built-in
|
||||
* record types to obtain the alarm condition for their generic input links.
|
||||
*
|
||||
* @param plink the link
|
||||
* @param status where to put the alarm status (or NULL)
|
||||
* @param severity where to put the severity (or NULL)
|
||||
* @returns status value
|
||||
*/
|
||||
long (*getAlarm)(const struct link *plink, epicsEnum16 *status,
|
||||
epicsEnum16 *severity);
|
||||
|
||||
/** @brief get the time-stamp from an input link
|
||||
*
|
||||
* Called to fetch the time-stamp for an input link target. This method is
|
||||
* used by many built-in device supports to obtain the precision for their
|
||||
* generic input links.
|
||||
*
|
||||
* @param plink the link
|
||||
* @param pstamp where to put the answer
|
||||
* @returns status value
|
||||
*/
|
||||
long (*getTimeStamp)(const struct link *plink, epicsTimeStamp *pstamp);
|
||||
|
||||
/* Put data */
|
||||
|
||||
/** @brief put a value to an output link
|
||||
*
|
||||
* Called to send nRequest elements of type dbrType found at pbuffer to an
|
||||
* output link target.
|
||||
*
|
||||
* @param plink the link
|
||||
* @param dbrType data type code
|
||||
* @param pbuffer where to put the value
|
||||
* @param nRequest number of elements to send
|
||||
* @returns status value
|
||||
*/
|
||||
long (*putValue)(struct link *plink, short dbrType,
|
||||
const void *pbuffer, long nRequest);
|
||||
|
||||
/** @brief put a value to an output link with asynchronous completion
|
||||
*
|
||||
* Called to send nRequest elements of type dbrType found at pbuffer to an
|
||||
* output link target. If the return status is zero, the link type will
|
||||
* later indicate the put has completed by calling dbLinkAsyncComplete()
|
||||
* from a background thread, which will be used to continue the record
|
||||
* process operation from where it left off.
|
||||
*
|
||||
* @param plink the link
|
||||
* @param dbrType data type code
|
||||
* @param pbuffer where to put the value
|
||||
* @param nRequest number of elements to send
|
||||
* @returns status value
|
||||
*/
|
||||
long (*putAsync)(struct link *plink, short dbrType,
|
||||
const void *pbuffer, long nRequest);
|
||||
|
||||
/* Process */
|
||||
|
||||
/** @brief trigger processing of a forward link
|
||||
*
|
||||
* Called to trigger processing of the record pointed to by a forward link.
|
||||
* This routine is optional, but if not provided no warning message will be
|
||||
* shown when called by dbScanFwdLink(). JSON link types that do not support
|
||||
* this operation should return NULL from their jlif::alloc_jlink() method
|
||||
* if it gets called with a dbfType of DBF_FWDLINK.
|
||||
*
|
||||
* @param plink the link
|
||||
*/
|
||||
void (*scanForward)(struct link *plink);
|
||||
|
||||
/* Atomicity */
|
||||
|
||||
/** @brief execute a callback routine with link locked
|
||||
*
|
||||
* Called on an input link when multiple link attributes need to be fetched
|
||||
* in an atomic fashion. The link type must call the callback routine and
|
||||
* prevent any background I/O from updating any cached link data until that
|
||||
* routine returns. This method is used by most input device support to
|
||||
* fetch the timestamp along with the value when the record's TSE field is
|
||||
* set to epicsTimeEventDeviceTime.
|
||||
*
|
||||
* @param plink the link
|
||||
* @param rtn routine to execute
|
||||
* @returns status value
|
||||
*/
|
||||
long (*doLocked)(struct link *plink, dbLinkUserCallback rtn, void *priv);
|
||||
} lset;
|
||||
|
||||
#define dbGetSevr(link, sevr) \
|
||||
dbGetAlarm(link, NULL, sevr)
|
||||
|
||||
epicsShareFunc const char * dbLinkFieldName(const struct link *plink);
|
||||
|
||||
epicsShareFunc void dbInitLink(struct link *plink, short dbfType);
|
||||
epicsShareFunc void dbAddLink(struct dbLocker *locker, struct link *plink,
|
||||
short dbfType, DBADDR *ptarget);
|
||||
@@ -97,9 +382,10 @@ epicsShareFunc long dbLoadLink(struct link *plink, short dbrType,
|
||||
epicsShareFunc long dbLoadLinkArray(struct link *, short dbrType, void *pbuffer,
|
||||
long *pnRequest);
|
||||
|
||||
epicsShareFunc long dbGetNelements(const struct link *plink, long *nelements);
|
||||
epicsShareFunc long dbGetNelements(const struct link *plink, long *pnElements);
|
||||
epicsShareFunc int dbIsLinkConnected(const struct link *plink); /* 0 or 1 */
|
||||
epicsShareFunc int dbGetLinkDBFtype(const struct link *plink);
|
||||
|
||||
epicsShareFunc long dbGetLink(struct link *, short dbrType, void *pbuffer,
|
||||
long *options, long *nRequest);
|
||||
epicsShareFunc long dbGetControlLimits(const struct link *plink, double *low,
|
||||
|
||||
@@ -13,6 +13,8 @@
|
||||
#ifndef INCdbLockh
|
||||
#define INCdbLockh
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include "ellLib.h"
|
||||
#include "shareLib.h"
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
* Copyright (c) 2013 Helmholtz-Zentrum Berlin
|
||||
* für Materialien und Energie GmbH.
|
||||
* EPICS BASE is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
* in file LICENSE that is included with this distribution.
|
||||
\*************************************************************************/
|
||||
/* dbScan.c */
|
||||
/* tasks and subroutines to scan the database */
|
||||
@@ -109,8 +109,8 @@ static char *priorityName[NUM_CALLBACK_PRIORITIES] = {
|
||||
typedef struct event_list {
|
||||
CALLBACK callback[NUM_CALLBACK_PRIORITIES];
|
||||
scan_list scan_list[NUM_CALLBACK_PRIORITIES];
|
||||
struct event_list *next;
|
||||
char event_name[MAX_STRING_SIZE];
|
||||
struct event_list *next;
|
||||
char eventname[1]; /* actually arbitrary size */
|
||||
} event_list;
|
||||
static event_list * volatile pevent_list[256];
|
||||
static epicsMutexId event_lock;
|
||||
@@ -247,11 +247,6 @@ void scanAdd(struct dbCommon *precord)
|
||||
event_list *pel;
|
||||
|
||||
eventname = precord->evnt;
|
||||
if (strlen(eventname) >= MAX_STRING_SIZE) {
|
||||
recGblRecordError(S_db_badField, (void *)precord,
|
||||
"scanAdd: too long EVNT value");
|
||||
return;
|
||||
}
|
||||
prio = precord->prio;
|
||||
if (prio < 0 || prio >= NUM_CALLBACK_PRIORITIES) {
|
||||
recGblRecordError(-1, (void *)precord,
|
||||
@@ -315,24 +310,17 @@ void scanDelete(struct dbCommon *precord)
|
||||
recGblRecordError(-1, (void *)precord,
|
||||
"scanDelete detected illegal SCAN value");
|
||||
} else if (scan == menuScanEvent) {
|
||||
char* eventname;
|
||||
int prio;
|
||||
event_list *pel;
|
||||
scan_list *psl = 0;
|
||||
|
||||
eventname = precord->evnt;
|
||||
prio = precord->prio;
|
||||
if (prio < 0 || prio >= NUM_CALLBACK_PRIORITIES) {
|
||||
recGblRecordError(-1, (void *)precord,
|
||||
"scanDelete detected illegal PRIO field");
|
||||
return;
|
||||
}
|
||||
do /* multithreading: make sure pel is consistent */
|
||||
pel = pevent_list[0];
|
||||
while (pel != pevent_list[0]);
|
||||
for (; pel; pel=pel->next) {
|
||||
if (strcmp(pel->event_name, eventname) == 0) break;
|
||||
}
|
||||
pel = eventNameToHandle(precord->evnt);
|
||||
if (pel && (psl = &pel->scan_list[prio]))
|
||||
deleteFromList(precord, psl);
|
||||
} else if (scan == menuScanI_O_Intr) {
|
||||
@@ -420,14 +408,12 @@ int scanpel(const char* eventname) /* print event list */
|
||||
int prio;
|
||||
event_list *pel;
|
||||
|
||||
do /* multithreading: make sure pel is consistent */
|
||||
pel = pevent_list[0];
|
||||
while (pel != pevent_list[0]);
|
||||
for (; pel; pel = pel->next) {
|
||||
if (!eventname || strcmp(pel->event_name, eventname) == 0) {
|
||||
for (pel = pevent_list[0]; pel; pel = pel->next) {
|
||||
if (!eventname || epicsStrGlobMatch(pel->eventname, eventname)) {
|
||||
printf("Event \"%s\"\n", pel->eventname);
|
||||
for (prio = 0; prio < NUM_CALLBACK_PRIORITIES; prio++) {
|
||||
if (ellCount(&pel->scan_list[prio].list) == 0) continue;
|
||||
sprintf(message, "Event \"%s\" Priority %s", pel->event_name, priorityName[prio]);
|
||||
sprintf(message, " Priority %s", priorityName[prio]);
|
||||
printList(&pel->scan_list[prio], message);
|
||||
}
|
||||
}
|
||||
@@ -478,20 +464,52 @@ event_list *eventNameToHandle(const char *eventname)
|
||||
int prio;
|
||||
event_list *pel;
|
||||
static epicsThreadOnceId onceId = EPICS_THREAD_ONCE_INIT;
|
||||
double eventnumber = 0;
|
||||
size_t namelength;
|
||||
|
||||
if (!eventname || eventname[0] == 0)
|
||||
return NULL;
|
||||
if (!eventname) return NULL;
|
||||
while (isspace((int) eventname[0])) eventname++;
|
||||
if (!eventname[0]) return NULL;
|
||||
namelength = strlen(eventname);
|
||||
while (isspace((int) eventname[namelength-1])) namelength--;
|
||||
|
||||
/* Backward compatibility with numeric events:
|
||||
Treat any string that represents a double with an
|
||||
integer part between 0 and 255 the same as the integer
|
||||
because it is most probably a conversion from double
|
||||
like from a calc record.
|
||||
*/
|
||||
if (epicsParseDouble(eventname, &eventnumber, NULL) == 0)
|
||||
{
|
||||
if (eventnumber >= 0 && eventnumber < 256)
|
||||
{
|
||||
if (eventnumber < 1)
|
||||
return NULL; /* 0 is no event */
|
||||
if ((pel = pevent_list[(int)eventnumber]) != NULL)
|
||||
return pel;
|
||||
}
|
||||
else
|
||||
eventnumber = 0; /* not a numeric event between 1 and 255 */
|
||||
}
|
||||
|
||||
epicsThreadOnce(&onceId, eventOnce, NULL);
|
||||
epicsMutexMustLock(event_lock);
|
||||
for (pel = pevent_list[0]; pel; pel=pel->next) {
|
||||
if (strcmp(pel->event_name, eventname) == 0) break;
|
||||
if (strncmp(pel->eventname, eventname, namelength) == 0
|
||||
&& pel->eventname[namelength] == 0)
|
||||
break;
|
||||
}
|
||||
if (pel == NULL) {
|
||||
pel = calloc(1, sizeof(event_list));
|
||||
pel = calloc(1, sizeof(event_list) + namelength);
|
||||
if (!pel)
|
||||
goto done;
|
||||
strcpy(pel->event_name, eventname);
|
||||
if (eventnumber > 0) {
|
||||
/* backward compatibility: make all numeric events look like integers */
|
||||
sprintf(pel->eventname, "%i", (int)eventnumber);
|
||||
pevent_list[(int)eventnumber] = pel;
|
||||
}
|
||||
else
|
||||
strncpy(pel->eventname, eventname, namelength);
|
||||
for (prio = 0; prio < NUM_CALLBACK_PRIORITIES; prio++) {
|
||||
callbackSetUser(&pel->scan_list[prio], &pel->callback[prio]);
|
||||
callbackSetPriority(prio, &pel->callback[prio]);
|
||||
@@ -501,12 +519,6 @@ event_list *eventNameToHandle(const char *eventname)
|
||||
}
|
||||
pel->next=pevent_list[0];
|
||||
pevent_list[0]=pel;
|
||||
{ /* backward compatibility */
|
||||
char* p;
|
||||
long e = strtol(eventname, &p, 0);
|
||||
if (*p == 0 && e > 0 && e <= 255)
|
||||
pevent_list[e] = pel;
|
||||
}
|
||||
}
|
||||
done:
|
||||
epicsMutexUnlock(event_lock);
|
||||
@@ -528,13 +540,8 @@ void postEvent(event_list *pel)
|
||||
/* backward compatibility */
|
||||
void post_event(int event)
|
||||
{
|
||||
event_list* pel;
|
||||
|
||||
if (event <= 0 || event > 255) return;
|
||||
do { /* multithreading: make sure pel is consistent */
|
||||
pel = pevent_list[event];
|
||||
} while (pel != pevent_list[event]);
|
||||
postEvent(pel);
|
||||
postEvent(pevent_list[event]);
|
||||
}
|
||||
|
||||
static void ioscanOnce(void *arg)
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#include "menuScan.h"
|
||||
#include "shareLib.h"
|
||||
#include "compilerDependencies.h"
|
||||
#include "devSup.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
@@ -33,9 +34,7 @@ extern "C" {
|
||||
#define MIN_PHASE SHRT_MIN
|
||||
|
||||
/*definitions for I/O Interrupt Scanning */
|
||||
struct ioscan_head;
|
||||
|
||||
typedef struct ioscan_head *IOSCANPVT;
|
||||
/* IOSCANPVT now defined in devSup.h */
|
||||
typedef struct event_list *EVENTPVT;
|
||||
|
||||
struct dbCommon;
|
||||
@@ -51,7 +50,7 @@ epicsShareFunc void scanCleanup(void);
|
||||
|
||||
epicsShareFunc EVENTPVT eventNameToHandle(const char* event);
|
||||
epicsShareFunc void postEvent(EVENTPVT epvt);
|
||||
epicsShareFunc void post_event(int event) EPICS_DEPRECATED;
|
||||
epicsShareFunc void post_event(int event);
|
||||
epicsShareFunc void scanAdd(struct dbCommon *);
|
||||
epicsShareFunc void scanDelete(struct dbCommon *);
|
||||
epicsShareFunc double scanPeriod(int scan);
|
||||
|
||||
@@ -15,6 +15,10 @@
|
||||
|
||||
#include "shareLib.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/** @file dbState.h
|
||||
* @brief Generic IOC state facility
|
||||
*
|
||||
@@ -89,4 +93,9 @@ epicsShareFunc void dbStateShow(dbStateId id, unsigned int level);
|
||||
*/
|
||||
epicsShareFunc void dbStateShowAll(unsigned int level);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // INCdbStateH
|
||||
|
||||
@@ -42,12 +42,13 @@
|
||||
#include "special.h"
|
||||
|
||||
#define MAXLINE 80
|
||||
#define MAXMESS 128
|
||||
struct msgBuff { /* line output structure */
|
||||
char out_buff[MAXLINE + 1];
|
||||
char *pNext;
|
||||
char *pLast;
|
||||
char *pNexTab;
|
||||
char message[128];
|
||||
char message[MAXMESS];
|
||||
};
|
||||
typedef struct msgBuff TAB_BUFFER;
|
||||
|
||||
@@ -257,7 +258,30 @@ long dbla(const char *pmask)
|
||||
dbFinishEntry(pdbentry);
|
||||
return 0;
|
||||
}
|
||||
|
||||
long dbli(const char *pattern)
|
||||
{
|
||||
DBENTRY dbentry;
|
||||
void* ptr;
|
||||
|
||||
if (!pdbbase) {
|
||||
printf("No database loaded\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
dbInitEntry(pdbbase, &dbentry);
|
||||
while (dbNextMatchingInfo(&dbentry, pattern) == 0)
|
||||
{
|
||||
printf("%s info(%s, \"%s\"", dbGetRecordName(&dbentry),
|
||||
dbGetInfoName(&dbentry), dbGetInfoString(&dbentry));
|
||||
if ((ptr = dbGetInfoPointer(&dbentry)) != NULL)
|
||||
printf(", %p", ptr);
|
||||
printf(")\n");
|
||||
}
|
||||
dbFinishEntry(&dbentry);
|
||||
return 0;
|
||||
}
|
||||
|
||||
long dbgrep(const char *pmask)
|
||||
{
|
||||
DBENTRY dbentry;
|
||||
@@ -309,6 +333,11 @@ long dbgf(const char *pname)
|
||||
if (nameToAddr(pname, &addr))
|
||||
return -1;
|
||||
|
||||
if (addr.precord->lset == NULL) {
|
||||
printf("dbgf only works after iocInit\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
no_elements = MIN(addr.no_elements, sizeof(buffer)/addr.field_size);
|
||||
if (addr.dbr_field_type == DBR_ENUM) {
|
||||
long status = dbGetField(&addr, DBR_STRING, pbuffer,
|
||||
@@ -345,6 +374,11 @@ long dbpf(const char *pname,const char *pvalue)
|
||||
if (nameToAddr(pname, &addr))
|
||||
return -1;
|
||||
|
||||
if (addr.precord->lset == NULL) {
|
||||
printf("dbpf only works after iocInit\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (addr.no_elements > 1 &&
|
||||
(addr.dbr_field_type == DBR_CHAR || addr.dbr_field_type == DBR_UCHAR)) {
|
||||
dbrType = addr.dbr_field_type;
|
||||
@@ -399,6 +433,11 @@ long dbtr(const char *pname)
|
||||
if (nameToAddr(pname, &addr))
|
||||
return -1;
|
||||
|
||||
if (addr.precord->lset == NULL) {
|
||||
printf("dbtr only works after iocInit\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
precord = (struct dbCommon*)addr.precord;
|
||||
if (precord->pact) {
|
||||
printf("record active\n");
|
||||
@@ -438,6 +477,11 @@ long dbtgf(const char *pname)
|
||||
if (nameToAddr(pname, &addr))
|
||||
return -1;
|
||||
|
||||
if (addr.precord->lset == NULL) {
|
||||
printf("dbtgf only works after iocInit\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* try all options first */
|
||||
req_options = 0xffffffff;
|
||||
ret_options = req_options;
|
||||
@@ -534,6 +578,11 @@ long dbtpf(const char *pname, const char *pvalue)
|
||||
if (nameToAddr(pname, &addr))
|
||||
return -1;
|
||||
|
||||
if (addr.precord->lset == NULL) {
|
||||
printf("dbtpf only works after iocInit\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (put_type = DBR_STRING; put_type <= DBF_ENUM; put_type++) {
|
||||
union {
|
||||
epicsInt8 i8;
|
||||
@@ -795,7 +844,7 @@ static void printBuffer(
|
||||
|
||||
printf("no_strs = %u:\n",
|
||||
pdbr_enumStrs->no_str);
|
||||
for (i = 0; i < pdbr_enumStrs->no_str; i++)
|
||||
for (i = 0; i < pdbr_enumStrs->no_str; i++)
|
||||
printf("\t\"%s\"\n", pdbr_enumStrs->strs[i]);
|
||||
}
|
||||
else {
|
||||
@@ -938,7 +987,7 @@ static void printBuffer(
|
||||
int chunk = (len > MAXLINE - 5) ? MAXLINE - 5 : len;
|
||||
|
||||
sprintf(pmsg, "\"%.*s\"", chunk, (char *)pbuffer + i);
|
||||
len -= chunk;
|
||||
len -= chunk; i += chunk;
|
||||
if (len > 0)
|
||||
strcat(pmsg, " +");
|
||||
dbpr_msgOut(pMsgBuff, tab_size);
|
||||
@@ -1104,7 +1153,7 @@ static int dbpr_report(
|
||||
case DBF_DEVICE:
|
||||
status = dbFindField(pdbentry,pfield_name);
|
||||
pfield_value = dbGetString(pdbentry);
|
||||
sprintf(pmsg, "%s: %s", pfield_name,
|
||||
sprintf(pmsg, "%-4s: %s", pfield_name,
|
||||
(pfield_value ? pfield_value : "<nil>"));
|
||||
dbpr_msgOut(pMsgBuff, tab_size);
|
||||
break;
|
||||
@@ -1114,19 +1163,18 @@ static int dbpr_report(
|
||||
case DBF_FWDLINK: {
|
||||
DBLINK *plink = (DBLINK *)pfield;
|
||||
int ind;
|
||||
const char *type = "LINK";
|
||||
|
||||
status = dbFindField(pdbentry,pfield_name);
|
||||
for (ind=0; ind<LINK_NTYPES; ind++) {
|
||||
if (pamaplinkType[ind].value == plink->type)
|
||||
break;
|
||||
}
|
||||
if (ind>=LINK_NTYPES) {
|
||||
sprintf(pmsg,"%s: Illegal Link Type", pfield_name);
|
||||
}
|
||||
else {
|
||||
sprintf(pmsg,"%s:%s %s", pfield_name,
|
||||
pamaplinkType[ind].strvalue,dbGetString(pdbentry));
|
||||
}
|
||||
if (!plink->text)
|
||||
for (ind=0; ind<LINK_NTYPES; ind++) {
|
||||
if (pamaplinkType[ind].value == plink->type) {
|
||||
type = pamaplinkType[ind].strvalue;
|
||||
break;
|
||||
}
|
||||
}
|
||||
epicsSnprintf(pmsg, MAXMESS, "%-4s: %s %s", pfield_name,
|
||||
type, dbGetString(pdbentry));
|
||||
dbpr_msgOut(pMsgBuff, tab_size);
|
||||
}
|
||||
break;
|
||||
@@ -1137,13 +1185,21 @@ static int dbpr_report(
|
||||
char time_buf[40];
|
||||
epicsTimeToStrftime(time_buf, 40, "%Y-%m-%d %H:%M:%S.%09f",
|
||||
&paddr->precord->time);
|
||||
sprintf(pmsg, "%s: %s", pfield_name, time_buf);
|
||||
sprintf(pmsg, "%-4s: %s", pfield_name, time_buf);
|
||||
dbpr_msgOut(pMsgBuff, tab_size);
|
||||
}
|
||||
else if (pdbFldDes->size == sizeof(void *) &&
|
||||
strchr(pdbFldDes->extra, '*')) {
|
||||
/* Special for pointers, needed on little-endian CPUs */
|
||||
sprintf(pmsg, "%s: %p", pfield_name, *(void **)pfield);
|
||||
/* Special for pointers */
|
||||
sprintf(pmsg, "%-4s: PTR %p", pfield_name, *(void **)pfield);
|
||||
dbpr_msgOut(pMsgBuff, tab_size);
|
||||
}
|
||||
else if (pdbFldDes->size == sizeof(ELLLIST) &&
|
||||
!strncmp(pdbFldDes->extra, "ELLLIST", 7)) {
|
||||
/* Special for linked lists */
|
||||
ELLLIST *plist = (ELLLIST *)pfield;
|
||||
sprintf(pmsg, "%-4s: ELL %d [%p .. %p]", pfield_name,
|
||||
ellCount(plist), ellFirst(plist), ellLast(plist));
|
||||
dbpr_msgOut(pMsgBuff, tab_size);
|
||||
}
|
||||
else { /* just print field as hex bytes */
|
||||
@@ -1159,7 +1215,7 @@ static int dbpr_report(
|
||||
value = (unsigned int)*pchar;
|
||||
sprintf(ptemp_buf, "%02x ", value);
|
||||
}
|
||||
sprintf(pmsg, "%s: %s", pfield_name,temp_buf);
|
||||
sprintf(pmsg, "%-4s: %s", pfield_name,temp_buf);
|
||||
dbpr_msgOut(pMsgBuff, tab_size);
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -25,6 +25,8 @@ epicsShareFunc long dbl(
|
||||
epicsShareFunc long dbnr(int verbose);
|
||||
/* list aliases */
|
||||
epicsShareFunc long dbla(const char *pmask);
|
||||
/* list infos */
|
||||
epicsShareFunc long dbli(const char *patern);
|
||||
/*list records with mask*/
|
||||
epicsShareFunc long dbgrep(const char *pmask);
|
||||
/*get field value*/
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
#include "osiUnistd.h"
|
||||
#include "registry.h"
|
||||
#include "epicsEvent.h"
|
||||
#include "epicsThread.h"
|
||||
|
||||
#define epicsExportSharedSymbols
|
||||
#include "dbAccess.h"
|
||||
@@ -414,3 +415,26 @@ unsigned testMonitorCount(testMonitor *mon, unsigned reset)
|
||||
return count;
|
||||
}
|
||||
|
||||
static
|
||||
epicsMutexId test_global;
|
||||
|
||||
static
|
||||
epicsThreadOnceId test_global_once = EPICS_THREAD_ONCE_INIT;
|
||||
|
||||
static
|
||||
void test_global_init(void* ignored)
|
||||
{
|
||||
test_global = epicsMutexMustCreate();
|
||||
}
|
||||
|
||||
void testGlobalLock(void)
|
||||
{
|
||||
epicsThreadOnce(&test_global_once, &test_global_init, NULL);
|
||||
epicsMutexMustLock(test_global);
|
||||
}
|
||||
|
||||
void testGlobalUnlock(void)
|
||||
{
|
||||
epicsMutexUnlock(test_global);
|
||||
}
|
||||
|
||||
|
||||
@@ -98,6 +98,65 @@ epicsShareFunc void testMonitorWait(testMonitor*);
|
||||
*/
|
||||
epicsShareFunc unsigned testMonitorCount(testMonitor*, unsigned reset);
|
||||
|
||||
/** Synchronize the shared callback queues.
|
||||
*
|
||||
* Block until all callback queue jobs which were queued, or running,
|
||||
* have completed.
|
||||
*/
|
||||
epicsShareFunc void testSyncCallback(void);
|
||||
|
||||
/** Global mutex for use by test code.
|
||||
*
|
||||
* This utility mutex is intended to be used to avoid races in situations
|
||||
* where some other syncronization primitive is being destroyed (epicsEvent,
|
||||
* epicsMutex, ...).
|
||||
*
|
||||
* For example. The following has a subtle race where the event may be
|
||||
* destroyed (free()'d) before the call to epicsEventMustSignal() has
|
||||
* returned. On some targets this leads to a use after free() error.
|
||||
*
|
||||
@code
|
||||
epicsEventId evt;
|
||||
void thread1() {
|
||||
evt = epicsEventMustCreate(...);
|
||||
// spawn thread2()
|
||||
epicsEventMustWait(evt);
|
||||
epicsEventDestroy(evt);
|
||||
}
|
||||
// ...
|
||||
void thread2() {
|
||||
epicsEventMustSignal(evt);
|
||||
}
|
||||
@endcode
|
||||
*
|
||||
* One way to avoid this race is to use a global mutex to ensure
|
||||
* that epicsEventMustSignal() has returned before destroying
|
||||
* the event.
|
||||
*
|
||||
@code
|
||||
epicsEventId evt;
|
||||
void thread1() {
|
||||
evt = epicsEventMustCreate(...);
|
||||
// spawn thread2()
|
||||
epicsEventMustWait(evt);
|
||||
testGlobalLock(); // <-- added
|
||||
epicsEventDestroy(evt);
|
||||
testGlobalUnlock(); // <-- added
|
||||
}
|
||||
// ...
|
||||
void thread2() {
|
||||
testGlobalLock(); // <-- added
|
||||
epicsEventMustSignal(evt);
|
||||
testGlobalUnlock(); // <-- added
|
||||
}
|
||||
@endcode
|
||||
*
|
||||
* This must be a global mutex to avoid simply shifting the race
|
||||
* from the event to a locally allocated mutex.
|
||||
*/
|
||||
epicsShareFunc void testGlobalLock(void);
|
||||
epicsShareFunc void testGlobalUnlock(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
|
||||
#include "alarm.h"
|
||||
#include "dbDefs.h"
|
||||
#include "alarm.h"
|
||||
#include "epicsMath.h"
|
||||
#include "epicsPrint.h"
|
||||
#include "epicsStdlib.h"
|
||||
@@ -178,6 +179,9 @@ unsigned short recGblResetAlarms(void *precord)
|
||||
epicsEnum16 val_mask = 0;
|
||||
epicsEnum16 stat_mask = 0;
|
||||
|
||||
if (new_sevr > INVALID_ALARM)
|
||||
new_sevr = INVALID_ALARM;
|
||||
|
||||
pdbc->stat = new_stat;
|
||||
pdbc->sevr = new_sevr;
|
||||
pdbc->nsta = 0;
|
||||
|
||||
@@ -175,6 +175,8 @@ testHarness_SRCS += epicsRunDbTests.c
|
||||
dbTestHarness_SRCS += $(testHarness_SRCS)
|
||||
dbTestHarness_SRCS_RTEMS += rtemsTestHarness.c
|
||||
|
||||
PROD_SRCS_RTEMS += rtemsTestData.c
|
||||
|
||||
PROD_vxWorks = dbTestHarness
|
||||
PROD_RTEMS = dbTestHarness
|
||||
|
||||
@@ -182,6 +184,10 @@ TESTSPEC_vxWorks = dbTestHarness.munch; epicsRunDbTests
|
||||
TESTSPEC_RTEMS = dbTestHarness.boot; epicsRunDbTests
|
||||
|
||||
TESTSCRIPTS_HOST += $(TESTS:%=%.t)
|
||||
ifneq ($(filter $(T_A),$(CROSS_COMPILER_RUNTEST_ARCHS)),)
|
||||
TESTPROD_RTEMS = $(TESTPROD_HOST)
|
||||
TESTSCRIPTS_RTEMS += $(TESTS:%=%.t)
|
||||
endif
|
||||
|
||||
include $(TOP)/configure/RULES
|
||||
|
||||
@@ -193,3 +199,5 @@ devx$(DEP): $(COMMON_DIR)/xRecord.h
|
||||
scanIoTest$(DEP): $(COMMON_DIR)/xRecord.h
|
||||
xRecord$(DEP): $(COMMON_DIR)/xRecord.h
|
||||
|
||||
rtemsTestData.c : $(TESTFILES) $(TOOLS)/epicsMakeMemFs.pl
|
||||
$(PERL) $(TOOLS)/epicsMakeMemFs.pl $@ epicsRtemsFSImage $(TESTFILES)
|
||||
|
||||
@@ -34,6 +34,8 @@
|
||||
*
|
||||
* Two time intervals are measured. The time to queue and run each of
|
||||
* the immediate callbacks, and the actual delay of the delayed callback.
|
||||
*
|
||||
* Slow callbacks no longer fail the test, they just emit a diagnostic.
|
||||
*/
|
||||
|
||||
#define NCALLBACKS 169
|
||||
@@ -108,7 +110,7 @@ MAIN(callbackParallelTest)
|
||||
for (j = 0; j < 5; j++)
|
||||
setupError[i][j] = timeError[i][j] = defaultError[j];
|
||||
|
||||
testPlan(4);
|
||||
testPlan(2);
|
||||
|
||||
testDiag("Starting %d parallel callback threads", noCpus);
|
||||
|
||||
@@ -165,7 +167,8 @@ MAIN(callbackParallelTest)
|
||||
}
|
||||
}
|
||||
testOk(faults == 0, "%d faults during callback setup", faults);
|
||||
testOk(slowups <= 1, "%d slowups during callback setup", slowups);
|
||||
if (slowups)
|
||||
testDiag("%d slowups during callback setup", slowups);
|
||||
|
||||
slowups = 0;
|
||||
for (i = 0; i < NCALLBACKS ; i++) {
|
||||
@@ -182,7 +185,8 @@ MAIN(callbackParallelTest)
|
||||
}
|
||||
updateStats(timeError[i%NUM_CALLBACK_PRIORITIES], error);
|
||||
}
|
||||
testOk(slowups < 5, "%d slowups during callbacks", slowups);
|
||||
if (slowups)
|
||||
testDiag("%d slowups during callback setup", slowups);
|
||||
|
||||
testDiag("Setup time statistics");
|
||||
printStats(setupError[0], "LOW");
|
||||
|
||||
@@ -34,6 +34,8 @@
|
||||
*
|
||||
* Two time intervals are measured. The time to queue and run each of
|
||||
* the immediate callbacks, and the actual delay of the delayed callback.
|
||||
*
|
||||
* Slow callbacks no longer fail the test, they just emit a diagnostic.
|
||||
*/
|
||||
|
||||
#define NCALLBACKS 169
|
||||
@@ -108,7 +110,7 @@ MAIN(callbackTest)
|
||||
for (j = 0; j < 5; j++)
|
||||
setupError[i][j] = timeError[i][j] = defaultError[j];
|
||||
|
||||
testPlan(4);
|
||||
testPlan(2);
|
||||
|
||||
callbackInit();
|
||||
epicsThreadSleep(1.0);
|
||||
@@ -162,7 +164,8 @@ MAIN(callbackTest)
|
||||
}
|
||||
}
|
||||
testOk(faults == 0, "%d faults during callback setup", faults);
|
||||
testOk(slowups <= 1, "%d slowups during callback setup", slowups);
|
||||
if (slowups)
|
||||
testDiag("%d slowups during callback setup", slowups);
|
||||
|
||||
slowups = 0;
|
||||
for (i = 0; i < NCALLBACKS ; i++) {
|
||||
@@ -179,7 +182,8 @@ MAIN(callbackTest)
|
||||
}
|
||||
updateStats(timeError[i%NUM_CALLBACK_PRIORITIES], error);
|
||||
}
|
||||
testOk(slowups < 5, "%d slowups during callbacks", slowups);
|
||||
if (slowups)
|
||||
testDiag("%d slowups during callback setup", slowups);
|
||||
|
||||
testDiag("Setup time statistics");
|
||||
printStats(setupError[0], "LOW");
|
||||
|
||||
@@ -14,3 +14,8 @@ record(x, "eINST_IO") {
|
||||
field(DTYP, "Unit Test INST_IO")
|
||||
field(INP, "hello")
|
||||
}
|
||||
|
||||
record(x, "eINST_IO2") {
|
||||
field(DTYP, "Unit Test INST_IO")
|
||||
field(INP, "RL @no")
|
||||
}
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
|
||||
/* Declarations from cadef.h and db_access.h which we can't include here */
|
||||
typedef void * chid;
|
||||
typedef void * evid;
|
||||
epicsShareExtern const unsigned short dbr_value_size[];
|
||||
epicsShareExtern short epicsShareAPI ca_field_type (chid chan);
|
||||
#define MAX_UNITS_SIZE 8
|
||||
|
||||
@@ -60,7 +60,9 @@ static const struct testParseDataT {
|
||||
TEST_PV_LINK(" world MSICP", "world", pvlOptMSI|pvlOptCP),
|
||||
|
||||
{"#C14 S145 @testing", {VME_IO, "testing", 0, "CS", {14, 145}}},
|
||||
{"#C14 S145", {VME_IO, "", 0, "CS", {14, 145}}},
|
||||
{"#B11 C12 N13 A14 F15 @cparam", {CAMAC_IO, "cparam", 0, "BCNAF", {11, 12, 13, 14, 15}}},
|
||||
{"#B11 C12 N13 A14 F15", {CAMAC_IO, "", 0, "BCNAF", {11, 12, 13, 14, 15}}},
|
||||
{" #B111 C112 N113 @cparam", {CAMAC_IO, "cparam", 0, "BCN", {111, 112, 113}}},
|
||||
{" @hello world ", {INST_IO, "hello world", 0, "", /*{}*/}},
|
||||
{" {\"x\":true} ", {JSON_LINK, "{\"x\":true}", 0, "", /*{}*/}},
|
||||
@@ -87,11 +89,15 @@ static void testLinkParse(void)
|
||||
for (;td->str; td++) {
|
||||
int i, N;
|
||||
testDiag("Parsing \"%s\"", td->str);
|
||||
testOk(dbParseLink(td->str, DBF_INLINK, &info, 0) == 0, "Parser returned OK");
|
||||
testOk(dbParseLink(td->str, DBF_INLINK, &info) == 0, "Parser returned OK");
|
||||
if (!testOk(info.ltype == td->info.ltype, "Link type value"))
|
||||
testDiag("Expected %d, got %d", td->info.ltype, info.ltype);
|
||||
if (td->info.target)
|
||||
if (td->info.target && info.target)
|
||||
testStrcmp(0, info.target, td->info.target);
|
||||
else if(!!td->info.target ^ !!info.target)
|
||||
testFail("info target NULL mis-match %s %s", info.target, td->info.target);
|
||||
else
|
||||
testPass("info target NULL as expected");
|
||||
if (info.ltype == td->info.ltype) {
|
||||
switch (info.ltype) {
|
||||
case PV_LINK:
|
||||
@@ -125,7 +131,6 @@ static const char *testParseFailData[] = {
|
||||
"#A0 B @",
|
||||
"#A0 B C @",
|
||||
"#R1 M2 D3 E4 @oops", /* RF_IO has no parm */
|
||||
"#C1 S2", /* VME_IO needs parm */
|
||||
NULL
|
||||
};
|
||||
|
||||
@@ -147,7 +152,7 @@ static void testLinkFailParse(void)
|
||||
eltc(1);
|
||||
|
||||
for(;*td; td++) {
|
||||
testOk(dbParseLink(*td, DBF_INLINK, &info, 0) == S_dbLib_badField,
|
||||
testOk(dbParseLink(*td, DBF_INLINK, &info) == S_dbLib_badField,
|
||||
"dbParseLink correctly rejected \"%s\"", *td);
|
||||
}
|
||||
|
||||
@@ -480,6 +485,13 @@ static void testLinkInitFail(void)
|
||||
testOk1(plink->type == INST_IO);
|
||||
testOk1(plink->value.instio.string != NULL);
|
||||
|
||||
testdbGetFieldEqual("eINST_IO2.INP", DBR_STRING, "@");
|
||||
|
||||
prec = (xRecord *) testdbRecordPtr("eINST_IO2");
|
||||
plink = &prec->inp;
|
||||
testOk1(plink->type == INST_IO);
|
||||
testOk1(plink->value.instio.string != NULL);
|
||||
|
||||
testIocShutdownOk();
|
||||
|
||||
testdbCleanup();
|
||||
@@ -684,7 +696,7 @@ void testTSEL(void)
|
||||
|
||||
MAIN(dbPutLinkTest)
|
||||
{
|
||||
testPlan(320);
|
||||
testPlan(337);
|
||||
testLinkParse();
|
||||
testLinkFailParse();
|
||||
testCADBSet();
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
|
||||
record(x, "testrec") {
|
||||
info("A", "B")
|
||||
alias("testalias")
|
||||
info("A", "B")
|
||||
}
|
||||
|
||||
alias("testrec", "testalias2")
|
||||
|
||||
@@ -228,6 +228,11 @@ MAIN(dbStressTest)
|
||||
|
||||
testPlan(80+nworkers*3);
|
||||
|
||||
#if defined(__rtems__)
|
||||
testSkip(80+nworkers*3, "Test assumes time sliced preempting scheduling");
|
||||
return testDone();
|
||||
#endif
|
||||
|
||||
priv = callocMustSucceed(nworkers, sizeof(*priv), "no memory");
|
||||
|
||||
testDiag("lock set stress test");
|
||||
|
||||
@@ -246,7 +246,8 @@ static jlif jlifZ = {
|
||||
NULL, /* end child */
|
||||
&z_lset,
|
||||
NULL, /* report */
|
||||
NULL /* map child */
|
||||
NULL, /* map child */
|
||||
NULL /* start child */
|
||||
};
|
||||
|
||||
epicsExportAddress(jlif, jlifZ);
|
||||
|
||||
@@ -38,7 +38,7 @@ static void testBasicGet(void)
|
||||
|
||||
getter(&addr, scratch, 1, s_input_len, 0);
|
||||
|
||||
testOk1(scratch[0]==s_input[0]);
|
||||
testOk1(scratch[0]==s_input[0] && scratch[1]==0);
|
||||
|
||||
memset(scratch, 0x42, sizeof(s_input));
|
||||
}
|
||||
@@ -128,7 +128,7 @@ static void testBasicPut(void)
|
||||
|
||||
putter(&addr, s_input, 1, s_input_len, 0);
|
||||
|
||||
testOk1(scratch[0]==s_input[0]);
|
||||
testOk1(scratch[0]==s_input[0] && scratch[1]==0);
|
||||
|
||||
memset(scratch, 0x42, sizeof(s_input));
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* Copyright (c) 2016 UChicago Argonne LLC, as Operator of Argonne
|
||||
* National Laboratory.
|
||||
* EPICS BASE is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
* in file LICENSE that is included with this distribution.
|
||||
\*************************************************************************/
|
||||
/* xLink.c */
|
||||
|
||||
@@ -82,7 +82,6 @@ static jlif xlinkIf = {
|
||||
NULL, NULL, NULL,
|
||||
NULL, NULL,
|
||||
NULL, xlink_get_lset,
|
||||
NULL, NULL
|
||||
NULL, NULL, NULL
|
||||
};
|
||||
epicsExportAddress(jlif, xlinkIf);
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
* 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.
|
||||
* in file LICENSE that is included with this distribution.
|
||||
\*************************************************************************/
|
||||
|
||||
/* Author: Marty Kraimer Date: 13JUL95*/
|
||||
@@ -169,7 +169,7 @@ static char *dbOpenFile(DBBASE *pdbbase,const char *filename,FILE **fp)
|
||||
}
|
||||
pdbPathNode = (dbPathNode *)ellFirst(ppathList);
|
||||
while (pdbPathNode) {
|
||||
fullfilename = dbMalloc(strlen(pdbPathNode->directory) +
|
||||
fullfilename = dbMalloc(strlen(pdbPathNode->directory) +
|
||||
strlen(filename) + 2);
|
||||
strcpy(fullfilename, pdbPathNode->directory);
|
||||
strcat(fullfilename, "/");
|
||||
@@ -190,7 +190,7 @@ static void freeInputFileList(void)
|
||||
inputFile *pinputFileNow;
|
||||
|
||||
while((pinputFileNow=(inputFile *)ellFirst(&inputFileList))) {
|
||||
if(fclose(pinputFileNow->fp))
|
||||
if(fclose(pinputFileNow->fp))
|
||||
errPrintf(0,__FILE__, __LINE__,
|
||||
"Closing file %s",pinputFileNow->filename);
|
||||
free((void *)pinputFileNow->filename);
|
||||
@@ -215,7 +215,7 @@ static long dbReadCOM(DBBASE **ppdbbase,const char *filename, FILE *fp,
|
||||
inputFile *pinputFile = NULL;
|
||||
char *penv;
|
||||
char **macPairs;
|
||||
|
||||
|
||||
if(ellCount(&tempList)) {
|
||||
epicsPrintf("dbReadCOM: Parser stack dirty %d\n", ellCount(&tempList));
|
||||
}
|
||||
@@ -341,7 +341,7 @@ static int db_yyinput(char *buf, int max_size)
|
||||
{
|
||||
size_t l,n;
|
||||
char *fgetsRtn;
|
||||
|
||||
|
||||
if(yyAbort) return(0);
|
||||
if(*my_buffer_ptr==0) {
|
||||
while(TRUE) { /*until we get some input*/
|
||||
@@ -360,7 +360,7 @@ static int db_yyinput(char *buf, int max_size)
|
||||
fgetsRtn = fgets(my_buffer,MY_BUFFER_SIZE,pinputFileNow->fp);
|
||||
}
|
||||
if(fgetsRtn) break;
|
||||
if(fclose(pinputFileNow->fp))
|
||||
if(fclose(pinputFileNow->fp))
|
||||
errPrintf(0,__FILE__, __LINE__,
|
||||
"Closing file %s",pinputFileNow->filename);
|
||||
free((void *)pinputFileNow->filename);
|
||||
@@ -511,7 +511,7 @@ static void dbRecordtypeFieldHead(char *name,char *type)
|
||||
{
|
||||
dbFldDes *pdbFldDes;
|
||||
int i;
|
||||
|
||||
|
||||
if(duplicate) return;
|
||||
pdbFldDes = dbCalloc(1,sizeof(dbFldDes));
|
||||
allocTemp(pdbFldDes);
|
||||
@@ -544,7 +544,7 @@ static short findOrAddGuiGroup(const char *name)
|
||||
static void dbRecordtypeFieldItem(char *name,char *value)
|
||||
{
|
||||
dbFldDes *pdbFldDes;
|
||||
|
||||
|
||||
if(duplicate) return;
|
||||
pdbFldDes = (dbFldDes *)getLastTemp();
|
||||
if(strcmp(name,"asl")==0) {
|
||||
@@ -636,11 +636,11 @@ static void dbRecordtypeCdef(char *text) {
|
||||
dbText *pdbCdef;
|
||||
tempListNode *ptempListNode;
|
||||
dbRecordType *pdbRecordType;
|
||||
|
||||
|
||||
if (!pdbbase->loadCdefs || duplicate) return;
|
||||
ptempListNode = (tempListNode *)ellFirst(&tempList);
|
||||
pdbRecordType = ptempListNode->item;
|
||||
|
||||
|
||||
pdbCdef = dbCalloc(1,sizeof(dbText));
|
||||
if (text[0] == ' ') text++; /* strip leading space if present */
|
||||
pdbCdef->text = epicsStrDup(text);
|
||||
@@ -699,7 +699,7 @@ static void dbRecordtypeBody(void)
|
||||
if((field_type==DBF_STRING) && (pdbFldDes->size==0))
|
||||
fprintf(stderr,"recordtype(%s).%s size not specified\n",
|
||||
pdbRecordType->name,pdbFldDes->name);
|
||||
if((field_type==DBF_NOACCESS) && (pdbFldDes->extra==0))
|
||||
if((field_type==DBF_NOACCESS) && (pdbFldDes->extra==0))
|
||||
fprintf(stderr,"recordtype(%s).%s extra not specified\n",
|
||||
pdbRecordType->name,pdbFldDes->name);
|
||||
}
|
||||
@@ -811,7 +811,7 @@ static void dbDriver(char *name)
|
||||
pgphentry = gphAdd(pdbbase->pgpHash,pdrvSup->name,&pdbbase->drvList);
|
||||
if(!pgphentry) {
|
||||
yyerrorAbort("gphAdd failed");
|
||||
}
|
||||
}
|
||||
pgphentry->userPvt = pdrvSup;
|
||||
ellAdd(&pdbbase->drvList,&pdrvSup->node);
|
||||
}
|
||||
@@ -831,7 +831,7 @@ static void dbLinkType(char *name, char *jlif_name)
|
||||
pgphentry = gphAdd(pdbbase->pgpHash, pLinkSup->name, &pdbbase->linkList);
|
||||
if (!pgphentry) {
|
||||
yyerrorAbort("gphAdd failed");
|
||||
}
|
||||
}
|
||||
pgphentry->userPvt = pLinkSup;
|
||||
ellAdd(&pdbbase->linkList, &pLinkSup->node);
|
||||
}
|
||||
@@ -850,7 +850,7 @@ static void dbRegistrar(char *name)
|
||||
pgphentry = gphAdd(pdbbase->pgpHash,ptext->text,&pdbbase->registrarList);
|
||||
if(!pgphentry) {
|
||||
yyerrorAbort("gphAdd failed");
|
||||
}
|
||||
}
|
||||
pgphentry->userPvt = ptext;
|
||||
ellAdd(&pdbbase->registrarList,&ptext->node);
|
||||
}
|
||||
@@ -889,7 +889,7 @@ static void dbVariable(char *name, char *type)
|
||||
pgphentry = gphAdd(pdbbase->pgpHash,pvar->name,&pdbbase->variableList);
|
||||
if(!pgphentry) {
|
||||
yyerrorAbort("gphAdd failed");
|
||||
}
|
||||
}
|
||||
pgphentry->userPvt = pvar;
|
||||
ellAdd(&pdbbase->variableList,&pvar->node);
|
||||
}
|
||||
@@ -948,11 +948,11 @@ static void dbBreakBody(void)
|
||||
pnewbrkTable->paBrkInt = paBrkInt = dbCalloc(number, sizeof(brkInt));
|
||||
for (i=0; i<number; i++) {
|
||||
char *str;
|
||||
|
||||
|
||||
str = (char *)popFirstTemp();
|
||||
(void) epicsScanDouble(str, &paBrkInt[i].raw);
|
||||
free(str);
|
||||
|
||||
|
||||
str = (char *)popFirstTemp();
|
||||
(void) epicsScanDouble(str, &paBrkInt[i].eng);
|
||||
free(str);
|
||||
@@ -1064,32 +1064,39 @@ static void dbRecordHead(char *recordType, char *name, int visible)
|
||||
|
||||
static void dbRecordField(char *name,char *value)
|
||||
{
|
||||
DBENTRY *pdbentry;
|
||||
tempListNode *ptempListNode;
|
||||
long status;
|
||||
DBENTRY *pdbentry;
|
||||
tempListNode *ptempListNode;
|
||||
long status;
|
||||
|
||||
if(duplicate) return;
|
||||
if (duplicate) return;
|
||||
ptempListNode = (tempListNode *)ellFirst(&tempList);
|
||||
pdbentry = ptempListNode->item;
|
||||
status = dbFindField(pdbentry,name);
|
||||
if(status) {
|
||||
epicsPrintf("Record \"%s\" does not have a field \"%s\"\n",
|
||||
dbGetRecordName(pdbentry), name);
|
||||
yyerror(NULL);
|
||||
return;
|
||||
if (status) {
|
||||
epicsPrintf("Record \"%s\" does not have a field \"%s\"\n",
|
||||
dbGetRecordName(pdbentry), name);
|
||||
yyerror(NULL);
|
||||
return;
|
||||
}
|
||||
if (pdbentry->indfield == 0) {
|
||||
epicsPrintf("Can't set \"NAME\" field of record \"%s\"\n",
|
||||
dbGetRecordName(pdbentry));
|
||||
yyerror(NULL);
|
||||
return;
|
||||
}
|
||||
if (*value == '"') {
|
||||
/* jsonSTRING values still have their quotes */
|
||||
/* jsonSTRING values still have their quotes */
|
||||
value++;
|
||||
value[strlen(value) - 1] = 0;
|
||||
value[strlen(value) - 1] = 0;
|
||||
}
|
||||
dbTranslateEscape(value, value); /* in-place; safe & legal */
|
||||
status = dbPutString(pdbentry,value);
|
||||
if(status) {
|
||||
if (status) {
|
||||
char msg[128];
|
||||
|
||||
errSymLookup(status, msg, sizeof(msg));
|
||||
epicsPrintf("Can't set \"%s.%s\" to \"%s\" %s\n",
|
||||
dbGetRecordName(pdbentry), name, value, msg);
|
||||
dbGetRecordName(pdbentry), name, value, msg);
|
||||
yyerror(NULL);
|
||||
return;
|
||||
}
|
||||
@@ -1097,39 +1104,39 @@ static void dbRecordField(char *name,char *value)
|
||||
|
||||
static void dbRecordInfo(char *name, char *value)
|
||||
{
|
||||
DBENTRY *pdbentry;
|
||||
tempListNode *ptempListNode;
|
||||
long status;
|
||||
DBENTRY *pdbentry;
|
||||
tempListNode *ptempListNode;
|
||||
long status;
|
||||
|
||||
if(duplicate) return;
|
||||
if (duplicate) return;
|
||||
ptempListNode = (tempListNode *)ellFirst(&tempList);
|
||||
pdbentry = ptempListNode->item;
|
||||
if (*value == '"') {
|
||||
/* jsonSTRING values still have their quotes */
|
||||
/* jsonSTRING values still have their quotes */
|
||||
value++;
|
||||
value[strlen(value) - 1] = 0;
|
||||
value[strlen(value) - 1] = 0;
|
||||
}
|
||||
dbTranslateEscape(value, value); /* yuck: in-place, but safe */
|
||||
status = dbPutInfo(pdbentry,name,value);
|
||||
if(status) {
|
||||
epicsPrintf("Can't set \"%s\" info \"%s\" to \"%s\"\n",
|
||||
if (status) {
|
||||
epicsPrintf("Can't set \"%s\" info \"%s\" to \"%s\"\n",
|
||||
dbGetRecordName(pdbentry), name, value);
|
||||
yyerror(NULL);
|
||||
return;
|
||||
yyerror(NULL);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static void dbRecordAlias(char *name)
|
||||
{
|
||||
DBENTRY *pdbentry;
|
||||
tempListNode *ptempListNode;
|
||||
long status;
|
||||
DBENTRY *pdbentry;
|
||||
tempListNode *ptempListNode;
|
||||
long status;
|
||||
|
||||
if(duplicate) return;
|
||||
if (duplicate) return;
|
||||
ptempListNode = (tempListNode *)ellFirst(&tempList);
|
||||
pdbentry = ptempListNode->item;
|
||||
status = dbCreateAlias(pdbentry, name);
|
||||
if(status) {
|
||||
if (status) {
|
||||
epicsPrintf("Can't create alias \"%s\" for \"%s\"\n",
|
||||
name, dbGetRecordName(pdbentry));
|
||||
yyerror(NULL);
|
||||
@@ -1139,15 +1146,16 @@ static void dbRecordAlias(char *name)
|
||||
|
||||
static void dbAlias(char *name, char *alias)
|
||||
{
|
||||
DBENTRY dbEntry;
|
||||
DBENTRY *pdbEntry = &dbEntry;
|
||||
DBENTRY dbEntry;
|
||||
DBENTRY *pdbEntry = &dbEntry;
|
||||
|
||||
dbInitEntry(pdbbase, pdbEntry);
|
||||
if (dbFindRecord(pdbEntry, name)) {
|
||||
epicsPrintf("Alias \"%s\" refers to unknown record \"%s\"\n",
|
||||
alias, name);
|
||||
yyerror(NULL);
|
||||
} else if (dbCreateAlias(pdbEntry, alias)) {
|
||||
}
|
||||
else if (dbCreateAlias(pdbEntry, alias)) {
|
||||
epicsPrintf("Can't create alias \"%s\" referring to \"%s\"\n",
|
||||
alias, name);
|
||||
yyerror(NULL);
|
||||
@@ -1157,14 +1165,14 @@ static void dbAlias(char *name, char *alias)
|
||||
|
||||
static void dbRecordBody(void)
|
||||
{
|
||||
DBENTRY *pdbentry;
|
||||
DBENTRY *pdbentry;
|
||||
|
||||
if(duplicate) {
|
||||
duplicate = FALSE;
|
||||
return;
|
||||
if (duplicate) {
|
||||
duplicate = FALSE;
|
||||
return;
|
||||
}
|
||||
pdbentry = (DBENTRY *)popFirstTemp();
|
||||
if(ellCount(&tempList))
|
||||
yyerrorAbort("dbRecordBody: tempList not empty");
|
||||
if (ellCount(&tempList))
|
||||
yyerrorAbort("dbRecordBody: tempList not empty");
|
||||
dbFreeEntry(pdbentry);
|
||||
}
|
||||
|
||||
@@ -51,8 +51,11 @@ static char *pNullString = "";
|
||||
#define messagesize 276
|
||||
#define RPCL_LEN INFIX_TO_POSTFIX_SIZE(80)
|
||||
|
||||
/* must be long enough to hold 32-bit signed integer in base 10 */
|
||||
STATIC_ASSERT(messagesize>=11);
|
||||
/* Must be big enough to hold a 64-bit integer in base 10, but in
|
||||
* the future when fields hold large JSON objects this fixed size
|
||||
* allocation will probably have to become variable sized.
|
||||
*/
|
||||
STATIC_ASSERT(messagesize >= 21);
|
||||
|
||||
static char *ppstring[5]={" NPP"," PP"," CA"," CP"," CPP"};
|
||||
static char *msstring[4]={" NMS"," MS"," MSI"," MSS"};
|
||||
@@ -208,11 +211,13 @@ static void zeroDbentry(DBENTRY *pdbentry)
|
||||
static char *getpMessage(DBENTRY *pdbentry)
|
||||
{
|
||||
char *msg = pdbentry->message;
|
||||
|
||||
if (!msg) {
|
||||
msg = dbCalloc(1, messagesize);
|
||||
pdbentry->message = msg;
|
||||
}
|
||||
*msg = '\0';
|
||||
else
|
||||
*msg = '\0';
|
||||
return msg;
|
||||
}
|
||||
|
||||
@@ -224,6 +229,17 @@ void dbMsgCpy(DBENTRY *pdbentry, const char *msg)
|
||||
pdbentry->message[messagesize-1] = '\0';
|
||||
}
|
||||
|
||||
static
|
||||
void dbMsgNCpy(DBENTRY *pdbentry, const char *msg, size_t len)
|
||||
{
|
||||
getpMessage(pdbentry);
|
||||
if (len >= messagesize)
|
||||
len = messagesize-1; /* FIXME: Quietly truncates */
|
||||
|
||||
strncpy(pdbentry->message, msg, len);
|
||||
pdbentry->message[len] = '\0';
|
||||
}
|
||||
|
||||
static
|
||||
void dbMsgPrint(DBENTRY *pdbentry, const char *fmt, ...)
|
||||
{
|
||||
@@ -879,10 +895,14 @@ long dbWriteRecordFP(
|
||||
status=dbNextField(pdbentry,dctonly);
|
||||
}
|
||||
status = dbFirstInfo(pdbentry);
|
||||
while(!status) {
|
||||
fprintf(fp,"\tinfo(\"%s\",\"%s\")\n",
|
||||
dbGetInfoName(pdbentry), dbGetInfoString(pdbentry));
|
||||
status=dbNextInfo(pdbentry);
|
||||
while (!status) {
|
||||
const char *pinfostr = dbGetInfoString(pdbentry);
|
||||
|
||||
fprintf(fp, "\tinfo(\"%s\",\"",
|
||||
dbGetInfoName(pdbentry));
|
||||
epicsStrPrintEscaped(fp, pinfostr, strlen(pinfostr));
|
||||
fprintf(fp, "\")\n");
|
||||
status = dbNextInfo(pdbentry);
|
||||
}
|
||||
fprintf(fp,"}\n");
|
||||
status = dbNextRecord(pdbentry);
|
||||
@@ -1674,21 +1694,27 @@ int dbIsVisibleRecord(DBENTRY *pdbentry)
|
||||
|
||||
long dbCreateAlias(DBENTRY *pdbentry, const char *alias)
|
||||
{
|
||||
dbRecordType *precordType = pdbentry->precordType;
|
||||
dbRecordNode *precnode = pdbentry->precnode;
|
||||
dbRecordNode *pnewnode;
|
||||
PVDENTRY *ppvd;
|
||||
ELLLIST *preclist = NULL;
|
||||
if (!precordType) return S_dbLib_recordTypeNotFound;
|
||||
dbRecordType *precordType = pdbentry->precordType;
|
||||
dbRecordNode *precnode = pdbentry->precnode;
|
||||
dbRecordNode *pnewnode;
|
||||
DBENTRY tempEntry;
|
||||
PVDENTRY *ppvd;
|
||||
|
||||
if (!precordType)
|
||||
return S_dbLib_recordTypeNotFound;
|
||||
|
||||
/* alias of alias still references actual record */
|
||||
while(precnode && (precnode->flags&DBRN_FLAGS_ISALIAS))
|
||||
while (precnode && (precnode->flags & DBRN_FLAGS_ISALIAS))
|
||||
precnode = precnode->aliasedRecnode;
|
||||
if (!precnode) return S_dbLib_recNotFound;
|
||||
zeroDbentry(pdbentry);
|
||||
if (!dbFindRecord(pdbentry, alias)) return S_dbLib_recExists;
|
||||
zeroDbentry(pdbentry);
|
||||
pdbentry->precordType = precordType;
|
||||
preclist = &precordType->recList;
|
||||
|
||||
if (!precnode)
|
||||
return S_dbLib_recNotFound;
|
||||
|
||||
dbInitEntry(pdbentry->pdbbase, &tempEntry);
|
||||
if (!dbFindRecord(&tempEntry, alias))
|
||||
return S_dbLib_recExists;
|
||||
dbFinishEntry(&tempEntry);
|
||||
|
||||
pnewnode = dbCalloc(1, sizeof(dbRecordNode));
|
||||
pnewnode->recordname = epicsStrDup(alias);
|
||||
pnewnode->precord = precnode->precord;
|
||||
@@ -1696,11 +1722,16 @@ long dbCreateAlias(DBENTRY *pdbentry, const char *alias)
|
||||
pnewnode->flags = DBRN_FLAGS_ISALIAS;
|
||||
precnode->flags |= DBRN_FLAGS_HASALIAS;
|
||||
ellInit(&pnewnode->infoList);
|
||||
ellAdd(preclist, &pnewnode->node);
|
||||
|
||||
ellAdd(&precordType->recList, &pnewnode->node);
|
||||
precordType->no_aliases++;
|
||||
pdbentry->precnode = pnewnode;
|
||||
|
||||
ppvd = dbPvdAdd(pdbentry->pdbbase, precordType, pnewnode);
|
||||
if (!ppvd) {errMessage(-1,"Logic Err: Could not add to PVD");return(-1);}
|
||||
if (!ppvd) {
|
||||
errMessage(-1, "dbCreateAlias: Add to PVD failed");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1884,7 +1915,8 @@ char * dbGetString(DBENTRY *pdbentry)
|
||||
|
||||
switch (pflddes->field_type) {
|
||||
case DBF_STRING:
|
||||
dbMsgCpy(pdbentry, (char *)pfield);
|
||||
/* Protect against a missing nil-terminator */
|
||||
dbMsgNCpy(pdbentry, (char *)pfield, pflddes->size);
|
||||
break;
|
||||
case DBF_CHAR:
|
||||
case DBF_UCHAR:
|
||||
@@ -1907,6 +1939,8 @@ char * dbGetString(DBENTRY *pdbentry)
|
||||
case CONSTANT:
|
||||
if (plink->value.constantStr) {
|
||||
dbMsgCpy(pdbentry, plink->value.constantStr);
|
||||
} else if (plink->text) {
|
||||
dbMsgCpy(pdbentry, plink->text);
|
||||
} else {
|
||||
dbMsgCpy(pdbentry, "");
|
||||
}
|
||||
@@ -2007,7 +2041,13 @@ char * dbGetString(DBENTRY *pdbentry)
|
||||
|
||||
switch(plink->type) {
|
||||
case CONSTANT:
|
||||
dbMsgCpy(pdbentry, "0");
|
||||
if (plink->value.constantStr) {
|
||||
dbMsgCpy(pdbentry, plink->value.constantStr);
|
||||
} else if (plink->text) {
|
||||
dbMsgCpy(pdbentry, plink->text);
|
||||
} else {
|
||||
dbMsgCpy(pdbentry, "");
|
||||
}
|
||||
break;
|
||||
case MACRO_LINK:
|
||||
if (plink->value.macro_link.macroStr) {
|
||||
@@ -2201,7 +2241,7 @@ long dbInitRecordLinks(dbRecordType *rtyp, struct dbCommon *prec)
|
||||
if(!plink->text)
|
||||
continue;
|
||||
|
||||
if(dbParseLink(plink->text, pflddes->field_type, &link_info, 0)!=0) {
|
||||
if(dbParseLink(plink->text, pflddes->field_type, &link_info)!=0) {
|
||||
/* This was already parsed once when ->text was set.
|
||||
* Any syntax error messages were printed at that time.
|
||||
*/
|
||||
@@ -2230,7 +2270,7 @@ void dbFreeLinkInfo(dbLinkInfo *pinfo)
|
||||
pinfo->target = NULL;
|
||||
}
|
||||
|
||||
long dbParseLink(const char *str, short ftype, dbLinkInfo *pinfo, unsigned opts)
|
||||
long dbParseLink(const char *str, short ftype, dbLinkInfo *pinfo)
|
||||
{
|
||||
char *pstr;
|
||||
size_t len;
|
||||
@@ -2266,7 +2306,7 @@ long dbParseLink(const char *str, short ftype, dbLinkInfo *pinfo, unsigned opts)
|
||||
|
||||
/* Check for braces => JSON */
|
||||
if (*str == '{' && str[len-1] == '}') {
|
||||
if (dbJLinkParse(str, len, ftype, &pinfo->jlink, opts))
|
||||
if (dbJLinkParse(str, len, ftype, &pinfo->jlink))
|
||||
goto fail;
|
||||
|
||||
pinfo->ltype = JSON_LINK;
|
||||
@@ -2313,9 +2353,13 @@ long dbParseLink(const char *str, short ftype, dbLinkInfo *pinfo, unsigned opts)
|
||||
else if (strcmp(pinfo->hwid, "VS")==0) pinfo->ltype = VXI_IO;
|
||||
else goto fail;
|
||||
|
||||
if (parm && pinfo->ltype != RF_IO) {
|
||||
/* move parm string to beginning of buffer */
|
||||
memmove(pinfo->target, parm, len + 1);
|
||||
if (pinfo->ltype != RF_IO) {
|
||||
if (!parm) {
|
||||
pinfo->target[0] = '\0';
|
||||
} else {
|
||||
/* move parm string to beginning of buffer */
|
||||
memmove(pinfo->target, parm, len + 1);
|
||||
}
|
||||
} else if (!parm && pinfo->ltype == RF_IO) {
|
||||
/* RF_IO, the string isn't needed at all */
|
||||
free(pinfo->target);
|
||||
@@ -2601,21 +2645,8 @@ long dbPutString(DBENTRY *pdbentry,const char *pstring)
|
||||
case DBF_FWDLINK: {
|
||||
dbLinkInfo link_info;
|
||||
DBLINK *plink = (DBLINK *)pfield;
|
||||
DBENTRY infoentry;
|
||||
unsigned opts = 0;
|
||||
|
||||
if(pdbentry->precnode && ellCount(&pdbentry->precnode->infoList)) {
|
||||
dbCopyEntryContents(pdbentry, &infoentry);
|
||||
|
||||
if(dbFindInfo(&infoentry, "base:lsetDebug")==0 && epicsStrCaseCmp(dbGetInfoString(&infoentry), "YES")==0)
|
||||
opts |= LINK_DEBUG_LSET;
|
||||
if(dbFindInfo(&infoentry, "base:jlinkDebug")==0 && epicsStrCaseCmp(dbGetInfoString(&infoentry), "YES")==0)
|
||||
opts |= LINK_DEBUG_JPARSE;
|
||||
|
||||
dbFinishEntry(&infoentry);
|
||||
}
|
||||
|
||||
status = dbParseLink(pstring, pflddes->field_type, &link_info, opts);
|
||||
status = dbParseLink(pstring, pflddes->field_type, &link_info);
|
||||
if (status) break;
|
||||
|
||||
if (plink->type==CONSTANT && plink->value.constantStr==NULL) {
|
||||
@@ -2676,6 +2707,32 @@ long dbNextInfo(DBENTRY *pdbentry)
|
||||
return (pinfo ? 0 : S_dbLib_infoNotFound);
|
||||
}
|
||||
|
||||
long dbNextMatchingInfo(DBENTRY *pdbentry, const char *pattern)
|
||||
{
|
||||
long status;
|
||||
|
||||
if (!pdbentry->precordType)
|
||||
{
|
||||
status = dbFirstRecordType(pdbentry);
|
||||
goto first;
|
||||
}
|
||||
while(1) {
|
||||
status = dbNextInfo(pdbentry);
|
||||
while (status) {
|
||||
status = dbNextRecord(pdbentry);
|
||||
while (status) {
|
||||
status = dbNextRecordType(pdbentry);
|
||||
first:
|
||||
if (status) return status;
|
||||
status = dbFirstRecord(pdbentry);
|
||||
}
|
||||
status = dbFirstInfo(pdbentry);
|
||||
}
|
||||
if (!pattern || !*pattern) return 0;
|
||||
if (epicsStrGlobMatch(dbGetInfoName(pdbentry), pattern)) return 0;
|
||||
}
|
||||
}
|
||||
|
||||
long dbFindInfo(DBENTRY *pdbentry,const char *name)
|
||||
{
|
||||
dbRecordNode *precnode = pdbentry->precnode;
|
||||
|
||||
@@ -188,6 +188,8 @@ epicsShareFunc long dbFirstInfo(DBENTRY *pdbentry);
|
||||
epicsShareFunc long dbNextInfo(DBENTRY *pdbentry);
|
||||
epicsShareFunc long dbFindInfo(DBENTRY *pdbentry,
|
||||
const char *name);
|
||||
epicsShareFunc long dbNextMatchingInfo(DBENTRY *pdbentry,
|
||||
const char *pattern);
|
||||
epicsShareFunc long dbDeleteInfo(DBENTRY *pdbentry);
|
||||
epicsShareFunc const char * dbGetInfoName(DBENTRY *pdbentry);
|
||||
epicsShareFunc const char * dbGetInfoString(DBENTRY *pdbentry);
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
* Operator of Los Alamos National Laboratory.
|
||||
* EPICS BASE Versions 3.13.7
|
||||
* and higher are distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
* in file LICENSE that is included with this distribution.
|
||||
\*************************************************************************/
|
||||
/* dbStaticPvt.h */
|
||||
/*
|
||||
@@ -59,13 +59,10 @@ typedef struct dbLinkInfo {
|
||||
|
||||
long dbInitRecordLinks(dbRecordType *rtyp, struct dbCommon *prec);
|
||||
|
||||
#define LINK_DEBUG_LSET 1
|
||||
#define LINK_DEBUG_JPARSE 2
|
||||
|
||||
/* Parse link string. no record locks needed.
|
||||
* on success caller must free pinfo->target
|
||||
*/
|
||||
epicsShareFunc long dbParseLink(const char *str, short ftype, dbLinkInfo *pinfo, unsigned opts);
|
||||
epicsShareFunc long dbParseLink(const char *str, short ftype, dbLinkInfo *pinfo);
|
||||
/* Check if link type allow the parsed link value pinfo
|
||||
* to be assigned to the given link.
|
||||
* Record containing plink must be locked.
|
||||
|
||||
@@ -6,7 +6,11 @@
|
||||
* EPICS BASE is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
\*************************************************************************/
|
||||
/* devSup.h Device Support */
|
||||
/** @file devSup.h
|
||||
*
|
||||
* @brief Device support routines
|
||||
*/
|
||||
|
||||
/*
|
||||
* Author: Marty Kraimer
|
||||
* Date: 6-1-90
|
||||
@@ -21,6 +25,111 @@
|
||||
/* structures defined elsewhere */
|
||||
struct dbCommon;
|
||||
struct devSup;
|
||||
typedef struct ioscan_head *IOSCANPVT;
|
||||
struct link; /* aka DBLINK */
|
||||
|
||||
/** Type safe version of 'struct dset'
|
||||
*
|
||||
* Recommended usage:
|
||||
*
|
||||
* In Makefile:
|
||||
@code
|
||||
USR_CFLAGS += -DUSE_TYPED_RSET -DUSE_TYPED_DSET
|
||||
@endcode
|
||||
*
|
||||
* In C source file:
|
||||
@code
|
||||
#include <devSup.h>
|
||||
#include <dbScan.h> // For IOCSCANPVT
|
||||
...
|
||||
#include <epicsExport.h> // defines epicsExportSharedSymbols
|
||||
...
|
||||
static long init_record(dbCommon *prec);
|
||||
static long get_iointr_info(int detach, dbCommon *prec, IOCSCANPVT* pscan);
|
||||
static long longin_read(longinRecord *prec);
|
||||
|
||||
const struct {
|
||||
dset common;
|
||||
long (*read)(longinRecord *prec);
|
||||
} devLiDevName = {
|
||||
{
|
||||
5, // 4 from dset + 1 from longinRecord
|
||||
NULL,
|
||||
NULL,
|
||||
&init_record,
|
||||
&get_iointr_info
|
||||
},
|
||||
&longin_read
|
||||
};
|
||||
epicsExportAddress(dset, devLiDevName);
|
||||
@endcode
|
||||
*/
|
||||
typedef struct typed_dset {
|
||||
/** Number of function pointers which follow.
|
||||
* The value depends on the recordtype, but must be >=4 */
|
||||
long number;
|
||||
/** Called from dbior() */
|
||||
long (*report)(int lvl);
|
||||
/** Called twice during iocInit().
|
||||
* First with @a after = 0 before init_record() or array field allocation.
|
||||
* Again with @a after = 1 after init_record() has finished.
|
||||
*/
|
||||
long (*init)(int after);
|
||||
/** Called once per record instance */
|
||||
long (*init_record)(struct dbCommon *prec);
|
||||
/** Called when SCAN="I/O Intr" on startup, or after SCAN is changed.
|
||||
*
|
||||
* Caller must assign the third arguement (IOCSCANPVT*). eg.
|
||||
@code
|
||||
struct mpvt {
|
||||
IOSCANPVT drvlist;
|
||||
};
|
||||
...
|
||||
// init_record() routine calls
|
||||
scanIoInit(&pvt->drvlist);
|
||||
...
|
||||
static long get_ioint_info(int detach, struct dbCommon *prec, IOCSCANPVT* pscan) {
|
||||
if(prec->dpvt)
|
||||
*pscan = &((mypvt*)prec->dpvt)->drvlist;
|
||||
@endcode
|
||||
*
|
||||
* When a particular record instance can/will only used a single scan list,
|
||||
* the @a detach argument can be ignored.
|
||||
*
|
||||
* If this is not the case, then the following should be noted.
|
||||
* + get_ioint_info() is called with @a detach = 0 to fetch the scan list to
|
||||
* which this record will be added.
|
||||
* + get_ioint_info() is called later with @a detach = 1 to fetch the scan
|
||||
* list from which this record should be removed.
|
||||
* + Calls will be balanced, so a call with @a detach = 0 will be followed
|
||||
* by one with @a detach = 1.
|
||||
*
|
||||
* @note get_ioint_info() will be called during IOC shutdown if the
|
||||
* dsxt::del_record() extended callback is defined. (from 3.15.0.1)
|
||||
*/
|
||||
long (*get_ioint_info)(int detach, struct dbCommon *prec, IOSCANPVT* pscan);
|
||||
/* Any further functions are specified by the record type. */
|
||||
} typed_dset;
|
||||
|
||||
/** Device support extension table.
|
||||
*
|
||||
* Optional routines to allow run-time address modifications to be communicated
|
||||
* to device support, which must register a struct dsxt by calling devExtend()
|
||||
* from its init() routine.
|
||||
*/
|
||||
typedef struct dsxt {
|
||||
/** Optional, called to offer device support a new record to control.
|
||||
*
|
||||
* Routine may return a non-zero error code to refuse record.
|
||||
*/
|
||||
long (*add_record)(struct dbCommon *precord);
|
||||
/** Optional, called to remove record from device support control.
|
||||
*
|
||||
* Routine return a non-zero error code to refuse record removal.
|
||||
*/
|
||||
long (*del_record)(struct dbCommon *precord);
|
||||
/* Only future Base releases may extend this table. */
|
||||
} dsxt;
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
@@ -29,6 +138,8 @@ extern "C" {
|
||||
typedef long (*DEVSUPFUN)(); /* ptr to device support function*/
|
||||
#endif
|
||||
|
||||
#ifndef USE_TYPED_DSET
|
||||
|
||||
typedef struct dset { /* device support entry table */
|
||||
long number; /*number of support routines*/
|
||||
DEVSUPFUN report; /*print report*/
|
||||
@@ -38,11 +149,15 @@ typedef struct dset { /* device support entry table */
|
||||
/*other functions are record dependent*/
|
||||
} dset;
|
||||
|
||||
typedef struct dsxt { /* device support extension table */
|
||||
long (*add_record)(struct dbCommon *precord);
|
||||
long (*del_record)(struct dbCommon *precord);
|
||||
/* Recordtypes are *not* allowed to extend this table */
|
||||
} dsxt;
|
||||
#else
|
||||
typedef typed_dset dset;
|
||||
#endif /* USE_TYPED_DSET */
|
||||
|
||||
/** Fetch INP or OUT link (or NULL if record type has neither).
|
||||
*
|
||||
* Recommended for use in device support init_record()
|
||||
*/
|
||||
epicsShareFunc struct link* dbGetDevLink(struct dbCommon* prec);
|
||||
|
||||
epicsShareExtern dsxt devSoft_DSXT; /* Allow anything table */
|
||||
|
||||
|
||||
@@ -6,7 +6,10 @@
|
||||
* EPICS BASE is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
\*************************************************************************/
|
||||
/* drvSup.h Driver Support */
|
||||
/** @file drvSup.h
|
||||
*
|
||||
* @brief Driver support routines.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Author: Marty Kraimer
|
||||
@@ -18,16 +21,38 @@
|
||||
|
||||
#include "errMdef.h"
|
||||
|
||||
typedef long (*DRVSUPFUN) (); /* ptr to driver support function*/
|
||||
/** Driver entry table */
|
||||
typedef struct typed_drvet {
|
||||
/** Number of function pointers which follow. Must be >=2 */
|
||||
long number;
|
||||
/** Called from dbior() */
|
||||
long (*report)(int lvl);
|
||||
/** Called during iocInit() */
|
||||
long (*init)(void);
|
||||
/* Any further functions are driver-specific */
|
||||
} typed_drvet;
|
||||
|
||||
#ifdef USE_TYPED_DRVET
|
||||
|
||||
typedef typed_drvet drvet;
|
||||
|
||||
#else
|
||||
|
||||
/* These interfaces may eventually get deprecated */
|
||||
|
||||
typedef long (*DRVSUPFUN) (); /* ptr to driver support function */
|
||||
|
||||
typedef struct drvet { /* driver entry table */
|
||||
long number; /* number of support routines */
|
||||
DRVSUPFUN report; /* print report */
|
||||
DRVSUPFUN init; /* init support */
|
||||
/* Any further functions are driver-specific */
|
||||
} drvet;
|
||||
|
||||
typedef struct drvet { /* driver entry table */
|
||||
long number; /*number of support routines*/
|
||||
DRVSUPFUN report; /*print report*/
|
||||
DRVSUPFUN init; /*init support*/
|
||||
/*other functions are device dependent*/
|
||||
}drvet;
|
||||
#define DRVETNUMBER ( (sizeof(struct drvet) -sizeof(long))/sizeof(DRVSUPFUN) )
|
||||
|
||||
#endif /* USE_TYPED_DRVET */
|
||||
|
||||
#define S_drv_noDrvSup (M_drvSup| 1) /*SDR_DRVSUP: Driver support missing*/
|
||||
#define S_drv_noDrvet (M_drvSup| 3) /*Missing driver support entry table*/
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user