Compare commits
54 Commits
R7.0.4
...
R7.0.4.1-r
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
aee99a5987 | ||
|
|
809a1553bf | ||
|
|
8029a72fec | ||
|
|
bcbaffc158 | ||
|
|
10d472202d | ||
|
|
f99bb637b9 | ||
|
|
c55a95fc98 | ||
|
|
7d9746003b | ||
|
|
7d2e352f6a | ||
|
|
5c03f8ba79 | ||
|
|
1eeac6da2f | ||
|
|
d4781bca28 | ||
|
|
32ff3b2ed9 | ||
|
|
25681eca4c | ||
|
|
dcee015f71 | ||
|
|
fddd65ccb1 | ||
|
|
7f9fefc2a4 | ||
|
|
bf7a1605c6 | ||
|
|
0fbfc74182 | ||
|
|
b34aa594c8 | ||
|
|
3124d972bf | ||
|
|
49889d8549 | ||
|
|
a4bdee82c3 | ||
|
|
4160610885 | ||
|
|
19146a597b | ||
|
|
4eb8ea33ea | ||
|
|
1e425159ed | ||
|
|
9259e40483 | ||
|
|
b8f0fd4cdb | ||
|
|
b248023eb2 | ||
|
|
92374b2be2 | ||
|
|
579fc9d0c7 | ||
|
|
cd47bbf99b | ||
|
|
d3d40689c8 | ||
|
|
d5eb055bb7 | ||
|
|
97b29129af | ||
|
|
801710b8c7 | ||
|
|
30f5c3b301 | ||
|
|
69d4c238e7 | ||
|
|
7d1ff1411f | ||
|
|
f0e143b907 | ||
|
|
545550dd9a | ||
|
|
eb060e7fcd | ||
|
|
786c4c2ca2 | ||
|
|
48eb4ff4ac | ||
|
|
6c914d19c3 | ||
|
|
d6f8e9038c | ||
|
|
5917990908 | ||
|
|
eeb198db15 | ||
|
|
1fec685eed | ||
|
|
0bfe0eda0c | ||
|
|
6188d3fdaf | ||
|
|
cd07888149 | ||
|
|
688bc3247c |
@@ -37,6 +37,8 @@ skip_commits:
|
||||
# build matrix configuration #
|
||||
#---------------------------------#
|
||||
|
||||
image: Visual Studio 2015
|
||||
|
||||
# Build Configurations: dll/static, regular/debug
|
||||
configuration:
|
||||
- dynamic
|
||||
@@ -49,6 +51,7 @@ environment:
|
||||
# common / default variables for all jobs
|
||||
SETUP_PATH: .ci-local:.ci
|
||||
BASE: SELF
|
||||
EPICS_TEST_IMPRECISE_TIMING: YES
|
||||
|
||||
matrix:
|
||||
- CMP: vs2019
|
||||
@@ -59,7 +62,7 @@ environment:
|
||||
- CMP: vs2013
|
||||
- CMP: vs2012
|
||||
- CMP: vs2010
|
||||
- CMP: mingw
|
||||
- CMP: gcc
|
||||
|
||||
# Platform: processor architecture
|
||||
platform:
|
||||
@@ -89,17 +92,17 @@ matrix:
|
||||
|
||||
install:
|
||||
- cmd: git submodule update --init --recursive
|
||||
- cmd: python .ci/appveyor/do.py prepare
|
||||
- cmd: python .ci/cue.py prepare
|
||||
|
||||
build_script:
|
||||
- cmd: python .ci/appveyor/do.py build
|
||||
- cmd: python .ci/cue.py build
|
||||
|
||||
test_script:
|
||||
- cmd: python .ci/appveyor/do.py test
|
||||
- cmd: python .ci/cue.py test
|
||||
|
||||
on_finish:
|
||||
- ps: Get-ChildItem *.tap -Recurse -Force | % { Push-AppveyorArtifact $_.FullName -FileName $_.Name }
|
||||
- cmd: python .ci/appveyor/do.py build test-results -s
|
||||
- cmd: python .ci/cue.py build test-results -s
|
||||
|
||||
#---------------------------------#
|
||||
# debugging #
|
||||
|
||||
2
.ci
2
.ci
Submodule .ci updated: 55038b7315...87942a7c29
19
.ci-local/travis-fixup.sh
Executable file
19
.ci-local/travis-fixup.sh
Executable file
@@ -0,0 +1,19 @@
|
||||
#!/bin/sh
|
||||
set -e -u -x
|
||||
|
||||
env|grep TRAVIS
|
||||
|
||||
[ "$TRAVIS_OS_NAME" = "linux" ] || exit 0
|
||||
|
||||
# Ensure there is an interface with a (correct) broadcast address
|
||||
# eg. 'trusty' VMs have interface broadcast address mis-configured
|
||||
# (why oh why do people insist on setting this explicitly?)
|
||||
|
||||
sudo ip tuntap add dev tap42 mode tap
|
||||
|
||||
sudo ip addr add 192.168.240.1/24 broadcast + dev tap42
|
||||
|
||||
sudo ip link set dev tap42 up
|
||||
|
||||
# note that this device will be UP but not RUNNING
|
||||
# so java will see it as not UP since java confuses UP and RUNNING
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/bin/sh
|
||||
#!/bin/bash
|
||||
#
|
||||
# Make tar for git repo w/ one level of sub modules.
|
||||
#
|
||||
@@ -9,14 +9,18 @@ die() {
|
||||
exit 1
|
||||
}
|
||||
|
||||
TOPREV="$1"
|
||||
FINALTAR="$2"
|
||||
PREFIX="$3"
|
||||
maybedie() {
|
||||
if [ "$DEVMODE" ]; then
|
||||
echo "Warning: $1" >&2
|
||||
else
|
||||
echo "Error: $1" >&2
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
if ! [ "$TOPREV" ]
|
||||
then
|
||||
usage() {
|
||||
cat <<EOF >&2
|
||||
usage: $0 <rev> [<outfile> [<prefix>]]
|
||||
usage: $0 [-v] [-s] <rev> [<outfile> [<prefix>]]
|
||||
|
||||
<rev> may be any git revision spec. (tag, branch, or commit id).
|
||||
|
||||
@@ -24,9 +28,33 @@ usage: $0 <rev> [<outfile> [<prefix>]]
|
||||
If <outfile> is omitted, "base-<rev>.tar.gz" will be used.
|
||||
If provided, <prefix> must end with "/". If <prefix> is omitted,
|
||||
the default is "base-<rev>/".
|
||||
|
||||
Options:
|
||||
|
||||
-v Enable verbose prints
|
||||
-d Enable permissive developer mode
|
||||
EOF
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
export DEVMODE=
|
||||
|
||||
while getopts "vd" OPT
|
||||
do
|
||||
case "$OPT" in
|
||||
v) set -x;;
|
||||
d) DEVMODE=1;;
|
||||
?) echo "Unknown option"
|
||||
usage;;
|
||||
esac
|
||||
done
|
||||
shift $(($OPTIND - 1))
|
||||
|
||||
TOPREV="$1"
|
||||
FINALTAR="$2"
|
||||
PREFIX="${3:-}"
|
||||
|
||||
[ "$TOPREV" ] || usage
|
||||
|
||||
case "$FINALTAR" in
|
||||
"")
|
||||
@@ -59,16 +87,13 @@ case "$PREFIX" in
|
||||
esac
|
||||
|
||||
# Check for both <tag> and R<tag>
|
||||
if [ "$TOPREV" = "HEAD" ]
|
||||
then
|
||||
true
|
||||
elif ! [ `git tag -l $TOPREV` ]
|
||||
if ! [ `git tag -l $TOPREV` ]
|
||||
then
|
||||
if [ `git tag -l R$TOPREV` ]
|
||||
then
|
||||
TOPREV="R$TOPREV"
|
||||
else
|
||||
die "No tags exist '$TOPREV' or 'R$TOPREV'"
|
||||
maybedie "No tags exist '$TOPREV' or 'R$TOPREV'"
|
||||
fi
|
||||
fi
|
||||
|
||||
@@ -89,11 +114,17 @@ git archive --prefix=$PREFIX $TOPREV | tar -C "$TDIR"/tar -x
|
||||
#
|
||||
# sub-modules appear in tree as eg.:
|
||||
# 160000 commit c3a6cfcf0dad4a4eeecf59b474710d06ff3eb68a modules/ca
|
||||
git ls-tree -r $TOPREV | awk '/^[0-9]+ commit / {print $3, $4}' | \
|
||||
git ls-tree -r $TOPREV | \
|
||||
awk '/^[0-9]+ commit / && $4 != ".ci" {print $3, $4}' | \
|
||||
while read HASH MODDIR
|
||||
do
|
||||
echo "Visiting $HASH $MODDIR"
|
||||
git -C $MODDIR archive --prefix=${PREFIX}${MODDIR}/ $HASH | tar -C "$TDIR"/tar -x
|
||||
if [ -e $MODDIR/.git ]
|
||||
then
|
||||
git -C $MODDIR archive --prefix=${PREFIX}${MODDIR}/ $HASH | tar -C "$TDIR"/tar -x
|
||||
else
|
||||
maybedie " Submodule not checked out."
|
||||
fi
|
||||
done
|
||||
|
||||
# make a list of files copied and filter out undesirables
|
||||
@@ -105,13 +136,17 @@ sed -i -e 's|^\./||' "$TDIR"/list.1
|
||||
|
||||
# Exclude files
|
||||
sed \
|
||||
-e '/\/\.\?ci\//d' \
|
||||
-e '/\/\.ci\//d' \
|
||||
-e '/\/\.ci-local\//d' \
|
||||
-e '/\/\.tools\//d' \
|
||||
-e '/\/jenkins\//d' \
|
||||
-e '/\/\.git/d' \
|
||||
-e '/\/\.hgtags$/d' \
|
||||
-e '/\/\.cproject$/d' \
|
||||
-e '/\/\.project$/d' \
|
||||
-e '/\/\.travis\.yml$/d' \
|
||||
-e '/\/\.appveyor\.yml$/d' \
|
||||
-e '/\/\.readthedocs\.yml$/d' \
|
||||
"$TDIR"/list.1 > "$TDIR"/list.2
|
||||
|
||||
if ! diff -U 0 "$TDIR"/list.1 "$TDIR"/list.2
|
||||
@@ -129,5 +164,5 @@ tar -t $TAROPT -f "$FINALTAR" > "$TDIR"/list.3
|
||||
# make sure we haven't picked up anything extra
|
||||
if ! diff -u "$TDIR"/list.2 "$TDIR"/list.3
|
||||
then
|
||||
echo "Oops! Tarfile diff against plan shown above"
|
||||
die "Oops! Tarfile diff against plan shown above"
|
||||
fi
|
||||
|
||||
20
.travis.yml
20
.travis.yml
@@ -13,6 +13,7 @@ env:
|
||||
global:
|
||||
- SETUP_PATH=.ci-local:.ci
|
||||
- BASE=SELF
|
||||
- EPICS_TEST_IMPRECISE_TIMING=YES
|
||||
|
||||
addons:
|
||||
apt:
|
||||
@@ -35,10 +36,13 @@ addons:
|
||||
update: true
|
||||
|
||||
install:
|
||||
- ./.ci/travis/prepare.sh
|
||||
- ./.ci-local/travis-fixup.sh
|
||||
- python .ci/cue.py prepare
|
||||
|
||||
script:
|
||||
- ./.ci/travis/build.sh
|
||||
- python .ci/cue.py build
|
||||
- python .ci/cue.py test
|
||||
- python .ci/cue.py test-results
|
||||
|
||||
# Define build jobs
|
||||
|
||||
@@ -51,10 +55,10 @@ jobs:
|
||||
- dist: xenial
|
||||
|
||||
- dist: bionic
|
||||
env: EXTRA="CMD_CXXFLAGS=-std=c++11"
|
||||
env: BCFG=static EXTRA="CMD_CXXFLAGS=-std=c++11"
|
||||
|
||||
- dist: trusty
|
||||
env: STATIC=YES EXTRA="CMD_CXXFLAGS=-std=c++11"
|
||||
env: EXTRA="CMD_CXXFLAGS=-std=c++11"
|
||||
|
||||
- dist: bionic
|
||||
compiler: clang
|
||||
@@ -63,15 +67,13 @@ jobs:
|
||||
|
||||
- dist: trusty
|
||||
compiler: clang
|
||||
env: STATIC=YES
|
||||
env: BCFG=static
|
||||
|
||||
# Cross-compilations to Windows using MinGW and WINE
|
||||
|
||||
- env: WINE=32 TEST=NO STATIC=YES
|
||||
compiler: mingw
|
||||
- env: WINE=32 TEST=NO BCFG=static
|
||||
|
||||
- env: WINE=32 TEST=NO STATIC=NO
|
||||
compiler: mingw
|
||||
- env: WINE=32 TEST=NO
|
||||
|
||||
# Cross-compilation to RTEMS
|
||||
|
||||
|
||||
@@ -58,12 +58,8 @@ include $(CONFIG)/CONFIG_BASE_VERSION
|
||||
include $(CONFIG)/os/CONFIG.$(EPICS_HOST_ARCH).Common
|
||||
-include $(CONFIG)/os/CONFIG_SITE.$(EPICS_HOST_ARCH).Common
|
||||
|
||||
# Parse configure/RELEASE
|
||||
# except when building Base itself, where this file is empty,
|
||||
# and would error in src/tools/ anyway.
|
||||
ifndef BASE_TOP
|
||||
# Parse configure/RELEASE to find all upstream modules
|
||||
RELEASE_TOPS := $(shell $(CONVERTRELEASE) -T $(TOP) releaseTops)
|
||||
endif
|
||||
|
||||
ifdef T_A
|
||||
|
||||
|
||||
@@ -52,15 +52,15 @@ EPICS_MODIFICATION = 4
|
||||
|
||||
# EPICS_PATCH_LEVEL must be a number (win32 resource file requirement)
|
||||
# Not included in the official EPICS version number if zero
|
||||
EPICS_PATCH_LEVEL = 0
|
||||
EPICS_PATCH_LEVEL = 1
|
||||
|
||||
# Immediately after an official release the EPICS_PATCH_LEVEL is incremented
|
||||
# and the -DEV suffix is added (similar to the Maven -SNAPSHOT versions)
|
||||
EPICS_DEV_SNAPSHOT=
|
||||
#EPICS_DEV_SNAPSHOT=
|
||||
#EPICS_DEV_SNAPSHOT=-DEV
|
||||
#EPICS_DEV_SNAPSHOT=-pre1
|
||||
#EPICS_DEV_SNAPSHOT=-pre1-DEV
|
||||
#EPICS_DEV_SNAPSHOT=-rc1
|
||||
EPICS_DEV_SNAPSHOT=-rc1
|
||||
#EPICS_DEV_SNAPSHOT=-rc1-DEV
|
||||
|
||||
# No changes should be needed below here
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
EPICS_CA_MAJOR_VERSION = 4
|
||||
EPICS_CA_MINOR_VERSION = 13
|
||||
EPICS_CA_MAINTENANCE_VERSION = 6
|
||||
EPICS_CA_MAINTENANCE_VERSION = 7
|
||||
|
||||
# Development flag, set to zero for release versions
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
EPICS_DATABASE_MAJOR_VERSION = 3
|
||||
EPICS_DATABASE_MINOR_VERSION = 18
|
||||
EPICS_DATABASE_MAINTENANCE_VERSION = 0
|
||||
EPICS_DATABASE_MAINTENANCE_VERSION = 1
|
||||
|
||||
# Development flag, set to zero for release versions
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
EPICS_LIBCOM_MAJOR_VERSION = 3
|
||||
EPICS_LIBCOM_MINOR_VERSION = 18
|
||||
EPICS_LIBCOM_MAINTENANCE_VERSION = 0
|
||||
EPICS_LIBCOM_MAINTENANCE_VERSION = 1
|
||||
|
||||
# Development flag, set to zero for release versions
|
||||
|
||||
|
||||
@@ -116,7 +116,7 @@ ifneq (,$(filter $(T_A), $(EPICS_HOST_ARCH) $(CROSS_COMPILER_RUNTEST_ARCHS)))
|
||||
RUNTESTS_ENABLED = YES
|
||||
TESTSCRIPTS.t = $(filter %.t, $(TESTSCRIPTS))
|
||||
TAPFILES.t += $(TESTSCRIPTS.t:.t=.tap)
|
||||
JUNITFILES.t += $(TESTSCRIPTS.t:.t=.xml)
|
||||
JUNITFILES.t += $(TESTSCRIPTS.t:.t=-results.xml)
|
||||
TAPFILES += $(TAPFILES.t)
|
||||
JUNITFILES += $(JUNITFILES.t)
|
||||
endif
|
||||
@@ -390,7 +390,7 @@ ifdef RUNTESTS_ENABLED
|
||||
$(PERL) $< -tap > $@
|
||||
endif
|
||||
|
||||
$(JUNITFILES.t): %.xml: %.tap
|
||||
$(JUNITFILES.t): %-results.xml: %.tap
|
||||
$(TAPTOJUNIT) --puretap --output $@ --input $< $*
|
||||
|
||||
# If there's a perl test script (.plt) available, use it
|
||||
|
||||
@@ -50,9 +50,11 @@ realclean:
|
||||
# Append all our live submodule failure files
|
||||
FAILURE_FILES = $(addsuffix /$(TEST_FAILURE_FILENAME), $(LIVE_SUBMODULES))
|
||||
|
||||
define combine_failure_files
|
||||
@$(TOUCH) $(FAILURE_FILES)
|
||||
@$(CAT) $(FAILURE_FILES) >> $(TEST_FAILURE_FILE)
|
||||
endef
|
||||
runtests: | $(addsuffix $(DIVIDER)runtests, $(LIVE_SUBMODULES))
|
||||
@$(TOUCH) $(FAILURE_FILES)
|
||||
@$(CAT) $(FAILURE_FILES) >> $(TEST_FAILURE_FILE)
|
||||
$(if $(FAILURE_FILES), $(combine_failure_files))
|
||||
test-results: | $(addsuffix $(DIVIDER)test-results, $(LIVE_SUBMODULES))
|
||||
@$(TOUCH) $(FAILURE_FILES)
|
||||
@$(CAT) $(FAILURE_FILES) >> $(TEST_FAILURE_FILE)
|
||||
$(if $(FAILURE_FILES), $(combine_failure_files))
|
||||
|
||||
3
documentation/.gitignore
vendored
Normal file
3
documentation/.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
*.db
|
||||
epics-base.tag
|
||||
html/
|
||||
2407
documentation/Doxyfile@
Normal file
2407
documentation/Doxyfile@
Normal file
File diff suppressed because it is too large
Load Diff
33
documentation/Makefile
Normal file
33
documentation/Makefile
Normal file
@@ -0,0 +1,33 @@
|
||||
TOP = ..
|
||||
include $(TOP)/configure/CONFIG
|
||||
|
||||
ifdef T_A
|
||||
|
||||
DOXYGEN=doxygen
|
||||
|
||||
EXPAND = Doxyfile
|
||||
|
||||
EXPAND_ME += EPICS_VERSION
|
||||
EXPAND_ME += EPICS_REVISION
|
||||
EXPAND_ME += EPICS_MODIFICATION
|
||||
EXPAND_ME += EPICS_PATCH_LEVEL
|
||||
|
||||
ME = documentation/O.$(T_A)/html
|
||||
|
||||
install: doxygen
|
||||
|
||||
doxygen: Doxyfile
|
||||
$(DOXYGEN)
|
||||
rsync -av $(TOP)/html/ html/
|
||||
|
||||
.PHONY: doxygen
|
||||
|
||||
commit: doxygen
|
||||
$(TOUCH) html/.nojekyll
|
||||
(cd $(TOP) && $(CURDIR)/../commit-gh.sh $(ME)/ $(ME)/.nojekyll $(ME)/*.* $(ME)/*/*.*)
|
||||
|
||||
.PHONY: commit
|
||||
|
||||
endif # T_A
|
||||
|
||||
include $(TOP)/configure/RULES
|
||||
@@ -1,4 +1,4 @@
|
||||
# Installation Instructions
|
||||
# Installation Instructions {#install}
|
||||
|
||||
## EPICS Base Release 7.0.3.1
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# EPICS 7.0 Release Notes
|
||||
# EPICS 7.0 Release Notes {#releasenotes}
|
||||
|
||||
These release notes describe changes that have been made since the previous
|
||||
release of this series of EPICS Base. **Note that changes which were merged up
|
||||
@@ -11,6 +11,36 @@ release.
|
||||
The PVA submodules each have their own individual sets of release notes which
|
||||
should also be read to understand what has changed since earlier releases.
|
||||
|
||||
**This version of EPICS has not been released yet.**
|
||||
|
||||
## Changes made on the 7.0 branch since 7.0.4.1
|
||||
|
||||
<!-- Insert new items immediately below here ... -->
|
||||
|
||||
## EPICS Release 7.0.4.1
|
||||
|
||||
### Bug fixes
|
||||
|
||||
The following launchpad bugs have fixes included in this release:
|
||||
|
||||
- [lp: 1884339](https://bugs.launchpad.net/epics-base/+bug/1884339), Inaccessible CA servers on Windows
|
||||
|
||||
|
||||
### Record Name Validation
|
||||
|
||||
Historically, there have been very few restrictions on which characters
|
||||
may be present in record and alias names. Base 3.14.12.3 added a warning
|
||||
for names containing space, single or double quote, period/dot, or
|
||||
dollar sign.
|
||||
|
||||
```
|
||||
Bad character ' ' in record name "bad practice"
|
||||
```
|
||||
|
||||
7.0.4.1 Turns this warning into an error, and adds a new warning
|
||||
if a record name begins with a minus, plus, left square bracket,
|
||||
or left curly bracket.
|
||||
|
||||
## EPICS Release 7.0.4
|
||||
|
||||
### Bug fixes
|
||||
@@ -1322,6 +1352,15 @@ header and removed the need for dbScan.c to reach into the internals of its
|
||||
# Changes incorporated from the 3.15 branch
|
||||
|
||||
|
||||
## Changes made on the 3.15 branch since 3.15.8
|
||||
|
||||
### Change to the `junitfiles` self-test build target
|
||||
|
||||
The names of the generated junit xml test output files have been changed
|
||||
from `<testname>.xml` to `<testname>-results.xml`, to allow better
|
||||
distinction from other xml files. (I.e., for easy wildcard matching.)
|
||||
|
||||
|
||||
## Changes made between 3.15.7 and 3.15.8
|
||||
|
||||
### Bug fixes
|
||||
@@ -1345,7 +1384,6 @@ The following launchpad bugs have fixes included in this release:
|
||||
- [lp: 1868486](https://bugs.launchpad.net/epics-base/+bug/1868486),
|
||||
epicsMessageQueue lost messages
|
||||
|
||||
|
||||
### Improvements to the self-test build targets
|
||||
|
||||
This release contains changes that make it possible to integrate another test
|
||||
@@ -1361,7 +1399,7 @@ results; previously the `-k` flag to make was needed and even that didn't always
|
||||
work.
|
||||
|
||||
Continuous Integration systems are recommended to run `make tapfiles` (or if
|
||||
they can read junittest output instead of TAP `make junitests`) followed by
|
||||
they can read junittest output instead of TAP `make junitfiles`) followed by
|
||||
`make -s test-results` to display the results of the tests. If multiple CPUs are
|
||||
available the `-j` flag can be used to run tests in parallel, giving the maximum
|
||||
jobs that should be allowed so `make -j4 tapfiles` for a system with 4 CPUs say.
|
||||
@@ -1609,8 +1647,8 @@ cases. This fixes
|
||||
Some documentation has been added to the `dbdToHtml.pl` script
|
||||
explaining how Perl POD (Plain Old Documentation) markup can be added to
|
||||
`.dbd` files to generate HTML documentation for the record types. To see
|
||||
these instructions, run `perl bin/<host>/dbdToHtml.pl -H`
|
||||
or `perldoc bin/<host>/dbdToHtml.pl`.
|
||||
these instructions, run `perl bin/<host>/dbdToHtml.pl -H`
|
||||
or `perldoc bin/<host>/dbdToHtml.pl`.
|
||||
|
||||
### Fix problem with numeric soft events
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Record Reference Documentation
|
||||
# Record Reference Documentation {#recordrefmanual}
|
||||
|
||||
The following documentation for the record types and menus include with Base was
|
||||
converted from the old EPICS Wiki pages and updated. This list only includes the
|
||||
|
||||
@@ -147,17 +147,17 @@ starting at <a href="#ReleaseApproval">Release Approval</a>.</p>
|
||||
<td>Tag the module in Git, using these tag conventions:
|
||||
<ul>
|
||||
<li>
|
||||
<tt>R7.0.3.2-pre<i>n</i></tt>
|
||||
<tt>R7.0.4.1-pre<i>n</i></tt>
|
||||
— pre-release tag
|
||||
</li>
|
||||
<li>
|
||||
<tt>R7.0.3.2-rc<i>n</i></tt>
|
||||
<tt>R7.0.4.1-rc<i>n</i></tt>
|
||||
— release candidate tag
|
||||
</li>
|
||||
</ul>
|
||||
<blockquote><tt>
|
||||
cd base-7.0<br />
|
||||
git tag -m 'ANJ: Tagged for 7.0.3.2-rc1' R7.0.3.2-rc1
|
||||
git tag -m 'ANJ: Tagged for 7.0.4.1-rc1' R7.0.4.1-rc1
|
||||
</tt></blockquote>
|
||||
Note that submodules must <em>not</em> be tagged with the version used
|
||||
for the top-level, they each have their own separate version numbers
|
||||
@@ -171,11 +171,11 @@ starting at <a href="#ReleaseApproval">Release Approval</a>.</p>
|
||||
files and directories that are only used for continuous integration:
|
||||
<blockquote><tt>
|
||||
cd base-7.0<br />
|
||||
./.tools/make-tar.sh R7.0.3.2-rc1 base-7.0.3.2-rc1.tar.gz base-7.0.3.2-rc1/
|
||||
./.tools/make-tar.sh R7.0.4.1-rc1 base-7.0.4.1-rc1.tar.gz base-7.0.4.1-rc1/
|
||||
</tt></blockquote>
|
||||
Create a GPG signature file of the tarfile as follows:
|
||||
<blockquote><tt>
|
||||
gpg --armor --sign --detach-sig base-7.0.3.2-rc1.tar.gz
|
||||
gpg --armor --sign --detach-sig base-7.0.4.1-rc1.tar.gz
|
||||
</tt></blockquote>
|
||||
</td>
|
||||
</tr>
|
||||
@@ -298,7 +298,7 @@ starting at <a href="#ReleaseApproval">Release Approval</a>.</p>
|
||||
|
||||
<li>Tag the module:
|
||||
<blockquote><tt>
|
||||
git tag -m 'ANJ: Tag for EPICS 7.0.3.2' <module-version>
|
||||
git tag -m 'ANJ: Tag for EPICS 7.0.4.1' <module-version>
|
||||
</tt></blockquote>
|
||||
</li>
|
||||
|
||||
@@ -355,7 +355,7 @@ starting at <a href="#ReleaseApproval">Release Approval</a>.</p>
|
||||
<td>Tag the epics-base module in Git:
|
||||
<blockquote><tt>
|
||||
cd base-7.0<br />
|
||||
git tag -m 'ANJ: Tagged for 7.0.3.2' R7.0.3.2
|
||||
git tag -m 'ANJ: Tagged for release' R7.0.4.1
|
||||
</tt></blockquote>
|
||||
<p>Don't push these commits or the new tag to the Launchpad repository
|
||||
yet.</p>
|
||||
@@ -387,12 +387,12 @@ starting at <a href="#ReleaseApproval">Release Approval</a>.</p>
|
||||
files and directories that are only used for continuous integration:
|
||||
<blockquote><tt>
|
||||
cd base-7.0<br />
|
||||
./.tools/make-tar.sh R7.0.3.2 ../base-7.0.3.2.tar.gz base-7.0.3.2/
|
||||
./.tools/make-tar.sh R7.0.4.1 ../base-7.0.4.1.tar.gz base-7.0.4.1/
|
||||
</tt></blockquote>
|
||||
Create a GPG signature file of the tarfile as follows:
|
||||
<blockquote><tt>
|
||||
cd ..<br />
|
||||
gpg --armor --sign --detach-sig base-7.0.3.2.tar.gz
|
||||
gpg --armor --sign --detach-sig base-7.0.4.1.tar.gz
|
||||
</tt></blockquote>
|
||||
</td>
|
||||
</tr>
|
||||
@@ -457,7 +457,7 @@ starting at <a href="#ReleaseApproval">Release Approval</a>.</p>
|
||||
<td>Upload the tar file and its <tt>.asc</tt> signature file to the
|
||||
epics-controls web-server.
|
||||
<blockquote><tt>
|
||||
scp base-7.0.3.2.tar.gz base-7.0.3.2.tar.gz.asc epics-controls:download/base<br />
|
||||
scp base-7.0.4.1.tar.gz base-7.0.4.1.tar.gz.asc epics-controls:download/base<br />
|
||||
</tt></blockquote>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
16
documentation/mainpage.dox
Normal file
16
documentation/mainpage.dox
Normal file
@@ -0,0 +1,16 @@
|
||||
/**
|
||||
@mainpage EPICS Base
|
||||
|
||||
Documentation index
|
||||
|
||||
@ul
|
||||
@li @ref releasenotes
|
||||
@li @ref install
|
||||
@li @ref recordrefmanual
|
||||
@li <a href="CAref.html">Channel Access Reference Manual</a>
|
||||
@li <a href="filters.html">Server Side Filters Reference</a>
|
||||
@li <a href="msi.html">msi: Macro Substitution and Include Tool</a>
|
||||
@li <a href="links.html">JSON Link Types</a>
|
||||
@li <a href="CA.html">Perl 5 Interface to Channel Access</a>
|
||||
|
||||
*/
|
||||
@@ -603,7 +603,7 @@ void epicsStdCall ca_signal_formated ( long ca_status, const char *pfilenm,
|
||||
}
|
||||
else {
|
||||
fprintf ( stderr, "CA exception in thread w/o CA ctx: status=%s file=%s line=%d: \n",
|
||||
ca_message ( ca_status ), pfilenm, lineno );
|
||||
ca_message ( ca_status ), pfilenm ? pfilenm : "<null>", lineno );
|
||||
if ( pFormat ) {
|
||||
vfprintf ( stderr, pFormat, theArgs );
|
||||
}
|
||||
|
||||
@@ -13,11 +13,11 @@ ifdef T_A
|
||||
PERL_ARCHNAME = $(shell $(PERL) ../perlConfig.pl archname)
|
||||
PERL_ARCHPATH := $(PERL_VERSION)/$(PERL_ARCHNAME)
|
||||
|
||||
PERL_ARCHLIB := $(shell $(PERL) ../perlConfig.pl archlib)
|
||||
PERL_ARCHLIB := $(shell $(PERL) ../perlConfig.pl archlibexp)
|
||||
PERL_h = $(PERL_ARCHLIB)/CORE/perl.h
|
||||
|
||||
EXTUTILS := $(shell $(PERL) ../perlConfig.pl privlib)/ExtUtils
|
||||
PERLBIN := $(shell $(PERL) ../perlConfig.pl bin)
|
||||
EXTUTILS := $(shell $(PERL) ../perlConfig.pl privlibexp)/ExtUtils
|
||||
PERLBIN := $(shell $(PERL) ../perlConfig.pl binexp)
|
||||
XSUBPP := $(firstword $(wildcard $(PERLBIN)/xsubpp $(EXTUTILS)/xsubpp))
|
||||
|
||||
# Special settings for Darwin:
|
||||
|
||||
@@ -35,9 +35,11 @@ my %fieldType = (
|
||||
DBF_DOUBLE => 'DBF_FLOAT',
|
||||
DBF_FLOAT => 'DBF_FLOAT',
|
||||
DBF_LONG => 'DBF_LONG',
|
||||
DBF_INT64 => 'DBF_FLOAT',
|
||||
DBF_SHORT => 'DBF_LONG',
|
||||
DBF_ULONG => 'DBF_LONG',
|
||||
DBF_USHORT => 'DBF_LONG',
|
||||
DBF_UINT64 => 'DBF_FLOAT',
|
||||
DBF_DEVICE => 'DBF_STRING',
|
||||
DBF_ENUM => 'DBF_STRING',
|
||||
DBF_FWDLINK => 'DBF_STRING',
|
||||
@@ -86,7 +88,7 @@ if (@ARGV) {
|
||||
printRecord($_, @ARGV);
|
||||
} else {
|
||||
if (m/^ \s* ([]+:;<>0-9A-Za-z[-]+) (?:\. \w+)? \s* , \s* (\d+) \s* $/x) {
|
||||
# Recognizes ",n" as an interest leve, drops any ".FIELD" part
|
||||
# Recognizes ",n" as an interest level, drops any ".FIELD" part
|
||||
printRecord($1, $2);
|
||||
} else {
|
||||
# Drop any ".FIELD" part
|
||||
@@ -235,8 +237,9 @@ sub printField {
|
||||
$outStr = sprintf('%-5s %.8f', $field, $fieldData);
|
||||
} elsif ( $dataType eq 'DBF_CHAR' ) {
|
||||
$outStr = sprintf('%-5s %d', $field, ord($fieldData));
|
||||
}else {
|
||||
# DBF_LONG, DBF_SHORT, DBF_UCHAR, DBF_ULONG, DBF_USHORT
|
||||
} else {
|
||||
# DBF_INT64, DBF_LONG, DBF_SHORT,
|
||||
# DBF_UINT64, DBF_ULONG, DBF_USHORT, DBF_UCHAR,
|
||||
$outStr = sprintf('%-5s %d', $field, $fieldData);
|
||||
}
|
||||
|
||||
@@ -270,17 +273,18 @@ sub printField {
|
||||
sub caget {
|
||||
my @chans = map { CA->new($_); } @_;
|
||||
|
||||
#clear results;
|
||||
#clear any previous results;
|
||||
%callback_data = ();
|
||||
%timed_out = ();
|
||||
|
||||
eval { CA->pend_io($opt_w); };
|
||||
if ($@) {
|
||||
if ($@ =~ m/^ECA_TIMEOUT/) {
|
||||
my $err = (@chans > 1) ? 'some PV(s)' : '"' . $chans[0]->name . '"';
|
||||
my $name = $chans[0]->name;
|
||||
my $err = (@chans > 1) ? 'some fields' : "'$name'";
|
||||
print "Channel connect timed out: $err not found.\n";
|
||||
foreach my $chan (@chans) {
|
||||
$timed_out{$chan->name} = $chan->is_connected;
|
||||
$timed_out{$chan->name} = !$chan->is_connected;
|
||||
}
|
||||
@chans = grep { $_->is_connected } @chans;
|
||||
} else {
|
||||
@@ -289,14 +293,12 @@ sub caget {
|
||||
}
|
||||
|
||||
map {
|
||||
my $type;
|
||||
$type = $_->field_type;
|
||||
$_->get_callback(\&caget_callback, $type);
|
||||
$_->get_callback(\&caget_callback, $_->field_type);
|
||||
} @chans;
|
||||
|
||||
my $fields_read = @chans;
|
||||
$callback_incomplete = @chans;
|
||||
CA->pend_event(0.1) while $callback_incomplete;
|
||||
my $fields_read = $callback_incomplete = @chans;
|
||||
CA->pend_event(0.1)
|
||||
while $callback_incomplete;
|
||||
return $fields_read;
|
||||
}
|
||||
|
||||
@@ -325,9 +327,10 @@ sub printRecord {
|
||||
my @bases = (); #bases, from parser
|
||||
foreach my $field (sort keys %{$record{$recType}}) {
|
||||
# Skip DTYP field if this rec type doesn't have device support defined
|
||||
if ($field eq 'DTYP' && !(exists($device{$recType}))) { next; }
|
||||
next if $field eq 'DTYP' && !exists($device{$recType});
|
||||
|
||||
my ($fType, $fInterest, $base) = getFieldParams($recType, $field);
|
||||
# FIXME: Support waveform.VAL fields etc.
|
||||
unless( $fType eq 'DBF_NOACCESS' ) {
|
||||
if ($interest >= $fInterest ) {
|
||||
my $fToGet = "$name.$field";
|
||||
@@ -346,15 +349,10 @@ sub printRecord {
|
||||
my $field = $fields_pr[$i];
|
||||
my $fToGet = $readlist[$i];
|
||||
my ($fType, $data, $base);
|
||||
if ($timed_out{$fToGet}) {
|
||||
$fType = $fieldType{DBF_STRING};
|
||||
$data = '<timeout>';
|
||||
}
|
||||
else {
|
||||
$fType = $ftypes[$i];
|
||||
$base = $bases[$i];
|
||||
$data = $callback_data{$fToGet};
|
||||
}
|
||||
next if $timed_out{$fToGet};
|
||||
$fType = $ftypes[$i];
|
||||
$base = $bases[$i];
|
||||
$data = $callback_data{$fToGet};
|
||||
$col = printField($field, $data, $fType, $base, $col);
|
||||
}
|
||||
print("\n"); # Final newline
|
||||
@@ -387,8 +385,8 @@ sub printRecordList {
|
||||
if (exists($record{$type}) ) {
|
||||
print("Record type - $type\n");
|
||||
foreach my $fkey (sort keys %{$record{$type}}) {
|
||||
printf('%-4s', $fkey);
|
||||
printf(" interest = $record{$type}{$fkey}[$iIdx]");
|
||||
printf('%-8s', $fkey);
|
||||
printf(" interest = $record{$type}{$fkey}[$iIdx]");
|
||||
printf(" type = %-12s ",$record{$type}{$fkey}[$tIdx]);
|
||||
print (" base = $record{$type}{$fkey}[$bIdx]\n");
|
||||
}
|
||||
|
||||
@@ -157,7 +157,7 @@ int main (int argc, char *argv[])
|
||||
}
|
||||
break;
|
||||
case 's': /* ca_client_status interest level */
|
||||
if (sscanf(optarg,"%du", &statLevel) != 1)
|
||||
if (sscanf(optarg,"%u", &statLevel) != 1)
|
||||
{
|
||||
fprintf(stderr, "'%s' is not a valid interest level "
|
||||
"- ignored. ('cainfo -h' for help.)\n", optarg);
|
||||
|
||||
@@ -258,7 +258,7 @@ int main (int argc, char *argv[])
|
||||
}
|
||||
break;
|
||||
case '#': /* Array count */
|
||||
if (sscanf(optarg,"%ld", &reqElems) != 1)
|
||||
if (sscanf(optarg,"%lu", &reqElems) != 1)
|
||||
{
|
||||
fprintf(stderr, "'%s' is not a valid array element count "
|
||||
"- ignored. ('camonitor -h' for help.)\n", optarg);
|
||||
|
||||
@@ -437,6 +437,7 @@ int main (int argc, char *argv[])
|
||||
dbuf = calloc (count, sizeof(double));
|
||||
if(!sbuf || !dbuf) {
|
||||
fprintf(stderr, "Memory allocation failed\n");
|
||||
free(sbuf); free(dbuf);
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -450,6 +451,7 @@ int main (int argc, char *argv[])
|
||||
result = ca_pend_io(caTimeout);
|
||||
if (result == ECA_TIMEOUT) {
|
||||
fprintf(stderr, "Read operation timed out: ENUM data was not read.\n");
|
||||
free(sbuf); free(dbuf);
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -460,6 +462,7 @@ int main (int argc, char *argv[])
|
||||
if (*(argv+optind+i) == pend) { /* Conversion didn't work */
|
||||
fprintf(stderr, "Enum index value '%s' is not a number.\n",
|
||||
*(argv+optind+i));
|
||||
free(sbuf); free(dbuf);
|
||||
return 1;
|
||||
}
|
||||
if (dbuf[i] >= bufGrEnum.no_str) {
|
||||
@@ -486,6 +489,7 @@ int main (int argc, char *argv[])
|
||||
dbuf[i] = epicsStrtod(sbuf[i], &pend);
|
||||
if (sbuf[i] == pend || enumAsString) {
|
||||
fprintf(stderr, "Enum string value '%s' invalid.\n", sbuf[i]);
|
||||
free(sbuf); free(dbuf);
|
||||
return 1;
|
||||
}
|
||||
if (dbuf[i] >= bufGrEnum.no_str) {
|
||||
@@ -503,6 +507,7 @@ int main (int argc, char *argv[])
|
||||
ebuf = calloc(len, sizeof(char));
|
||||
if(!ebuf) {
|
||||
fprintf(stderr, "Memory allocation failed\n");
|
||||
free(sbuf); free(dbuf); free(ebuf);
|
||||
return 1;
|
||||
}
|
||||
count = epicsStrnRawFromEscaped(ebuf, len, cbuf, len-1) + 1;
|
||||
@@ -537,12 +542,14 @@ int main (int argc, char *argv[])
|
||||
}
|
||||
if (result != ECA_NORMAL) {
|
||||
fprintf(stderr, "Error from put operation: %s\n", ca_message(result));
|
||||
free(sbuf); free(dbuf); free(ebuf);
|
||||
return 1;
|
||||
}
|
||||
|
||||
result = ca_pend_io(caTimeout);
|
||||
if (result == ECA_TIMEOUT) {
|
||||
fprintf(stderr, "Write operation timed out: Data was not written.\n");
|
||||
free(sbuf); free(dbuf); free(ebuf);
|
||||
return 1;
|
||||
}
|
||||
if (request == callback) { /* Also wait for callbacks */
|
||||
@@ -556,6 +563,7 @@ int main (int argc, char *argv[])
|
||||
|
||||
if (result != ECA_NORMAL) {
|
||||
fprintf(stderr, "Error occured writing data: %s\n", ca_message(result));
|
||||
free(sbuf); free(dbuf); free(ebuf);
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -567,6 +575,7 @@ int main (int argc, char *argv[])
|
||||
|
||||
/* Shut down Channel Access */
|
||||
ca_context_destroy();
|
||||
free(sbuf); free(dbuf); free(ebuf);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -736,6 +736,8 @@ long dbValueSize(short dbr_type)
|
||||
sizeof(epicsFloat64), /* DOUBLE */
|
||||
sizeof(epicsEnum16)}; /* ENUM */
|
||||
|
||||
if(dbr_type>=NELEMENTS(size))
|
||||
return 0;
|
||||
return(size[dbr_type]);
|
||||
}
|
||||
|
||||
@@ -796,15 +798,12 @@ int dbLoadRecords(const char* file, const char* subs)
|
||||
static long getLinkValue(DBADDR *paddr, short dbrType,
|
||||
char *pbuf, long *nRequest)
|
||||
{
|
||||
dbCommon *precord = paddr->precord;
|
||||
dbFldDes *pfldDes = paddr->pfldDes;
|
||||
/* size of pbuf storage in bytes, including space for trailing nil */
|
||||
int maxlen;
|
||||
DBENTRY dbEntry;
|
||||
long status;
|
||||
long nReq = nRequest ? *nRequest : 1;
|
||||
|
||||
/* dbFindRecord() below will always succeed as we have a
|
||||
/* below will always succeed as we have a
|
||||
* valid DBADDR, so no point to check again.
|
||||
* Request for zero elements always succeeds
|
||||
*/
|
||||
@@ -830,10 +829,8 @@ static long getLinkValue(DBADDR *paddr, short dbrType,
|
||||
return S_db_badDbrtype;
|
||||
}
|
||||
|
||||
dbInitEntry(pdbbase, &dbEntry);
|
||||
status = dbFindRecord(&dbEntry, precord->name);
|
||||
if (!status) status = dbFindField(&dbEntry, pfldDes->name);
|
||||
if (!status) {
|
||||
dbInitEntryFromAddr(paddr, &dbEntry);
|
||||
{
|
||||
const char *rtnString = dbGetString(&dbEntry);
|
||||
|
||||
strncpy(pbuf, rtnString, maxlen-1);
|
||||
@@ -843,7 +840,7 @@ static long getLinkValue(DBADDR *paddr, short dbrType,
|
||||
if(nRequest) *nRequest = nReq;
|
||||
}
|
||||
dbFinishEntry(&dbEntry);
|
||||
return status;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long getAttrValue(DBADDR *paddr, short dbrType,
|
||||
|
||||
@@ -52,6 +52,9 @@
|
||||
#include "recGbl.h"
|
||||
#include "recSup.h"
|
||||
|
||||
/* from dbAccessDefs.h which can't be included here */
|
||||
#define S_db_badDbrtype (M_dbAccess| 3)
|
||||
|
||||
/* defined in dbContext.cpp
|
||||
* Setup local CA access
|
||||
*/
|
||||
@@ -457,6 +460,9 @@ long dbCaPutLinkCallback(struct link *plink,short dbrType,
|
||||
long status = 0;
|
||||
short link_action = 0;
|
||||
|
||||
if(INVALID_DB_REQ(dbrType))
|
||||
return S_db_badDbrtype;
|
||||
|
||||
assert(pca);
|
||||
/* put the new value in */
|
||||
epicsMutexMustLock(pca->lock);
|
||||
|
||||
@@ -603,6 +603,10 @@ long dbChannelOpen(dbChannel *chan)
|
||||
probe.no_elements = dbChannelElements(chan);
|
||||
probe.field_size = dbChannelFieldSize(chan);
|
||||
probe.sevr = NO_ALARM;
|
||||
probe.stat = NO_ALARM;
|
||||
probe.time.secPastEpoch = 0;
|
||||
probe.time.nsec = 0;
|
||||
|
||||
p = probe;
|
||||
|
||||
/*
|
||||
|
||||
@@ -382,7 +382,7 @@ The B<DPVT> field is is for private use of the device support modules.
|
||||
prompt("DSET address")
|
||||
special(SPC_NOMOD)
|
||||
interest(4)
|
||||
extra("unambiguous_dset *dset")
|
||||
extra("unambiguous_dset *dset")
|
||||
}
|
||||
field(DPVT,DBF_NOACCESS) {
|
||||
prompt("Device Private")
|
||||
@@ -491,7 +491,7 @@ field which is then used to acquire a timestamp.
|
||||
prompt("Break Point")
|
||||
special(SPC_NOMOD)
|
||||
interest(1)
|
||||
extra("char bkpt")
|
||||
extra("epicsUInt8 bkpt")
|
||||
}
|
||||
field(UDF,DBF_UCHAR) {
|
||||
prompt("Undefined")
|
||||
|
||||
@@ -159,6 +159,9 @@ long dbPutConvertJSON(const char *json, short dbrType,
|
||||
size_t jlen = strlen(json);
|
||||
long status;
|
||||
|
||||
if(INVALID_DB_REQ(dbrType))
|
||||
return S_db_badDbrtype;
|
||||
|
||||
parser->depth = 0;
|
||||
parser->dbrType = dbrType;
|
||||
parser->dbrSize = dbValueSize(dbrType);
|
||||
|
||||
@@ -69,7 +69,7 @@ void recGblDbaddrError(long status, const struct dbAddr *paddr,
|
||||
errPrintf(status,0,0,
|
||||
"PV: %s.%s "
|
||||
"error detected in routine: %s\n",
|
||||
(paddr ? precord->name : "Unknown"),
|
||||
(precord ? precord->name : "Unknown"),
|
||||
(pdbFldDes ? pdbFldDes->name : ""),
|
||||
(pmessage ? pmessage : "Unknown"));
|
||||
return;
|
||||
@@ -106,7 +106,7 @@ void recGblRecSupError(long status, const struct dbAddr *paddr,
|
||||
" %s\n",
|
||||
(psupport_name ? psupport_name : "Unknown"),
|
||||
(pdbRecordType ? pdbRecordType->name : "Unknown"),
|
||||
(paddr ? precord->name : "Unknown"),
|
||||
(precord ? precord->name : "Unknown"),
|
||||
(pdbFldDes ? pdbFldDes->name : ""),
|
||||
(pmessage ? pmessage : ""));
|
||||
return;
|
||||
|
||||
@@ -136,12 +136,14 @@ static void allocTemp(void *pvoid)
|
||||
static void *popFirstTemp(void)
|
||||
{
|
||||
tempListNode *ptempListNode;
|
||||
void *ptemp;
|
||||
void *ptemp = NULL;
|
||||
|
||||
ptempListNode = (tempListNode *)ellFirst(&tempList);
|
||||
ptemp = ptempListNode->item;
|
||||
ellDelete(&tempList,(ELLNODE *)ptempListNode);
|
||||
freeListFree(freeListPvt,ptempListNode);
|
||||
if(ptempListNode) {
|
||||
ptemp = ptempListNode->item;
|
||||
ellDelete(&tempList,(ELLNODE *)ptempListNode);
|
||||
freeListFree(freeListPvt,ptempListNode);
|
||||
}
|
||||
return(ptemp);
|
||||
}
|
||||
|
||||
@@ -477,12 +479,16 @@ static void dbMenuBody(void)
|
||||
return;
|
||||
}
|
||||
pnewMenu = (dbMenu *)popFirstTemp();
|
||||
if(!pnewMenu)
|
||||
return;
|
||||
pnewMenu->nChoice = nChoice = ellCount(&tempList)/2;
|
||||
pnewMenu->papChoiceName = dbCalloc(pnewMenu->nChoice,sizeof(char *));
|
||||
pnewMenu->papChoiceValue = dbCalloc(pnewMenu->nChoice,sizeof(char *));
|
||||
for(i=0; i<nChoice; i++) {
|
||||
pnewMenu->papChoiceName[i] = (char *)popFirstTemp();
|
||||
pnewMenu->papChoiceValue[i] = (char *)popFirstTemp();
|
||||
if(!pnewMenu->papChoiceName[i] || !pnewMenu->papChoiceValue[i])
|
||||
return;
|
||||
}
|
||||
if(ellCount(&tempList)) yyerrorAbort("dbMenuBody: tempList not empty");
|
||||
/* Add menu in sorted order */
|
||||
@@ -703,6 +709,8 @@ static void dbRecordtypeBody(void)
|
||||
return;
|
||||
}
|
||||
pdbRecordType= (dbRecordType *)popFirstTemp();
|
||||
if(!pdbRecordType)
|
||||
return;
|
||||
pdbRecordType->no_fields = no_fields = ellCount(&tempList);
|
||||
pdbRecordType->papFldDes = dbCalloc(no_fields,sizeof(dbFldDes *));
|
||||
pdbRecordType->papsortFldName = dbCalloc(no_fields,sizeof(char *));
|
||||
@@ -710,6 +718,8 @@ static void dbRecordtypeBody(void)
|
||||
no_prompt = no_links = 0;
|
||||
for(i=0; i<no_fields; i++) {
|
||||
pdbFldDes = (dbFldDes *)popFirstTemp();
|
||||
if(!pdbFldDes)
|
||||
return;
|
||||
pdbFldDes->pdbRecordType = pdbRecordType;
|
||||
pdbFldDes->indRecordType = i;
|
||||
pdbRecordType->papFldDes[i] = pdbFldDes;
|
||||
@@ -974,6 +984,8 @@ static void dbBreakBody(void)
|
||||
return;
|
||||
}
|
||||
pnewbrkTable = (brkTable *)popFirstTemp();
|
||||
if(!pnewbrkTable)
|
||||
return;
|
||||
number = ellCount(&tempList);
|
||||
if (number % 2) {
|
||||
yyerrorAbort("breaktable: Raw value missing");
|
||||
@@ -990,10 +1002,14 @@ static void dbBreakBody(void)
|
||||
char *str;
|
||||
|
||||
str = (char *)popFirstTemp();
|
||||
if(!str)
|
||||
return;
|
||||
(void) epicsScanDouble(str, &paBrkInt[i].raw);
|
||||
free(str);
|
||||
|
||||
str = (char *)popFirstTemp();
|
||||
if(!str)
|
||||
return;
|
||||
(void) epicsScanDouble(str, &paBrkInt[i].eng);
|
||||
free(str);
|
||||
}
|
||||
@@ -1034,22 +1050,49 @@ static void dbBreakBody(void)
|
||||
}
|
||||
pgphentry->userPvt = pnewbrkTable;
|
||||
}
|
||||
|
||||
|
||||
static
|
||||
int dbRecordNameValidate(const char *name)
|
||||
{
|
||||
size_t i=0u;
|
||||
const char *pos = name;
|
||||
|
||||
if (!*name) {
|
||||
yyerrorAbort("Error: Record/Alias name can't be empty");
|
||||
return 1;
|
||||
}
|
||||
|
||||
for(; *pos; i++, pos++) {
|
||||
char c = *pos;
|
||||
if(i==0) {
|
||||
/* first character restrictions */
|
||||
if(c=='-' || c=='+' || c=='[' || c=='{') {
|
||||
errlogPrintf("Warning: Record/Alias name '%s' should not begin with '%c'\n", name, c);
|
||||
}
|
||||
}
|
||||
/* any character restrictions */
|
||||
if(c < ' ') {
|
||||
errlogPrintf("Warning: Record/Alias name '%s' should not contain non-printable 0x%02u\n",
|
||||
name, (unsigned)c);
|
||||
|
||||
} else if(c==' ' || c=='\t' || c=='"' || c=='\'' || c=='.' || c=='$') {
|
||||
epicsPrintf("Error: Bad character '%c' in Record/Alias name \"%s\"\n",
|
||||
c, name);
|
||||
yyerrorAbort(NULL);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void dbRecordHead(char *recordType, char *name, int visible)
|
||||
{
|
||||
char *badch;
|
||||
DBENTRY *pdbentry;
|
||||
long status;
|
||||
|
||||
if (!*name) {
|
||||
yyerrorAbort("dbRecordHead: Record name can't be empty");
|
||||
if(dbRecordNameValidate(name))
|
||||
return;
|
||||
}
|
||||
badch = strpbrk(name, " \"'.$");
|
||||
if (badch) {
|
||||
epicsPrintf("Bad character '%c' in record name \"%s\"\n",
|
||||
*badch, name);
|
||||
}
|
||||
|
||||
pdbentry = dbAllocEntry(pdbbase);
|
||||
if (ellCount(&tempList))
|
||||
@@ -1180,10 +1223,9 @@ static void dbRecordAlias(char *name)
|
||||
tempListNode *ptempListNode;
|
||||
long status;
|
||||
|
||||
if (!*name) {
|
||||
yyerrorAbort("dbRecordAlias: Alias name can't be empty");
|
||||
if(dbRecordNameValidate(name))
|
||||
return;
|
||||
}
|
||||
|
||||
if (duplicate) return;
|
||||
ptempListNode = (tempListNode *)ellFirst(&tempList);
|
||||
pdbentry = ptempListNode->item;
|
||||
@@ -1201,10 +1243,9 @@ static void dbAlias(char *name, char *alias)
|
||||
DBENTRY dbEntry;
|
||||
DBENTRY *pdbEntry = &dbEntry;
|
||||
|
||||
if (!*alias) {
|
||||
yyerrorAbort("dbAlias: Alias name can't be empty");
|
||||
if(dbRecordNameValidate(alias))
|
||||
return;
|
||||
}
|
||||
|
||||
dbInitEntry(pdbbase, pdbEntry);
|
||||
if (dbFindRecord(pdbEntry, name)) {
|
||||
epicsPrintf("Alias \"%s\" refers to unknown record \"%s\"\n",
|
||||
|
||||
@@ -553,7 +553,12 @@ static long lnkCalc_getValue(struct link *plink, short dbrType, void *pbuffer,
|
||||
dbCommon *prec = plink->precord;
|
||||
int i;
|
||||
long status;
|
||||
FASTCONVERT conv = dbFastPutConvertRoutine[DBR_DOUBLE][dbrType];
|
||||
FASTCONVERT conv;
|
||||
|
||||
if(INVALID_DB_REQ(dbrType))
|
||||
return S_db_badDbrtype;
|
||||
|
||||
conv = dbFastPutConvertRoutine[DBR_DOUBLE][dbrType];
|
||||
|
||||
/* Any link errors will trigger a LINK/INVALID alarm in the child link */
|
||||
for (i = 0; i < clink->nArgs; i++) {
|
||||
@@ -624,7 +629,12 @@ static long lnkCalc_putValue(struct link *plink, short dbrType,
|
||||
dbCommon *prec = plink->precord;
|
||||
int i;
|
||||
long status;
|
||||
FASTCONVERT conv = dbFastGetConvertRoutine[dbrType][DBR_DOUBLE];
|
||||
FASTCONVERT conv;
|
||||
|
||||
if(INVALID_DB_REQ(dbrType))
|
||||
return S_db_badDbrtype;
|
||||
|
||||
conv = dbFastGetConvertRoutine[dbrType][DBR_DOUBLE];
|
||||
|
||||
/* Any link errors will trigger a LINK/INVALID alarm in the child link */
|
||||
for (i = 0; i < clink->nArgs; i++) {
|
||||
|
||||
@@ -361,6 +361,9 @@ static long lnkConst_loadScalar(struct link *plink, short dbrType, void *pbuffer
|
||||
const_link *clink = CONTAINER(plink->value.json.jlink, const_link, jlink);
|
||||
long status;
|
||||
|
||||
if(INVALID_DB_REQ(dbrType))
|
||||
return S_db_badDbrtype;
|
||||
|
||||
switch (clink->type) {
|
||||
case si64:
|
||||
if (clink->jlink.debug)
|
||||
@@ -451,12 +454,17 @@ static long lnkConst_loadArray(struct link *plink, short dbrType, void *pbuffer,
|
||||
long *pnReq)
|
||||
{
|
||||
const_link *clink = CONTAINER(plink->value.json.jlink, const_link, jlink);
|
||||
short dbrSize = dbValueSize(dbrType);
|
||||
short dbrSize;
|
||||
char *pdest = pbuffer;
|
||||
int nElems = clink->nElems;
|
||||
FASTCONVERT conv;
|
||||
long status;
|
||||
|
||||
if(INVALID_DB_REQ(dbrType))
|
||||
return S_db_badDbrtype;
|
||||
|
||||
dbrSize = dbValueSize(dbrType);
|
||||
|
||||
if (nElems > *pnReq)
|
||||
nElems = *pnReq;
|
||||
|
||||
|
||||
@@ -142,7 +142,12 @@ static long lnkState_getValue(struct link *plink, short dbrType, void *pbuffer,
|
||||
{
|
||||
state_link *slink = CONTAINER(plink->value.json.jlink,
|
||||
struct state_link, jlink);
|
||||
FASTCONVERT conv = dbFastPutConvertRoutine[DBR_SHORT][dbrType];
|
||||
FASTCONVERT conv;
|
||||
|
||||
if(INVALID_DB_REQ(dbrType))
|
||||
return S_db_badDbrtype;
|
||||
|
||||
conv = dbFastPutConvertRoutine[DBR_SHORT][dbrType];
|
||||
|
||||
slink->val = slink->invert ^ dbStateGet(slink->state);
|
||||
return conv(&slink->val, pbuffer, NULL);
|
||||
|
||||
@@ -113,7 +113,6 @@ static long cvt_dbaddr(DBADDR *paddr)
|
||||
{
|
||||
arrRecord *prec = (arrRecord *) paddr->precord;
|
||||
|
||||
paddr->pfield = prec->bptr;
|
||||
paddr->no_elements = prec->nelm;
|
||||
paddr->field_type = prec->ftvl;
|
||||
paddr->field_size = dbValueSize(prec->ftvl);
|
||||
@@ -126,6 +125,7 @@ static long get_array_info(DBADDR *paddr, long *no_elements, long *offset)
|
||||
{
|
||||
arrRecord *prec = (arrRecord *) paddr->precord;
|
||||
|
||||
paddr->pfield = prec->bptr;
|
||||
*no_elements = prec->nord;
|
||||
*offset = prec->off;
|
||||
|
||||
|
||||
@@ -416,7 +416,7 @@ static void testArrayLink(unsigned nsrc, unsigned ntarg)
|
||||
dbScanLock((dbCommon*)ptarg);
|
||||
fillArray(buftarg, ptarg->nelm, 1);
|
||||
ptarg->nord = ptarg->nelm;
|
||||
db_post_events(ptarg, ptarg->bptr, DBE_VALUE|DBE_ALARM|DBE_ARCHIVE);
|
||||
db_post_events(ptarg, &ptarg->val, DBE_VALUE|DBE_ALARM|DBE_ARCHIVE);
|
||||
dbScanUnlock((dbCommon*)ptarg);
|
||||
|
||||
waitForUpdateN(psrclnk, 2);
|
||||
@@ -540,7 +540,7 @@ static void testreTargetTypeChange(void)
|
||||
dbScanLock((dbCommon*)ptarg1);
|
||||
fillArrayDouble(buftarg1, ptarg1->nelm, 1);
|
||||
ptarg1->nord = ptarg1->nelm;
|
||||
db_post_events(ptarg1, ptarg1->bptr, DBE_VALUE|DBE_ALARM|DBE_ARCHIVE);
|
||||
db_post_events(ptarg1, &ptarg1->val, DBE_VALUE|DBE_ALARM|DBE_ARCHIVE);
|
||||
dbScanUnlock((dbCommon*)ptarg1);
|
||||
|
||||
epicsEventMustWait(waitEvent); /* wait for update */
|
||||
|
||||
@@ -119,11 +119,26 @@ void testLongField(void)
|
||||
testdbGetArrFieldEqual("lnktest.NAME$", DBR_CHAR, 8, 8, "lnktest");
|
||||
}
|
||||
|
||||
static
|
||||
void testPutArr(void)
|
||||
{
|
||||
epicsUInt32 buf[10];
|
||||
testDiag("testPutArr()");
|
||||
|
||||
testdbGetArrFieldEqual("arr", DBR_LONG, 1, 0, NULL);
|
||||
|
||||
buf[0] = 1; buf[1] = 2; buf[2] = 3; buf[3] = 0;
|
||||
testdbPutArrFieldOk("arr", DBF_ULONG, 3, buf);
|
||||
|
||||
buf[3] = 0xdeadbeef;
|
||||
testdbGetArrFieldEqual("arr", DBR_LONG, 4, 3, buf);
|
||||
}
|
||||
|
||||
void dbTestIoc_registerRecordDeviceDriver(struct dbBase *);
|
||||
|
||||
MAIN(dbPutGet)
|
||||
{
|
||||
testPlan(41);
|
||||
testPlan(44);
|
||||
testdbPrepare();
|
||||
|
||||
testdbReadDatabase("dbTestIoc.dbd", NULL, NULL);
|
||||
@@ -142,6 +157,8 @@ MAIN(dbPutGet)
|
||||
testLongAttr();
|
||||
testLongField();
|
||||
|
||||
testPutArr();
|
||||
|
||||
testIocShutdownOk();
|
||||
|
||||
testdbCleanup();
|
||||
|
||||
@@ -39,3 +39,8 @@ record(x, "lnktarget") {}
|
||||
record(x, "lnktest") {
|
||||
field(INP, "lnktarget NPP NMS")
|
||||
}
|
||||
|
||||
record(arr, "arr") {
|
||||
field(FTVL, "ULONG")
|
||||
field(NELM, "10")
|
||||
}
|
||||
|
||||
@@ -117,9 +117,14 @@ long z_putval(struct link *plink, short dbrType,
|
||||
const void *pbuffer, long nRequest)
|
||||
{
|
||||
long ret;
|
||||
long (*pconv)(epicsInt32 *, const void *, const dbAddr *) = dbFastPutConvertRoutine[DBF_LONG][dbrType];
|
||||
long (*pconv)(epicsInt32 *, const void *, const dbAddr *);
|
||||
zpriv *priv = CONTAINER(plink->value.json.jlink, zpriv, base);
|
||||
|
||||
if(INVALID_DB_REQ(dbrType))
|
||||
return S_db_badDbrtype;
|
||||
|
||||
pconv = dbFastPutConvertRoutine[DBF_LONG][dbrType];
|
||||
|
||||
if(nRequest==0) return 0;
|
||||
|
||||
epicsMutexLock(priv->lock);
|
||||
|
||||
@@ -13,6 +13,8 @@
|
||||
* Ralph Lange <Ralph.Lange@bessy.de>
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "dbAccessDefs.h"
|
||||
#include "recSup.h"
|
||||
#include "recGbl.h"
|
||||
|
||||
@@ -12,14 +12,7 @@
|
||||
*
|
||||
* \brief Contains a few templates out of the C++ standard header algorithm
|
||||
*
|
||||
* \note The templates are provided here in a much smaller file. Standard algorithm
|
||||
* contains many templates for sorting and searching through C++ template containers
|
||||
* which are not used in EPICS. If all you need from there is std::min(),
|
||||
* std::max() and/or std::swap() your code may compile faster if you include
|
||||
* epicsAlgorithm.h and use epicsMin(), epicsMax() and epicsSwap() instead.
|
||||
*
|
||||
* The C++ standard only requires types to be less-than comparable, so
|
||||
* the epicsMin and epicsMax templates only use operator <.
|
||||
* \deprecated Use std::min()/max()/swap() in new code
|
||||
*/
|
||||
|
||||
#ifndef __EPICS_ALGORITHM_H__
|
||||
|
||||
@@ -438,7 +438,7 @@ int htoi(unsigned char *str)
|
||||
{
|
||||
int result;
|
||||
|
||||
(void) sscanf( (char *) str, "%x", &result );
|
||||
(void) sscanf( (char *) str, "%x", (unsigned *) &result );
|
||||
|
||||
return ( result );
|
||||
}
|
||||
@@ -653,7 +653,7 @@ int otoi(Char *str)
|
||||
{
|
||||
int result;
|
||||
|
||||
(void) sscanf( (char *) str, "%o", &result );
|
||||
(void) sscanf( (char *) str, "%o", (unsigned *) &result );
|
||||
|
||||
return ( result );
|
||||
}
|
||||
|
||||
@@ -73,9 +73,7 @@
|
||||
/** \brief Size of a record name without the nil terminator */
|
||||
#define PVNAME_SZ (PVNAME_STRINGSZ - 1)
|
||||
|
||||
/**
|
||||
* \def PVLINK_STRINGSZ
|
||||
* \brief Buffer size for the string representation of a DBF_*LINK field
|
||||
/** \brief Buffer size for the string representation of a DBF_*LINK field
|
||||
*/
|
||||
#define PVLINK_STRINGSZ 1024
|
||||
|
||||
|
||||
@@ -35,17 +35,7 @@ extern "C" {
|
||||
|
||||
typedef void (*REGISTRAR)(void);
|
||||
|
||||
/** \brief Generate a name for an export object.
|
||||
* \param typ Object's data type.
|
||||
* \param obj Object's name.
|
||||
* \return C identifier for the export object.
|
||||
*/
|
||||
#define EPICS_EXPORT_POBJ(typ, obj) pvar_ ## typ ## _ ## obj
|
||||
|
||||
/** \brief Generate a name for an export function object.
|
||||
* \param fun Function's name.
|
||||
* \return C identifier for the export object.
|
||||
*/
|
||||
#define EPICS_EXPORT_PFUNC(fun) EPICS_EXPORT_POBJ(func, fun)
|
||||
|
||||
/** \brief Declare an object for exporting.
|
||||
@@ -79,6 +69,8 @@ typedef void (*REGISTRAR)(void);
|
||||
*
|
||||
* \param typ Object's data type.
|
||||
* \param obj Object's name.
|
||||
*
|
||||
* \note C++ code needs to wrap with @code extern "C" { } @endcode
|
||||
*/
|
||||
#define epicsExportAddress(typ, obj) \
|
||||
epicsShareExtern typ *EPICS_EXPORT_POBJ(typ,obj); \
|
||||
@@ -96,6 +88,8 @@ typedef void (*REGISTRAR)(void);
|
||||
\endcode
|
||||
*
|
||||
* \param fun Registrar function's name.
|
||||
*
|
||||
* \note C++ code needs to wrap with @code extern "C" { } @endcode
|
||||
*/
|
||||
#define epicsExportRegistrar(fun) \
|
||||
epicsShareFunc REGISTRAR EPICS_EXPORT_PFUNC(fun) = (REGISTRAR) &fun
|
||||
@@ -111,6 +105,8 @@ typedef void (*REGISTRAR)(void);
|
||||
\endcode
|
||||
*
|
||||
* \param fun Function's name
|
||||
*
|
||||
* \note C++ code needs to wrap with @code extern "C" { } @endcode
|
||||
*/
|
||||
#define epicsRegisterFunction(fun) \
|
||||
static void register_func_ ## fun(void) \
|
||||
|
||||
@@ -88,14 +88,16 @@
|
||||
|
||||
MAIN(iocTest)
|
||||
{
|
||||
iocBuildIsolated() || iocRun();
|
||||
|
||||
... test code ...
|
||||
|
||||
iocShutdown();
|
||||
dbFreeBase(pdbbase);
|
||||
registryFree();
|
||||
pdbbase = NULL;
|
||||
testPlan(0);
|
||||
testdbPrepare();
|
||||
testdbReadDatabase("<dbdname>.dbd", 0, 0);
|
||||
<dbdname>_registerRecordDeviceDriver(pdbbase);
|
||||
testdbReadDatabase("some.db", 0, 0);
|
||||
... test code before iocInit(). eg. dbGetString() ...
|
||||
testIocInitOk();
|
||||
... test code with IOC running. eg. dbGet()
|
||||
testIocShutdownOk();
|
||||
testdbCleanup();
|
||||
return testDone();
|
||||
}
|
||||
\endcode
|
||||
@@ -235,6 +237,15 @@ LIBCOM_API int testDiag(const char *fmt, ...)
|
||||
*/
|
||||
LIBCOM_API int testDone(void);
|
||||
|
||||
/** \brief Return non-zero in shared/oversubscribed testing envrionments
|
||||
*
|
||||
* May be used to testSkip(), or select longer timeouts, for some cases
|
||||
* when the test process may be preempted for arbitrarily long times.
|
||||
* This is common in shared CI environments.
|
||||
*
|
||||
* The environment variable $EPICS_TEST_IMPRECISE_TIMING=YES should be
|
||||
* set in by such testing environments.
|
||||
*/
|
||||
LIBCOM_API
|
||||
int testImpreciseTiming(void);
|
||||
|
||||
|
||||
@@ -20,12 +20,12 @@
|
||||
*
|
||||
* When creating the consumer thread also create an epicsEvent.
|
||||
\code
|
||||
epicsEvent *pevent = new epicsEvent;
|
||||
epicsEvent event;
|
||||
\endcode
|
||||
* The consumer thread has code containing:
|
||||
\code
|
||||
while(1) {
|
||||
pevent->wait();
|
||||
pevent.wait();
|
||||
while( {more work} ) {
|
||||
{process work}
|
||||
}
|
||||
@@ -33,7 +33,7 @@
|
||||
\endcode
|
||||
* Producers create requests and issue the statement:
|
||||
\code
|
||||
pevent->trigger();
|
||||
pevent.trigger();
|
||||
\endcode
|
||||
**/
|
||||
|
||||
|
||||
@@ -19,12 +19,18 @@
|
||||
*
|
||||
* The typical C++ use of a mutual exclusion semaphore is:
|
||||
\code
|
||||
epicsMutex *plock = new epicsMutex;
|
||||
epicsMutex lock;
|
||||
...
|
||||
...
|
||||
plock->lock();
|
||||
// process resources
|
||||
plock->unlock();
|
||||
{
|
||||
epicsMutex::guard_t G(lock); // lock
|
||||
// process resources
|
||||
} // unlock
|
||||
// or for compatiblity
|
||||
{
|
||||
epicsGuard<epicsMutex> G(lock); // lock
|
||||
// process resources
|
||||
} // unlock
|
||||
\endcode
|
||||
*
|
||||
* \note The implementation:
|
||||
|
||||
@@ -155,10 +155,10 @@ public:
|
||||
static epicsTime getCurrent ();
|
||||
/** \brief Get current monotonic time
|
||||
*
|
||||
* Returns an epicsTime containing the current monotonic time, a
|
||||
* high-resolution OS clock that counts at a steady rate, never
|
||||
* going backwards or jumping forwards. This time is only useful
|
||||
* for measuring time differences.
|
||||
* Returns an epicsTime containing the current monotonic time, an
|
||||
* OS clock which never going backwards or jumping forwards.
|
||||
* This time is has an undefined epoch, and is only useful for
|
||||
* measuring time differences.
|
||||
*/
|
||||
static epicsTime getMonotonic ();
|
||||
|
||||
|
||||
@@ -1,250 +0,0 @@
|
||||
/*************************************************************************\
|
||||
* Copyright (c) 2002 The University of Chicago, as Operator of Argonne
|
||||
* National Laboratory.
|
||||
* Copyright (c) 2002 The Regents of the University of California, as
|
||||
* Operator of Los Alamos National Laboratory.
|
||||
* EPICS BASE Versions 3.13.7
|
||||
* and higher are distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
\*************************************************************************/
|
||||
/*
|
||||
* Author W. Eric Norum
|
||||
* norume@aps.anl.gov
|
||||
* 630 252 4793
|
||||
*/
|
||||
|
||||
/*
|
||||
* We want to access information which is
|
||||
* normally hidden from application programs.
|
||||
*/
|
||||
#define __RTEMS_VIOLATE_KERNEL_VISIBILITY__ 1
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <rtems.h>
|
||||
#include <rtems/error.h>
|
||||
#include "epicsMessageQueue.h"
|
||||
#include "errlog.h"
|
||||
|
||||
LIBCOM_API epicsMessageQueueId epicsStdCall
|
||||
epicsMessageQueueCreate(unsigned int capacity, unsigned int maximumMessageSize)
|
||||
{
|
||||
rtems_status_code sc;
|
||||
epicsMessageQueueId id = calloc(1, sizeof(*id));
|
||||
rtems_interrupt_level level;
|
||||
static char c1 = 'a';
|
||||
static char c2 = 'a';
|
||||
static char c3 = 'a';
|
||||
|
||||
if(!id)
|
||||
return NULL;
|
||||
|
||||
sc = rtems_message_queue_create (rtems_build_name ('Q', c3, c2, c1),
|
||||
capacity,
|
||||
maximumMessageSize,
|
||||
RTEMS_FIFO|RTEMS_LOCAL,
|
||||
&id->id);
|
||||
if (sc != RTEMS_SUCCESSFUL) {
|
||||
free(id);
|
||||
errlogPrintf ("Can't create message queue: %s\n", rtems_status_text (sc));
|
||||
return NULL;
|
||||
}
|
||||
id->maxSize = maximumMessageSize;
|
||||
id->localBuf = NULL;
|
||||
rtems_interrupt_disable (level);
|
||||
if (c1 == 'z') {
|
||||
if (c2 == 'z') {
|
||||
if (c3 == 'z') {
|
||||
c3 = 'a';
|
||||
}
|
||||
else {
|
||||
c3++;
|
||||
}
|
||||
c2 = 'a';
|
||||
}
|
||||
else {
|
||||
c2++;
|
||||
}
|
||||
c1 = 'a';
|
||||
}
|
||||
else {
|
||||
c1++;
|
||||
}
|
||||
rtems_interrupt_enable (level);
|
||||
return id;
|
||||
}
|
||||
|
||||
static rtems_status_code rtems_message_queue_send_timeout(
|
||||
rtems_id id,
|
||||
void *buffer,
|
||||
uint32_t size,
|
||||
rtems_interval timeout)
|
||||
{
|
||||
Message_queue_Control *the_message_queue;
|
||||
Objects_Locations location;
|
||||
CORE_message_queue_Status msg_status;
|
||||
|
||||
the_message_queue = _Message_queue_Get( id, &location );
|
||||
switch ( location )
|
||||
{
|
||||
case OBJECTS_ERROR:
|
||||
return RTEMS_INVALID_ID;
|
||||
|
||||
case OBJECTS_LOCAL:
|
||||
msg_status = _CORE_message_queue_Send(
|
||||
&the_message_queue->message_queue,
|
||||
buffer,
|
||||
size,
|
||||
id,
|
||||
NULL,
|
||||
1,
|
||||
timeout
|
||||
);
|
||||
|
||||
_Thread_Enable_dispatch();
|
||||
|
||||
/*
|
||||
* If we had to block, then this is where the task returns
|
||||
* after it wakes up. The returned status is correct for
|
||||
* non-blocking operations but if we blocked, then we need
|
||||
* to look at the status in our TCB.
|
||||
*/
|
||||
|
||||
if ( msg_status == CORE_MESSAGE_QUEUE_STATUS_UNSATISFIED_WAIT )
|
||||
msg_status = _Thread_Executing->Wait.return_code;
|
||||
return _Message_queue_Translate_core_message_queue_return_code( msg_status );
|
||||
}
|
||||
return RTEMS_INTERNAL_ERROR; /* unreached - only to remove warnings */
|
||||
}
|
||||
|
||||
LIBCOM_API int epicsStdCall epicsMessageQueueSend(
|
||||
epicsMessageQueueId id,
|
||||
void *message,
|
||||
unsigned int messageSize)
|
||||
{
|
||||
if (rtems_message_queue_send_timeout(id->id, message, messageSize, RTEMS_NO_TIMEOUT) == RTEMS_SUCCESSFUL)
|
||||
return 0;
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
|
||||
LIBCOM_API int epicsStdCall epicsMessageQueueSendWithTimeout(
|
||||
epicsMessageQueueId id,
|
||||
void *message,
|
||||
unsigned int messageSize,
|
||||
double timeout)
|
||||
{
|
||||
rtems_interval delay;
|
||||
extern double rtemsTicksPerSecond_double;
|
||||
|
||||
/*
|
||||
* Convert time to ticks
|
||||
*/
|
||||
if (timeout <= 0.0)
|
||||
return epicsMessageQueueTrySend(id, message, messageSize);
|
||||
delay = (int)(timeout * rtemsTicksPerSecond_double);
|
||||
if (delay == 0)
|
||||
delay++;
|
||||
if (rtems_message_queue_send_timeout(id->id, message, messageSize, delay) == RTEMS_SUCCESSFUL)
|
||||
return 0;
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int receiveMessage(
|
||||
epicsMessageQueueId id,
|
||||
void *buffer,
|
||||
uint32_t size,
|
||||
uint32_t wait,
|
||||
rtems_interval delay)
|
||||
{
|
||||
size_t rsize;
|
||||
rtems_status_code sc;
|
||||
|
||||
if (size < id->maxSize) {
|
||||
if (id->localBuf == NULL) {
|
||||
id->localBuf = malloc(id->maxSize);
|
||||
if (id->localBuf == NULL)
|
||||
return -1;
|
||||
}
|
||||
rsize = receiveMessage(id, id->localBuf, id->maxSize, wait, delay);
|
||||
if (rsize > size)
|
||||
return -1;
|
||||
memcpy(buffer, id->localBuf, rsize);
|
||||
}
|
||||
else {
|
||||
sc = rtems_message_queue_receive(id->id, buffer, &rsize, wait, delay);
|
||||
if (sc != RTEMS_SUCCESSFUL)
|
||||
return -1;
|
||||
}
|
||||
return rsize;
|
||||
}
|
||||
|
||||
LIBCOM_API int epicsStdCall epicsMessageQueueTryReceive(
|
||||
epicsMessageQueueId id,
|
||||
void *message,
|
||||
unsigned int size)
|
||||
{
|
||||
return receiveMessage(id, message, size, RTEMS_NO_WAIT, 0);
|
||||
}
|
||||
|
||||
LIBCOM_API int epicsStdCall epicsMessageQueueReceive(
|
||||
epicsMessageQueueId id,
|
||||
void *message,
|
||||
unsigned int size)
|
||||
{
|
||||
return receiveMessage(id, message, size, RTEMS_WAIT, RTEMS_NO_TIMEOUT);
|
||||
}
|
||||
|
||||
LIBCOM_API int epicsStdCall epicsMessageQueueReceiveWithTimeout(
|
||||
epicsMessageQueueId id,
|
||||
void *message,
|
||||
unsigned int size,
|
||||
double timeout)
|
||||
{
|
||||
rtems_interval delay;
|
||||
uint32_t wait;
|
||||
extern double rtemsTicksPerSecond_double;
|
||||
|
||||
/*
|
||||
* Convert time to ticks
|
||||
*/
|
||||
if (timeout <= 0.0) {
|
||||
wait = RTEMS_NO_WAIT;
|
||||
delay = 0;
|
||||
}
|
||||
else {
|
||||
wait = RTEMS_WAIT;
|
||||
delay = (int)(timeout * rtemsTicksPerSecond_double);
|
||||
if (delay == 0)
|
||||
delay++;
|
||||
}
|
||||
return receiveMessage(id, message, size, wait, delay);
|
||||
}
|
||||
|
||||
LIBCOM_API int epicsStdCall epicsMessageQueuePending(
|
||||
epicsMessageQueueId id)
|
||||
{
|
||||
uint32_t count;
|
||||
rtems_status_code sc;
|
||||
|
||||
sc = rtems_message_queue_get_number_pending(id->id, &count);
|
||||
if (sc != RTEMS_SUCCESSFUL) {
|
||||
errlogPrintf("Message queue %x get number pending failed: %s\n",
|
||||
(unsigned int)id,
|
||||
rtems_status_text(sc));
|
||||
return -1;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
LIBCOM_API void epicsStdCall epicsMessageQueueShow(
|
||||
epicsMessageQueueId id,
|
||||
int level)
|
||||
{
|
||||
int pending = epicsMessageQueuePending(id);
|
||||
if (pending >= 0)
|
||||
printf ("Message queue %lx -- Pending: %d\n", (unsigned long)id, pending);
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
/*************************************************************************\
|
||||
* Copyright (c) 2002 The University of Chicago, as Operator of Argonne
|
||||
* National Laboratory.
|
||||
* Copyright (c) 2002 The Regents of the University of California, as
|
||||
* Operator of Los Alamos National Laboratory.
|
||||
* EPICS BASE Versions 3.13.7
|
||||
* and higher are distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
\*************************************************************************/
|
||||
/*
|
||||
* Author W. Eric Norum
|
||||
* norume@aps.anl.gov
|
||||
* 630 252 4793
|
||||
*/
|
||||
|
||||
/*
|
||||
* Very thin shims around RTEMS routines
|
||||
*/
|
||||
#include <rtems.h>
|
||||
|
||||
struct epicsMessageQueueOSD {
|
||||
rtems_id id;
|
||||
unsigned int maxSize;
|
||||
void *localBuf;
|
||||
|
||||
};
|
||||
#define epicsMessageQueueDestroy(q) (rtems_message_queue_delete((q)->id))
|
||||
|
||||
#define epicsMessageQueueTrySend(q,m,l) (rtems_message_queue_send((q)->id, (m), (l)) == RTEMS_SUCCESSFUL ? 0 : -1)
|
||||
@@ -211,7 +211,7 @@ LIBCOM_API void epicsStdCall osiSockDiscoverBroadcastAddresses
|
||||
pNewNode->addr.sa = pIfreqList->ifr_broadaddr;
|
||||
ifDepenDebugPrintf ( ( "found broadcast addr = %x\n", ntohl ( baddr.ia.sin_addr.s_addr ) ) );
|
||||
} else {
|
||||
ifDepenDebugPrintf ( ( "Ignoring broadcast addr = \n", ntohl ( baddr.ia.sin_addr.s_addr ) ) );
|
||||
ifDepenDebugPrintf ( ( "Ignoring broadcast addr = %x\n", ntohl ( baddr.ia.sin_addr.s_addr ) ) );
|
||||
free ( pNewNode );
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -22,6 +22,15 @@
|
||||
LIBCOM_API void epicsStdCall
|
||||
epicsSocketEnableAddressReuseDuringTimeWaitState ( SOCKET s )
|
||||
{
|
||||
#ifdef _WIN32
|
||||
/*
|
||||
* Note: WINSOCK appears to assign a different functionality for
|
||||
* SO_REUSEADDR compared to other OS. With WINSOCK SO_REUSEADDR indicates
|
||||
* that simultaneously servers can bind to the same TCP port on the same host!
|
||||
* Also, servers are always enabled to reuse a port immediately after
|
||||
* they exit ( even if SO_REUSEADDR isnt set ).
|
||||
*/
|
||||
#else
|
||||
int yes = true;
|
||||
int status;
|
||||
status = setsockopt ( s, SOL_SOCKET, SO_REUSEADDR,
|
||||
@@ -31,6 +40,7 @@ LIBCOM_API void epicsStdCall
|
||||
"epicsSocketEnableAddressReuseDuringTimeWaitState: "
|
||||
"unable to set SO_REUSEADDR?\n");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static
|
||||
|
||||
@@ -71,6 +71,7 @@ LIBCOM_API int epicsThreadHookAdd(EPICS_THREAD_HOOK_ROUTINE hook)
|
||||
return 0;
|
||||
}
|
||||
fprintf(stderr, "epicsThreadHookAdd: Locking problem\n");
|
||||
free(pHook);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
@@ -468,8 +468,9 @@ LIBCOM_API void epicsThreadMap ( EPICS_THREAD_HOOK_ROUTINE func )
|
||||
while (noTasks == 0) {
|
||||
noTasks = taskIdListGet(taskIdList, taskIdListSize);
|
||||
if (noTasks == taskIdListSize) {
|
||||
taskIdList = realloc(taskIdList, (taskIdListSize+ID_LIST_CHUNK)*sizeof(int));
|
||||
assert(taskIdList);
|
||||
int *newlist = realloc(taskIdList, (taskIdListSize+ID_LIST_CHUNK)*sizeof(int));
|
||||
assert(newlist);
|
||||
taskIdList = newlist;
|
||||
taskIdListSize += ID_LIST_CHUNK;
|
||||
noTasks = 0;
|
||||
}
|
||||
|
||||
@@ -25,12 +25,9 @@
|
||||
static const char *msg1 = "1234567890This is a very long message.";
|
||||
static volatile int sendExit = 0;
|
||||
static volatile int recvExit = 0;
|
||||
static epicsEventId finished;
|
||||
static unsigned int mediumStack;
|
||||
|
||||
#define SLEEPY_TESTS 500
|
||||
static int numSent, numReceived;
|
||||
static epicsEventId complete;
|
||||
|
||||
/*
|
||||
* In Numerical Recipes in C: The Art of Scientific Computing (William H.
|
||||
@@ -116,7 +113,6 @@ receiver(void *arg)
|
||||
if (!testOk1(errors == 0))
|
||||
testDiag("Error count was %d", errors);
|
||||
testDiag("%s exiting", myName);
|
||||
epicsEventSignal(finished);
|
||||
}
|
||||
|
||||
extern "C" void
|
||||
@@ -133,15 +129,18 @@ fastReceiver(void *arg)
|
||||
}
|
||||
}
|
||||
recvExit = 0;
|
||||
epicsEventSignal(complete);
|
||||
}
|
||||
|
||||
void sleepySender(double delay)
|
||||
{
|
||||
epicsThreadOpts opts = {epicsThreadPriorityMedium, epicsThreadStackMedium, 1};
|
||||
epicsThreadId rxThread;
|
||||
|
||||
testDiag("sleepySender: sending every %.3f seconds", delay);
|
||||
epicsMessageQueue q(4, 20);
|
||||
epicsThreadCreate("Fast Receiver", epicsThreadPriorityMedium,
|
||||
mediumStack, fastReceiver, &q);
|
||||
rxThread = epicsThreadCreateOpt("Fast Receiver", fastReceiver, &q, &opts);
|
||||
if (!rxThread)
|
||||
testAbort("Task create failed");
|
||||
|
||||
numSent = 0;
|
||||
for (int i = 0 ; i < SLEEPY_TESTS ; i++) {
|
||||
@@ -159,7 +158,7 @@ void sleepySender(double delay)
|
||||
recvExit = 1;
|
||||
while (q.send((void *)msg1, 4) != 0)
|
||||
epicsThreadSleep(0.01);
|
||||
epicsEventMustWait(complete);
|
||||
epicsThreadMustJoin(rxThread);
|
||||
}
|
||||
|
||||
extern "C" void
|
||||
@@ -179,11 +178,14 @@ fastSender(void *arg)
|
||||
}
|
||||
}
|
||||
sendExit = 0;
|
||||
epicsEventSignal(complete);
|
||||
}
|
||||
|
||||
void sleepyReceiver(double delay)
|
||||
{
|
||||
epicsThreadOpts opts = {epicsThreadPriorityMedium,
|
||||
epicsThreadStackMedium, 1};
|
||||
epicsThreadId txThread;
|
||||
|
||||
testDiag("sleepyReceiver: acquiring every %.3f seconds", delay);
|
||||
epicsMessageQueue q(4, 20);
|
||||
|
||||
@@ -192,8 +194,9 @@ void sleepyReceiver(double delay)
|
||||
q.send((void *)msg1, 4);
|
||||
}
|
||||
|
||||
epicsThreadCreate("Fast Sender", epicsThreadPriorityMedium,
|
||||
mediumStack, fastSender, &q);
|
||||
txThread = epicsThreadCreateOpt("Fast Sender", fastSender, &q, &opts);
|
||||
if (!txThread)
|
||||
testAbort("Task create failed");
|
||||
epicsThreadSleep(0.5);
|
||||
|
||||
char cbuf[80];
|
||||
@@ -208,21 +211,15 @@ void sleepyReceiver(double delay)
|
||||
epicsThreadSleep(delay);
|
||||
}
|
||||
|
||||
#ifdef __rtems__
|
||||
testTodoBegin("RTEMS failure expected");
|
||||
#endif
|
||||
testOk(numSent == SLEEPY_TESTS, "Sent %d (should be %d)",
|
||||
numSent, SLEEPY_TESTS);
|
||||
#ifdef __rtems__
|
||||
testTodoEnd();
|
||||
#endif
|
||||
testOk(numReceived == SLEEPY_TESTS, "Received %d (should be %d)",
|
||||
numReceived, SLEEPY_TESTS);
|
||||
|
||||
sendExit = 1;
|
||||
while (q.receive(cbuf, sizeof cbuf) <= 0)
|
||||
epicsThreadSleep(0.01);
|
||||
epicsEventMustWait(complete);
|
||||
epicsThreadMustJoin(txThread);
|
||||
}
|
||||
|
||||
extern "C" void
|
||||
@@ -242,130 +239,136 @@ sender(void *arg)
|
||||
testDiag("%s exiting, sent %d messages", epicsThreadGetNameSelf(), i);
|
||||
}
|
||||
|
||||
#define NUM_SENDERS 4
|
||||
extern "C" void messageQueueTest(void *parm)
|
||||
{
|
||||
epicsThreadId myThreadId = epicsThreadGetIdSelf();
|
||||
epicsThreadId rxThread;
|
||||
epicsThreadId senderId[NUM_SENDERS];
|
||||
epicsThreadOpts opts = {epicsThreadPriorityMedium,
|
||||
epicsThreadStackMedium, 1};
|
||||
|
||||
unsigned int i;
|
||||
char cbuf[80];
|
||||
int len;
|
||||
int pass;
|
||||
int want;
|
||||
|
||||
epicsMessageQueue *q1 = new epicsMessageQueue(4, 20);
|
||||
epicsMessageQueue q1(4, 20);
|
||||
|
||||
testDiag("Simple single-thread tests:");
|
||||
i = 0;
|
||||
testOk1(q1->pending() == 0);
|
||||
while (q1->trySend((void *)msg1, i ) == 0) {
|
||||
i++;
|
||||
testOk(q1->pending() == i, "q1->pending() == %d", i);
|
||||
testOk1(q1.pending() == 0);
|
||||
for (i = 0; i < 4;) {
|
||||
int ret = q1.trySend((void *)msg1, i++);
|
||||
testOk(ret == 0, "trySend succeeded (%d == 0)", ret);
|
||||
testOk(q1.pending() == i, "loop: q1.pending() == %d", i);
|
||||
}
|
||||
testOk1(q1->pending() == 4);
|
||||
testOk1(q1.pending() == 4);
|
||||
|
||||
want = 0;
|
||||
len = q1->receive(cbuf, sizeof cbuf);
|
||||
testOk1(q1->pending() == 3);
|
||||
len = q1.receive(cbuf, sizeof cbuf);
|
||||
testOk1(q1.pending() == 3);
|
||||
if (!testOk1((len == want) && (strncmp(msg1, cbuf, len) == 0)))
|
||||
testDiag("wanted:%d '%.*s' got:%d '%.*s'",
|
||||
want, want, msg1, len, len, cbuf);
|
||||
|
||||
want++;
|
||||
len = q1->receive(cbuf, sizeof cbuf);
|
||||
testOk1(q1->pending() == 2);
|
||||
len = q1.receive(cbuf, sizeof cbuf);
|
||||
testOk1(q1.pending() == 2);
|
||||
if (!testOk1((len == want) && (strncmp(msg1, cbuf, len) == 0)))
|
||||
testDiag("wanted:%d '%.*s' got:%d '%.*s'",
|
||||
want, want, msg1, len, len, cbuf);
|
||||
q1->trySend((void *)msg1, i++);
|
||||
q1.trySend((void *)msg1, i++);
|
||||
|
||||
want++;
|
||||
len = q1->receive(cbuf, sizeof cbuf);
|
||||
testOk1(q1->pending() == 2);
|
||||
len = q1.receive(cbuf, sizeof cbuf);
|
||||
testOk1(q1.pending() == 2);
|
||||
if (!testOk1((len == want) && (strncmp(msg1, cbuf, len) == 0)))
|
||||
testDiag("wanted:%d '%.*s' got:%d '%.*s'",
|
||||
want, want, msg1, len, len, cbuf);
|
||||
q1->trySend((void *)msg1, i++);
|
||||
testOk1(q1->pending() == 3);
|
||||
q1.trySend((void *)msg1, i++);
|
||||
testOk1(q1.pending() == 3);
|
||||
|
||||
i = 3;
|
||||
while ((len = q1->receive(cbuf, sizeof cbuf, 1.0)) >= 0) {
|
||||
while ((len = q1.receive(cbuf, sizeof cbuf, 1.0)) >= 0) {
|
||||
--i;
|
||||
testOk(q1->pending() == i, "q1->pending() == %d", i);
|
||||
testOk(q1.pending() == i, "loop: q1.pending() == %d", i);
|
||||
want++;
|
||||
if (!testOk1((len == want) & (strncmp(msg1, cbuf, len) == 0)))
|
||||
testDiag("wanted:%d '%.*s' got:%d '%.*s'",
|
||||
want, want, msg1, len, len, cbuf);
|
||||
}
|
||||
testOk1(q1->pending() == 0);
|
||||
testOk1(q1.pending() == 0);
|
||||
|
||||
testDiag("Test sender timeout:");
|
||||
i = 0;
|
||||
testOk1(q1->pending() == 0);
|
||||
while (q1->send((void *)msg1, i, 1.0 ) == 0) {
|
||||
testOk1(q1.pending() == 0);
|
||||
while (q1.send((void *)msg1, i, 1.0 ) == 0) {
|
||||
i++;
|
||||
testOk(q1->pending() == i, "q1->pending() == %d", i);
|
||||
testOk(q1.pending() == i, "loop: q1.pending() == %d", i);
|
||||
}
|
||||
testOk1(q1->pending() == 4);
|
||||
testOk1(q1.pending() == 4);
|
||||
|
||||
want = 0;
|
||||
len = q1->receive(cbuf, sizeof cbuf);
|
||||
testOk1(q1->pending() == 3);
|
||||
len = q1.receive(cbuf, sizeof cbuf);
|
||||
testOk1(q1.pending() == 3);
|
||||
if (!testOk1((len == want) && (strncmp(msg1, cbuf, len) == 0)))
|
||||
testDiag("wanted:%d '%.*s' got:%d '%.*s'",
|
||||
want, want, msg1, len, len, cbuf);
|
||||
|
||||
want++;
|
||||
len = q1->receive(cbuf, sizeof cbuf);
|
||||
testOk1(q1->pending() == 2);
|
||||
len = q1.receive(cbuf, sizeof cbuf);
|
||||
testOk1(q1.pending() == 2);
|
||||
if (!testOk1((len == want) && (strncmp(msg1, cbuf, len) == 0)))
|
||||
testDiag("wanted:%d '%.*s' got:%d '%.*s'",
|
||||
want, want, msg1, len, len, cbuf);
|
||||
q1->send((void *)msg1, i++, 1.0);
|
||||
q1.send((void *)msg1, i++, 1.0);
|
||||
|
||||
want++;
|
||||
len = q1->receive(cbuf, sizeof cbuf);
|
||||
testOk1(q1->pending() == 2);
|
||||
len = q1.receive(cbuf, sizeof cbuf);
|
||||
testOk1(q1.pending() == 2);
|
||||
if (!testOk1((len == want) && (strncmp(msg1, cbuf, len) == 0)))
|
||||
testDiag("wanted:%d '%.*s' got:%d '%.*s'",
|
||||
want, want, msg1, len, len, cbuf);
|
||||
q1->send((void *)msg1, i++, 1.0);
|
||||
testOk1(q1->pending() == 3);
|
||||
q1.send((void *)msg1, i++, 1.0);
|
||||
testOk1(q1.pending() == 3);
|
||||
|
||||
i = 3;
|
||||
while ((len = q1->receive(cbuf, sizeof cbuf, 1.0)) >= 0) {
|
||||
while ((len = q1.receive(cbuf, sizeof cbuf, 1.0)) >= 0) {
|
||||
--i;
|
||||
testOk(q1->pending() == i, "q1->pending() == %d", i);
|
||||
testOk(q1.pending() == i, "loop: q1.pending() == %d", i);
|
||||
want++;
|
||||
if (!testOk1((len == want) && (strncmp(msg1, cbuf, len) == 0)))
|
||||
testDiag("wanted:%d '%.*s' got:%d '%.*s'",
|
||||
want, want, msg1, len, len, cbuf);
|
||||
}
|
||||
testOk1(q1->pending() == 0);
|
||||
testOk1(q1.pending() == 0);
|
||||
|
||||
testDiag("Test receiver with timeout:");
|
||||
for (i = 0 ; i < 4 ; i++)
|
||||
testOk1 (q1->send((void *)msg1, i, 1.0) == 0);
|
||||
testOk1(q1->pending() == 4);
|
||||
testOk1 (q1.send((void *)msg1, i, 1.0) == 0);
|
||||
testOk1(q1.pending() == 4);
|
||||
for (i = 0 ; i < 4 ; i++)
|
||||
testOk(q1->receive((void *)cbuf, sizeof cbuf, 1.0) == (int)i,
|
||||
"q1->receive(...) == %d", i);
|
||||
testOk1(q1->pending() == 0);
|
||||
testOk1(q1->receive((void *)cbuf, sizeof cbuf, 1.0) < 0);
|
||||
testOk1(q1->pending() == 0);
|
||||
testOk(q1.receive((void *)cbuf, sizeof cbuf, 1.0) == (int)i,
|
||||
"q1.receive(...) == %d", i);
|
||||
testOk1(q1.pending() == 0);
|
||||
testOk1(q1.receive((void *)cbuf, sizeof cbuf, 1.0) < 0);
|
||||
testOk1(q1.pending() == 0);
|
||||
|
||||
testDiag("Single receiver with invalid size, single sender tests:");
|
||||
epicsThreadCreate("Bad Receiver", epicsThreadPriorityMedium,
|
||||
mediumStack, badReceiver, q1);
|
||||
rxThread = epicsThreadCreateOpt("Bad Receiver", badReceiver, &q1, &opts);
|
||||
if (!rxThread)
|
||||
testAbort("epicsThreadCreate failed");
|
||||
epicsThreadSleep(1.0);
|
||||
testOk(q1->send((void *)msg1, 10) == 0, "Send with waiting receiver");
|
||||
epicsThreadSleep(2.0);
|
||||
testOk(q1->send((void *)msg1, 10) == 0, "Send with no receiver");
|
||||
testOk(q1.send((void *)msg1, 10) == 0, "Send with waiting receiver");
|
||||
epicsThreadSleep(2.0);
|
||||
testOk(q1.send((void *)msg1, 10) == 0, "Send with no receiver");
|
||||
epicsThreadMustJoin(rxThread);
|
||||
|
||||
testDiag("6 Single receiver single sender 'Sleepy timeout' tests,");
|
||||
testDiag(" these should take about %.2f seconds each:",
|
||||
SLEEPY_TESTS * 0.010);
|
||||
|
||||
complete = epicsEventMustCreate(epicsEventEmpty);
|
||||
sleepySender(0.009);
|
||||
sleepySender(0.010);
|
||||
sleepySender(0.011);
|
||||
@@ -375,11 +378,12 @@ extern "C" void messageQueueTest(void *parm)
|
||||
|
||||
testDiag("Single receiver, single sender tests:");
|
||||
epicsThreadSetPriority(myThreadId, epicsThreadPriorityHigh);
|
||||
epicsThreadCreate("Receiver one", epicsThreadPriorityMedium,
|
||||
mediumStack, receiver, q1);
|
||||
rxThread = epicsThreadCreateOpt("Receiver one", receiver, &q1, &opts);
|
||||
if (!rxThread)
|
||||
testAbort("epicsThreadCreate failed");
|
||||
for (pass = 1 ; pass <= 3 ; pass++) {
|
||||
for (i = 0 ; i < 10 ; i++) {
|
||||
if (q1->trySend((void *)msg1, i) < 0)
|
||||
if (q1.trySend((void *)msg1, i) < 0)
|
||||
break;
|
||||
if (pass >= 3)
|
||||
epicsThreadSleep(0.5);
|
||||
@@ -408,18 +412,20 @@ extern "C" void messageQueueTest(void *parm)
|
||||
*/
|
||||
testDiag("Single receiver, multiple sender tests:");
|
||||
testDiag("This test lasts 30 seconds...");
|
||||
testOk(!!epicsThreadCreate("Sender 1", epicsThreadPriorityLow,
|
||||
mediumStack, sender, q1),
|
||||
"Created Sender 1");
|
||||
testOk(!!epicsThreadCreate("Sender 2", epicsThreadPriorityMedium,
|
||||
mediumStack, sender, q1),
|
||||
"Created Sender 2");
|
||||
testOk(!!epicsThreadCreate("Sender 3", epicsThreadPriorityHigh,
|
||||
mediumStack, sender, q1),
|
||||
"Created Sender 3");
|
||||
testOk(!!epicsThreadCreate("Sender 4", epicsThreadPriorityHigh,
|
||||
mediumStack, sender, q1),
|
||||
"Created Sender 4");
|
||||
for (i=0; i<NUM_SENDERS; i++) {
|
||||
char name[16];
|
||||
const int pri[NUM_SENDERS] = {
|
||||
epicsThreadPriorityLow,
|
||||
epicsThreadPriorityMedium,
|
||||
epicsThreadPriorityHigh,
|
||||
epicsThreadPriorityHigh
|
||||
};
|
||||
sprintf(name, "Sender %d", i+1);
|
||||
opts.priority = pri[i];
|
||||
senderId[i] = epicsThreadCreateOpt(name, sender, &q1, &opts);
|
||||
if (!senderId[i])
|
||||
testAbort("epicsThreadCreate failed");
|
||||
}
|
||||
|
||||
for (i = 0; i < 6; i++) {
|
||||
testDiag("... %2d", 6 - i);
|
||||
@@ -428,23 +434,30 @@ extern "C" void messageQueueTest(void *parm)
|
||||
|
||||
sendExit = 1;
|
||||
epicsThreadSleep(1.0);
|
||||
for (i=0; i<NUM_SENDERS; i++) {
|
||||
epicsThreadMustJoin(senderId[i]);
|
||||
}
|
||||
recvExit = 1;
|
||||
testDiag("Scheduler exiting");
|
||||
epicsThreadMustJoin(rxThread);
|
||||
}
|
||||
|
||||
MAIN(epicsMessageQueueTest)
|
||||
{
|
||||
testPlan(74);
|
||||
epicsThreadOpts opts = {
|
||||
epicsThreadPriorityMedium,
|
||||
epicsThreadStackMedium,
|
||||
1
|
||||
};
|
||||
epicsThreadId testThread;
|
||||
|
||||
finished = epicsEventMustCreate(epicsEventEmpty);
|
||||
mediumStack = epicsThreadGetStackSize(epicsThreadStackMedium);
|
||||
testPlan(70 + NUM_SENDERS);
|
||||
|
||||
epicsThreadCreate("messageQueueTest", epicsThreadPriorityMedium,
|
||||
mediumStack, messageQueueTest, NULL);
|
||||
testThread = epicsThreadCreateOpt("messageQueueTest",
|
||||
messageQueueTest, NULL, &opts);
|
||||
if (!testThread)
|
||||
testAbort("epicsThreadCreate failed");
|
||||
|
||||
epicsEventMustWait(finished);
|
||||
testDiag("Main thread signalled");
|
||||
epicsThreadSleep(1.0);
|
||||
epicsThreadMustJoin(testThread);
|
||||
|
||||
return testDone();
|
||||
}
|
||||
|
||||
@@ -7,12 +7,18 @@
|
||||
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "epicsAssert.h"
|
||||
#include "dbDefs.h"
|
||||
#include "osiSock.h"
|
||||
#include "epicsTime.h"
|
||||
#include "epicsThread.h"
|
||||
#include "epicsUnitTest.h"
|
||||
#include "testMain.h"
|
||||
|
||||
/* This could easily be generalized to test more options */
|
||||
static
|
||||
void udpBroadcast(SOCKET s, int put)
|
||||
{
|
||||
int status;
|
||||
@@ -27,6 +33,7 @@ void udpBroadcast(SOCKET s, int put)
|
||||
"getsockopt BROADCAST => %d", flag);
|
||||
}
|
||||
|
||||
static
|
||||
void multiCastLoop(SOCKET s, int put)
|
||||
{
|
||||
int status;
|
||||
@@ -42,6 +49,7 @@ void multiCastLoop(SOCKET s, int put)
|
||||
"getsockopt MULTICAST_LOOP => %d", (int) flag);
|
||||
}
|
||||
|
||||
static
|
||||
void multiCastTTL(SOCKET s, int put)
|
||||
{
|
||||
int status;
|
||||
@@ -57,10 +65,13 @@ void multiCastTTL(SOCKET s, int put)
|
||||
"getsockopt IP_MULTICAST_TTL => %d", (int) flag);
|
||||
}
|
||||
|
||||
static
|
||||
void udpSockTest(void)
|
||||
{
|
||||
SOCKET s;
|
||||
|
||||
testDiag("udpSockTest()");
|
||||
|
||||
s = epicsSocketCreate(AF_INET, SOCK_DGRAM, 0);
|
||||
testOk(s != INVALID_SOCKET, "epicsSocketCreate INET, DGRAM, 0");
|
||||
|
||||
@@ -107,11 +118,43 @@ int doBind(int expect, SOCKET S, unsigned* port)
|
||||
}
|
||||
}
|
||||
|
||||
void udpSockFanoutTest(void)
|
||||
static
|
||||
void tcpSockReuseBindTest(int reuse)
|
||||
{
|
||||
SOCKET A, B;
|
||||
unsigned port=0; /* choose random port */
|
||||
|
||||
testDiag("tcpSockReuseBindTest(%d)", reuse);
|
||||
|
||||
A = epicsSocketCreate(AF_INET, SOCK_STREAM, 0);
|
||||
B = epicsSocketCreate(AF_INET, SOCK_STREAM, 0);
|
||||
|
||||
if(A==INVALID_SOCKET || B==INVALID_SOCKET)
|
||||
testAbort("Insufficient sockets");
|
||||
|
||||
if(reuse) {
|
||||
testDiag("epicsSocketEnableAddressReuseDuringTimeWaitState");
|
||||
epicsSocketEnableAddressReuseDuringTimeWaitState(A);
|
||||
epicsSocketEnableAddressReuseDuringTimeWaitState(B);
|
||||
}
|
||||
|
||||
doBind(0, A, &port);
|
||||
if(listen(A, 4))
|
||||
testFail("listen(A) -> %d", (int)SOCKERRNO);
|
||||
doBind(1, B, &port);
|
||||
|
||||
epicsSocketDestroy(A);
|
||||
epicsSocketDestroy(B);
|
||||
}
|
||||
|
||||
static
|
||||
void udpSockFanoutBindTest(void)
|
||||
{
|
||||
SOCKET A, B, C;
|
||||
unsigned port=0; /* choose random port */
|
||||
|
||||
testDiag("udpSockFanoutBindTest()");
|
||||
|
||||
A = epicsSocketCreate(AF_INET, SOCK_DGRAM, 0);
|
||||
B = epicsSocketCreate(AF_INET, SOCK_DGRAM, 0);
|
||||
C = epicsSocketCreate(AF_INET, SOCK_DGRAM, 0);
|
||||
@@ -138,16 +181,233 @@ void udpSockFanoutTest(void)
|
||||
epicsSocketDestroy(C);
|
||||
}
|
||||
|
||||
struct CASearch {
|
||||
epicsUInt16 cmd, size, dtype, dcnt;
|
||||
epicsUInt32 p1, p2;
|
||||
char body[16];
|
||||
};
|
||||
|
||||
STATIC_ASSERT(sizeof(struct CASearch)==32);
|
||||
|
||||
union CASearchU {
|
||||
struct CASearch msg;
|
||||
char bytes[sizeof(struct CASearch)];
|
||||
};
|
||||
|
||||
static
|
||||
unsigned nsuccess;
|
||||
|
||||
static
|
||||
const unsigned nrepeat = 6u;
|
||||
|
||||
struct TInfo {
|
||||
SOCKET sock;
|
||||
unsigned id;
|
||||
epicsUInt32 key;
|
||||
epicsUInt8 rxmask;
|
||||
epicsUInt8 dupmask;
|
||||
};
|
||||
|
||||
static
|
||||
void udpSockFanoutTestRx(void* raw)
|
||||
{
|
||||
struct TInfo *info = raw;
|
||||
epicsTimeStamp start, now;
|
||||
#ifdef _WIN32
|
||||
/* ms */
|
||||
DWORD timeout = 10000;
|
||||
#else
|
||||
struct timeval timeout;
|
||||
memset(&timeout, 0, sizeof(struct timeval));
|
||||
timeout.tv_sec = 10;
|
||||
timeout.tv_usec = 0;
|
||||
#endif
|
||||
unsigned nremain = nrepeat;
|
||||
|
||||
(void)epicsTimeGetCurrent(&start);
|
||||
now = start;
|
||||
testDiag("RX%u start", info->id);
|
||||
|
||||
if(setsockopt(info->sock, SOL_SOCKET, SO_RCVTIMEO, (void*)&timeout, sizeof(timeout))) {
|
||||
testFail("Unable to set socket timeout");
|
||||
return;
|
||||
}
|
||||
|
||||
while(epicsTimeDiffInSeconds(&now, &start)<=5.0) {
|
||||
union CASearchU buf;
|
||||
osiSockAddr src;
|
||||
osiSocklen_t srclen = sizeof(src);
|
||||
|
||||
int n = recvfrom(info->sock, buf.bytes, sizeof(buf.bytes), 0, &src.sa, &srclen);
|
||||
buf.bytes[sizeof(buf.bytes)-1] = '\0';
|
||||
|
||||
if(n<0) {
|
||||
testDiag("recvfrom error (%d)", (int)SOCKERRNO);
|
||||
break;
|
||||
} else if((n==sizeof(buf.bytes)) && buf.msg.cmd==htons(6) && buf.msg.size==htons(16)
|
||||
&&buf.msg.dtype==htons(5) && buf.msg.dcnt==0 && strcmp(buf.msg.body, "totallyinvalid")==0)
|
||||
{
|
||||
unsigned ord = ntohl(buf.msg.p1)-info->key;
|
||||
testDiag("RX%u success %u", info->id, ord);
|
||||
if(ord<8) {
|
||||
const epicsUInt8 mask = 1u<<ord;
|
||||
if(info->rxmask&mask)
|
||||
info->dupmask|=mask;
|
||||
info->rxmask|=mask;
|
||||
}
|
||||
if(0==--nremain)
|
||||
break;
|
||||
} else {
|
||||
testDiag("RX ignore");
|
||||
}
|
||||
}
|
||||
testDiag("RX%u end", info->id);
|
||||
}
|
||||
|
||||
static
|
||||
void udpSockFanoutTestIface(const osiSockAddr* addr)
|
||||
{
|
||||
SOCKET sender;
|
||||
struct TInfo rx1, rx2;
|
||||
epicsThreadId trx1, trx2;
|
||||
epicsThreadOpts topts = EPICS_THREAD_OPTS_INIT;
|
||||
int opt = 1;
|
||||
unsigned i;
|
||||
osiSockAddr any;
|
||||
epicsUInt32 key = 0xdeadbeef ^ ntohl(addr->ia.sin_addr.s_addr);
|
||||
union CASearchU buf;
|
||||
|
||||
topts.joinable = 1;
|
||||
|
||||
/* we bind to any for lack of a portable way to find the
|
||||
* interface address from the interface broadcast address
|
||||
*/
|
||||
memset(&any, 0, sizeof(any));
|
||||
any.ia.sin_family = AF_INET;
|
||||
any.ia.sin_addr.s_addr = htonl(INADDR_ANY);
|
||||
any.ia.sin_port = addr->ia.sin_port;
|
||||
|
||||
buf.msg.cmd = htons(6);
|
||||
buf.msg.size = htons(16);
|
||||
buf.msg.dtype = htons(5);
|
||||
buf.msg.dcnt = htons(0); /* version 0, which newer servers should ignore */
|
||||
/* .p1 and .p2 set below */
|
||||
memcpy(buf.msg.body, "tota" "llyi" "nval" "id\0\0", 16);
|
||||
|
||||
sender = epicsSocketCreate(AF_INET, SOCK_DGRAM, 0);
|
||||
rx1.sock = epicsSocketCreate(AF_INET, SOCK_DGRAM, 0);
|
||||
rx2.sock = epicsSocketCreate(AF_INET, SOCK_DGRAM, 0);
|
||||
if((sender==INVALID_SOCKET) || (rx1.sock==INVALID_SOCKET) || (rx2.sock==INVALID_SOCKET))
|
||||
testAbort("Unable to allocate test socket(s)");
|
||||
|
||||
rx1.id = 1;
|
||||
rx2.id = 2;
|
||||
rx1.key = rx2.key = key;
|
||||
rx1.rxmask = rx2.rxmask = 0u;
|
||||
rx1.dupmask = rx2.dupmask = 0u;
|
||||
|
||||
if(setsockopt(sender, SOL_SOCKET, SO_BROADCAST, (void*)&opt, sizeof(opt))!=0) {
|
||||
testFail("setsockopt SOL_SOCKET, SO_BROADCAST error -> %d", (int)SOCKERRNO);
|
||||
}
|
||||
|
||||
epicsSocketEnableAddressUseForDatagramFanout(rx1.sock);
|
||||
epicsSocketEnableAddressUseForDatagramFanout(rx2.sock);
|
||||
|
||||
if(bind(rx1.sock, &any.sa, sizeof(any)))
|
||||
testFail("Can't bind test socket rx1 %d", (int)SOCKERRNO);
|
||||
if(bind(rx2.sock, &any.sa, sizeof(any)))
|
||||
testFail("Can't bind test socket rx2 %d", (int)SOCKERRNO);
|
||||
|
||||
trx1 = epicsThreadCreateOpt("rx1", &udpSockFanoutTestRx, &rx1, &topts);
|
||||
trx2 = epicsThreadCreateOpt("rx2", &udpSockFanoutTestRx, &rx2, &topts);
|
||||
|
||||
for(i=0; i<nrepeat; i++) {
|
||||
int ret;
|
||||
|
||||
/* don't spam */
|
||||
epicsThreadSleep(0.5);
|
||||
|
||||
buf.msg.p1 = buf.msg.p2 = htonl(key + i);
|
||||
ret = sendto(sender, buf.bytes, sizeof(buf.bytes), 0, &addr->sa, sizeof(*addr));
|
||||
if(ret!=(int)sizeof(buf.bytes))
|
||||
testDiag("sendto() error %d (%d)", ret, (int)SOCKERRNO);
|
||||
}
|
||||
|
||||
epicsThreadMustJoin(trx1);
|
||||
epicsThreadMustJoin(trx2);
|
||||
|
||||
testDiag("Result: RX1 %x:%x RX2 %x:%x",
|
||||
rx1.rxmask, rx1.dupmask, rx2.rxmask, rx2.dupmask);
|
||||
/* success if any one packet was seen by both sockets */
|
||||
if(rx1.rxmask & rx2.rxmask)
|
||||
nsuccess++;
|
||||
|
||||
epicsSocketDestroy(sender);
|
||||
epicsSocketDestroy(rx1.sock);
|
||||
epicsSocketDestroy(rx2.sock);
|
||||
}
|
||||
|
||||
/* This test violates the principle of unittest isolation by broadcasting
|
||||
* on the well known CA search port on all interfaces. There is no
|
||||
* portable way to avoid this. (eg. 127.255.255.255 is Linux specific)
|
||||
*/
|
||||
static
|
||||
void udpSockFanoutTest()
|
||||
{
|
||||
ELLLIST ifaces = ELLLIST_INIT;
|
||||
ELLNODE *cur;
|
||||
SOCKET dummy;
|
||||
osiSockAddr match;
|
||||
int foundNotLo = 0;
|
||||
|
||||
testDiag("udpSockFanoutTest()");
|
||||
|
||||
memset(&match, 0, sizeof(match));
|
||||
match.ia.sin_family = AF_INET;
|
||||
match.ia.sin_addr.s_addr = htonl(INADDR_ANY);
|
||||
|
||||
if((dummy = epicsSocketCreate(AF_INET, SOCK_DGRAM, 0))==INVALID_SOCKET)
|
||||
testAbort("Unable to allocate discovery socket");
|
||||
|
||||
osiSockDiscoverBroadcastAddresses(&ifaces, dummy, &match);
|
||||
|
||||
for(cur = ellFirst(&ifaces); cur; cur = ellNext(cur)) {
|
||||
char name[64];
|
||||
osiSockAddrNode* node = CONTAINER(cur, osiSockAddrNode, node);
|
||||
|
||||
node->addr.ia.sin_port = htons(5064);
|
||||
(void)sockAddrToDottedIP(&node->addr.sa, name, sizeof(name));
|
||||
|
||||
testDiag("Interface %s", name);
|
||||
if(node->addr.ia.sin_addr.s_addr!=htonl(INADDR_LOOPBACK)) {
|
||||
testDiag("Not LO");
|
||||
foundNotLo = 1;
|
||||
}
|
||||
|
||||
udpSockFanoutTestIface(&node->addr);
|
||||
}
|
||||
|
||||
ellFree(&ifaces);
|
||||
|
||||
testOk(foundNotLo, "Found non-loopback interface");
|
||||
testOk(nsuccess>0, "Successes %u", nsuccess);
|
||||
|
||||
epicsSocketDestroy(dummy);
|
||||
}
|
||||
|
||||
MAIN(osiSockTest)
|
||||
{
|
||||
int status;
|
||||
testPlan(18);
|
||||
testPlan(24);
|
||||
|
||||
status = osiSockAttach();
|
||||
testOk(status, "osiSockAttach");
|
||||
|
||||
udpSockTest();
|
||||
udpSockFanoutBindTest();
|
||||
udpSockFanoutTest();
|
||||
tcpSockReuseBindTest(0);
|
||||
tcpSockReuseBindTest(1);
|
||||
|
||||
osiSockRelease();
|
||||
return testDone();
|
||||
|
||||
Submodule modules/normativeTypes updated: 1250a3c236...7a2d264f2c
Submodule modules/pvAccess updated: 1641bd18b4...8e183c23e1
Submodule modules/pvData updated: d9b3f98e35...b2b42d5f8c
Submodule modules/pvDatabase updated: 3f5bfd067f...cb5d9f976a
Submodule modules/pva2pva updated: cb13435d15...b1d28cca39
Submodule modules/pvaClient updated: bc9ac8422c...7722fdf353
@@ -48,38 +48,38 @@ sub ReplaceFilename { # (filename)
|
||||
my($file) = $_[0];
|
||||
$file =~ s|.*/CVS/?.*||; # Ignore CVS files and Replace.pl scripts
|
||||
$file =~ s|.*/$apptypename/Replace\.pl$||;
|
||||
|
||||
|
||||
if($opt_i) {
|
||||
# Handle name@arch stuff, copy only the closest matching file
|
||||
# NB: Won't work with directories, don't use '@' in a directory name!
|
||||
my($base,$filearch) = split /@/, $file;
|
||||
if ($base ne $file) { # This file is arch-specific
|
||||
my($os,$cpu,$toolset) = split /-/, $arch, 3;
|
||||
if (-r "$base\@$arch") { # A version exists for this arch
|
||||
$base = '' unless ($filearch eq $arch && -s $file);
|
||||
} elsif (-r "$base\@$os") { # A version exists for this os
|
||||
$base = '' unless ($filearch eq $os && -s $file);
|
||||
} elsif ( $ENV{EPICS_HOST_ARCH} !~ "$os-$cpu" &&
|
||||
# Handle name@arch stuff, copy only the closest matching file
|
||||
# NB: Won't work with directories, don't use '@' in a directory name!
|
||||
my($base,$filearch) = split /@/, $file;
|
||||
if ($base ne $file) { # This file is arch-specific
|
||||
my($os,$cpu,$toolset) = split /-/, $arch, 3;
|
||||
if (-r "$base\@$arch") { # A version exists for this arch
|
||||
$base = '' unless ($filearch eq $arch && -s $file);
|
||||
} elsif (-r "$base\@$os") { # A version exists for this os
|
||||
$base = '' unless ($filearch eq $os && -s $file);
|
||||
} elsif ( $ENV{EPICS_HOST_ARCH} !~ "$os-$cpu" &&
|
||||
-r "$base\@Cross" ) { # Cross version exists
|
||||
$base = '' unless ($filearch eq "Cross" && -s $file);
|
||||
} elsif (-r "$base\@Common") { # Default version exists
|
||||
$base = '' unless ($filearch eq "Common" && -s $file);
|
||||
} else { # No default version
|
||||
$base = '';
|
||||
}
|
||||
$file = $base; # Strip the @... part from the target name
|
||||
}
|
||||
$file =~ s|/$apptypename|/iocBoot|; # templateBoot => iocBoot
|
||||
$base = '' unless ($filearch eq "Cross" && -s $file);
|
||||
} elsif (-r "$base\@Common") { # Default version exists
|
||||
$base = '' unless ($filearch eq "Common" && -s $file);
|
||||
} else { # No default version
|
||||
$base = '';
|
||||
}
|
||||
$file = $base; # Strip the @... part from the target name
|
||||
}
|
||||
$file =~ s|/$apptypename|/iocBoot|; # templateBoot => iocBoot
|
||||
}
|
||||
if ($ioc) {
|
||||
$file =~ s|/iocBoot/ioc|/iocBoot/$ioc|; # name the ioc subdirectory
|
||||
$file =~ s|_IOC_|$ioc|;
|
||||
$file =~ s|/iocBoot/ioc|/iocBoot/$ioc|; # name the ioc subdirectory
|
||||
$file =~ s|_IOC_|$ioc|;
|
||||
} else {
|
||||
$file =~ s|.*/iocBoot/ioc/?.*||; # Not doing IOCs here
|
||||
$file =~ s|.*/iocBoot/ioc/?.*||; # Not doing IOCs here
|
||||
}
|
||||
if ($app) {
|
||||
$file =~ s|/$apptypename|/$appdir|; # templateApp => namedApp
|
||||
$file =~ s|/$appdir/configure|/configure/$apptype|;
|
||||
$file =~ s|/$apptypename|/$appdir|; # templateApp => namedApp
|
||||
$file =~ s|/$appdir/configure|/configure/$apptype|;
|
||||
}
|
||||
$file =~ s|_APPNAME_|$appname|;
|
||||
$file =~ s|_APPTYPE_|$apptype|;
|
||||
@@ -129,14 +129,14 @@ if ($opt_i) {
|
||||
|
||||
$appname=$appnameIn if $appnameIn;
|
||||
foreach $ioc ( @names ) {
|
||||
($appname = $ioc) =~ s/App$// if !$appnameIn;
|
||||
($csafeappname = $appname) =~ s/$bad_ident_chars/_/og;
|
||||
$ioc = "ioc" . $ioc unless ($ioc =~ m/ioc/);
|
||||
if (-d "iocBoot/$ioc") {
|
||||
print "iocBoot/$ioc exists, not modified.\n";
|
||||
next;
|
||||
}
|
||||
find({wanted => \&FCopyTree, follow => 1}, "$top/$apptypename/ioc");
|
||||
($appname = $ioc) =~ s/App$// if !$appnameIn;
|
||||
($csafeappname = $appname) =~ s/$bad_ident_chars/_/og;
|
||||
$ioc = "ioc" . $ioc unless ($ioc =~ m/ioc/);
|
||||
if (-d "iocBoot/$ioc") {
|
||||
print "iocBoot/$ioc exists, not modified.\n";
|
||||
next;
|
||||
}
|
||||
find({wanted => \&FCopyTree, follow => 1}, "$top/$apptypename/ioc");
|
||||
}
|
||||
exit 0; # finished here for -i (no xxxApps)
|
||||
}
|
||||
@@ -149,10 +149,10 @@ foreach $app ( @names ) {
|
||||
($csafeappname = $appname) =~ s/$bad_ident_chars/_/og;
|
||||
$appdir = $appname . "App";
|
||||
if (-d "$appdir") {
|
||||
print "$appname exists, not modified.\n";
|
||||
next;
|
||||
print "$appname exists, not modified.\n";
|
||||
next;
|
||||
}
|
||||
print "Creating $appname from template type $apptypename\n" if $opt_d;
|
||||
print "Creating $appname from template type $apptypename\n" if $opt_d;
|
||||
find({wanted => \&FCopyTree, follow => 1}, "$top/$apptypename/");
|
||||
}
|
||||
|
||||
@@ -163,22 +163,22 @@ exit 0; # END OF SCRIPT
|
||||
#
|
||||
sub get_commandline_opts { #no args
|
||||
getopts("a:b:dhilp:T:t:u:") or Cleanup(1);
|
||||
|
||||
|
||||
# Options help
|
||||
Cleanup(0) if $opt_h;
|
||||
|
||||
# Locate epics_base
|
||||
my ($command) = UnixPath($0);
|
||||
if ($opt_b) { # first choice is -b base
|
||||
$epics_base = UnixPath($opt_b);
|
||||
$epics_base = UnixPath($opt_b);
|
||||
} elsif ($release{"EPICS_BASE"}) { # second choice is configure/RELEASE
|
||||
$epics_base = UnixPath($release{"EPICS_BASE"});
|
||||
$epics_base =~s|^\$\(TOP\)/||;
|
||||
$epics_base = UnixPath($release{"EPICS_BASE"});
|
||||
$epics_base =~s|^\$\(TOP\)/||;
|
||||
} elsif ($ENV{EPICS_MBA_BASE}) { # third choice is env var EPICS_MBA_BASE
|
||||
$epics_base = UnixPath($ENV{EPICS_MBA_BASE});
|
||||
} elsif ($command =~ m|/bin/|) { # assume script was run with full path to base
|
||||
$epics_base = $command;
|
||||
$epics_base =~ s|^(.*)/bin/.*makeBaseApp.*|$1|;
|
||||
$epics_base = $command;
|
||||
$epics_base =~ s|^(.*)/bin/.*makeBaseApp.*|$1|;
|
||||
}
|
||||
$epics_base and -d "$epics_base" or Cleanup(1, "Can't find EPICS base");
|
||||
$app_epics_base = LocalPath($epics_base);
|
||||
@@ -186,11 +186,11 @@ sub get_commandline_opts { #no args
|
||||
|
||||
# Locate template top directory
|
||||
if ($opt_T) { # first choice is -T templ-top
|
||||
$top = UnixPath($opt_T);
|
||||
$top = UnixPath($opt_T);
|
||||
} elsif ($release{"TEMPLATE_TOP"}) { # second choice is configure/RELEASE
|
||||
$top = UnixPath($release{"TEMPLATE_TOP"});
|
||||
$top =~s|^\$\(EPICS_BASE\)|$epics_base|;
|
||||
$top =~s|^\$\(TOP\)/||;
|
||||
$top = UnixPath($release{"TEMPLATE_TOP"});
|
||||
$top =~s|^\$\(EPICS_BASE\)|$epics_base|;
|
||||
$top =~s|^\$\(TOP\)/||;
|
||||
}
|
||||
$top = $ENV{EPICS_MBA_TEMPLATE_TOP} unless $top && -d $top; # third choice is env var
|
||||
$top = $epics_base . "/templates/makeBaseApp/top" unless $top && -d $top; # final
|
||||
@@ -201,101 +201,101 @@ sub get_commandline_opts { #no args
|
||||
|
||||
# Print application type list?
|
||||
if ($opt_l) {
|
||||
ListAppTypes();
|
||||
exit 0; # finished for -l command
|
||||
ListAppTypes();
|
||||
exit 0; # finished for -l command
|
||||
}
|
||||
|
||||
|
||||
if (!@ARGV){
|
||||
if ($opt_t) {
|
||||
if ($opt_i) {
|
||||
my @iocs = map {s/iocBoot\///; $_} glob 'iocBoot/ioc*';
|
||||
if (@iocs) {
|
||||
print "The following IOCs already exist here:\n",
|
||||
map {" $_\n"} @iocs;
|
||||
}
|
||||
print "Name the IOC(s) to be created.\n",
|
||||
"Names given will have \"ioc\" prepended to them.\n",
|
||||
"IOC names? ";
|
||||
} else {
|
||||
print "Name the application(s) to be created.\n",
|
||||
"Names given will have \"App\" appended to them.\n",
|
||||
"Application names? ";
|
||||
}
|
||||
$namelist = <STDIN>;
|
||||
chomp($namelist);
|
||||
@names = split /[\s,]/, $namelist;
|
||||
} else {
|
||||
Cleanup(1);
|
||||
}
|
||||
if ($opt_t) {
|
||||
if ($opt_i) {
|
||||
my @iocs = map {s/iocBoot\///; $_} glob 'iocBoot/ioc*';
|
||||
if (@iocs) {
|
||||
print "The following IOCs already exist here:\n",
|
||||
map {" $_\n"} @iocs;
|
||||
}
|
||||
print "Name the IOC(s) to be created.\n",
|
||||
"Names given will have \"ioc\" prepended to them.\n",
|
||||
"IOC names? ";
|
||||
} else {
|
||||
print "Name the application(s) to be created.\n",
|
||||
"Names given will have \"App\" appended to them.\n",
|
||||
"Application names? ";
|
||||
}
|
||||
$namelist = <STDIN>;
|
||||
chomp($namelist);
|
||||
@names = split /[\s,]/, $namelist;
|
||||
} else {
|
||||
Cleanup(1);
|
||||
}
|
||||
} else {
|
||||
@names = @ARGV;
|
||||
}
|
||||
@names = @ARGV;
|
||||
}
|
||||
|
||||
# ioc architecture and application name
|
||||
if ($opt_i && @names) {
|
||||
|
||||
# ioc architecture
|
||||
opendir BINDIR, "$epics_base/bin" or die "Can't open $epics_base/bin: $!";
|
||||
my @archs = grep !/^\./, readdir BINDIR; # exclude .files
|
||||
closedir BINDIR;
|
||||
if ($opt_a) {
|
||||
$arch = $opt_a;
|
||||
} elsif (@archs == 1) {
|
||||
$arch = $archs[0];
|
||||
print "Using target architecture $arch (only one available)\n";
|
||||
} else {
|
||||
print "The following target architectures are available in base:\n";
|
||||
foreach $arch (@archs) {
|
||||
print " $arch\n";
|
||||
}
|
||||
print "What architecture do you want to use? ";
|
||||
$arch = <STDIN>;
|
||||
chomp($arch);
|
||||
}
|
||||
grep /^$arch$/, @archs or Cleanup(1, "Target architecture $arch not available");
|
||||
# ioc architecture
|
||||
opendir BINDIR, "$epics_base/bin" or die "Can't open $epics_base/bin: $!";
|
||||
my @archs = grep !/^\./, readdir BINDIR; # exclude .files
|
||||
closedir BINDIR;
|
||||
if ($opt_a) {
|
||||
$arch = $opt_a;
|
||||
} elsif (@archs == 1) {
|
||||
$arch = $archs[0];
|
||||
print "Using target architecture $arch (only one available)\n";
|
||||
} else {
|
||||
print "The following target architectures are available in base:\n";
|
||||
foreach $arch (@archs) {
|
||||
print " $arch\n";
|
||||
}
|
||||
print "What architecture do you want to use? ";
|
||||
$arch = <STDIN>;
|
||||
chomp($arch);
|
||||
}
|
||||
grep /^$arch$/, @archs or Cleanup(1, "Target architecture $arch not available");
|
||||
|
||||
# Application name
|
||||
if ($opt_p){
|
||||
$appnameIn = $opt_p if ($opt_p);
|
||||
} else {
|
||||
my @apps = glob '*App';
|
||||
if (@apps) {
|
||||
print "The following applications are available:\n",
|
||||
map {s/App$//; " $_\n"} @apps;
|
||||
}
|
||||
print "What application should the IOC(s) boot?\n",
|
||||
"The default uses the IOC's name, even if not listed above.\n",
|
||||
"Application name? ";
|
||||
$appnameIn = <STDIN>;
|
||||
chomp($appnameIn);
|
||||
}
|
||||
if ($opt_p){
|
||||
$appnameIn = $opt_p if ($opt_p);
|
||||
} else {
|
||||
my @apps = glob '*App';
|
||||
if (@apps) {
|
||||
print "The following applications are available:\n",
|
||||
map {s/App$//; " $_\n"} @apps;
|
||||
}
|
||||
print "What application should the IOC(s) boot?\n",
|
||||
"The default uses the IOC's name, even if not listed above.\n",
|
||||
"Application name? ";
|
||||
$appnameIn = <STDIN>;
|
||||
chomp($appnameIn);
|
||||
}
|
||||
}
|
||||
|
||||
# Application type
|
||||
$appext = $opt_i ? "Boot" : "App";
|
||||
if ($opt_t) { # first choice is -t type
|
||||
$apptype = $opt_t;
|
||||
$apptype =~ s/$appext$//;
|
||||
$apptype = $opt_t;
|
||||
$apptype =~ s/$appext$//;
|
||||
} elsif ($ENV{EPICS_MBA_DEF_APP_TYPE}) { # second choice is environment var
|
||||
$apptype = $ENV{EPICS_MBA_DEF_APP_TYPE};
|
||||
$apptype =~ s/(App)|(Boot)$//;
|
||||
$apptype = $ENV{EPICS_MBA_DEF_APP_TYPE};
|
||||
$apptype =~ s/(App)|(Boot)$//;
|
||||
} elsif (-d "$top/default$appext") { # third choice is default
|
||||
$apptype = "default";
|
||||
$apptype = "default";
|
||||
} elsif (-d "$top/example$appext") { # fourth choice is example
|
||||
$apptype = "example";
|
||||
$apptype = "example";
|
||||
}
|
||||
$apptype or Cleanup(1, "No application type set");
|
||||
$apptypename = $apptype . $appext;
|
||||
(-r "$top/$apptypename") or
|
||||
Cleanup(1, "Can't access template directory '$top/$apptypename'.\n");
|
||||
Cleanup(1, "Can't access template directory '$top/$apptypename'.\n");
|
||||
|
||||
print "\nCommand line / environment options validated:\n"
|
||||
. " Templ-Top: $top\n"
|
||||
. "Templ-Type: $apptype\n"
|
||||
. "Templ-Name: $apptypename\n"
|
||||
. " opt_i: $opt_i\n"
|
||||
. " arch: $arch\n"
|
||||
. "EPICS-Base: $epics_base\n\n" if $opt_d;
|
||||
. " Templ-Top: $top\n"
|
||||
. "Templ-Type: $apptype\n"
|
||||
. "Templ-Name: $apptypename\n"
|
||||
. " opt_i: $opt_i\n"
|
||||
. " arch: $arch\n"
|
||||
. "EPICS-Base: $epics_base\n\n" if $opt_d;
|
||||
}
|
||||
|
||||
#
|
||||
@@ -309,13 +309,13 @@ sub ListAppTypes { # no args
|
||||
my @boots = grep /.*Boot$/, @allfiles;
|
||||
print "Valid application types are:\n";
|
||||
foreach $name (@apps) {
|
||||
$name =~ s|App||;
|
||||
printf "\t$name\n" if ($name && -r "$top/$name" . "App");
|
||||
$name =~ s|App||;
|
||||
printf "\t$name\n" if ($name && -r "$top/$name" . "App");
|
||||
}
|
||||
print "Valid iocBoot types are:\n";
|
||||
foreach $name (@boots) {
|
||||
$name =~ s|Boot||;
|
||||
printf "\t$name\n" if ($name && -r "$top/$name" . "Boot");;
|
||||
$name =~ s|Boot||;
|
||||
printf "\t$name\n" if ($name && -r "$top/$name" . "Boot");;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -327,28 +327,28 @@ sub CopyFile { # (source)
|
||||
$target = ReplaceFilename($source);
|
||||
|
||||
if ($target and !-e $target) {
|
||||
open(INP, "<$source") and open(OUT, ">$target")
|
||||
or die "$! Copying $source -> $target";
|
||||
open(INP, "<$source") and open(OUT, ">$target")
|
||||
or die "$! Copying $source -> $target";
|
||||
|
||||
print "Copying file $source -> $target\n" if $opt_d;
|
||||
while (<INP>) {
|
||||
print OUT ReplaceLine($_);
|
||||
}
|
||||
close INP; close OUT;
|
||||
print "Copying file $source -> $target\n" if $opt_d;
|
||||
while (<INP>) {
|
||||
print OUT ReplaceLine($_);
|
||||
}
|
||||
close INP; close OUT;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#
|
||||
# Find() callback for file or structure copy
|
||||
#
|
||||
sub FCopyTree {
|
||||
chdir $app_top; # Sigh
|
||||
if (-d "$File::Find::name"
|
||||
and ($dir = ReplaceFilename($File::Find::name))) {
|
||||
print "Creating directory $dir\n" if $opt_d;
|
||||
mkpath($dir) unless (-d "$dir");
|
||||
and ($dir = ReplaceFilename($File::Find::name))) {
|
||||
print "Creating directory $dir\n" if $opt_d;
|
||||
mkpath($dir) unless (-d "$dir");
|
||||
} else {
|
||||
CopyFile($File::Find::name);
|
||||
CopyFile($File::Find::name);
|
||||
}
|
||||
chdir $File::Find::dir;
|
||||
}
|
||||
@@ -360,9 +360,9 @@ sub Cleanup { # (return-code [ messsage-line1, line 2, ... ])
|
||||
my ($rtncode, @message) = @_;
|
||||
|
||||
if (@message) {
|
||||
print join("\n", @message), "\n";
|
||||
print join("\n", @message), "\n";
|
||||
} else {
|
||||
Usage();
|
||||
Usage();
|
||||
}
|
||||
exit $rtncode;
|
||||
}
|
||||
@@ -410,7 +410,7 @@ EPICS_MBA_DEF_APP_TYPE Application type you want to use as default
|
||||
EPICS_MBA_TEMPLATE_TOP Template top directory
|
||||
EPICS_MBA_BASE Location of EPICS base
|
||||
|
||||
Example: Create exampleApp
|
||||
Example: Create exampleApp
|
||||
|
||||
<base>/bin/<arch>/makeBaseApp.pl -t example example
|
||||
<base>/bin/<arch>/makeBaseApp.pl -i -t example example
|
||||
@@ -422,10 +422,10 @@ sub GetUser {
|
||||
$user = Win32::LoginName() if !$user && $^ eq 'MSWin32';
|
||||
|
||||
unless ($user) {
|
||||
print "Strange, I cannot figure out your user name!\n";
|
||||
print "What should you be called ? ";
|
||||
$user = <STDIN>;
|
||||
chomp $user;
|
||||
print "Strange, I cannot figure out your user name!\n";
|
||||
print "What should you be called ? ";
|
||||
$user = <STDIN>;
|
||||
chomp $user;
|
||||
}
|
||||
$user =~ tr/-a-zA-Z0-9_:;[]<>//cd; # Sanitize; these are the legal chars
|
||||
die "No user name" unless $user;
|
||||
|
||||
@@ -19,7 +19,7 @@ use Getopt::Std;
|
||||
$Getopt::Std::STANDARD_HELP_VERSION = 1;
|
||||
|
||||
use FindBin qw($Bin);
|
||||
use lib ("$Bin/../../lib/perl");
|
||||
use lib ("$Bin/../../lib/perl", $Bin);
|
||||
|
||||
use EPICS::Path;
|
||||
use EPICS::Release;
|
||||
|
||||
@@ -6,7 +6,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.
|
||||
#*************************************************************************
|
||||
|
||||
# Converts text file in DOS CR/LF format to unix ISO format
|
||||
@@ -23,9 +23,9 @@ foreach( @files ) {
|
||||
open(OUTPUT, ">$_");
|
||||
binmode OUTPUT, ":raw";
|
||||
while(<INPUT>) {
|
||||
# Remove CR-LF sequences
|
||||
s/\r\n/\n/;
|
||||
print OUTPUT;
|
||||
# Remove CR-LF sequences
|
||||
s/\r\n/\n/;
|
||||
print OUTPUT;
|
||||
}
|
||||
close INPUT;
|
||||
close OUTPUT;
|
||||
|
||||
@@ -6,7 +6,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.
|
||||
#*************************************************************************
|
||||
#
|
||||
# makeMakefile.pl
|
||||
@@ -24,16 +24,16 @@ $b_t="";
|
||||
|
||||
if ($type ne "")
|
||||
{
|
||||
$b_t = "B_T=$type";
|
||||
$b_t = "B_T=$type";
|
||||
}
|
||||
|
||||
if ($dir =~ m'O.(.+)')
|
||||
{
|
||||
$t_a = $1;
|
||||
$t_a = $1;
|
||||
}
|
||||
else
|
||||
{
|
||||
die "Cannot extract T_A from $dir";
|
||||
die "Cannot extract T_A from $dir";
|
||||
}
|
||||
|
||||
mkdir ($dir, 0777) unless -d $dir;
|
||||
|
||||
Reference in New Issue
Block a user