Compare commits
237 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
65574b5971 | ||
|
|
4284222b54 | ||
|
|
54bab1e2f0 | ||
|
|
df200de27f | ||
|
|
22d6ebe7e1 | ||
|
|
9c3ed1bfe2 | ||
|
|
23fd4e202b | ||
|
|
a5b8b0b890 | ||
|
|
988614ae8a | ||
|
|
fe887b4f83 | ||
|
|
7be7ad2768 | ||
|
|
6c9555310a | ||
|
|
4ccc2e9d3a | ||
|
|
122fb6c731 | ||
|
|
3b8fd13152 | ||
|
|
04a9fdb4e3 | ||
|
|
3c61880d79 | ||
|
|
8081d3ada4 | ||
|
|
6d35ee9c3c | ||
|
|
b5a0657adc | ||
|
|
b87f3eaaee | ||
|
|
6db0e13809 | ||
|
|
0f6c997288 | ||
|
|
704c748fbd | ||
|
|
82456f83ee | ||
|
|
e98a6bbafa | ||
|
|
9b51444fb7 | ||
|
|
3be97865b3 | ||
|
|
67097456e3 | ||
|
|
3c8af4c571 | ||
|
|
26c04844cf | ||
|
|
a3d981ad0a | ||
|
|
924aa2f93b | ||
|
|
457fb8fa13 | ||
|
|
b8b259de6f | ||
|
|
7ce8e5ea01 | ||
|
|
496414c88c | ||
|
|
2acde8bac7 | ||
|
|
2c69ddbee5 | ||
|
|
ba0d5f9443 | ||
|
|
08fd987c60 | ||
|
|
5a605fa0c3 | ||
|
|
0e5dc2a21c | ||
|
|
230603f4ac | ||
|
|
396ff3c484 | ||
|
|
09fbeaf6d3 | ||
|
|
b4a8a1ac98 | ||
|
|
ee87aecd6e | ||
|
|
054a234c70 | ||
|
|
24ddcd2524 | ||
|
|
2226f3acb9 | ||
|
|
f0f89b4b1c | ||
|
|
fdda079b8f | ||
|
|
1f36670175 | ||
|
|
1ede873200 | ||
|
|
51dd371784 | ||
|
|
72745d7b0c | ||
|
|
c933d77963 | ||
|
|
93597e20d5 | ||
|
|
f0453faf36 | ||
|
|
473992cfb9 | ||
|
|
6a362f467d | ||
|
|
af07e0fd51 | ||
|
|
de38b80795 | ||
|
|
7bb27ad3e7 | ||
|
|
1e1799c30e | ||
|
|
8358580190 | ||
|
|
24e39b252d | ||
|
|
ce7044c711 | ||
|
|
177090e35e | ||
|
|
430da57a35 | ||
|
|
d2d637d0c2 | ||
|
|
106fae3b26 | ||
|
|
3179e65791 | ||
|
|
6b9bfb09a5 | ||
|
|
4e312b9f64 | ||
|
|
59fea64390 | ||
|
|
f260124733 | ||
|
|
bd2ada3f8c | ||
|
|
113076a009 | ||
|
|
9502841b55 | ||
|
|
b4404c8266 | ||
|
|
f73f3332fa | ||
|
|
3f4c6abb31 | ||
|
|
76c422c5fd | ||
|
|
fee62836cd | ||
|
|
2c48bcc26b | ||
|
|
a3ab342833 | ||
|
|
3304470323 | ||
|
|
4f2f910d09 | ||
|
|
217f515985 | ||
|
|
ebc59a4223 | ||
|
|
58285501de | ||
|
|
fa07bc3efe | ||
|
|
bfa8e324f7 | ||
|
|
51a69862e7 | ||
|
|
4b8c11aeef | ||
|
|
62efa2e96a | ||
|
|
c7bcb09540 | ||
|
|
5ee778b0c2 | ||
|
|
7b3a040b34 | ||
|
|
e721be4ff5 | ||
|
|
88b47c021c | ||
|
|
d0a316f7ca | ||
|
|
ba307ba6e7 | ||
|
|
d943f6966f | ||
|
|
76d7ce1f76 | ||
|
|
66b0acbe3e | ||
|
|
822c6815c5 | ||
|
|
684ae7b35c | ||
|
|
e18eca80db | ||
|
|
b45622ac5e | ||
|
|
89e6fdbca0 | ||
|
|
599e6635fb | ||
|
|
4241b4e6cb | ||
|
|
9eedf0581e | ||
|
|
ffb23a3480 | ||
|
|
61e61aab24 | ||
|
|
d3e046a466 | ||
|
|
99d331e50a | ||
|
|
9d95528eba | ||
|
|
95a1998e0b | ||
|
|
5769db3862 | ||
|
|
00a74fbb4e | ||
|
|
03fde89dd4 | ||
|
|
8421b46398 | ||
|
|
650aaea203 | ||
|
|
fcfe7ed312 | ||
|
|
f2c4b2c81e | ||
|
|
167dea3735 | ||
|
|
1c1eb030a9 | ||
|
|
43cf5af621 | ||
|
|
235e4bd835 | ||
|
|
601d6d9fe8 | ||
|
|
b961b25151 | ||
|
|
eb9246e4c9 | ||
|
|
d729006908 | ||
|
|
0aeba281be | ||
|
|
04fee51795 | ||
|
|
cba449d53b | ||
|
|
cdffcbfbae | ||
|
|
ba5ea5da93 | ||
|
|
c884175a86 | ||
|
|
a4e9dcff00 | ||
|
|
c9323da5e3 | ||
|
|
ffe441d032 | ||
|
|
45c8a173f3 | ||
|
|
98504d1cdc | ||
|
|
568ece9c27 | ||
|
|
f876bdb42c | ||
|
|
605074a580 | ||
|
|
64da181e6f | ||
|
|
823613fb4f | ||
|
|
f837add8c4 | ||
|
|
15307c4db6 | ||
|
|
8d5815ac95 | ||
|
|
25d7d46e08 | ||
|
|
5f31d9d2b1 | ||
|
|
bb67c9db2f | ||
|
|
cb965611ce | ||
|
|
e805abe971 | ||
|
|
c80f71e294 | ||
|
|
692bbafffe | ||
|
|
fd863738be | ||
|
|
2052062324 | ||
|
|
5d74216017 | ||
|
|
991ff308e0 | ||
|
|
77a5f0db77 | ||
|
|
8d6469957e | ||
|
|
ef90d2d3c7 | ||
|
|
2d9c529f5e | ||
|
|
05d3dbf453 | ||
|
|
695f516cbf | ||
|
|
fda3824ab4 | ||
|
|
42a67524e0 | ||
|
|
d4ed70154b | ||
|
|
f776f6b422 | ||
|
|
cd746a339f | ||
|
|
6438750615 | ||
|
|
2ec5c80652 | ||
|
|
e1039bacfc | ||
|
|
54381b7bf9 | ||
|
|
69d3c94328 | ||
|
|
a02860728e | ||
|
|
440ccc3c4a | ||
|
|
3d19f19d81 | ||
|
|
214edd42d1 | ||
|
|
f8b0b2f5f0 | ||
|
|
8596dc41f2 | ||
|
|
88cb33a04d | ||
|
|
5af0663058 | ||
|
|
830704021d | ||
|
|
96ee2cd00c | ||
|
|
c2d2f671bb | ||
|
|
4b9958304d | ||
|
|
d0875f4f12 | ||
|
|
c74eb27474 | ||
|
|
5aa145920d | ||
|
|
bc3cbd990f | ||
|
|
53bf7cd994 | ||
|
|
a9c4d59537 | ||
|
|
d32332d545 | ||
|
|
88864e949b | ||
|
|
fe4f607e63 | ||
|
|
9e82a96700 | ||
|
|
ef7399159c | ||
|
|
d7b3293ba3 | ||
|
|
9014ca899d | ||
|
|
c5130468cd | ||
|
|
7dd067e887 | ||
|
|
c56091978c | ||
|
|
2f8e6bf17e | ||
|
|
144281e0a3 | ||
|
|
83c2414ad0 | ||
|
|
504665bf09 | ||
|
|
1411522a11 | ||
|
|
ae3d3904d9 | ||
|
|
002bafdf07 | ||
|
|
8bfa40d858 | ||
|
|
138e2f1ad5 | ||
|
|
fefe6fd1fc | ||
|
|
5eb49ebaf0 | ||
|
|
625172419e | ||
|
|
80343363a4 | ||
|
|
444b89f557 | ||
|
|
fa1ddeeb1b | ||
|
|
ca46bf70b7 | ||
|
|
c0e2a44365 | ||
|
|
84c3ec7c64 | ||
|
|
65e781d58a | ||
|
|
a613a96ad3 | ||
|
|
fbd2d4dd31 | ||
|
|
2c28d4b69e | ||
|
|
9946681f72 | ||
|
|
196baac7da | ||
|
|
ce93142fe1 | ||
|
|
5d61a512d1 |
@@ -29,7 +29,7 @@ EPICS_VERSION = 3
|
||||
EPICS_REVISION = 15
|
||||
|
||||
# EPICS_MODIFICATION must be a number >=0 and <256
|
||||
EPICS_MODIFICATION = 2
|
||||
EPICS_MODIFICATION = 4
|
||||
|
||||
# EPICS_PATCH_LEVEL must be a number (win32 resource file requirement)
|
||||
# Not included if zero
|
||||
|
||||
@@ -118,6 +118,13 @@ CROSS_COMPILER_TARGET_ARCHS=
|
||||
# configure/os/CONFIG_SITE.<host>.Common files instead.
|
||||
CROSS_COMPILER_HOST_ARCHS=
|
||||
|
||||
# The 'make runtests' and 'make tapfiles' build targets normally only run
|
||||
# self-tests for the EPICS_HOST_ARCH architecture. If the host can execute
|
||||
# the self-test programs for any other cross-built architectures such as
|
||||
# a -debug architecture, those architectures can be named here.
|
||||
#
|
||||
CROSS_COMPILER_RUNTEST_ARCHS=
|
||||
|
||||
# Build shared libraries (DLLs on Windows).
|
||||
# Must be either YES or NO. Definitions in the target-specific
|
||||
# os/CONFIG.Common.<target> and os/CONFIG_SITE.Common.<target> files may
|
||||
|
||||
@@ -30,10 +30,8 @@
|
||||
# local timezone info for vxWorks and RTEMS IOCs. The format is
|
||||
# <name>::<minutesWest>:<start daylight>:<end daylight>
|
||||
# where the start and end are mmddhh - that is month,day,hour
|
||||
# e.g. for ANL in 2015: EPICS_TIMEZONE=CST/CDT::360:030802:110102
|
||||
# e.g. for ANL in 2016: EPICS_TIMEZONE=CST/CDT::360:031302:110602
|
||||
#
|
||||
# DST for 2015 US: Mar 08 - Nov 01
|
||||
# EU: Mar 29 - Oct 25
|
||||
# DST for 2016 US: Mar 13 - Nov 06
|
||||
# EU: Mar 27 - Oct 30
|
||||
# DST for 2017 US: Mar 12 - Nov 05
|
||||
@@ -42,11 +40,15 @@
|
||||
# EU: Mar 25 - Oct 28
|
||||
# DST for 2019 US: Mar 10 - Nov 03
|
||||
# EU: Mar 31 - Oct 27
|
||||
# (see: http://www.timeanddate.com/time/map/)
|
||||
# DST for 2020 US: Mar 08 - Nov 01
|
||||
# EU: Mar 29 - Oct 25
|
||||
# DST for 2021 US: Mar 14 - Nov 07
|
||||
# EU: Mar 28 - Oct 31
|
||||
# (see: http://www.timeanddate.com/time/dst/2016.html etc. )
|
||||
#
|
||||
# These values are for 2015:
|
||||
EPICS_TIMEZONE=CST/CDT::360:030802:110102
|
||||
#EPICS_TIMEZONE=CET/CEST::-60:032902:102502
|
||||
# These values are for 2016:
|
||||
EPICS_TIMEZONE=CST/CDT::360:031302:110602
|
||||
#EPICS_TIMEZONE=CET/CEST::-60:032702:103002
|
||||
|
||||
# EPICS_TS_NTP_INET
|
||||
# NTP time server ip address. Uses boot host if not set.
|
||||
|
||||
@@ -24,11 +24,15 @@ else
|
||||
clean$(DIVIDER)$(ARCH) clean:
|
||||
endif
|
||||
|
||||
cdCommands envPaths dllPath.bat relPaths.sh: \
|
||||
cdCommands dllPath.bat relPaths.sh: \
|
||||
$(wildcard $(TOP)/configure/RELEASE*) \
|
||||
$(wildcard $(TOP)/configure/CONFIG_SITE*) $(INSTALL_BIN)
|
||||
$(wildcard $(TOP)/configure/CONFIG_SITE*) | $(INSTALL_BIN)
|
||||
$(CONVERTRELEASE) -a $(ARCH) -t $(IOCS_APPL_TOP) $@
|
||||
|
||||
envPaths: $(wildcard $(TOP)/configure/RELEASE*) \
|
||||
$(wildcard $(TOP)/configure/CONFIG_SITE*) | $(INSTALL_BIN)
|
||||
$(CONVERTRELEASE) -t $(IOCS_APPL_TOP) $@
|
||||
|
||||
realclean:
|
||||
$(RM) cdCommands envPaths dllPath.bat relPaths.sh
|
||||
|
||||
|
||||
@@ -81,6 +81,8 @@ else
|
||||
host:
|
||||
endif
|
||||
|
||||
-include $(CONFIG)/RULES_FILE_TYPE
|
||||
|
||||
-include $(CONFIG)/RULES.Db
|
||||
|
||||
#---------------------------------------------------------------
|
||||
@@ -122,8 +124,6 @@ LIBTARGETS += $(LIBNAME) $(INSTALL_LIBS) $(TESTLIBNAME) \
|
||||
$(LOADABLE_SHRLIBNAME) $(INSTALL_LOADABLE_SHRLIBS)
|
||||
|
||||
|
||||
-include $(CONFIG)/RULES_FILE_TYPE
|
||||
|
||||
# Main targets
|
||||
|
||||
install: buildInstall
|
||||
@@ -170,7 +170,13 @@ endif
|
||||
$(DIRECTORY_TARGETS) :
|
||||
$(MKDIR) -p $@
|
||||
|
||||
$(PRODNAME): $(INSTALL_LIB_INSTALLS)
|
||||
# Install LIB_INSTALLS libraries before linking executables
|
||||
$(TESTPRODNAME) $(PRODNAME): | $(INSTALL_LIB_INSTALLS)
|
||||
|
||||
# Install built libraries too, unless Makefile says to wait
|
||||
ifneq ($(DELAY_INSTALL_LIBS),YES)
|
||||
$(TESTPRODNAME) $(PRODNAME): | $(INSTALL_LIBS) $(INSTALL_DLLSTUB_LIBS)
|
||||
endif
|
||||
|
||||
# RELEASE file consistency checking
|
||||
checkRelease:
|
||||
@@ -319,7 +325,9 @@ $(MODNAME): %$(MODEXT): %$(EXE)
|
||||
# Automated testing
|
||||
|
||||
runtests: $(TESTSCRIPTS)
|
||||
ifneq (,$(findstring $(T_A),$(EPICS_HOST_ARCH) $(CROSS_COMPILER_RUNTEST_ARCHS)))
|
||||
-$(PERL) -MTest::Harness -e 'runtests @ARGV if @ARGV;' $^
|
||||
endif
|
||||
|
||||
testspec: $(TESTSCRIPTS)
|
||||
@$(RM) $@
|
||||
@@ -333,7 +341,9 @@ tapfiles: $(TESTSCRIPTS) $(TAPFILES)
|
||||
|
||||
# A .tap file is the output from running the associated test script
|
||||
%.tap: %.t
|
||||
ifneq (,$(findstring $(T_A),$(EPICS_HOST_ARCH) $(CROSS_COMPILER_RUNTEST_ARCHS)))
|
||||
-$(PERL) $< -tap > $@
|
||||
endif
|
||||
|
||||
# If there's a perl test script (.plt) available, use it
|
||||
%.t: ../%.plt
|
||||
|
||||
@@ -2,6 +2,9 @@
|
||||
|
||||
vpath %@ $(USR_VPATH) $(ALL_SRC_DIRS)
|
||||
|
||||
#---------------------------------------------------------------
|
||||
# Variable expansion
|
||||
|
||||
# Default settings
|
||||
EXPAND_TOOL ?= $(PERL) $(TOOLS)/expandVars.pl
|
||||
|
||||
@@ -24,3 +27,39 @@ expand_clean:
|
||||
@$(RM) $(EXPANDED)
|
||||
|
||||
.PHONY : expand_clean
|
||||
|
||||
#---------------------------------------------------------------
|
||||
# Assemblies (files assembled from snippets)
|
||||
|
||||
ASSEMBLE_TOOL ?= $(PERL) $(TOOLS)/assembleSnippets.pl
|
||||
|
||||
define COMMON_ASSEMBLY_template
|
||||
$1_SNIPPETS += $$(foreach dir, .. $$(SRC_DIRS), \
|
||||
$$(wildcard $$(dir)/$$($1_PATTERN)))
|
||||
$(COMMON_DIR)/$1: $$($1_SNIPPETS)
|
||||
$(ECHO) "Assembling common file $$@ from snippets"
|
||||
@$(RM) $1
|
||||
$(ASSEMBLE_TOOL) -o $1 $$^
|
||||
@$(MV) $1 $$@
|
||||
endef
|
||||
$(foreach asy, $(COMMON_ASSEMBLIES), \
|
||||
$(eval $(call COMMON_ASSEMBLY_template,$(strip $(asy)))))
|
||||
|
||||
define ASSEMBLY_template
|
||||
$1_SNIPPETS += $$(foreach dir, .. $$(SRC_DIRS), \
|
||||
$$(wildcard $$(dir)/$$($1_PATTERN)))
|
||||
$1: $$($1_SNIPPETS)
|
||||
$(ECHO) "Assembling file $$@ from snippets"
|
||||
@$(RM) $$@
|
||||
$(ASSEMBLE_TOOL) -o $$@ $$^
|
||||
endef
|
||||
$(foreach asy, $(ASSEMBLIES), \
|
||||
$(eval $(call ASSEMBLY_template,$(strip $(asy)))))
|
||||
|
||||
define ASSEMBLY_DEP_template
|
||||
$1$(DEP):
|
||||
@echo $1: > $$@
|
||||
endef
|
||||
$(foreach asy, $(sort $(COMMON_ASSEMBLIES) $(ASSEMBLIES)), \
|
||||
$(eval $(call ASSEMBLY_DEP_template,$(strip $(asy)))))
|
||||
|
||||
|
||||
@@ -57,7 +57,7 @@ SHRLIB_LDLIBS = $(addprefix -l, $($*_LDLIBS) $(LIB_LIBS) $(USR_LIBS)) \
|
||||
$(LDLIBS)
|
||||
|
||||
SHRLIB_DEPLIB_DIRS = $(foreach word, \
|
||||
$(sort $(dir $($*_DEPLIBS) $(SHRLIB_DEPLIBS))), \
|
||||
$(sort $(INSTALL_LIB)/ $(dir $($*_DEPLIBS) $(SHRLIB_DEPLIBS))), \
|
||||
$(shell $(FULLPATHNAME) $(word)))
|
||||
|
||||
SHRLIBDIR_LDFLAGS += $(SHRLIB_DEPLIB_DIRS:%=-L%)
|
||||
@@ -86,7 +86,7 @@ PROD_LDLIBS += $($(firstword $(LDLIBS_STATIC_$(STATIC_BUILD)) \
|
||||
$(LDLIBS_SHARED_$(SHARED_LIBRARIES))))
|
||||
|
||||
PROD_DEPLIB_DIRS = $(foreach word, \
|
||||
$(sort $(dir $($*_DEPLIBS) $(PROD_DEPLIBS))), \
|
||||
$(sort $(INSTALL_LIB)/ $(dir $($*_DEPLIBS) $(PROD_DEPLIBS))), \
|
||||
$(shell $(FULLPATHNAME) $(word)))
|
||||
|
||||
PRODDIR_LDFLAGS += $(PROD_DEPLIB_DIRS:%=-L%)
|
||||
|
||||
@@ -26,14 +26,14 @@ POSIX_LDLIBS += -lpthread
|
||||
ARCH_DEP_CFLAGS += -m32
|
||||
ARCH_DEP_LDFLAGS += -m32
|
||||
|
||||
# Compiler defines _X86_ 1
|
||||
# Compiler defines __MSVCRT__ 1
|
||||
# 32-bit compiler defines _X86_ 1
|
||||
# Compiler defines __CYGWIN__ 1
|
||||
# Compiler defines __CYGWIN32__ 1
|
||||
# 32-bit compiler defines __CYGWIN32__ 1
|
||||
# Compiler defines __unix__ 1
|
||||
# Compiler defines __unix 1
|
||||
# Compiler defines unix 1
|
||||
|
||||
# This macro now deprecated, use __CYGWIN__ in the future
|
||||
OP_SYS_CPPFLAGS += -DCYGWIN32
|
||||
|
||||
EXE = .exe
|
||||
|
||||
33
configure/os/CONFIG.cygwin-x86.linux-arm
Normal file
33
configure/os/CONFIG.cygwin-x86.linux-arm
Normal file
@@ -0,0 +1,33 @@
|
||||
# CONFIG.cygwin-x86.linux-arm
|
||||
#
|
||||
# Definitions for cywgin-x86 host - linux-arm target builds
|
||||
# Override these settings in CONFIG_SITE.cygwin-x86.linux-arm
|
||||
#-------------------------------------------------------
|
||||
|
||||
VALID_BUILDS = Ioc
|
||||
GNU_TARGET = arm-linux
|
||||
|
||||
# prefix of compiler tools
|
||||
CMPLR_SUFFIX =
|
||||
CMPLR_PREFIX = $(addsuffix -,$(GNU_TARGET))
|
||||
|
||||
# Provide a link-time path for shared libraries
|
||||
SHRLIBDIR_RPATH_LDFLAGS_YES += $(SHRLIB_DEPLIB_DIRS:%=-Wl,-rpath-link,%)
|
||||
SHRLIBDIR_LDFLAGS += $(SHRLIBDIR_RPATH_LDFLAGS_$(LINKER_USE_RPATH))
|
||||
|
||||
# Provide a link-time path for products
|
||||
PRODDIR_RPATH_LDFLAGS_YES += $(PROD_DEPLIB_DIRS:%=-Wl,-rpath-link,%)
|
||||
PRODDIR_LDFLAGS += $(PRODDIR_RPATH_LDFLAGS_$(LINKER_USE_RPATH))
|
||||
|
||||
# Provide a link-time path for readline
|
||||
RUNTIME_LDFLAGS_READLINE_YES = -Wl,-rpath-link,$(GNU_DIR)/lib
|
||||
RUNTIME_LDFLAGS_READLINE = $(RUNTIME_LDFLAGS_READLINE_$(LINKER_USE_RPATH))
|
||||
RUNTIME_LDFLAGS_READLINE_CURSES = $(RUNTIME_LDFLAGS_READLINE_$(LINKER_USE_RPATH))
|
||||
RUNTIME_LDFLAGS_READLINE_NCURSES = $(RUNTIME_LDFLAGS_READLINE_$(LINKER_USE_RPATH))
|
||||
|
||||
# Library flags
|
||||
STATIC_LDFLAGS_YES= -Wl,-Bstatic
|
||||
STATIC_LDFLAGS_NO=
|
||||
STATIC_LDLIBS_YES= -Wl,-Bdynamic
|
||||
STATIC_LDLIBS_NO=
|
||||
|
||||
7
configure/os/CONFIG.cygwin-x86_64.linux-arm
Normal file
7
configure/os/CONFIG.cygwin-x86_64.linux-arm
Normal file
@@ -0,0 +1,7 @@
|
||||
# CONFIG.cygwin-x86_64.linux-arm
|
||||
#
|
||||
# Definitions for cygwin-x86_64 host - linux-arkm targets
|
||||
# Override these settings in CONFIG_SITE.cygwin-x86_64.linux-arm
|
||||
#-------------------------------------------------------
|
||||
|
||||
include $(CONFIG)/os/CONFIG.cygwin-x86.linux-arm
|
||||
28
configure/os/CONFIG.linux-x86.windows-x64-mingw
Normal file
28
configure/os/CONFIG.linux-x86.windows-x64-mingw
Normal file
@@ -0,0 +1,28 @@
|
||||
# CONFIG.linux-x86.windows-x64-mingw
|
||||
#
|
||||
# Definitions for linux-x86 host windows-x64-mingw target builds
|
||||
# Override these definitions in CONFIG_SITE.linux-x86.windows-x64-mingw
|
||||
#-------------------------------------------------------
|
||||
|
||||
# Include common gnu compiler definitions
|
||||
include $(CONFIG)/CONFIG.gnuCommon
|
||||
|
||||
# Add resource compiler
|
||||
RCCMD = $(GNU_BIN)/$(CMPLR_PREFIX)windres$(CMPLR_SUFFIX) $(INCLUDES) $< $@
|
||||
|
||||
# Remove -fPIC flags, add out-implib
|
||||
SHRLIB_CFLAGS =
|
||||
SHRLIB_LDFLAGS = -shared \
|
||||
-Wl,--out-implib,$(DLLSTUB_PREFIX)$*$(DLLSTUB_SUFFIX)
|
||||
LOADABLE_SHRLIB_LDFLAGS = -shared \
|
||||
-Wl,--out-implib,$(DLLSTUB_PREFIX)$*$(DLLSTUB_SUFFIX)
|
||||
|
||||
# No need to explicitly link with gcc library
|
||||
GNU_LDLIBS_YES =
|
||||
|
||||
# Link with winsock2
|
||||
OP_SYS_LDLIBS = -lws2_32
|
||||
|
||||
# Use static compiler-support libraries
|
||||
OP_SYS_LDFLAGS += -static-libgcc -static-libstdc++
|
||||
# There is no compiler flag for static libwinpthread
|
||||
9
configure/os/CONFIG.linux-x86_64.windows-x64-mingw
Normal file
9
configure/os/CONFIG.linux-x86_64.windows-x64-mingw
Normal file
@@ -0,0 +1,9 @@
|
||||
# CONFIG.linux-x86_64.windows-x64-mingw
|
||||
#
|
||||
# Definitions for linux-x86_64 host windows-x64-mingw target builds
|
||||
# Override these definitions in CONFIG_SITE.linux-x86_64.windows-x64-mingw
|
||||
#-------------------------------------------------------
|
||||
|
||||
# Settings as for the linux-x86 host architecture
|
||||
include $(CONFIG)/os/CONFIG.linux-x86.windows-x64-mingw
|
||||
|
||||
@@ -1,22 +1,22 @@
|
||||
# CONFIG_SITE.Common.cygwin-x86_64
|
||||
#
|
||||
# $Revision-Id$
|
||||
#
|
||||
# Site Specific definitions for cygwin-x86_64 target
|
||||
# Only the local epics system manager should modify this file
|
||||
|
||||
# If readline is installed uncomment the following line
|
||||
# to add command-line editing and history support
|
||||
#COMMANDLINE_LIBRARY = READLINE
|
||||
|
||||
# Uncomment the following line if readline has problems
|
||||
#LDLIBS_READLINE = -lreadline -lcurses
|
||||
|
||||
|
||||
# It makes sense to include debugging symbols even in optimized builds
|
||||
# in case you want to attach gdb to the process or examine a core-dump.
|
||||
# This does cost disk space, but not memory as debug symbols are not
|
||||
# loaded into RAM when the binary is loaded.
|
||||
OPT_CFLAGS_YES += -g
|
||||
OPT_CXXFLAGS_YES += -g
|
||||
|
||||
# CONFIG_SITE.Common.cygwin-x86_64
|
||||
#
|
||||
# $Revision-Id$
|
||||
#
|
||||
# Site Specific definitions for cygwin-x86_64 target
|
||||
# Only the local epics system manager should modify this file
|
||||
|
||||
# If readline is installed uncomment the following line
|
||||
# to add command-line editing and history support
|
||||
#COMMANDLINE_LIBRARY = READLINE
|
||||
|
||||
# Uncomment the following line if readline has problems
|
||||
#LDLIBS_READLINE = -lreadline -lcurses
|
||||
|
||||
|
||||
# It makes sense to include debugging symbols even in optimized builds
|
||||
# in case you want to attach gdb to the process or examine a core-dump.
|
||||
# This does cost disk space, but not memory as debug symbols are not
|
||||
# loaded into RAM when the binary is loaded.
|
||||
OPT_CFLAGS_YES += -g
|
||||
OPT_CXXFLAGS_YES += -g
|
||||
|
||||
|
||||
@@ -6,5 +6,5 @@
|
||||
# Site override definitions for cygwin-x86 host builds
|
||||
#-------------------------------------------------------
|
||||
|
||||
#CROSS_COMPILER_TARGET_ARCHS =
|
||||
#CROSS_COMPILER_TARGET_ARCHS = linux-arm
|
||||
|
||||
|
||||
16
configure/os/CONFIG_SITE.cygwin-x86.linux-arm
Normal file
16
configure/os/CONFIG_SITE.cygwin-x86.linux-arm
Normal file
@@ -0,0 +1,16 @@
|
||||
# CONFIG_SITE.cygwin-x86.linux-arm
|
||||
#
|
||||
# $Revision-Id$
|
||||
#
|
||||
# Site specific definitions for cygwin-x86 host - linux-arm target builds
|
||||
#-------------------------------------------------------
|
||||
|
||||
# Tools install path
|
||||
GNU_DIR = /usr/local/arm-linux
|
||||
|
||||
# GNU crosscompiler target name
|
||||
GNU_TARGET = arm-linux
|
||||
|
||||
STATIC_BUILD = YES
|
||||
SHARED_LIBRARIES = NO
|
||||
|
||||
16
configure/os/CONFIG_SITE.cygwin-x86_64.linux-arm
Normal file
16
configure/os/CONFIG_SITE.cygwin-x86_64.linux-arm
Normal file
@@ -0,0 +1,16 @@
|
||||
# CONFIG_SITE.cygwin-x86_64.linux-arm
|
||||
#
|
||||
# $Revision-Id$
|
||||
#
|
||||
# Site specific definitions for cygwin-x86_64 host - linux-arm target builds
|
||||
#-------------------------------------------------------
|
||||
|
||||
# Tools install path
|
||||
GNU_DIR = /usr/local/arm-linux
|
||||
|
||||
# GNU crosscompiler target name
|
||||
GNU_TARGET = arm-linux
|
||||
|
||||
STATIC_BUILD = YES
|
||||
SHARED_LIBRARIES = NO
|
||||
|
||||
20
configure/os/CONFIG_SITE.linux-x86.windows-x64-mingw
Normal file
20
configure/os/CONFIG_SITE.linux-x86.windows-x64-mingw
Normal file
@@ -0,0 +1,20 @@
|
||||
# CONFIG_SITE.linux-x86.windows-x64-mingw
|
||||
#
|
||||
# Configuration for linux-x86 host windows-x64-mingw target builds
|
||||
#-------------------------------------------------------
|
||||
|
||||
# Early versions of the MinGW cross-build tools can only build
|
||||
# static (non-DLL) libraries. For example RHEL's cross-gcc 4.4.6
|
||||
# needs these uncommented, cross-gcc 4.6.3 for Ubuntu does not:
|
||||
#SHARED_LIBRARIES = NO
|
||||
#STATIC_BUILD = YES
|
||||
|
||||
# The cross-build tools are in $(GNU_DIR)/bin
|
||||
# Default is /usr
|
||||
#GNU_DIR = /usr/local
|
||||
|
||||
# Different distribution cross-build packages use different prefixes:
|
||||
# Ubuntu:
|
||||
#CMPLR_PREFIX = i686-w64-mingw32-
|
||||
# RHEL:
|
||||
CMPLR_PREFIX = x86_64-w64-mingw32-
|
||||
8
configure/os/CONFIG_SITE.linux-x86_64.windows-x64-mingw
Normal file
8
configure/os/CONFIG_SITE.linux-x86_64.windows-x64-mingw
Normal file
@@ -0,0 +1,8 @@
|
||||
# CONFIG_SITE.linux-x86_64.windows-x64-mingw
|
||||
#
|
||||
# Configuration for linux-x86_64 host windows-x64-mingw target builds
|
||||
#-------------------------------------------------------
|
||||
|
||||
# Inherit from the linux-x86 host architecture
|
||||
include $(CONFIG)/os/CONFIG_SITE.linux-x86.windows-x64-mingw
|
||||
|
||||
@@ -4,17 +4,17 @@
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
|
||||
<title>Known Problems in R3.15.2</title>
|
||||
<title>Known Problems in R3.15.3</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h1 style="text-align: center">EPICS Base R3.15.2: Known Problems</h1>
|
||||
<h1 style="text-align: center">EPICS Base R3.15.3: Known Problems</h1>
|
||||
|
||||
<p>Any patch files linked below should be applied at the root of the
|
||||
base-3.15.2 tree. Download them, then use the GNU Patch program as
|
||||
base-3.15.3 tree. Download them, then use the GNU Patch program as
|
||||
follows:</p>
|
||||
|
||||
<blockquote><pre>% <b>cd <i>/path/to/</i>base-3.15.2</b>
|
||||
<blockquote><pre>% <b>cd <i>/path/to/</i>base-3.15.3</b>
|
||||
% <b>patch -p0 < <i>/path/to/</i>file.patch</b></pre></blockquote>
|
||||
|
||||
<p>The following significant problems have been reported with this
|
||||
@@ -22,10 +22,18 @@ version of EPICS Base:</p>
|
||||
|
||||
<ul>
|
||||
|
||||
<li>See LaunchPad bug
|
||||
<a href="https://bugs.launchpad.net/epics-base/+bug/1466129">#1466129</a>:
|
||||
The feature that allows an IOC to bind to a single network interface is not
|
||||
working correctly. The bug mentioned above contains a patch that has been
|
||||
tested on Linux-64, a complete fix is expected for 3.15.4. Note that Windows
|
||||
systems are not affected by this bug.</li>
|
||||
|
||||
<li>Parallel builds ("make -j") on native Windows are not working properly.
|
||||
Builds tend to hang (make saturating one core) - interrupting and calling make
|
||||
again usually finishes the build. Sequential builds work and are recommended
|
||||
for automated build environments.</li>
|
||||
Builds tend to hang (saturating one core); interrupting and running make
|
||||
again usually finishes the build. Limiting the number of parallel jobs using
|
||||
something like "make -j8" also helps prevent this problem. Sequential builds
|
||||
always work and are recommended for automated build environments.</li>
|
||||
|
||||
<li>64-bit Windows builds of the CAS library may not work with some compilers.
|
||||
The code in <tt>src/legacy/gdd</tt> is currently incompatible with the LLP64
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
Installation Instructions
|
||||
|
||||
EPICS Base Release 3.15.2
|
||||
EPICS Base Release 3.15.3
|
||||
|
||||
--------------------------------------------------------------------------
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
<BODY>
|
||||
<CENTER>
|
||||
<H1>Installation Instructions</H1>
|
||||
<H2>EPICS Base Release 3.15.2</H2><BR>
|
||||
<H2>EPICS Base Release 3.15.3</H2><BR>
|
||||
</CENTER>
|
||||
<HR>
|
||||
<H3> Table of Contents</H3>
|
||||
|
||||
@@ -3,14 +3,270 @@
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="content-type" content="text/html; charset=iso-8859-1">
|
||||
<title>EPICS Base R3.15.2 Release Notes</title>
|
||||
<title>EPICS Base R3.15.4 Release Notes</title>
|
||||
</head>
|
||||
|
||||
<body lang="en">
|
||||
<h1 align="center">EPICS Base Release 3.15.2</h1>
|
||||
<h1 align="center">EPICS Base Release 3.15.4</h1>
|
||||
|
||||
<h2 align="center">Changes made on the 3.15 branch since 3.15.3</h2>
|
||||
<!-- Insert new items immediately below here ... -->
|
||||
|
||||
<h3>New string input device support "getenv"</h3>
|
||||
|
||||
<p>A new "getenv" device support for both the stringin and lsi (long string
|
||||
input) record types can be used to read the value of an environment variable
|
||||
from the IOC at runtime. See base/db/softIocExit.db for sample usage.</p>
|
||||
|
||||
<h3>Build rules and DELAY_INSTALL_LIBS</h3>
|
||||
|
||||
<p>A new order-only prerequisite build rule has been added to ensure that
|
||||
library files (and DLL stubs on Windows) get installed before linking any
|
||||
executables, which resolves parallel build problems on high-powered CPUs. There
|
||||
are some (rare) cases though where a Makefile has to build an executable and run
|
||||
it to be able to compile code for a library built by the same Makefile. With
|
||||
this new build rule GNUmake will complain about a circular dependency and the
|
||||
build will probably fail in those cases. To avoid this problem the failing
|
||||
Makefile should set <tt>DELAY_INSTALL_LIBS = YES</tt> before including the
|
||||
<tt>$(TOP)/configure/RULES</tt> file, disabling the new build rule.</p>
|
||||
|
||||
<h3>IOC environment variables and build parameters</h3>
|
||||
|
||||
<p>The IOC now sets a number of environment variables at startup that provide
|
||||
the version of EPICS Base it was built against (EPICS_VERSION_...) and its build
|
||||
architecture (ARCH). In some cases this allows a single iocBoot/ioc directory to
|
||||
be used to run the same IOC on several different architectures without any
|
||||
changes.</p>
|
||||
|
||||
<p>There are also 3 new environment parameters (EPICS_BUILD_...) available that
|
||||
C/C++ code can use to find out the target architecture, OS class and compiler
|
||||
class it was built with. These may be useful when writing interfaces to other
|
||||
languages.</p>
|
||||
|
||||
<h3>New implementation of promptgroup/gui_group field property</h3>
|
||||
|
||||
<p>The mechanism behind the "promptgroup()" field property inside a record type
|
||||
definition has been changed. Instead of using a fixed set of choices,
|
||||
the static database access library now collects the used gui group names
|
||||
while parsing DBD information. Group names should start with a two-digit number
|
||||
plus space-dash-space to allow proper sorting of groups.</p>
|
||||
|
||||
<p>The include file <tt>guigroup.h</tt> that defined the fixed set of choices
|
||||
has been deprecated. Instead, use the conversion functions between index number
|
||||
and group string that have been added to dbStaticLib.</p>
|
||||
|
||||
<p>When a DBD file containing record-type descriptions is expanded, any
|
||||
old-style <tt>GUI_xxx</tt> group names will be replaced by a new-style
|
||||
string for use by the IOC. This permits an older record type to be used with
|
||||
the 3.15.4 release, although eventually record types should be converted by
|
||||
hand with better group names used.</p>
|
||||
|
||||
<h3>CA server configuration changes</h3>
|
||||
|
||||
<p>RSRV now honors EPICS_CAS_INTF_ADDR_LIST and binds only to the provided list
|
||||
of network interfaces. Name searches (UDP and TCP) on other network interfaces
|
||||
are ignored. For example on a computer with interfaces 10.5.1.1/24, 10.5.2.1/24,
|
||||
and 10.5.3.1/24, setting "EPICS_CAS_INTF_ADDR_LIST='10.5.1.1 10.5.2.1'" will
|
||||
accept traffic on the .1.1 and .2.1, but ignore from .3.1</p>
|
||||
|
||||
<p>RSRV now honors EPICS_CAS_IGNORE_ADDR_LIST and ignores UDP messages received
|
||||
from addresses in this list.</p>
|
||||
|
||||
<p>Previously, CA servers (RSRV and PCAS) would build the beacon address list
|
||||
using EPICS_CA_ADDR_LIST if EPICS_CAS_BEACON_ADDR_LIST was no set. This is no
|
||||
longer done. Sites depending on this should set both envronment variables to the
|
||||
same value.</p>
|
||||
|
||||
<h3>IPv4 multicast for name search and beacons</h3>
|
||||
|
||||
<p>libca, RSRV, and PCAS may now use IPv4 multicasting for UDP traffic (name
|
||||
search and beacons). This is disabled by default. To enable multicast address(s)
|
||||
must be listed in EPICS_CA_ADDR_LIST for clients and EPICS_CAS_INTF_ADDR_LIST
|
||||
for servers (IOCs should set both). For example:
|
||||
"EPICS_CAS_INTF_ADDR_LIST='224.0.2.9' EPICS_CA_ADDR_LIST=224.0.2.9".</p>
|
||||
|
||||
<p>Please note that no IPv4 multicast address is officially assigned for Channel
|
||||
Access by IANA. The example 224.0.2.9 is taken from the AD-HOC Block I range.<p>
|
||||
|
||||
<h3>Moved <tt>mlockall()</tt> into its own epicsThread routine</h3>
|
||||
|
||||
<p>Since EPICS Base 3.15.0.2 on Posix OSs the initialization of the epicsThread
|
||||
subsystem has called <tt>mlockall()</tt> when the OS supports it and thread
|
||||
priority scheduling is enabled. Doing so has caused problems in third-party
|
||||
applications that call the CA client library, so the functionality has been
|
||||
moved to a separate routine <tt>epicsThreadRealtimeLock()</tt> which will be
|
||||
called by the IOC at iocInit (unless disabled by setting the global variable
|
||||
<tt>dbThreadRealtimeLock</tt> to zero).</p>
|
||||
|
||||
<h3>Added dbQuietMacroWarnings control</h3>
|
||||
|
||||
<p>When loading database files, macros get expanded even on comment lines. If a
|
||||
comment contains an undefined macro, the load still continues but an error
|
||||
message gets printed. For this release the error message has been changed to a
|
||||
warning, but even this warning can be made less verbose by setting this new
|
||||
variable to a non-zero value before loading the file, like this:</p>
|
||||
|
||||
<blockquote><pre>
|
||||
var dbQuietMacroWarnings 1 <i>iocsh</i>
|
||||
dbQuietMacroWarnings=1 <i>VxWorks</i>
|
||||
</pre></blockquote>
|
||||
|
||||
<p>This was <a href="https://bugs.launchpad.net/bugs/541119">Launchpad bug
|
||||
541119</a>.</p>
|
||||
|
||||
|
||||
<h2 align="center">Changes pulled from the 3.14 branch since 3.15.3</h2>
|
||||
<!-- Insert inherited items immediately below here ... -->
|
||||
|
||||
<h3>Making IOC ca_get operations atomic</h3>
|
||||
|
||||
<p>When a CA client gets data from an IOC record using a compound data type such
|
||||
as <tt>DBR_TIME_DOUBLE</tt> the value field is fetched from the database in a
|
||||
separate call than the other metadata, without keeping the record locked. This
|
||||
allows some other thread such as a periodic scan thread a chance to interrupt
|
||||
the get operation and process the record in between. CA monitors have always
|
||||
been atomic as long as the value data isn't a string or an array, but this race
|
||||
condition in the CA get path has now been fixed so the record will stay locked
|
||||
between the two fetch operations.</p>
|
||||
|
||||
<p>This fixes <a href="https://bugs.launchpad.net/epics-base/+bug/1581212">
|
||||
Launchpad bug #1581212</a>, thanks to Till Strauman and Dehong Zhang.</p>
|
||||
|
||||
<h3>New CONFIG_SITE variable for running self-tests</h3>
|
||||
|
||||
<p>The 'make runtests' and 'make tapfiles' build targets normally only run the
|
||||
self-tests for the main <tt>EPICS_HOST_ARCH</tt> architecture. If the host is
|
||||
able to execute self-test programs for other target architectures that are being
|
||||
built by the host, such as when building a <tt>-debug</tt> version of the host
|
||||
architecture for example, the names of those other architectures can be added to
|
||||
the new <tt>CROSS_COMPILER_RUNTEST_ARCHS</tt> variable in either the
|
||||
<tt>configure/CONFIG_SITE</tt> file or in an appropriate
|
||||
<tt>configure/os/CONFIG_SITE.<host>.Common</tt> file to have the test
|
||||
programs for those targets be run as well.</p>
|
||||
|
||||
<h3>Additional RELEASE file checks</h3>
|
||||
|
||||
<p>An additional check has been added at build-time for the contents of the
|
||||
configure/RELEASE file(s), which will mostly only affect users of the Debian
|
||||
EPICS packages published by NSLS-2. Support modules may share an install path,
|
||||
but all such modules must be listed adjacent to each other in any RELEASE files
|
||||
that point to them. For example the following will fail the new checks:</p>
|
||||
|
||||
<blockquote><pre>
|
||||
AUTOSAVE = /usr/lib/epics
|
||||
ASYN = /home/mdavidsaver/asyn
|
||||
EPICS_BASE = /usr/lib/epics
|
||||
</pre></blockquote>
|
||||
|
||||
<p>giving the compile-time error</p>
|
||||
|
||||
<blockquote><pre>
|
||||
This application's RELEASE file(s) define
|
||||
EPICS_BASE = /usr/lib/epics
|
||||
after but not adjacent to
|
||||
AUTOSAVE = /usr/lib/epics
|
||||
Module definitions that share paths must be grouped together.
|
||||
Either remove a definition, or move it to a line immediately
|
||||
above or below the other(s).
|
||||
Any non-module definitions belong in configure/CONFIG_SITE.
|
||||
</pre></blockquote>
|
||||
|
||||
|
||||
<p>In many cases such as the one above the order of the <tt>AUTOSAVE</tt> and
|
||||
<tt>ASYN</tt> lines can be swapped to let the checks pass, but if the
|
||||
<tt>AUTOSAVE</tt> module depended on <tt>ASYN</tt> and hence had to appear
|
||||
before it in the list this error indicates that <tt>AUTOSAVE</tt> should also be
|
||||
built in its own private area; a shared copy would likely be incompatible with
|
||||
the version of <tt>ASYN</tt> built in the home directory.</p>
|
||||
|
||||
<h3>String field buffer overflows</h3>
|
||||
|
||||
<p>Two buffer overflow bugs that can crash the IOC have been fixed, caused by
|
||||
initializing a string field with a value larger than the field size
|
||||
(<a href="https://bugs.launchpad.net/bugs/1563191">Launchpad bug
|
||||
#1563191</a>).</p>
|
||||
|
||||
<h3>Fixed stack corruption bug in epicsThread C++ API</h3>
|
||||
|
||||
<p>The C++ interface to the epicsThread API could corrupt the stack on thread
|
||||
exit in some rare circumstances, usually at program exit. This bug has been
|
||||
fixed (<a href="https://bugs.launchpad.net/bugs/1558206">Launchpad bug
|
||||
#1558206</a>).</p>
|
||||
|
||||
<h3>RTEMS NTP Support Issue</h3>
|
||||
|
||||
<p>On RTEMS the NTP Time Provider could in some circumstances get out of sync
|
||||
with the server because the osdNTPGet() code wasn't clearing its input socket
|
||||
before sending out a new request. This
|
||||
(<a href="https://bugs.launchpad.net/bugs/1549908">Launchpad bug 1549908</a>)
|
||||
has now been fixed.</p>
|
||||
|
||||
<h3>CALC engine bitwise operator fixes</h3>
|
||||
|
||||
<p>The bitwise operators in the CALC engine have been modified to work properly
|
||||
with values that have bit 31 (0x80000000) set. This modification involved
|
||||
back-porting some earlier changes from the 3.15 branch, and fixes
|
||||
<a href="https://code.launchpad.net/bugs/1514520">Launchpad bug
|
||||
#1514520</a>.</p>
|
||||
|
||||
<h3>Fix <tt>ipAddrToAsciiAsync()</tt>: Don't try to join the daemon thread</h3>
|
||||
|
||||
<p>On process exit, don't try to stop the worker thread that makes DNS lookups
|
||||
asynchronous. Previously this would wait for any lookups still in progress,
|
||||
delaying the exit unnecessarily. This was most obvious with catools (eg.
|
||||
cainfo).
|
||||
<a href="https://bugs.launchpad.net/bugs/1527636">lp:1527636</a></p>
|
||||
|
||||
<h3>Fix <tt>epicsTime_localtime()</tt> on Windows</h3>
|
||||
|
||||
<p>Simpler versions of the epicsTime_gmtime() and epicsTime_localtime()
|
||||
routines have been included in the Windows implementations, and a new test
|
||||
program added. The original versions do not report DST status properly. Fixes
|
||||
<a href="https://bugs.launchpad.net/bugs/1528284">Launchpad bug 1528284</a>.</p>
|
||||
|
||||
|
||||
<h2 align="center">Changes between 3.15.2 and 3.15.3</h2>
|
||||
|
||||
<h3>Make the NTP Time provider optional on VxWorks</h3>
|
||||
|
||||
<p>Recent versions of VxWorks (sometime after VxWorks 6) provide facilities for
|
||||
automatically synchronizing the OS clock time with an NTP server. The EPICS time
|
||||
system used to assume that it had to provide time synchronization on VxWorks,
|
||||
but now it tests for the existance of either of the two OS synchronization
|
||||
threads before starting the NTP time provider. It is still possible to force the
|
||||
NTP provider to be started even if the OS synchronization is running by defining
|
||||
the environment variable <tt>EPICS_TS_FORCE_NTPTIME</tt> in the startup script
|
||||
before loading the IOC's .munch file. Forcing may be necessary if the VxWorks
|
||||
image is not correctly configured with the IP address of a local NTP server.</p>
|
||||
|
||||
<h3>Assembling files from numbered snippets</h3>
|
||||
|
||||
<p>A tool has been added that assembles file snippets specified on the
|
||||
command line into a single output file, with sorting and replacing/adding of
|
||||
snippets done based on their file names. The build system integration requires
|
||||
the output file to be specified setting COMMON_ASSEMBLIES (arch independent)
|
||||
or ASSEMBLIES (created by arch), then defining the snippets for each assembly
|
||||
setting *_SNIPPETS (explicitly) or *_PATTERN (searched relative to all source
|
||||
directories).
|
||||
</p>
|
||||
|
||||
<h3>Clean up after GNU readline()</h3>
|
||||
|
||||
<p>If EPICS Base is built with readline support, any IOC that calls epicsExit()
|
||||
from a thread other than the main thread is likely to leave the user's terminal
|
||||
in a weird state, requiring the user to run something like 'stty sane' to clean
|
||||
it up. This release patches the readline support code to clean up automatically
|
||||
by registering an epicsAtExit() routine.</p>
|
||||
|
||||
<h3>Removed the last vestiges of RSET::get_value()</h3>
|
||||
|
||||
<p>The IOC has not called the get_value() routine in the RSET for a very long
|
||||
time, but there was still one implementation left in the event record support
|
||||
code, and a structure definition for one of the original arguments to that
|
||||
routine was defined in recGbl.h. Both of these have now been removed.</p>
|
||||
|
||||
|
||||
<h2 align="center">Changes between 3.15.1 and 3.15.2</h2>
|
||||
<!-- Insert new items immediately below here ... -->
|
||||
|
||||
<h3>Raised limit on link field length in database files</h3>
|
||||
|
||||
@@ -128,6 +384,7 @@ use a modified version of msi must provide support for both the <tt>-D</tt> and
|
||||
<tt>-o outfile</tt> options, and should then point the MSI3_15 variable in
|
||||
their applications' CONFIG_SITE files to that updated executable.</p>
|
||||
|
||||
|
||||
<h2 align="center">Changes between 3.15.0.2 and 3.15.1</h2>
|
||||
|
||||
<h3>epicsStrnEscapedFromRaw() and epicsStrnRawFromEscaped()</h3>
|
||||
|
||||
@@ -60,11 +60,8 @@ responsible for these tasks in the past and are expected to continue in the
|
||||
relevent roles unless the Release Manager designates otherwise:</p>
|
||||
|
||||
<dl>
|
||||
<dt><strong>Release Manager</strong> (Andrew Johnson)</dt>
|
||||
<dd>Responsible for the release</dd>
|
||||
<dt><strong>Configuration Manager</strong> (Janet Anderson)</dt>
|
||||
<dd>Responsible for committing version number updates and for
|
||||
creating tarfiles</dd>
|
||||
<dt><strong>Release Manager</strong> (Ralph Lange)</dt>
|
||||
<dd>Responsible for managing and tagging the release</dd>
|
||||
<dt><strong>Platform Developers</strong></dt>
|
||||
<dd>Responsible for individual operating system platforms</dd>
|
||||
<dt><strong>Application Developers</strong></dt>
|
||||
@@ -134,42 +131,42 @@ relevent roles unless the Release Manager designates otherwise:</p>
|
||||
</tr>
|
||||
<tr>
|
||||
<td> </td>
|
||||
<td>Configuration Manager</td>
|
||||
<td>Release Manager</td>
|
||||
<td>Edit and commit changes to the EPICS version number file
|
||||
configure/CONFIG_BASE_VERSION.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td> </td>
|
||||
<td>Configuration Manager</td>
|
||||
<td>Release Manager</td>
|
||||
<td>Tag the module in Bazaar, using these tag conventions:
|
||||
<ul>
|
||||
<li>
|
||||
<tt>R3.15.2-pre<i>n</i></tt>
|
||||
<tt>R3.15.3-pre<i>n</i></tt>
|
||||
— pre-release tag
|
||||
</li>
|
||||
<li>
|
||||
<tt>R3.15.2-rc<i>n</i></tt>
|
||||
<tt>R3.15.3-rc<i>n</i></tt>
|
||||
— release candidate tag, note the <tt>rc</tt> is now
|
||||
lower-case</li>
|
||||
</ul>
|
||||
<blockquote><tt>
|
||||
cd ~/base/mirror-3.15<br />
|
||||
bzr tag R3.15.2-rc<i>n</i>
|
||||
bzr tag R3.15.3-rc<i>n</i>
|
||||
</tt></blockquote>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td> </td>
|
||||
<td>Configuration Manager</td>
|
||||
<td>Release Manager</td>
|
||||
<td>Export the tagged version into a tarfile. Note that this command
|
||||
generates a gzipped tarfile directly from the repository:
|
||||
<blockquote><tt>
|
||||
cd ~/base<br />
|
||||
bzr export --keywords=publish
|
||||
--root=base-3.15.2-rc<i>n</i>
|
||||
-r tag:R3.15.2-rc<i>n</i>
|
||||
baseR3.15.2-rc<i>n</i>.tar.gz
|
||||
mirror-3.15
|
||||
--root=base-3.15.3-rc<i>n</i>
|
||||
-r tag:R3.15.3-rc<i>n</i>
|
||||
base-3.15.3-rc<i>n</i>.tar.gz
|
||||
mirror-3.15
|
||||
</tt></blockquote>
|
||||
This requires that the Bazaar keywords plugin is installed and
|
||||
configured properly.
|
||||
@@ -177,14 +174,15 @@ relevent roles unless the Release Manager designates otherwise:</p>
|
||||
</tr>
|
||||
<tr>
|
||||
<td> </td>
|
||||
<td>Configuration Manager</td>
|
||||
<td>Release Manager</td>
|
||||
<td>Test the tarfile by extracting its contents and building it on at
|
||||
least one supported platform.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td> </td>
|
||||
<td>Configuration Manager</td>
|
||||
<td>Copy the tar file to the Base download area of the website.</td>
|
||||
<td>Website Manager</td>
|
||||
<td>If necessary recreate the tarfile following the instructions above.
|
||||
Copy the tar file to the Base download area of the website</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td> </td>
|
||||
@@ -223,13 +221,6 @@ relevent roles unless the Release Manager designates otherwise:</p>
|
||||
<td>Run the CA client side regression tests on all available host
|
||||
platforms.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td> </td>
|
||||
<td>Platform Developers</td>
|
||||
<td>Build and run the
|
||||
<a href="https://launchpad.net/epics-base-tests">epics-base-tests</a>
|
||||
suite on all available platforms.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td> </td>
|
||||
<td>Platform Developers</td>
|
||||
@@ -279,32 +270,32 @@ relevent roles unless the Release Manager designates otherwise:</p>
|
||||
</tr>
|
||||
<tr>
|
||||
<td> </td>
|
||||
<td>Configuration Manager</td>
|
||||
<td>Release Manager</td>
|
||||
<td>Edit and commit changes to the EPICS version number file
|
||||
configure/CONFIG_BASE_VERSION.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td> </td>
|
||||
<td>Configuration Manager</td>
|
||||
<td>Release Manager</td>
|
||||
<td>Tag the module in Bazaar:
|
||||
<blockquote><tt>
|
||||
cd ~/base/mirror-3.15<br />
|
||||
bzr tag R3.15.2</i>
|
||||
bzr tag R3.15.3</i>
|
||||
</tt></blockquote>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td> </td>
|
||||
<td>Configuration Manager</td>
|
||||
<td>Release Manager</td>
|
||||
<td>Export the tagged version into a tarfile. Note that this command
|
||||
generates a gzipped tarfile directly from the repository:
|
||||
<blockquote><tt>
|
||||
cd ~/base<br />
|
||||
bzr export --keywords=publish
|
||||
--root=base-3.15.2
|
||||
-r tag:R3.15.2
|
||||
baseR3.15.2.tar.gz
|
||||
mirror-3.15
|
||||
--root=base-3.15.3
|
||||
-r tag:R3.15.3
|
||||
base-3.15.3.tar.gz
|
||||
mirror-3.15
|
||||
</tt></blockquote>
|
||||
This requires that the Bazaar keywords plugin is installed and
|
||||
configured properly.
|
||||
@@ -312,7 +303,7 @@ relevent roles unless the Release Manager designates otherwise:</p>
|
||||
</tr>
|
||||
<tr>
|
||||
<td> </td>
|
||||
<td>Configuration Manager</td>
|
||||
<td>Release Manager</td>
|
||||
<td>Test the tar file by extracting its contents and building it on at
|
||||
least one supported platform</td>
|
||||
</tr>
|
||||
@@ -321,8 +312,9 @@ relevent roles unless the Release Manager designates otherwise:</p>
|
||||
</tr>
|
||||
<tr>
|
||||
<td> </td>
|
||||
<td>Configuration Manager</td>
|
||||
<td>Copy the tar file to the Base download area of the website</td>
|
||||
<td>Website Manager</td>
|
||||
<td>If necessary recreate the tarfile following the instructions above.
|
||||
Copy the tar file to the Base download area of the website</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td> </td>
|
||||
|
||||
@@ -209,6 +209,7 @@ $Date$</span></small></p>
|
||||
<li><a href="#ca_pend_event">ca_poll</a></li>
|
||||
<li><a href="#ca_puser">ca_puser</a></li>
|
||||
<li><a href="#ca_put">ca_put</a></li>
|
||||
<li><a href="#ca_put">ca_put_callback</a></li>
|
||||
<li><a href="#ca_set_puser">ca_set_puser</a></li>
|
||||
<li><a href="#ca_signal">ca_signal</a></li>
|
||||
<li><a href="#ca_sg_block">ca_sg_block</a></li>
|
||||
@@ -223,7 +224,7 @@ $Date$</span></small></p>
|
||||
<li><a href="#ca_test_io">ca_test_io</a></li>
|
||||
<li><a href="#ca_write_access">ca_write_access</a></li>
|
||||
<li><a href="#ca_state">channel_state</a></li>
|
||||
<li><a href="#dbr_size[]">dbr_size[]</a></li>
|
||||
<li><a href="#dbr_size">dbr_size[]</a></li>
|
||||
<li><a href="#dbr_size_n">dbr_size_n</a></li>
|
||||
<li><a href="#dbr_value_size">dbr_value_size[]</a></li>
|
||||
<li><a href="#dbr_type_t">dbr_type_to_text</a></li>
|
||||
@@ -840,23 +841,18 @@ seconds. See also <a href="#Disconnect">EPICS_CA_CONN_TMO</a> and
|
||||
Interval</a>.</p>
|
||||
|
||||
<p>CA servers build a list of addresses to send beacons to during
|
||||
initialization. If EPICS_CAS_AUTO_BEACON_ADDR_LIST has the value "YES" then the
|
||||
beacon address list will be automatically configured to contain the broadcast
|
||||
addresses of all LAN interfaces found in the host and the destination address
|
||||
of all point-to-point interfaces found in the host. However, if the user also
|
||||
initialization. If EPICS_CAS_AUTO_BEACON_ADDR_LIST has the value "YES"
|
||||
(the default) this list will be automatically populated with the broadcast
|
||||
addresses of all network interfaces. However, if the user also
|
||||
defines EPICS_CAS_INTF_ADDR_LIST then beacon address list automatic
|
||||
configuration is constrained to the network interfaces specified therein, and
|
||||
therefore only the broadcast addresses of the specified LAN interfaces, and the
|
||||
destination addresses of all specified point-to-point links, will be
|
||||
therefore only the broadcast addresses of the specified LAN interfaces, will be
|
||||
automatically configured.</p>
|
||||
|
||||
<p>If EPICS_CAS_BEACON_ADDR_LIST is defined then its contents will be used to
|
||||
augment any automatic configuration of the beacon address list. Individual
|
||||
entries in EPICS_CAS_BEACON_ADDR_LIST may override the destination port number
|
||||
if ":nnn" follows the host name or IP address there. Alternatively, when both
|
||||
EPICS_CAS_BEACON_ADDR_LIST and EPICS_CAS_INTF_ADDR_LIST are not defined then
|
||||
the contents of EPICS_CA_ADDR_LIST is used to augment the list. Otherwise, the
|
||||
list is not augmented.</p>
|
||||
if ":nnn" follows the host name or IP address there.</p>
|
||||
|
||||
<p>The EPICS_CAS_BEACON_PORT parameter specifies the destination port for
|
||||
server beacons. The only exception to this occurs when ports are specified in
|
||||
@@ -867,20 +863,18 @@ specified in EPICS_CA_REPEATER_PORT.</p>
|
||||
<h4>Binding a Server to a Limited Set of Network Interfaces</h4>
|
||||
|
||||
<p>The parameter EPICS_CAS_INTF_ADDR_LIST allows a ca server to bind itself to,
|
||||
and therefore accept messages only over, a limited set of the local host's
|
||||
and therefore accept messages received by, a limited set of the local host's
|
||||
network interfaces (each specified by its IP address). On UNIX systems type
|
||||
"netstat -i" (type "ipconfig" on windows) to see a list of the local host's
|
||||
network interfaces. Specifically, UDP search messages addressed to both the IP
|
||||
addresses in EPICS_CAS_INTF_ADDR_LIST and also to the broadcast addresses of
|
||||
the corresponding LAN interfaces will be accepted by the server. By default,
|
||||
"netstat -ie" (type "ipconfig" on windows) to see a list of the local host's
|
||||
network interfaces. By default,
|
||||
the CA server is accessible from all network interfaces configured into its
|
||||
host.</p>
|
||||
|
||||
<p>In R3.14 and previous releases the CA server employed by iocCore did not
|
||||
implement the EPICS_CAS_INTF_ADDR_LIST feature. In this release the iocCore
|
||||
server will read the first IP address from the parameter variable and use that
|
||||
to select which interface to bind to. Any additional IP addresses will be
|
||||
ignored and a warning message displayed during IOC initialization.</p>
|
||||
<p>Until R3.15.4 the CA server employed by iocCore did not
|
||||
implement the EPICS_CAS_INTF_ADDR_LIST feature.</p>
|
||||
|
||||
<p>Prior to R3.15.4 CA servers would build the beacon address list
|
||||
using EPICS_CA_ADDR_LIST if EPICS_CAS_BEACON_ADDR_LIST was no set.</p>
|
||||
|
||||
<h4>Ignoring Process Variable Name Resolution Requests From Certain Hosts</h4>
|
||||
|
||||
@@ -3831,7 +3825,7 @@ specified channel and boolean false otherwise.</p>
|
||||
channel and boolean false otherwise</dd>
|
||||
</dl>
|
||||
|
||||
<h3><code><a name="dbr_size[]">dbr_size[]</a></code></h3>
|
||||
<h3><code><a name="dbr_size">dbr_size[]</a></code></h3>
|
||||
<pre>#include <db_access.h>
|
||||
extern unsigned dbr_size[/* TYPE */];</pre>
|
||||
|
||||
@@ -3953,11 +3947,12 @@ int ca_sg_create ( CA_SYNC_GID *PGID );</pre>
|
||||
|
||||
<p>A synchronous group can be used to guarantee that a set of channel access
|
||||
requests have completed. Once a synchronous group has been created then channel
|
||||
access get and put requests may be issued within it using <code>ca_sg_get()</code> and
|
||||
<code>ca_sg_put()</code> respectively. The routines <code>ca_sg_block()</code> and <code>ca_sg_test()</code> can be
|
||||
access get and put requests may be issued within it using
|
||||
<code>ca_sg_array_get()</code> and <code>ca_sg_array_put()</code> respectively.
|
||||
The routines <code>ca_sg_block()</code> and <code>ca_sg_test()</code> can be
|
||||
used to block for and test for completion respectively. The routine
|
||||
<code>ca_sg_reset()</code> is used to discard knowledge of old requests which have timed out
|
||||
and in all likelihood will never be satisfied.</p>
|
||||
<code>ca_sg_reset()</code> is used to discard knowledge of old requests which
|
||||
have timed out and in all likelihood will never be satisfied.</p>
|
||||
|
||||
<p>Any number of asynchronous groups can have application requested operations
|
||||
outstanding within them at any given time.</p>
|
||||
@@ -4131,16 +4126,20 @@ status = ca_sg_reset(gid);</pre>
|
||||
|
||||
<h3><code><a name="ca_sg_put">ca_sg_array_put()</a></code></h3>
|
||||
<pre>#include <cadef.h>
|
||||
int ca_sg_put ( CA_SYNC_GID GID, chtype TYPE,
|
||||
chid CHID, void *PVALUE );
|
||||
int ca_sg_array_put ( CA_SYNC_GID GID, chtype TYPE,
|
||||
unsigned long COUNT, chid CHID, void *PVALUE );</pre>
|
||||
|
||||
<p>Write a value, or array of values, to a channel and increment the
|
||||
outstanding request count of a synchronous group. The <code>ca_sg_array_put()</code>
|
||||
functionality is implemented using <code>ca_array_put_callback()</code>.</p>
|
||||
<p>Write a value, or array of values, to a channel and increment the outstanding
|
||||
request count of a synchronous group. The <code>ca_sg_put()</code> and
|
||||
<code>ca_sg_array_put()</code> functionality is implemented using
|
||||
<code>ca_array_put_callback()</code>.</p>
|
||||
|
||||
<p>All remote operation requests such as the above are accumulated (buffered)
|
||||
and not forwarded to the server until one of <code>ca_flush_io()</code>, <code>ca_pend_io()</code>,
|
||||
<code>ca_pend_event()</code>, or <code>ca_sg_block()</code> are called. This allows several requests to be
|
||||
and not forwarded to the server until one of <code>ca_flush_io()</code>,
|
||||
<code>ca_pend_io()</code>, <code>ca_pend_event()</code>, or
|
||||
<code>ca_sg_block()</code> are called. This allows several requests to be
|
||||
efficiently sent in one message.</p>
|
||||
|
||||
<p>If a connection is lost and then resumed outstanding puts are not
|
||||
@@ -4193,6 +4192,8 @@ reissued.</p>
|
||||
|
||||
<h3><code><a name="ca_sg_get">ca_sg_array_get()</a></code></h3>
|
||||
<pre>#include <cadef.h>
|
||||
int ca_sg_get ( CA_SYNC_GID GID, chtype TYPE,
|
||||
chid CHID, void *PVALUE );
|
||||
int ca_sg_array_get ( CA_SYNC_GID GID,
|
||||
chtype TYPE, unsigned long COUNT,
|
||||
chid CHID, void *PVALUE );</pre>
|
||||
@@ -4200,16 +4201,19 @@ int ca_sg_array_get ( CA_SYNC_GID GID,
|
||||
<h4>Description</h4>
|
||||
|
||||
<p>Read a value from a channel and increment the outstanding request count of a
|
||||
synchronous group. The <code>ca_sg_array_get()</code> functionality is implemented using
|
||||
synchronous group. The <code>ca_sg_get()</code> and
|
||||
<code>ca_sg_array_get()</code> functionality is implemented using
|
||||
<code>ca_array_get_callback()</code>.</p>
|
||||
|
||||
<p>The values written into your program's variables by <code>ca_sg_get()</code> should not be
|
||||
referenced by your program until ECA_NORMAL has been received from <code>ca_sg_block()</code>,
|
||||
or until <code>ca_sg_test()</code> returns ECA_IODONE.</p>
|
||||
<p>The values written into your program's variables by <code>ca_sg_get()</code>
|
||||
or <code>ca_sg_array_get()</code> should not be referenced by your program until
|
||||
ECA_NORMAL has been received from <code>ca_sg_block()</code>, or until
|
||||
<code>ca_sg_test()</code> returns ECA_IODONE.</p>
|
||||
|
||||
<p>All remote operation requests such as the above are accumulated (buffered)
|
||||
and not forwarded to the server until one of <code>ca_flush_io()</code>, <code>ca_pend_io()</code>,
|
||||
<code>ca_pend_event()</code>, or <code>ca_sg_block()</code> are called. This allows several requests to be
|
||||
and not forwarded to the server until one of <code>ca_flush_io()</code>,
|
||||
<code>ca_pend_io()</code>, <code>ca_pend_event()</code>, or
|
||||
<code>ca_sg_block()</code> are called. This allows several requests to be
|
||||
efficiently sent in one message.</p>
|
||||
|
||||
<p>If a connection is lost and then resumed outstanding gets are not
|
||||
|
||||
@@ -22,7 +22,7 @@ extern "C" {
|
||||
epicsShareFunc void epicsShareAPI configureChannelAccessAddressList
|
||||
( struct ELLLIST *pList, SOCKET sock, unsigned short port );
|
||||
|
||||
epicsShareFunc void epicsShareAPI addAddrToChannelAccessAddressList
|
||||
epicsShareFunc int epicsShareAPI addAddrToChannelAccessAddressList
|
||||
( struct ELLLIST *pList, const ENV_PARAM *pEnv,
|
||||
unsigned short port, int ignoreNonDefaultPort );
|
||||
|
||||
|
||||
@@ -333,6 +333,12 @@ cac::~cac ()
|
||||
|
||||
this->ipToAEngine.release ();
|
||||
|
||||
// clean-up the list of un-notified msg objects
|
||||
while ( msgForMultiplyDefinedPV * msg = this->msgMultiPVList.get() ) {
|
||||
msg->~msgForMultiplyDefinedPV ();
|
||||
this->mdpvFreeList.release ( msg );
|
||||
}
|
||||
|
||||
errlogFlush ();
|
||||
|
||||
osiSockRelease ();
|
||||
@@ -606,6 +612,8 @@ void cac::transferChanToVirtCircuit (
|
||||
msgForMultiplyDefinedPV * pMsg = new ( this->mdpvFreeList )
|
||||
msgForMultiplyDefinedPV ( this->ipToAEngine,
|
||||
*this, pChan->pName ( guard ), acc );
|
||||
// cac keeps a list of these objects for proper clean-up in ~cac
|
||||
this->msgMultiPVList.add ( *pMsg );
|
||||
// It is possible for the ioInitiate call below to
|
||||
// call the callback directly if queue quota is exceeded.
|
||||
// This callback takes the callback lock and therefore we
|
||||
@@ -1297,6 +1305,8 @@ void cac::pvMultiplyDefinedNotify ( msgForMultiplyDefinedPV & mfmdpv,
|
||||
epicsGuard < epicsMutex > guard ( this->mutex );
|
||||
this->exception ( mgr.cbGuard, guard, ECA_DBLCHNL, buf, __FILE__, __LINE__ );
|
||||
}
|
||||
// remove from the list and delete msg object
|
||||
this->msgMultiPVList.remove ( mfmdpv );
|
||||
mfmdpv.~msgForMultiplyDefinedPV ();
|
||||
this->mdpvFreeList.release ( & mfmdpv );
|
||||
}
|
||||
|
||||
@@ -237,6 +237,7 @@ private:
|
||||
resTable < tcpiiu, caServerID > serverTable;
|
||||
tsDLList < tcpiiu > circuitList;
|
||||
tsDLList < SearchDest > searchDestList;
|
||||
tsDLList < msgForMultiplyDefinedPV > msgMultiPVList;
|
||||
tsFreeList
|
||||
< class tcpiiu, 32, epicsMutexNOOP >
|
||||
freeListVirtualCircuit;
|
||||
|
||||
@@ -73,7 +73,7 @@ static char *getToken ( const char **ppString, char *pBuf, unsigned bufSIze )
|
||||
/*
|
||||
* addAddrToChannelAccessAddressList ()
|
||||
*/
|
||||
extern "C" void epicsShareAPI addAddrToChannelAccessAddressList
|
||||
extern "C" int epicsShareAPI addAddrToChannelAccessAddressList
|
||||
( ELLLIST *pList, const ENV_PARAM *pEnv,
|
||||
unsigned short port, int ignoreNonDefaultPort )
|
||||
{
|
||||
@@ -82,11 +82,11 @@ extern "C" void epicsShareAPI addAddrToChannelAccessAddressList
|
||||
const char *pToken;
|
||||
struct sockaddr_in addr;
|
||||
char buf[32u]; /* large enough to hold an IP address */
|
||||
int status;
|
||||
int status, ret = -1;
|
||||
|
||||
pStr = envGetConfigParamPtr (pEnv);
|
||||
if (!pStr) {
|
||||
return;
|
||||
return ret;
|
||||
}
|
||||
|
||||
while ( ( pToken = getToken (&pStr, buf, sizeof (buf) ) ) ) {
|
||||
@@ -104,7 +104,7 @@ extern "C" void epicsShareAPI addAddrToChannelAccessAddressList
|
||||
pNewNode = (osiSockAddrNode *) calloc (1, sizeof(*pNewNode));
|
||||
if (pNewNode==NULL) {
|
||||
fprintf ( stderr, "addAddrToChannelAccessAddressList(): no memory available for configuration\n");
|
||||
return;
|
||||
break;
|
||||
}
|
||||
|
||||
pNewNode->addr.ia = addr;
|
||||
@@ -113,9 +113,10 @@ extern "C" void epicsShareAPI addAddrToChannelAccessAddressList
|
||||
* LOCK applied externally
|
||||
*/
|
||||
ellAdd (pList, &pNewNode->node);
|
||||
ret = 0; /* success if anything is added to the list */
|
||||
}
|
||||
|
||||
return;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -55,8 +55,10 @@ msgForMultiplyDefinedPV::~msgForMultiplyDefinedPV ()
|
||||
|
||||
void msgForMultiplyDefinedPV::transactionComplete ( const char * pHostNameRej )
|
||||
{
|
||||
// calls into cac for the notification
|
||||
// the msg object (= this) is being deleted as part of the notification
|
||||
this->cb.pvMultiplyDefinedNotify ( *this, this->channel, this->acc, pHostNameRej );
|
||||
// !! dont touch this pointer after this point because object has been deleted !!
|
||||
// !! dont touch 'this' pointer after this point because object has been deleted !!
|
||||
}
|
||||
|
||||
void * msgForMultiplyDefinedPV::operator new ( size_t size,
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
|
||||
#include "ipAddrToAsciiAsynchronous.h"
|
||||
#include "tsFreeList.h"
|
||||
#include "tsDLList.h"
|
||||
#include "compilerDependencies.h"
|
||||
|
||||
#ifdef msgForMultiplyDefinedPVh_epicsExportSharedSymbols
|
||||
@@ -47,7 +48,9 @@ public:
|
||||
const char * pAcc, const char * pRej ) = 0;
|
||||
};
|
||||
|
||||
class msgForMultiplyDefinedPV : public ipAddrToAsciiCallBack {
|
||||
class msgForMultiplyDefinedPV :
|
||||
public ipAddrToAsciiCallBack,
|
||||
public tsDLNode < msgForMultiplyDefinedPV > {
|
||||
public:
|
||||
msgForMultiplyDefinedPV ( ipAddrToAsciiEngine & engine,
|
||||
callbackForMultiplyDefinedPV &, const char * pChannelName,
|
||||
@@ -62,8 +65,8 @@ private:
|
||||
ipAddrToAsciiTransaction & dnsTransaction;
|
||||
callbackForMultiplyDefinedPV & cb;
|
||||
void transactionComplete ( const char * pHostName );
|
||||
msgForMultiplyDefinedPV ( const msgForMultiplyDefinedPV & );
|
||||
msgForMultiplyDefinedPV & operator = ( const msgForMultiplyDefinedPV & );
|
||||
msgForMultiplyDefinedPV ( const msgForMultiplyDefinedPV & );
|
||||
msgForMultiplyDefinedPV & operator = ( const msgForMultiplyDefinedPV & );
|
||||
void operator delete ( void * );
|
||||
};
|
||||
|
||||
|
||||
@@ -1138,6 +1138,20 @@ void CA_add_exception_event(const char *class, SV *sub) {
|
||||
static
|
||||
SV * printf_sub = NULL;
|
||||
|
||||
#ifndef va_copy
|
||||
# ifdef __GNUC__
|
||||
# define va_copy(d, s) __va_copy(d, s)
|
||||
# else
|
||||
# define va_copy(d, s) ((d) = (s))
|
||||
/* The above macro is NOT PORTABLE but works on Windows when
|
||||
* stdarg.h doesn't provide va_copy(). Some architectures need
|
||||
* define va_copy(d, s) ((*d) = (*s))
|
||||
* while others may be even more complicated, but hopefully the
|
||||
* system stdarg.h header defines va_copy() in those cases.
|
||||
*/
|
||||
# endif
|
||||
#endif
|
||||
|
||||
static
|
||||
int printf_handler(const char *format, va_list args) {
|
||||
if (! printf_sub)
|
||||
@@ -1152,11 +1166,7 @@ int printf_handler(const char *format, va_list args) {
|
||||
ENTER;
|
||||
SAVETMPS;
|
||||
|
||||
#ifdef __GNUC__
|
||||
__va_copy(argcopy, args);
|
||||
#else
|
||||
va_copy(argcopy, args);
|
||||
#endif
|
||||
|
||||
printf_str = NEWSV(0, strlen(format) + 32);
|
||||
sv_vsetpvf(printf_str, format, &argcopy);
|
||||
|
||||
@@ -20,10 +20,19 @@ ifeq ($(OS_CLASS),Darwin)
|
||||
LOADABLE_SHRLIB_SUFFIX = .$(shell $(PERL) ../perlConfig.pl dlext)
|
||||
endif
|
||||
|
||||
PERL_VERSION = $(shell $(PERL) ../perlConfig.pl version)
|
||||
PERL_ARCHNAME = $(shell $(PERL) ../perlConfig.pl archname)
|
||||
PERL_ARCHPATH = $(PERL_VERSION)/$(PERL_ARCHNAME)
|
||||
ifdef T_A
|
||||
PERL_VERSION = $(shell $(PERL) ../perlConfig.pl version)
|
||||
PERL_ARCHNAME = $(shell $(PERL) ../perlConfig.pl archname)
|
||||
PERL_ARCHPATH := $(PERL_VERSION)/$(PERL_ARCHNAME)
|
||||
|
||||
EXTUTILS := $(shell $(PERL) ../perlConfig.pl privlib)/ExtUtils
|
||||
PERLBIN := $(shell $(PERL) ../perlConfig.pl bin)
|
||||
XSUBPP := $(firstword $(wildcard $(PERLBIN)/xsubpp $(EXTUTILS)/xsubpp))
|
||||
|
||||
ifeq ($(strip $(XSUBPP)),)
|
||||
$(warning Perl's xsubpp program was not found.)
|
||||
$(warning The Perl CA module will not be built.)
|
||||
else
|
||||
ifeq ($(T_A),$(EPICS_HOST_ARCH)) # No cross-builds (wrong Perl!)
|
||||
ifeq ($(findstring $(OS_CLASS),WIN32 cygwin32),) # Doesn't build on WIN32
|
||||
LOADABLE_LIBRARY_HOST = Cap5
|
||||
@@ -41,6 +50,8 @@ ifeq ($(findstring $(OS_CLASS),WIN32 cygwin32),) # Doesn't build on WIN32
|
||||
HTMLS = CA.html
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
|
||||
Cap5_SRCS = Cap5.xs
|
||||
Cap5_LIBS = ca Com
|
||||
@@ -52,10 +63,6 @@ CLEANS += Cap5.c pod2htmd.tmp pod2htmi.tmp
|
||||
include $(TOP)/configure/RULES
|
||||
|
||||
ifdef T_A
|
||||
EXTUTILS = $(shell $(PERL) ../perlConfig.pl privlib)/ExtUtils
|
||||
PERLBIN = $(shell $(PERL) ../perlConfig.pl bin)
|
||||
XSUBPP = $(firstword $(wildcard $(PERLBIN)/xsubpp $(EXTUTILS)/xsubpp))
|
||||
|
||||
%.c: ../%.xs
|
||||
$(RM) $@ $@_new
|
||||
$(PERL) $(XSUBPP) -typemap $(EXTUTILS)/typemap $< > $@_new && $(MV) $@_new $@
|
||||
|
||||
@@ -3,7 +3,13 @@
|
||||
# This script is used to extract information about the Perl build
|
||||
# configuration, so the EPICS build system uses the same settings.
|
||||
|
||||
use strict;
|
||||
use Config;
|
||||
|
||||
my $arg = shift;
|
||||
print $Config{$arg};
|
||||
my $val = $Config{$arg};
|
||||
|
||||
$val =~ s{\\}{/}go
|
||||
if $^O eq 'MSWin32';
|
||||
|
||||
print $val;
|
||||
|
||||
@@ -492,14 +492,13 @@ int main (int argc, char *argv[])
|
||||
} else { /* Not an ENUM */
|
||||
|
||||
if (charArrAsStr) {
|
||||
count = len;
|
||||
dbrType = DBR_CHAR;
|
||||
ebuf = calloc(strlen(cbuf)+1, sizeof(char));
|
||||
ebuf = calloc(len, sizeof(char));
|
||||
if(!ebuf) {
|
||||
fprintf(stderr, "Memory allocation failed\n");
|
||||
return 1;
|
||||
}
|
||||
epicsStrnRawFromEscaped(ebuf, strlen(cbuf)+1, cbuf, strlen(cbuf));
|
||||
count = epicsStrnRawFromEscaped(ebuf, len, cbuf, len-1) + 1;
|
||||
} else {
|
||||
for (i = 0; i < count; ++i) {
|
||||
epicsStrnRawFromEscaped(sbuf[i], sizeof(EpicsStr), *(argv+optind+i), sizeof(EpicsStr));
|
||||
|
||||
@@ -163,6 +163,19 @@ udpiiu::udpiiu (
|
||||
throwWithLocation ( noSocket () );
|
||||
}
|
||||
|
||||
#ifdef IP_ADD_MEMBERSHIP
|
||||
{
|
||||
int flag = 1;
|
||||
if ( setsockopt ( this->sock, IPPROTO_IP, IP_MULTICAST_LOOP,
|
||||
(char *) &flag, sizeof ( flag ) ) == -1 ) {
|
||||
char sockErrBuf[64];
|
||||
epicsSocketConvertErrnoToString (
|
||||
sockErrBuf, sizeof ( sockErrBuf ) );
|
||||
errlogPrintf("CAC: failed to set mcast loopback\n");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
int boolValue = true;
|
||||
int status = setsockopt ( this->sock, SOL_SOCKET, SO_BROADCAST,
|
||||
(char *) &boolValue, sizeof ( boolValue ) );
|
||||
|
||||
@@ -69,6 +69,11 @@ CLEANS += $(COMMON_DIR)/aitConvertGenerated.cc
|
||||
USR_CXXFLAGS_Linux = -fno-strict-aliasing
|
||||
USR_CXXFLAGS_RTEMS = -fno-strict-aliasing
|
||||
|
||||
ifeq ($(T_A),$(EPICS_HOST_ARCH))
|
||||
# genApps and aitGen are needed to finish libgdd
|
||||
DELAY_INSTALL_LIBS = YES
|
||||
endif
|
||||
|
||||
include $(TOP)/configure/RULES
|
||||
|
||||
# Manual dependencies
|
||||
|
||||
@@ -141,6 +141,39 @@ caStatus caServerI::attachInterface ( const caNetAddr & addrIn,
|
||||
return S_cas_success;
|
||||
}
|
||||
|
||||
void caServerI::addMCast(const osiSockAddr& addr)
|
||||
{
|
||||
#ifdef IP_ADD_MEMBERSHIP
|
||||
epicsGuard < epicsMutex > locker ( this->mutex );
|
||||
tsDLIter < casIntfOS > iter = this->intfList.firstIter ();
|
||||
while ( iter.valid () ) {
|
||||
struct ip_mreq mreq;
|
||||
|
||||
memset(&mreq, 0, sizeof(mreq));
|
||||
mreq.imr_interface = iter->serverAddress().getSockIP().sin_addr;
|
||||
mreq.imr_multiaddr = addr.ia.sin_addr;
|
||||
|
||||
if ( setsockopt ( iter->casDGIntfIO::getFD (), IPPROTO_IP,
|
||||
IP_ADD_MEMBERSHIP, (char *) &mreq,
|
||||
sizeof ( mreq ) ) < 0) {
|
||||
struct sockaddr_in temp;
|
||||
char name[40];
|
||||
char sockErrBuf[64];
|
||||
temp.sin_family = AF_INET;
|
||||
temp.sin_addr = mreq.imr_multiaddr;
|
||||
temp.sin_port = addr.ia.sin_port;
|
||||
epicsSocketConvertErrnoToString (
|
||||
sockErrBuf, sizeof ( sockErrBuf ) );
|
||||
ipAddrToDottedIP (&temp, name, sizeof(name));
|
||||
fprintf(stderr, "CAS: Socket mcast join %s failed with \"%s\"\n",
|
||||
name, sockErrBuf );
|
||||
}
|
||||
|
||||
iter++;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void caServerI::sendBeacon ( ca_uint32_t beaconNo )
|
||||
{
|
||||
epicsGuard < epicsMutex > locker ( this->mutex );
|
||||
|
||||
@@ -103,6 +103,8 @@ private:
|
||||
caStatus attachInterface ( const caNetAddr & addr, bool autoBeaconAddr,
|
||||
bool addConfigAddr );
|
||||
|
||||
virtual void addMCast(const osiSockAddr&);
|
||||
|
||||
void sendBeacon ( ca_uint32_t beaconNo );
|
||||
|
||||
caServerI ( const caServerI & );
|
||||
|
||||
@@ -348,10 +348,8 @@ caStatus casDGClient::searchResponse ( const caHdrLargeArray & msg,
|
||||
//
|
||||
caStatus casDGClient::searchFailResponse ( const caHdrLargeArray * mp )
|
||||
{
|
||||
int status;
|
||||
|
||||
epicsGuard < epicsMutex > guard ( this->mutex );
|
||||
status = this->out.copyInHeader ( CA_PROTO_NOT_FOUND, 0,
|
||||
this->out.copyInHeader ( CA_PROTO_NOT_FOUND, 0,
|
||||
mp->m_dataType, mp->m_count, mp->m_cid, mp->m_available, 0 );
|
||||
|
||||
this->out.commitMsg ();
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
//
|
||||
|
||||
#include <ctype.h>
|
||||
#include <list>
|
||||
|
||||
#include "epicsSignal.h"
|
||||
#include "envDefs.h"
|
||||
@@ -92,6 +93,9 @@ void caServerIO::locateInterfaces ()
|
||||
autoBeaconAddr = true;
|
||||
}
|
||||
|
||||
typedef std::list<osiSockAddr> mcastAddrs_t;
|
||||
mcastAddrs_t mcastAddrs;
|
||||
|
||||
//
|
||||
// bind to the the interfaces specified - otherwise wildcard
|
||||
// with INADDR_ANY and allow clients to attach from any interface
|
||||
@@ -114,6 +118,15 @@ void caServerIO::locateInterfaces ()
|
||||
pToken);
|
||||
continue;
|
||||
}
|
||||
|
||||
epicsUInt32 top = ntohl(saddr.sin_addr.s_addr)>>24;
|
||||
if (saddr.sin_family==AF_INET && top>=224 && top<=239) {
|
||||
osiSockAddr oaddr;
|
||||
oaddr.ia = saddr;
|
||||
mcastAddrs.push_back(oaddr);
|
||||
continue;
|
||||
}
|
||||
|
||||
stat = this->attachInterface (caNetAddr(saddr), autoBeaconAddr, configAddrOnceFlag);
|
||||
if (stat) {
|
||||
errMessage(stat, "unable to attach explicit interface");
|
||||
@@ -131,6 +144,10 @@ void caServerIO::locateInterfaces ()
|
||||
errMessage(stat, "unable to attach any interface");
|
||||
}
|
||||
}
|
||||
|
||||
for (mcastAddrs_t::const_iterator it = mcastAddrs.begin(); it!=mcastAddrs.end(); ++it) {
|
||||
this->addMCast(*it);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
@@ -47,6 +47,8 @@ private:
|
||||
virtual caStatus attachInterface (
|
||||
const caNetAddr & addr, bool autoBeaconAddr,
|
||||
bool addConfigAddr ) = 0;
|
||||
|
||||
virtual void addMCast(const osiSockAddr&) = 0;
|
||||
};
|
||||
|
||||
#endif // caServerIOh
|
||||
|
||||
@@ -19,6 +19,11 @@
|
||||
#include "addrList.h"
|
||||
#include "errlog.h"
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
# include <BaseTsd.h>
|
||||
typedef SSIZE_T ssize_t;
|
||||
#endif
|
||||
|
||||
#define epicsExportSharedSymbols
|
||||
#include "casDGIntfIO.h"
|
||||
#include "ipIgnoreEntry.h"
|
||||
@@ -148,26 +153,8 @@ casDGIntfIO::casDGIntfIO ( caServerI & serverIn, clientBufMemoryManager & memMgr
|
||||
}
|
||||
|
||||
if ( addConfigBeaconAddr ) {
|
||||
|
||||
//
|
||||
// by default use EPICS_CA_ADDR_LIST for the
|
||||
// beacon address list
|
||||
//
|
||||
const ENV_PARAM *pParam;
|
||||
|
||||
if ( envGetConfigParamPtr ( & EPICS_CAS_INTF_ADDR_LIST ) ||
|
||||
envGetConfigParamPtr ( & EPICS_CAS_BEACON_ADDR_LIST ) ) {
|
||||
pParam = & EPICS_CAS_BEACON_ADDR_LIST;
|
||||
}
|
||||
else {
|
||||
pParam = & EPICS_CA_ADDR_LIST;
|
||||
}
|
||||
|
||||
//
|
||||
// add in the configured addresses
|
||||
//
|
||||
addAddrToChannelAccessAddressList (
|
||||
& BCastAddrList, pParam, beaconPort, pParam == & EPICS_CA_ADDR_LIST );
|
||||
& BCastAddrList, &EPICS_CAS_BEACON_ADDR_LIST, beaconPort, 0 );
|
||||
}
|
||||
|
||||
removeDuplicateAddresses ( & this->beaconAddrList, & BCastAddrList, 0 );
|
||||
@@ -267,7 +254,7 @@ casDGIntfIO::~casDGIntfIO()
|
||||
|
||||
// avoid use of ellFree because problems on windows occur if the
|
||||
// free is in a different DLL than the malloc
|
||||
ELLNODE * nnode = this->beaconAddrList.node.next;
|
||||
ELLNODE * nnode = ellFirst(&this->beaconAddrList);
|
||||
while ( nnode )
|
||||
{
|
||||
ELLNODE * pnode = nnode;
|
||||
@@ -418,52 +405,24 @@ bufSizeT casDGIntfIO ::
|
||||
}
|
||||
|
||||
void casDGIntfIO::sendBeaconIO ( char & msg, unsigned length,
|
||||
aitUint16 & portField, aitUint32 & addrField )
|
||||
aitUint16 & portField, aitUint32 & )
|
||||
{
|
||||
caNetAddr addr = this->serverAddress ();
|
||||
struct sockaddr_in inetAddr = addr.getSockIP();
|
||||
osiSockAddrNode *pAddr;
|
||||
int status;
|
||||
char buf[64];
|
||||
|
||||
portField = inetAddr.sin_port; // the TCP port
|
||||
|
||||
for (pAddr = reinterpret_cast <osiSockAddrNode *> ( ellFirst ( & this->beaconAddrList ) );
|
||||
pAddr; pAddr = reinterpret_cast <osiSockAddrNode *> ( ellNext ( & pAddr->node ) ) ) {
|
||||
status = connect ( this->beaconSock, &pAddr->addr.sa, sizeof ( pAddr->addr.sa ) );
|
||||
if (status<0) {
|
||||
char sockErrBuf[64];
|
||||
epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) );
|
||||
ipAddrToDottedIP ( & pAddr->addr.ia, buf, sizeof ( buf ) );
|
||||
errlogPrintf ( "%s: CA beacon routing (connect to \"%s\") error was \"%s\"\n",
|
||||
__FILE__, buf, sockErrBuf );
|
||||
}
|
||||
else {
|
||||
osiSockAddr sockAddr;
|
||||
osiSocklen_t size = ( osiSocklen_t ) sizeof ( sockAddr.sa );
|
||||
status = getsockname ( this->beaconSock, &sockAddr.sa, &size );
|
||||
if ( status < 0 ) {
|
||||
char sockErrBuf[64];
|
||||
epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) );
|
||||
errlogPrintf ( "%s: CA beacon routing (getsockname) error was \"%s\"\n",
|
||||
__FILE__, sockErrBuf );
|
||||
}
|
||||
else if ( sockAddr.sa.sa_family == AF_INET ) {
|
||||
addrField = sockAddr.ia.sin_addr.s_addr;
|
||||
for (ELLNODE *pNode = ellFirst(&this->beaconAddrList); pNode; pNode = ellNext(pNode))
|
||||
{
|
||||
osiSockAddrNode *pAddr = reinterpret_cast<osiSockAddrNode *>(pNode);
|
||||
|
||||
status = send ( this->beaconSock, &msg, length, 0 );
|
||||
if ( status < 0 ) {
|
||||
char sockErrBuf[64];
|
||||
epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) );
|
||||
ipAddrToA ( &pAddr->addr.ia, buf, sizeof(buf) );
|
||||
errlogPrintf ( "%s: CA beacon (send to \"%s\") error was \"%s\"\n",
|
||||
__FILE__, buf, sockErrBuf );
|
||||
}
|
||||
else {
|
||||
unsigned statusAsLength = static_cast < unsigned > ( status );
|
||||
assert ( statusAsLength == length );
|
||||
}
|
||||
}
|
||||
ssize_t status = sendto(this->beaconSock, &msg, length, 0, &pAddr->addr.sa, sizeof(pAddr->addr.ia));
|
||||
if ( status != length ) {
|
||||
char sockErrBuf[64], buf[64];
|
||||
epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) );
|
||||
ipAddrToA ( &pAddr->addr.ia, buf, sizeof(buf) );
|
||||
errlogPrintf ( "%s: CA beacon (send to \"%s\") error was \"%s\" (%u)\n",
|
||||
__FILE__, buf, sockErrBuf, (unsigned)status );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,6 +54,7 @@ typedef struct cbQueueSet {
|
||||
epicsEventId semWakeUp;
|
||||
epicsRingPointerId queue;
|
||||
int queueOverflow;
|
||||
int shutdown;
|
||||
int threadsConfigured;
|
||||
int threadsRunning;
|
||||
} cbQueueSet;
|
||||
@@ -73,7 +74,8 @@ static epicsTimerQueueId timerQueue;
|
||||
enum ctl {ctlInit, ctlRun, ctlPause, ctlExit};
|
||||
static volatile enum ctl cbCtl;
|
||||
static epicsEventId startStopEvent;
|
||||
static void *exitCallback;
|
||||
|
||||
static int callbackIsInit;
|
||||
|
||||
/* Static data */
|
||||
static char *threadNamePrefix[NUM_CALLBACK_PRIORITIES] = {
|
||||
@@ -89,7 +91,7 @@ static int priorityValue[NUM_CALLBACK_PRIORITIES] = {0, 1, 2};
|
||||
|
||||
int callbackSetQueueSize(int size)
|
||||
{
|
||||
if (startStopEvent) {
|
||||
if (callbackIsInit) {
|
||||
errlogPrintf("Callback system already initialized\n");
|
||||
return -1;
|
||||
}
|
||||
@@ -99,7 +101,7 @@ int callbackSetQueueSize(int size)
|
||||
|
||||
int callbackParallelThreads(int count, const char *prio)
|
||||
{
|
||||
if (startStopEvent) {
|
||||
if (callbackIsInit) {
|
||||
errlogPrintf("Callback system already initialized\n");
|
||||
return -1;
|
||||
}
|
||||
@@ -149,12 +151,13 @@ int callbackParallelThreads(int count, const char *prio)
|
||||
|
||||
static void callbackTask(void *arg)
|
||||
{
|
||||
cbQueueSet *mySet = &callbackQueue[*(int*)arg];
|
||||
int prio = *(int*)arg;
|
||||
cbQueueSet *mySet = &callbackQueue[prio];
|
||||
|
||||
taskwdInsert(0, NULL, NULL);
|
||||
epicsEventSignal(startStopEvent);
|
||||
|
||||
while(TRUE) {
|
||||
while(!mySet->shutdown) {
|
||||
void *ptr;
|
||||
if (epicsRingPointerIsEmpty(mySet->queue))
|
||||
epicsEventMustWait(mySet->semWakeUp);
|
||||
@@ -163,42 +166,52 @@ static void callbackTask(void *arg)
|
||||
CALLBACK *pcallback = (CALLBACK *)ptr;
|
||||
if(!epicsRingPointerIsEmpty(mySet->queue))
|
||||
epicsEventMustTrigger(mySet->semWakeUp);
|
||||
if (ptr == &exitCallback) goto shutdown;
|
||||
mySet->queueOverflow = FALSE;
|
||||
(*pcallback->callback)(pcallback);
|
||||
}
|
||||
}
|
||||
|
||||
shutdown:
|
||||
mySet->threadsRunning--;
|
||||
if(!epicsAtomicDecrIntT(&mySet->threadsRunning))
|
||||
epicsEventSignal(startStopEvent);
|
||||
taskwdRemove(0);
|
||||
epicsEventSignal(startStopEvent);
|
||||
}
|
||||
|
||||
void callbackShutdown(void)
|
||||
void callbackStop(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (cbCtl == ctlExit) return;
|
||||
cbCtl = ctlExit;
|
||||
|
||||
/* sequential shutdown of workers */
|
||||
for (i = 0; i < NUM_CALLBACK_PRIORITIES; i++) {
|
||||
while (callbackQueue[i].threadsRunning) {
|
||||
if(epicsRingPointerPush(callbackQueue[i].queue, &exitCallback)) {
|
||||
epicsEventSignal(callbackQueue[i].semWakeUp);
|
||||
epicsEventWait(startStopEvent);
|
||||
} else {
|
||||
epicsThreadSleep(0.05);
|
||||
}
|
||||
}
|
||||
assert(callbackQueue[i].threadsRunning==0);
|
||||
epicsEventDestroy(callbackQueue[i].semWakeUp);
|
||||
epicsRingPointerDelete(callbackQueue[i].queue);
|
||||
callbackQueue[i].shutdown = 1;
|
||||
epicsEventSignal(callbackQueue[i].semWakeUp);
|
||||
}
|
||||
|
||||
for (i = 0; i < NUM_CALLBACK_PRIORITIES; i++) {
|
||||
cbQueueSet *mySet = &callbackQueue[i];
|
||||
|
||||
while (epicsAtomicGetIntT(&mySet->threadsRunning)) {
|
||||
epicsEventSignal(mySet->semWakeUp);
|
||||
epicsEventWaitWithTimeout(startStopEvent, 0.1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void callbackCleanup(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < NUM_CALLBACK_PRIORITIES; i++) {
|
||||
cbQueueSet *mySet = &callbackQueue[i];
|
||||
|
||||
assert(epicsAtomicGetIntT(&mySet->threadsRunning)==0);
|
||||
epicsEventDestroy(mySet->semWakeUp);
|
||||
epicsRingPointerDelete(mySet->queue);
|
||||
}
|
||||
|
||||
epicsTimerQueueRelease(timerQueue);
|
||||
epicsEventDestroy(startStopEvent);
|
||||
startStopEvent = NULL;
|
||||
callbackIsInit = 0;
|
||||
memset(callbackQueue, 0, sizeof(callbackQueue));
|
||||
}
|
||||
|
||||
@@ -208,10 +221,14 @@ void callbackInit(void)
|
||||
int j;
|
||||
char threadName[32];
|
||||
|
||||
if (startStopEvent)
|
||||
if (callbackIsInit) {
|
||||
errlogMessage("Warning: callbackInit called again before callbackCleanup\n");
|
||||
return;
|
||||
}
|
||||
callbackIsInit = 1;
|
||||
|
||||
startStopEvent = epicsEventMustCreate(epicsEventEmpty);
|
||||
if(!startStopEvent)
|
||||
startStopEvent = epicsEventMustCreate(epicsEventEmpty);
|
||||
cbCtl = ctlRun;
|
||||
timerQueue = epicsTimerQueueAllocate(0, epicsThreadPriorityScanHigh);
|
||||
|
||||
@@ -239,7 +256,7 @@ void callbackInit(void)
|
||||
cantProceed("Failed to spawn callback thread %s\n", threadName);
|
||||
} else {
|
||||
epicsEventWait(startStopEvent);
|
||||
callbackQueue[i].threadsRunning++;
|
||||
epicsAtomicIncrIntT(&callbackQueue[i].threadsRunning);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,7 +57,8 @@ typedef void (*CALLBACKFUNC)(struct callbackPvt*);
|
||||
( (USER) = (void *)((CALLBACK *)(PCALLBACK))->user )
|
||||
|
||||
epicsShareFunc void callbackInit(void);
|
||||
epicsShareFunc void callbackShutdown(void);
|
||||
epicsShareFunc void callbackStop(void);
|
||||
epicsShareFunc void callbackCleanup(void);
|
||||
epicsShareFunc int callbackRequest(CALLBACK *pCallback);
|
||||
epicsShareFunc void callbackSetProcess(
|
||||
CALLBACK *pcallback, int Priority, void *pRec);
|
||||
|
||||
@@ -402,19 +402,29 @@ struct rset * dbGetRset(const struct dbAddr *paddr)
|
||||
}
|
||||
|
||||
long dbPutAttribute(
|
||||
const char *recordTypename,const char *name,const char*value)
|
||||
const char *recordTypename, const char *name, const char *value)
|
||||
{
|
||||
DBENTRY dbEntry;
|
||||
DBENTRY *pdbEntry = &dbEntry;
|
||||
long status=0;
|
||||
DBENTRY dbEntry;
|
||||
DBENTRY *pdbEntry = &dbEntry;
|
||||
long status = 0;
|
||||
|
||||
if(!pdbbase) return(S_db_notFound);
|
||||
dbInitEntry(pdbbase,pdbEntry);
|
||||
status = dbFindRecordType(pdbEntry,recordTypename);
|
||||
if(!status) status = dbPutRecordAttribute(pdbEntry,name,value);
|
||||
dbFinishEntry(pdbEntry);
|
||||
if(status) errMessage(status,"dbPutAttribute failure");
|
||||
return(status);
|
||||
if (!pdbbase)
|
||||
return S_db_notFound;
|
||||
if (!name) {
|
||||
status = S_db_badField;
|
||||
goto done;
|
||||
}
|
||||
if (!value)
|
||||
value = "";
|
||||
dbInitEntry(pdbbase, pdbEntry);
|
||||
status = dbFindRecordType(pdbEntry, recordTypename);
|
||||
if (!status)
|
||||
status = dbPutRecordAttribute(pdbEntry, name, value);
|
||||
dbFinishEntry(pdbEntry);
|
||||
done:
|
||||
if (status)
|
||||
errMessage(status, "dbPutAttribute failure");
|
||||
return status;
|
||||
}
|
||||
|
||||
int dbIsValueField(const struct dbFldDes *pdbFldDes)
|
||||
@@ -812,11 +822,10 @@ long dbGet(DBADDR *paddr, short dbrType,
|
||||
void *pbuffer, long *options, long *nRequest, void *pflin)
|
||||
{
|
||||
char *pbuf = pbuffer;
|
||||
void *pfieldsave;
|
||||
void *pfieldsave = paddr->pfield;
|
||||
db_field_log *pfl = (db_field_log *)pflin;
|
||||
short field_type;
|
||||
long no_elements;
|
||||
long offset;
|
||||
long capacity, no_elements, offset;
|
||||
struct rset *prset;
|
||||
long status = 0;
|
||||
|
||||
@@ -826,18 +835,33 @@ long dbGet(DBADDR *paddr, short dbrType,
|
||||
return 0;
|
||||
|
||||
if (!pfl || pfl->type == dbfl_type_rec) {
|
||||
field_type = paddr->field_type;
|
||||
no_elements = paddr->no_elements;
|
||||
field_type = paddr->field_type;
|
||||
no_elements = capacity = paddr->no_elements;
|
||||
|
||||
/* Update field info from record
|
||||
* may modify paddr->pfield
|
||||
*/
|
||||
if (paddr->pfldDes->special == SPC_DBADDR &&
|
||||
(prset = dbGetRset(paddr)) &&
|
||||
prset->get_array_info) {
|
||||
status = prset->get_array_info(paddr, &no_elements, &offset);
|
||||
} else
|
||||
offset = 0;
|
||||
} else {
|
||||
field_type = pfl->field_type;
|
||||
no_elements = pfl->no_elements;
|
||||
field_type = pfl->field_type;
|
||||
no_elements = capacity = pfl->no_elements;
|
||||
offset = 0;
|
||||
}
|
||||
|
||||
if (field_type >= DBF_INLINK && field_type <= DBF_FWDLINK)
|
||||
return getLinkValue(paddr, dbrType, pbuf, nRequest);
|
||||
if (field_type >= DBF_INLINK && field_type <= DBF_FWDLINK) {
|
||||
status = getLinkValue(paddr, dbrType, pbuf, nRequest);
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (paddr->special == SPC_ATTRIBUTE)
|
||||
return getAttrValue(paddr, dbrType, pbuf, nRequest);
|
||||
if (paddr->special == SPC_ATTRIBUTE) {
|
||||
status = getAttrValue(paddr, dbrType, pbuf, nRequest);
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* Check for valid request */
|
||||
if (INVALID_DB_REQ(dbrType) || field_type > DBF_DEVICE) {
|
||||
@@ -845,26 +869,13 @@ long dbGet(DBADDR *paddr, short dbrType,
|
||||
|
||||
sprintf(message, "dbGet: Request type is %d\n", dbrType);
|
||||
recGblDbaddrError(S_db_badDbrtype, paddr, message);
|
||||
return S_db_badDbrtype;
|
||||
status = S_db_badDbrtype;
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* For SPC_DBADDR fields, the rset function
|
||||
* get_array_info() is allowed to modify
|
||||
* paddr->pfield. So we store the original
|
||||
* value and restore it later.
|
||||
*/
|
||||
pfieldsave = paddr->pfield;
|
||||
|
||||
/* Update field info */
|
||||
if (paddr->pfldDes->special == SPC_DBADDR &&
|
||||
(prset = dbGetRset(paddr)) &&
|
||||
prset->get_array_info) {
|
||||
status = prset->get_array_info(paddr, &no_elements, &offset);
|
||||
} else
|
||||
offset = 0;
|
||||
|
||||
if (offset == 0 && (!nRequest || no_elements == 1)) {
|
||||
if (nRequest) *nRequest = 1;
|
||||
if (nRequest)
|
||||
*nRequest = 1;
|
||||
if (!pfl || pfl->type == dbfl_type_rec) {
|
||||
status = dbFastGetConvertRoutine[field_type][dbrType]
|
||||
(paddr->pfield, pbuf, paddr);
|
||||
@@ -883,10 +894,11 @@ long dbGet(DBADDR *paddr, short dbrType,
|
||||
}
|
||||
} else {
|
||||
long n;
|
||||
long (*convert)();
|
||||
GETCONVERTFUNC convert;
|
||||
|
||||
if (nRequest) {
|
||||
if (no_elements<(*nRequest)) *nRequest = no_elements;
|
||||
if (no_elements < *nRequest)
|
||||
*nRequest = no_elements;
|
||||
n = *nRequest;
|
||||
} else {
|
||||
n = 1;
|
||||
@@ -901,11 +913,11 @@ long dbGet(DBADDR *paddr, short dbrType,
|
||||
status = S_db_badDbrtype;
|
||||
goto done;
|
||||
}
|
||||
/* convert database field and place it in the buffer */
|
||||
/* convert data into the caller's buffer */
|
||||
if (n <= 0) {
|
||||
;/*do nothing*/
|
||||
} else if (!pfl || pfl->type == dbfl_type_rec) {
|
||||
status = convert(paddr, pbuf, n, no_elements, offset);
|
||||
status = convert(paddr, pbuf, n, capacity, offset);
|
||||
} else {
|
||||
DBADDR localAddr = *paddr; /* Structure copy */
|
||||
|
||||
@@ -916,7 +928,7 @@ long dbGet(DBADDR *paddr, short dbrType,
|
||||
localAddr.pfield = (char *) &pfl->u.v.field;
|
||||
else
|
||||
localAddr.pfield = (char *) pfl->u.r.field;
|
||||
status = convert(&localAddr, pbuf, n, no_elements, offset);
|
||||
status = convert(&localAddr, pbuf, n, capacity, offset);
|
||||
}
|
||||
}
|
||||
done:
|
||||
@@ -1228,20 +1240,20 @@ long dbPut(DBADDR *paddr, short dbrType,
|
||||
status = dbPutConvertRoutine[dbrType][field_type](paddr, pbuffer,
|
||||
nRequest, no_elements, offset);
|
||||
}
|
||||
if (status) goto done;
|
||||
|
||||
/* update array info */
|
||||
if (paddr->pfldDes->special == SPC_DBADDR &&
|
||||
if (!status &&
|
||||
paddr->pfldDes->special == SPC_DBADDR &&
|
||||
prset && prset->put_array_info) {
|
||||
status = prset->put_array_info(paddr, nRequest);
|
||||
}
|
||||
if (status) goto done;
|
||||
|
||||
/* check if special processing is required */
|
||||
/* Always do special processing if needed */
|
||||
if (special) {
|
||||
status = dbPutSpecial(paddr,1);
|
||||
if (status) goto done;
|
||||
long status2 = dbPutSpecial(paddr, 1);
|
||||
if (status2) goto done;
|
||||
}
|
||||
if (status) goto done;
|
||||
|
||||
/* Propagate monitor events for this field, */
|
||||
/* unless the field is VAL and PP is true. */
|
||||
|
||||
@@ -100,7 +100,7 @@ static int dbca_chan_count;
|
||||
* that has been deleted.
|
||||
*
|
||||
* Just a few words about handling dbCaRemoveLink because this is when
|
||||
* it is essential that nothing trys to use a caLink that has been freed.
|
||||
* it is essential that nothing tries to use a caLink that has been freed.
|
||||
*
|
||||
* dbCaRemoveLink is called when links are being modified. This is only
|
||||
* done with the dbScan mechanism guranteeing that nothing from
|
||||
@@ -119,19 +119,16 @@ static int dbca_chan_count;
|
||||
* dbCaTask issues a ca_clear_channel and then frees the caLink.
|
||||
*
|
||||
* If any channel access callback gets called before the ca_clear_channel
|
||||
* it finds pca->plink=0 and does nothing. Once ca_clear_channel
|
||||
* it finds pca->plink==0 and does nothing. Once ca_clear_channel
|
||||
* is called no other callback for this caLink will be called.
|
||||
*
|
||||
* dbCaPutLinkCallback causes an additional complication because
|
||||
* when dbCaRemoveLink is called the callback may not have occured.
|
||||
* What is done is the following:
|
||||
* If callback has not occured dbCaRemoveLink sets plinkPutCallback=plink
|
||||
* If putCallback is called before dbCaTask calls ca_clear_channel
|
||||
* it does NOT call the users callback.
|
||||
* dbCaTask calls the users callback passing plinkPutCallback AFTER
|
||||
* it has called ca_clear_channel
|
||||
* Thus the users callback will get called exactly once.
|
||||
*/
|
||||
* If putComplete sees plink==0 it will not call the user's code.
|
||||
* If pca->putCallback is non-zero, dbCaTask will call the
|
||||
* user's callback AFTER it has called ca_clear_channel.
|
||||
* Thus the user's callback will get called exactly once.
|
||||
*/
|
||||
|
||||
static void addAction(caLink *pca, short link_action)
|
||||
{
|
||||
@@ -168,7 +165,7 @@ static void addAction(caLink *pca, short link_action)
|
||||
static void dbCaLinkFree(caLink *pca)
|
||||
{
|
||||
dbCaCallback callback;
|
||||
struct link *plinkPutCallback = 0;
|
||||
void *userPvt = 0;
|
||||
|
||||
if (pca->chid) {
|
||||
ca_clear_channel(pca->chid);
|
||||
@@ -176,8 +173,7 @@ static void dbCaLinkFree(caLink *pca)
|
||||
}
|
||||
callback = pca->putCallback;
|
||||
if (callback) {
|
||||
plinkPutCallback = pca->plinkPutCallback;
|
||||
pca->plinkPutCallback = 0;
|
||||
userPvt = pca->putUserPvt;
|
||||
pca->putCallback = 0;
|
||||
pca->putType = 0;
|
||||
}
|
||||
@@ -188,12 +184,12 @@ static void dbCaLinkFree(caLink *pca)
|
||||
free(pca->pvname);
|
||||
epicsMutexDestroy(pca->lock);
|
||||
free(pca);
|
||||
if (callback) callback(plinkPutCallback);
|
||||
if (callback) callback(userPvt);
|
||||
}
|
||||
|
||||
void dbCaCallbackProcess(void *usrPvt)
|
||||
void dbCaCallbackProcess(void *userPvt)
|
||||
{
|
||||
struct link *plink = (struct link *)usrPvt;
|
||||
struct link *plink = (struct link *)userPvt;
|
||||
dbCommon *pdbCommon = plink->value.pv_link.precord;
|
||||
|
||||
dbScanLock(pdbCommon);
|
||||
@@ -223,11 +219,6 @@ void dbCaShutdown(void)
|
||||
}
|
||||
}
|
||||
|
||||
static void dbCaExit(void *arg)
|
||||
{
|
||||
dbCaShutdown();
|
||||
}
|
||||
|
||||
void dbCaLinkInitIsolated(void)
|
||||
{
|
||||
if (!workListLock)
|
||||
@@ -235,7 +226,6 @@ void dbCaLinkInitIsolated(void)
|
||||
if (!workListEvent)
|
||||
workListEvent = epicsEventMustCreate(epicsEventEmpty);
|
||||
dbCaCtl = ctlExit;
|
||||
epicsAtExit(dbCaExit, NULL);
|
||||
}
|
||||
|
||||
void dbCaLinkInit(void)
|
||||
@@ -297,8 +287,6 @@ void dbCaRemoveLink(struct link *plink)
|
||||
epicsMutexMustLock(pca->lock);
|
||||
pca->plink = 0;
|
||||
plink->value.pv_link.pvt = 0;
|
||||
if (pca->putCallback)
|
||||
pca->plinkPutCallback = plink;
|
||||
/* Unlock before addAction or dbCaTask might free first */
|
||||
epicsMutexUnlock(pca->lock);
|
||||
addAction(pca, CA_CLEAR_CHANNEL);
|
||||
@@ -800,7 +788,7 @@ static void exceptionCallback(struct exception_handler_args args)
|
||||
}
|
||||
}
|
||||
|
||||
static void putCallback(struct event_handler_args arg)
|
||||
static void putComplete(struct event_handler_args arg)
|
||||
{
|
||||
caLink *pca = (caLink *)arg.usr;
|
||||
struct link *plink;
|
||||
@@ -963,7 +951,7 @@ static void dbCaTask(void *arg)
|
||||
status = ca_array_put_callback(
|
||||
pca->dbrType, pca->nelements,
|
||||
pca->chid, pca->pputNative,
|
||||
putCallback, pca);
|
||||
putComplete, pca);
|
||||
} else {
|
||||
status = ECA_PUTFAIL;
|
||||
}
|
||||
@@ -986,7 +974,7 @@ static void dbCaTask(void *arg)
|
||||
status = ca_array_put_callback(
|
||||
DBR_STRING, 1,
|
||||
pca->chid, pca->pputString,
|
||||
putCallback, pca);
|
||||
putComplete, pca);
|
||||
} else {
|
||||
status = ECA_PUTFAIL;
|
||||
}
|
||||
|
||||
@@ -63,7 +63,6 @@ typedef struct caLink
|
||||
short putType;
|
||||
dbCaCallback putCallback;
|
||||
void *putUserPvt;
|
||||
struct link *plinkPutCallback;
|
||||
/* The following are for access to additional attributes*/
|
||||
char gotAttributes;
|
||||
dbCaCallback getAttributes;
|
||||
|
||||
@@ -704,21 +704,27 @@ void dbChannelShow(dbChannel *chan, int level, const unsigned short indent)
|
||||
int pre = ellCount(&chan->pre_chain);
|
||||
int post = ellCount(&chan->post_chain);
|
||||
|
||||
printf("%*schannel name: %s\n", indent, "", chan->name);
|
||||
printf("%*s field_type=%s (%d bytes), dbr_type=%s, %ld element%s, %d filter%s", indent, "",
|
||||
dbGetFieldTypeString(chan->addr.field_type), chan->addr.field_size,
|
||||
dbGetFieldTypeString(chan->addr.dbr_field_type), elems, elems == 1 ? "" : "s",
|
||||
count, count == 1 ? "" : "s");
|
||||
if (count)
|
||||
printf(" (%d pre eventq, %d post eventq)\n", pre, post);
|
||||
else
|
||||
printf("\n");
|
||||
if (level > 0)
|
||||
dbChannelFilterShow(chan, level - 1, indent + 2);
|
||||
if (count) {
|
||||
printf("%*s final field_type=%s (%dB), %ld element%s\n", indent, "",
|
||||
dbGetFieldTypeString(chan->final_type), chan->final_field_size,
|
||||
felems, felems == 1 ? "" : "s");
|
||||
printf("%*sChannel: '%s'\n", indent, "", chan->name);
|
||||
if (level > 0) {
|
||||
printf("%*sfield_type=%s (%d bytes), dbr_type=%s, %ld element%s",
|
||||
indent + 4, "",
|
||||
dbGetFieldTypeString(chan->addr.field_type),
|
||||
chan->addr.field_size,
|
||||
dbGetFieldTypeString(chan->addr.dbr_field_type),
|
||||
elems, elems == 1 ? "" : "s");
|
||||
if (count)
|
||||
printf("\n%*s%d filter%s (%d pre eventq, %d post eventq)\n",
|
||||
indent + 4, "", count, count == 1 ? "" : "s", pre, post);
|
||||
else
|
||||
printf(", no filters\n");
|
||||
if (level > 1)
|
||||
dbChannelFilterShow(chan, level - 2, indent + 8);
|
||||
if (count) {
|
||||
printf("%*sfinal field_type=%s (%dB), %ld element%s\n", indent + 4, "",
|
||||
dbGetFieldTypeString(chan->final_type),
|
||||
chan->final_field_size,
|
||||
felems, felems == 1 ? "" : "s");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
# Copyright (c) 2002 The Regents of the University of California, as
|
||||
# Operator of Los Alamos National Laboratory.
|
||||
# EPICS BASE is distributed subject to a Software License Agreement found
|
||||
# in file LICENSE that is included with this distribution.
|
||||
# in file LICENSE that is included with this distribution.
|
||||
#*************************************************************************
|
||||
%#include "epicsTypes.h"
|
||||
%#include "link.h"
|
||||
@@ -15,59 +15,59 @@
|
||||
}
|
||||
field(DESC,DBF_STRING) {
|
||||
prompt("Descriptor")
|
||||
promptgroup(GUI_COMMON)
|
||||
promptgroup("10 - Common")
|
||||
size(41)
|
||||
}
|
||||
field(ASG,DBF_STRING) {
|
||||
prompt("Access Security Group")
|
||||
promptgroup(GUI_COMMON)
|
||||
promptgroup("10 - Common")
|
||||
special(SPC_AS)
|
||||
size(29)
|
||||
}
|
||||
field(SCAN,DBF_MENU) {
|
||||
prompt("Scan Mechanism")
|
||||
promptgroup(GUI_SCAN)
|
||||
promptgroup("20 - Scan")
|
||||
special(SPC_SCAN)
|
||||
interest(1)
|
||||
menu(menuScan)
|
||||
}
|
||||
field(PINI,DBF_MENU) {
|
||||
prompt("Process at iocInit")
|
||||
promptgroup(GUI_SCAN)
|
||||
promptgroup("20 - Scan")
|
||||
interest(1)
|
||||
menu(menuPini)
|
||||
}
|
||||
field(PHAS,DBF_SHORT) {
|
||||
prompt("Scan Phase")
|
||||
promptgroup(GUI_SCAN)
|
||||
promptgroup("20 - Scan")
|
||||
special(SPC_SCAN)
|
||||
interest(1)
|
||||
}
|
||||
field(EVNT,DBF_STRING) {
|
||||
prompt("Event Name")
|
||||
promptgroup(GUI_SCAN)
|
||||
promptgroup("20 - Scan")
|
||||
special(SPC_SCAN)
|
||||
size(40)
|
||||
interest(1)
|
||||
}
|
||||
field(TSE,DBF_SHORT) {
|
||||
prompt("Time Stamp Event")
|
||||
promptgroup(GUI_SCAN)
|
||||
promptgroup("20 - Scan")
|
||||
interest(1)
|
||||
}
|
||||
field(TSEL,DBF_INLINK) {
|
||||
prompt("Time Stamp Link")
|
||||
promptgroup(GUI_SCAN)
|
||||
promptgroup("20 - Scan")
|
||||
interest(1)
|
||||
}
|
||||
field(DTYP,DBF_DEVICE) {
|
||||
prompt("Device Type")
|
||||
promptgroup(GUI_LINKS)
|
||||
promptgroup("10 - Common")
|
||||
interest(1)
|
||||
}
|
||||
field(DISV,DBF_SHORT) {
|
||||
prompt("Disable Value")
|
||||
promptgroup(GUI_SCAN)
|
||||
promptgroup("20 - Scan")
|
||||
initial("1")
|
||||
}
|
||||
field(DISA,DBF_SHORT) {
|
||||
@@ -75,7 +75,7 @@
|
||||
}
|
||||
field(SDIS,DBF_INLINK) {
|
||||
prompt("Scanning Disable")
|
||||
promptgroup(GUI_SCAN)
|
||||
promptgroup("20 - Scan")
|
||||
interest(1)
|
||||
}
|
||||
%#include "epicsMutex.h"
|
||||
@@ -131,7 +131,7 @@
|
||||
}
|
||||
field(ACKT,DBF_MENU) {
|
||||
prompt("Alarm Ack Transient")
|
||||
promptgroup(GUI_ALARMS)
|
||||
promptgroup("70 - Alarm")
|
||||
special(SPC_NOMOD)
|
||||
interest(2)
|
||||
menu(menuYesNo)
|
||||
@@ -139,7 +139,7 @@
|
||||
}
|
||||
field(DISS,DBF_MENU) {
|
||||
prompt("Disable Alarm Sevrty")
|
||||
promptgroup(GUI_SCAN)
|
||||
promptgroup("70 - Alarm")
|
||||
interest(1)
|
||||
menu(menuAlarmSevr)
|
||||
}
|
||||
@@ -219,7 +219,7 @@
|
||||
}
|
||||
field(PRIO,DBF_MENU) {
|
||||
prompt("Scheduling Priority")
|
||||
promptgroup(GUI_SCAN)
|
||||
promptgroup("20 - Scan")
|
||||
special(SPC_SCAN)
|
||||
interest(1)
|
||||
menu(menuPriority)
|
||||
@@ -235,14 +235,14 @@
|
||||
}
|
||||
field(UDF,DBF_UCHAR) {
|
||||
prompt("Undefined")
|
||||
promptgroup(GUI_COMMON)
|
||||
promptgroup("10 - Common")
|
||||
pp(TRUE)
|
||||
interest(1)
|
||||
initial("1")
|
||||
}
|
||||
field(UDFS,DBF_MENU) {
|
||||
prompt("Undefined Alarm Sevrty")
|
||||
promptgroup(GUI_COMMON)
|
||||
promptgroup("70 - Alarm")
|
||||
interest(1)
|
||||
menu(menuAlarmSevr)
|
||||
initial("INVALID")
|
||||
@@ -256,6 +256,6 @@
|
||||
}
|
||||
field(FLNK,DBF_FWDLINK) {
|
||||
prompt("Forward Process Link")
|
||||
promptgroup(GUI_LINKS)
|
||||
promptgroup("20 - Scan")
|
||||
interest(1)
|
||||
}
|
||||
|
||||
@@ -979,6 +979,7 @@ static void event_task (void *pParm)
|
||||
{
|
||||
struct event_user * const evUser = (struct event_user *) pParm;
|
||||
struct event_que * ev_que;
|
||||
unsigned char pendexit;
|
||||
|
||||
/* init hook */
|
||||
if (evUser->init_func) {
|
||||
@@ -1020,9 +1021,10 @@ static void event_task (void *pParm)
|
||||
event_read (ev_que);
|
||||
epicsMutexMustLock ( evUser->lock );
|
||||
}
|
||||
pendexit = evUser->pendexit;
|
||||
epicsMutexUnlock ( evUser->lock );
|
||||
|
||||
} while ( ! evUser->pendexit );
|
||||
} while( ! pendexit );
|
||||
|
||||
epicsMutexDestroy(evUser->firstque.writelock);
|
||||
|
||||
|
||||
@@ -233,6 +233,17 @@ static void dbtpnCallFunc(const iocshArgBuf *args)
|
||||
static const iocshFuncDef dbNotifyDumpFuncDef = {"dbNotifyDump",0,0};
|
||||
static void dbNotifyDumpCallFunc(const iocshArgBuf *args) { dbNotifyDump();}
|
||||
|
||||
/* dbPutAttribute */
|
||||
static const iocshArg dbPutAttrArg0 = { "record type",iocshArgString};
|
||||
static const iocshArg dbPutAttrArg1 = { "attribute name",iocshArgString};
|
||||
static const iocshArg dbPutAttrArg2 = { "value",iocshArgString};
|
||||
static const iocshArg * const dbPutAttrArgs[] =
|
||||
{&dbPutAttrArg0, &dbPutAttrArg1, &dbPutAttrArg2};
|
||||
static const iocshFuncDef dbPutAttrFuncDef =
|
||||
{"dbPutAttribute",3,dbPutAttrArgs};
|
||||
static void dbPutAttrCallFunc(const iocshArgBuf *args)
|
||||
{ dbPutAttribute(args[0].sval,args[1].sval,args[2].sval);}
|
||||
|
||||
/* tpn */
|
||||
static const iocshArg tpnArg0 = { "record name",iocshArgString};
|
||||
static const iocshArg tpnArg1 = { "value",iocshArgString};
|
||||
@@ -405,6 +416,7 @@ void dbIocRegister(void)
|
||||
iocshRegister(&pftFuncDef,pftCallFunc);
|
||||
iocshRegister(&dbtpnFuncDef,dbtpnCallFunc);
|
||||
iocshRegister(&dbNotifyDumpFuncDef,dbNotifyDumpCallFunc);
|
||||
iocshRegister(&dbPutAttrFuncDef,dbPutAttrCallFunc);
|
||||
iocshRegister(&tpnFuncDef,tpnCallFunc);
|
||||
iocshRegister(&dblsrFuncDef,dblsrCallFunc);
|
||||
iocshRegister(&dbLockShowLockedFuncDef,dbLockShowLockedCallFunc);
|
||||
|
||||
@@ -224,6 +224,7 @@ static long dbDbGetValue(struct link *plink, short dbrType, void *pbuffer,
|
||||
return S_db_badDbrtype;
|
||||
|
||||
if (paddr->no_elements == 1 && (!pnRequest || *pnRequest == 1)
|
||||
&& paddr->special != SPC_DBADDR
|
||||
&& paddr->special != SPC_ATTRIBUTE) {
|
||||
ppv_link->getCvt = dbFastGetConvertRoutine[dbfType][dbrType];
|
||||
status = ppv_link->getCvt(paddr->pfield, pbuffer, paddr);
|
||||
|
||||
@@ -151,7 +151,7 @@ static void buildScanLists(void);
|
||||
static void addToList(struct dbCommon *precord, scan_list *psl);
|
||||
static void deleteFromList(struct dbCommon *precord, scan_list *psl);
|
||||
|
||||
void scanShutdown(void)
|
||||
void scanStop(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
@@ -168,16 +168,16 @@ void scanShutdown(void)
|
||||
|
||||
scanOnce((dbCommon *)&exitOnce);
|
||||
epicsEventWait(startStopEvent);
|
||||
}
|
||||
|
||||
void scanCleanup(void)
|
||||
{
|
||||
|
||||
deletePeriodic();
|
||||
ioscanDestroy();
|
||||
|
||||
epicsRingPointerDelete(onceQ);
|
||||
|
||||
epicsEventDestroy(startStopEvent);
|
||||
epicsEventDestroy(onceSem);
|
||||
onceSem = startStopEvent = NULL;
|
||||
|
||||
free(periodicTaskId);
|
||||
papPeriodic = NULL;
|
||||
periodicTaskId = NULL;
|
||||
@@ -187,7 +187,8 @@ long scanInit(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
startStopEvent = epicsEventMustCreate(epicsEventEmpty);
|
||||
if(!startStopEvent)
|
||||
startStopEvent = epicsEventMustCreate(epicsEventEmpty);
|
||||
scanCtl = ctlPause;
|
||||
|
||||
initPeriodic();
|
||||
@@ -639,7 +640,8 @@ static void initOnce(void)
|
||||
if ((onceQ = epicsRingPointerCreate(onceQueueSize)) == NULL) {
|
||||
cantProceed("initOnce: Ring buffer create failed\n");
|
||||
}
|
||||
onceSem = epicsEventMustCreate(epicsEventEmpty);
|
||||
if(!onceSem)
|
||||
onceSem = epicsEventMustCreate(epicsEventEmpty);
|
||||
onceTaskId = epicsThreadCreate("scanOnce",
|
||||
epicsThreadPriorityScanLow + nPeriodic,
|
||||
epicsThreadGetStackSize(epicsThreadStackBig), onceTask, 0);
|
||||
|
||||
@@ -46,7 +46,8 @@ struct dbCommon;
|
||||
epicsShareFunc long scanInit(void);
|
||||
epicsShareFunc void scanRun(void);
|
||||
epicsShareFunc void scanPause(void);
|
||||
epicsShareFunc void scanShutdown(void);
|
||||
epicsShareFunc void scanStop(void);
|
||||
epicsShareFunc void scanCleanup(void);
|
||||
|
||||
epicsShareFunc EVENTPVT eventNameToHandle(const char* event);
|
||||
epicsShareFunc void postEvent(EVENTPVT epvt);
|
||||
|
||||
@@ -334,7 +334,6 @@ long dbpf(const char *pname,const char *pvalue)
|
||||
{
|
||||
DBADDR addr;
|
||||
long status;
|
||||
epicsUInt16 value;
|
||||
short dbrType;
|
||||
size_t n = 1;
|
||||
|
||||
@@ -346,16 +345,7 @@ long dbpf(const char *pname,const char *pvalue)
|
||||
if (nameToAddr(pname, &addr))
|
||||
return -1;
|
||||
|
||||
/* For enumerated types must allow for ENUM rather than string */
|
||||
/* If entire field is digits then use DBR_ENUM else DBR_STRING */
|
||||
if (addr.dbr_field_type == DBR_ENUM && *pvalue &&
|
||||
strspn(pvalue,"0123456789") == strlen(pvalue)) {
|
||||
|
||||
sscanf(pvalue, "%hu", &value);
|
||||
pvalue = (char *) &value;
|
||||
dbrType = DBR_ENUM;
|
||||
}
|
||||
else if (addr.no_elements > 1 &&
|
||||
if (addr.no_elements > 1 &&
|
||||
(addr.dbr_field_type == DBR_CHAR || addr.dbr_field_type == DBR_UCHAR)) {
|
||||
dbrType = addr.dbr_field_type;
|
||||
n = strlen(pvalue) + 1;
|
||||
|
||||
@@ -39,6 +39,7 @@
|
||||
#include "dbChannel.h"
|
||||
#include "dbCommon.h"
|
||||
#include "dbEvent.h"
|
||||
#include "dbLock.h"
|
||||
#include "dbNotify.h"
|
||||
#include "dbStaticLib.h"
|
||||
#include "recSup.h"
|
||||
@@ -153,28 +154,30 @@ int dbChannel_get_count(
|
||||
* in the dbAccess.c dbGet() and getOptions() routines.
|
||||
*/
|
||||
|
||||
dbScanLock(dbChannelRecord(chan));
|
||||
|
||||
switch(buffer_type) {
|
||||
case(oldDBR_STRING):
|
||||
status = dbChannelGetField(chan, DBR_STRING, pbuffer, &zero, nRequest, pfl);
|
||||
status = dbChannelGet(chan, DBR_STRING, pbuffer, &zero, nRequest, pfl);
|
||||
break;
|
||||
/* case(oldDBR_INT): */
|
||||
case(oldDBR_SHORT):
|
||||
status = dbChannelGetField(chan, DBR_SHORT, pbuffer, &zero, nRequest, pfl);
|
||||
status = dbChannelGet(chan, DBR_SHORT, pbuffer, &zero, nRequest, pfl);
|
||||
break;
|
||||
case(oldDBR_FLOAT):
|
||||
status = dbChannelGetField(chan, DBR_FLOAT, pbuffer, &zero, nRequest, pfl);
|
||||
status = dbChannelGet(chan, DBR_FLOAT, pbuffer, &zero, nRequest, pfl);
|
||||
break;
|
||||
case(oldDBR_ENUM):
|
||||
status = dbChannelGetField(chan, DBR_ENUM, pbuffer, &zero, nRequest, pfl);
|
||||
status = dbChannelGet(chan, DBR_ENUM, pbuffer, &zero, nRequest, pfl);
|
||||
break;
|
||||
case(oldDBR_CHAR):
|
||||
status = dbChannelGetField(chan, DBR_CHAR, pbuffer, &zero, nRequest, pfl);
|
||||
status = dbChannelGet(chan, DBR_CHAR, pbuffer, &zero, nRequest, pfl);
|
||||
break;
|
||||
case(oldDBR_LONG):
|
||||
status = dbChannelGetField(chan, DBR_LONG, pbuffer, &zero, nRequest, pfl);
|
||||
status = dbChannelGet(chan, DBR_LONG, pbuffer, &zero, nRequest, pfl);
|
||||
break;
|
||||
case(oldDBR_DOUBLE):
|
||||
status = dbChannelGetField(chan, DBR_DOUBLE, pbuffer, &zero, nRequest, pfl);
|
||||
status = dbChannelGet(chan, DBR_DOUBLE, pbuffer, &zero, nRequest, pfl);
|
||||
break;
|
||||
|
||||
case(oldDBR_STS_STRING):
|
||||
@@ -187,10 +190,10 @@ int dbChannel_get_count(
|
||||
} newSt;
|
||||
|
||||
options = DBR_STATUS;
|
||||
status = dbChannelGetField(chan, DBR_STRING, &newSt, &options, &zero, pfl);
|
||||
status = dbChannelGet(chan, DBR_STRING, &newSt, &options, &zero, pfl);
|
||||
pold->status = newSt.status;
|
||||
pold->severity = newSt.severity;
|
||||
status = dbChannelGetField(chan, DBR_STRING, pold->value, &zero,
|
||||
status = dbChannelGet(chan, DBR_STRING, pold->value, &zero,
|
||||
nRequest, pfl);
|
||||
}
|
||||
break;
|
||||
@@ -203,10 +206,10 @@ int dbChannel_get_count(
|
||||
} newSt;
|
||||
|
||||
options = DBR_STATUS;
|
||||
status = dbChannelGetField(chan, DBR_SHORT, &newSt, &options, &zero, pfl);
|
||||
status = dbChannelGet(chan, DBR_SHORT, &newSt, &options, &zero, pfl);
|
||||
pold->status = newSt.status;
|
||||
pold->severity = newSt.severity;
|
||||
status = dbChannelGetField(chan, DBR_SHORT, &pold->value, &zero,
|
||||
status = dbChannelGet(chan, DBR_SHORT, &pold->value, &zero,
|
||||
nRequest, pfl);
|
||||
}
|
||||
break;
|
||||
@@ -218,10 +221,10 @@ int dbChannel_get_count(
|
||||
} newSt;
|
||||
|
||||
options = DBR_STATUS;
|
||||
status = dbChannelGetField(chan, DBR_FLOAT, &newSt, &options, &zero, pfl);
|
||||
status = dbChannelGet(chan, DBR_FLOAT, &newSt, &options, &zero, pfl);
|
||||
pold->status = newSt.status;
|
||||
pold->severity = newSt.severity;
|
||||
status = dbChannelGetField(chan, DBR_FLOAT, &pold->value, &zero,
|
||||
status = dbChannelGet(chan, DBR_FLOAT, &pold->value, &zero,
|
||||
nRequest, pfl);
|
||||
}
|
||||
break;
|
||||
@@ -233,10 +236,10 @@ int dbChannel_get_count(
|
||||
} newSt;
|
||||
|
||||
options = DBR_STATUS;
|
||||
status = dbChannelGetField(chan, DBR_ENUM, &newSt, &options, &zero, pfl);
|
||||
status = dbChannelGet(chan, DBR_ENUM, &newSt, &options, &zero, pfl);
|
||||
pold->status = newSt.status;
|
||||
pold->severity = newSt.severity;
|
||||
status = dbChannelGetField(chan, DBR_ENUM, &pold->value, &zero,
|
||||
status = dbChannelGet(chan, DBR_ENUM, &pold->value, &zero,
|
||||
nRequest, pfl);
|
||||
}
|
||||
break;
|
||||
@@ -248,10 +251,10 @@ int dbChannel_get_count(
|
||||
} newSt;
|
||||
|
||||
options = DBR_STATUS;
|
||||
status = dbChannelGetField(chan, DBR_UCHAR, &newSt, &options, &zero, pfl);
|
||||
status = dbChannelGet(chan, DBR_UCHAR, &newSt, &options, &zero, pfl);
|
||||
pold->status = newSt.status;
|
||||
pold->severity = newSt.severity;
|
||||
status = dbChannelGetField(chan, DBR_UCHAR, &pold->value, &zero,
|
||||
status = dbChannelGet(chan, DBR_UCHAR, &pold->value, &zero,
|
||||
nRequest, pfl);
|
||||
}
|
||||
break;
|
||||
@@ -263,10 +266,10 @@ int dbChannel_get_count(
|
||||
} newSt;
|
||||
|
||||
options = DBR_STATUS;
|
||||
status = dbChannelGetField(chan, DBR_LONG, &newSt, &options, &zero, pfl);
|
||||
status = dbChannelGet(chan, DBR_LONG, &newSt, &options, &zero, pfl);
|
||||
pold->status = newSt.status;
|
||||
pold->severity = newSt.severity;
|
||||
status = dbChannelGetField(chan, DBR_LONG, &pold->value, &zero,
|
||||
status = dbChannelGet(chan, DBR_LONG, &pold->value, &zero,
|
||||
nRequest, pfl);
|
||||
}
|
||||
break;
|
||||
@@ -278,11 +281,11 @@ int dbChannel_get_count(
|
||||
} newSt;
|
||||
|
||||
options = DBR_STATUS;
|
||||
status = dbChannelGetField(chan, DBR_DOUBLE, &newSt, &options, &zero, pfl);
|
||||
status = dbChannelGet(chan, DBR_DOUBLE, &newSt, &options, &zero, pfl);
|
||||
pold->status = newSt.status;
|
||||
pold->severity = newSt.severity;
|
||||
options = 0;
|
||||
status = dbChannelGetField(chan, DBR_DOUBLE, &pold->value, &options,
|
||||
status = dbChannelGet(chan, DBR_DOUBLE, &pold->value, &options,
|
||||
nRequest, pfl);
|
||||
}
|
||||
break;
|
||||
@@ -296,12 +299,12 @@ int dbChannel_get_count(
|
||||
} newSt;
|
||||
|
||||
options = DBR_STATUS | DBR_TIME;
|
||||
status = dbChannelGetField(chan, DBR_STRING, &newSt, &options, &zero, pfl);
|
||||
status = dbChannelGet(chan, DBR_STRING, &newSt, &options, &zero, pfl);
|
||||
pold->status = newSt.status;
|
||||
pold->severity = newSt.severity;
|
||||
pold->stamp = newSt.time; /* structure copy */
|
||||
options = 0;
|
||||
status = dbChannelGetField(chan, DBR_STRING, pold->value, &options,
|
||||
status = dbChannelGet(chan, DBR_STRING, pold->value, &options,
|
||||
nRequest, pfl);
|
||||
}
|
||||
break;
|
||||
@@ -315,12 +318,12 @@ int dbChannel_get_count(
|
||||
} newSt;
|
||||
|
||||
options = DBR_STATUS | DBR_TIME;
|
||||
status = dbChannelGetField(chan, DBR_SHORT, &newSt, &options, &zero, pfl);
|
||||
status = dbChannelGet(chan, DBR_SHORT, &newSt, &options, &zero, pfl);
|
||||
pold->status = newSt.status;
|
||||
pold->severity = newSt.severity;
|
||||
pold->stamp = newSt.time; /* structure copy */
|
||||
options = 0;
|
||||
status = dbChannelGetField(chan, DBR_SHORT, &pold->value, &options,
|
||||
status = dbChannelGet(chan, DBR_SHORT, &pold->value, &options,
|
||||
nRequest, pfl);
|
||||
}
|
||||
break;
|
||||
@@ -333,12 +336,12 @@ int dbChannel_get_count(
|
||||
} newSt;
|
||||
|
||||
options = DBR_STATUS | DBR_TIME;
|
||||
status = dbChannelGetField(chan, DBR_FLOAT, &newSt, &options, &zero, pfl);
|
||||
status = dbChannelGet(chan, DBR_FLOAT, &newSt, &options, &zero, pfl);
|
||||
pold->status = newSt.status;
|
||||
pold->severity = newSt.severity;
|
||||
pold->stamp = newSt.time; /* structure copy */
|
||||
options = 0;
|
||||
status = dbChannelGetField(chan, DBR_FLOAT, &pold->value, &options,
|
||||
status = dbChannelGet(chan, DBR_FLOAT, &pold->value, &options,
|
||||
nRequest, pfl);
|
||||
}
|
||||
break;
|
||||
@@ -351,12 +354,12 @@ int dbChannel_get_count(
|
||||
} newSt;
|
||||
|
||||
options = DBR_STATUS | DBR_TIME;
|
||||
status = dbChannelGetField(chan, DBR_ENUM, &newSt, &options, &zero, pfl);
|
||||
status = dbChannelGet(chan, DBR_ENUM, &newSt, &options, &zero, pfl);
|
||||
pold->status = newSt.status;
|
||||
pold->severity = newSt.severity;
|
||||
pold->stamp = newSt.time; /* structure copy */
|
||||
options = 0;
|
||||
status = dbChannelGetField(chan, DBR_ENUM, &pold->value, &options,
|
||||
status = dbChannelGet(chan, DBR_ENUM, &pold->value, &options,
|
||||
nRequest, pfl);
|
||||
}
|
||||
break;
|
||||
@@ -369,12 +372,12 @@ int dbChannel_get_count(
|
||||
} newSt;
|
||||
|
||||
options = DBR_STATUS | DBR_TIME;
|
||||
status = dbChannelGetField(chan, DBR_CHAR, &newSt, &options, &zero, pfl);
|
||||
status = dbChannelGet(chan, DBR_CHAR, &newSt, &options, &zero, pfl);
|
||||
pold->status = newSt.status;
|
||||
pold->severity = newSt.severity;
|
||||
pold->stamp = newSt.time; /* structure copy */
|
||||
options = 0;
|
||||
status = dbChannelGetField(chan, DBR_CHAR, &pold->value, &options,
|
||||
status = dbChannelGet(chan, DBR_CHAR, &pold->value, &options,
|
||||
nRequest, pfl);
|
||||
}
|
||||
break;
|
||||
@@ -387,12 +390,12 @@ int dbChannel_get_count(
|
||||
} newSt;
|
||||
|
||||
options = DBR_STATUS | DBR_TIME;
|
||||
status = dbChannelGetField(chan, DBR_LONG, &newSt, &options, &zero, pfl);
|
||||
status = dbChannelGet(chan, DBR_LONG, &newSt, &options, &zero, pfl);
|
||||
pold->status = newSt.status;
|
||||
pold->severity = newSt.severity;
|
||||
pold->stamp = newSt.time; /* structure copy */
|
||||
options = 0;
|
||||
status = dbChannelGetField(chan, DBR_LONG, &pold->value, &options,
|
||||
status = dbChannelGet(chan, DBR_LONG, &pold->value, &options,
|
||||
nRequest, pfl);
|
||||
}
|
||||
break;
|
||||
@@ -405,12 +408,12 @@ int dbChannel_get_count(
|
||||
} newSt;
|
||||
|
||||
options = DBR_STATUS | DBR_TIME;
|
||||
status = dbChannelGetField(chan, DBR_DOUBLE, &newSt, &options, &zero, pfl);
|
||||
status = dbChannelGet(chan, DBR_DOUBLE, &newSt, &options, &zero, pfl);
|
||||
pold->status = newSt.status;
|
||||
pold->severity = newSt.severity;
|
||||
pold->stamp = newSt.time; /* structure copy */
|
||||
options = 0;
|
||||
status = dbChannelGetField(chan, DBR_DOUBLE, &pold->value, &options,
|
||||
status = dbChannelGet(chan, DBR_DOUBLE, &pold->value, &options,
|
||||
nRequest, pfl);
|
||||
}
|
||||
break;
|
||||
@@ -428,7 +431,7 @@ int dbChannel_get_count(
|
||||
} newSt;
|
||||
|
||||
options = DBR_STATUS | DBR_UNITS | DBR_GR_LONG | DBR_AL_LONG;
|
||||
status = dbChannelGetField(chan, DBR_SHORT, &newSt, &options, &zero, pfl);
|
||||
status = dbChannelGet(chan, DBR_SHORT, &newSt, &options, &zero, pfl);
|
||||
pold->status = newSt.status;
|
||||
pold->severity = newSt.severity;
|
||||
strncpy(pold->units, newSt.units, MAX_UNITS_SIZE);
|
||||
@@ -440,7 +443,7 @@ int dbChannel_get_count(
|
||||
pold->lower_warning_limit = newSt.lower_warning_limit;
|
||||
pold->lower_alarm_limit = newSt.lower_alarm_limit;
|
||||
options = 0;
|
||||
status = dbChannelGetField(chan, DBR_SHORT, &pold->value, &options,
|
||||
status = dbChannelGet(chan, DBR_SHORT, &pold->value, &options,
|
||||
nRequest, pfl);
|
||||
}
|
||||
break;
|
||||
@@ -457,7 +460,7 @@ int dbChannel_get_count(
|
||||
|
||||
options = DBR_STATUS | DBR_UNITS | DBR_PRECISION | DBR_GR_DOUBLE |
|
||||
DBR_AL_DOUBLE;
|
||||
status = dbChannelGetField(chan, DBR_FLOAT, &newSt, &options, &zero, pfl);
|
||||
status = dbChannelGet(chan, DBR_FLOAT, &newSt, &options, &zero, pfl);
|
||||
pold->status = newSt.status;
|
||||
pold->severity = newSt.severity;
|
||||
pold->precision = (dbr_short_t) newSt.precision.dp;
|
||||
@@ -470,7 +473,7 @@ int dbChannel_get_count(
|
||||
pold->upper_warning_limit = epicsConvertDoubleToFloat(newSt.upper_warning_limit);
|
||||
pold->lower_warning_limit = epicsConvertDoubleToFloat(newSt.lower_warning_limit);
|
||||
options = 0;
|
||||
status = dbChannelGetField(chan, DBR_FLOAT, &pold->value, &options,
|
||||
status = dbChannelGet(chan, DBR_FLOAT, &pold->value, &options,
|
||||
nRequest, pfl);
|
||||
}
|
||||
break;
|
||||
@@ -486,7 +489,7 @@ int dbChannel_get_count(
|
||||
} newSt;
|
||||
|
||||
options = DBR_STATUS | DBR_UNITS | DBR_GR_LONG | DBR_AL_LONG;
|
||||
status = dbChannelGetField(chan, DBR_UCHAR, &newSt, &options, &zero, pfl);
|
||||
status = dbChannelGet(chan, DBR_UCHAR, &newSt, &options, &zero, pfl);
|
||||
pold->status = newSt.status;
|
||||
pold->severity = newSt.severity;
|
||||
strncpy(pold->units, newSt.units, MAX_UNITS_SIZE);
|
||||
@@ -498,7 +501,7 @@ int dbChannel_get_count(
|
||||
pold->lower_warning_limit = newSt.lower_warning_limit;
|
||||
pold->lower_alarm_limit = newSt.lower_alarm_limit;
|
||||
options = 0;
|
||||
status = dbChannelGetField(chan, DBR_UCHAR, &pold->value, &options,
|
||||
status = dbChannelGet(chan, DBR_UCHAR, &pold->value, &options,
|
||||
nRequest, pfl);
|
||||
}
|
||||
break;
|
||||
@@ -513,7 +516,7 @@ int dbChannel_get_count(
|
||||
} newSt;
|
||||
|
||||
options = DBR_STATUS | DBR_UNITS | DBR_GR_LONG | DBR_AL_LONG;
|
||||
status = dbChannelGetField(chan, DBR_LONG, &newSt, &options, &zero, pfl);
|
||||
status = dbChannelGet(chan, DBR_LONG, &newSt, &options, &zero, pfl);
|
||||
pold->status = newSt.status;
|
||||
pold->severity = newSt.severity;
|
||||
strncpy(pold->units, newSt.units, MAX_UNITS_SIZE);
|
||||
@@ -525,7 +528,7 @@ int dbChannel_get_count(
|
||||
pold->lower_warning_limit = newSt.lower_warning_limit;
|
||||
pold->lower_alarm_limit = newSt.lower_alarm_limit;
|
||||
options = 0;
|
||||
status = dbChannelGetField(chan, DBR_LONG, &pold->value, &options,
|
||||
status = dbChannelGet(chan, DBR_LONG, &pold->value, &options,
|
||||
nRequest, pfl);
|
||||
}
|
||||
break;
|
||||
@@ -542,7 +545,7 @@ int dbChannel_get_count(
|
||||
|
||||
options = DBR_STATUS | DBR_UNITS | DBR_PRECISION | DBR_GR_DOUBLE |
|
||||
DBR_AL_DOUBLE;
|
||||
status = dbChannelGetField(chan, DBR_DOUBLE, &newSt, &options, &zero, pfl);
|
||||
status = dbChannelGet(chan, DBR_DOUBLE, &newSt, &options, &zero, pfl);
|
||||
pold->status = newSt.status;
|
||||
pold->severity = newSt.severity;
|
||||
pold->precision = (dbr_short_t) newSt.precision.dp;
|
||||
@@ -555,7 +558,7 @@ int dbChannel_get_count(
|
||||
pold->lower_warning_limit = newSt.lower_warning_limit;
|
||||
pold->lower_alarm_limit = newSt.lower_alarm_limit;
|
||||
options = 0;
|
||||
status = dbChannelGetField(chan, DBR_DOUBLE, &pold->value, &options,
|
||||
status = dbChannelGet(chan, DBR_DOUBLE, &pold->value, &options,
|
||||
nRequest, pfl);
|
||||
}
|
||||
break;
|
||||
@@ -574,7 +577,7 @@ int dbChannel_get_count(
|
||||
|
||||
options = DBR_STATUS | DBR_UNITS | DBR_GR_LONG | DBR_CTRL_LONG |
|
||||
DBR_AL_LONG;
|
||||
status = dbChannelGetField(chan, DBR_SHORT, &newSt, &options, &zero, pfl);
|
||||
status = dbChannelGet(chan, DBR_SHORT, &newSt, &options, &zero, pfl);
|
||||
pold->status = newSt.status;
|
||||
pold->severity = newSt.severity;
|
||||
strncpy(pold->units, newSt.units, MAX_UNITS_SIZE);
|
||||
@@ -588,7 +591,7 @@ int dbChannel_get_count(
|
||||
pold->upper_ctrl_limit = newSt.upper_ctrl_limit;
|
||||
pold->lower_ctrl_limit = newSt.lower_ctrl_limit;
|
||||
options = 0;
|
||||
status = dbChannelGetField(chan, DBR_SHORT, &pold->value, &options,
|
||||
status = dbChannelGet(chan, DBR_SHORT, &pold->value, &options,
|
||||
nRequest, pfl);
|
||||
}
|
||||
break;
|
||||
@@ -606,7 +609,7 @@ int dbChannel_get_count(
|
||||
|
||||
options = DBR_STATUS | DBR_UNITS | DBR_PRECISION | DBR_GR_DOUBLE |
|
||||
DBR_CTRL_DOUBLE | DBR_AL_DOUBLE;
|
||||
status = dbChannelGetField(chan, DBR_FLOAT, &newSt, &options, &zero, pfl);
|
||||
status = dbChannelGet(chan, DBR_FLOAT, &newSt, &options, &zero, pfl);
|
||||
pold->status = newSt.status;
|
||||
pold->severity = newSt.severity;
|
||||
pold->precision = (dbr_short_t) newSt.precision.dp;
|
||||
@@ -621,7 +624,7 @@ int dbChannel_get_count(
|
||||
pold->upper_ctrl_limit = epicsConvertDoubleToFloat(newSt.upper_ctrl_limit);
|
||||
pold->lower_ctrl_limit = epicsConvertDoubleToFloat(newSt.lower_ctrl_limit);
|
||||
options = 0;
|
||||
status = dbChannelGetField(chan, DBR_FLOAT, &pold->value, &options,
|
||||
status = dbChannelGet(chan, DBR_FLOAT, &pold->value, &options,
|
||||
nRequest, pfl);
|
||||
}
|
||||
break;
|
||||
@@ -638,7 +641,7 @@ int dbChannel_get_count(
|
||||
memset(pold, '\0', sizeof(struct dbr_ctrl_enum));
|
||||
/* first get status and severity */
|
||||
options = DBR_STATUS | DBR_ENUM_STRS;
|
||||
status = dbChannelGetField(chan, DBR_ENUM, &newSt, &options, &zero, pfl);
|
||||
status = dbChannelGet(chan, DBR_ENUM, &newSt, &options, &zero, pfl);
|
||||
pold->status = newSt.status;
|
||||
pold->severity = newSt.severity;
|
||||
no_str = newSt.no_str;
|
||||
@@ -648,7 +651,7 @@ int dbChannel_get_count(
|
||||
strncpy(pold->strs[i], newSt.strs[i], sizeof(pold->strs[i]));
|
||||
/*now get values*/
|
||||
options = 0;
|
||||
status = dbChannelGetField(chan, DBR_ENUM, &pold->value, &options,
|
||||
status = dbChannelGet(chan, DBR_ENUM, &pold->value, &options,
|
||||
nRequest, pfl);
|
||||
}
|
||||
break;
|
||||
@@ -665,7 +668,7 @@ int dbChannel_get_count(
|
||||
|
||||
options = DBR_STATUS | DBR_UNITS | DBR_GR_LONG | DBR_CTRL_LONG |
|
||||
DBR_AL_LONG;
|
||||
status = dbChannelGetField(chan, DBR_UCHAR, &newSt, &options, &zero, pfl);
|
||||
status = dbChannelGet(chan, DBR_UCHAR, &newSt, &options, &zero, pfl);
|
||||
pold->status = newSt.status;
|
||||
pold->severity = newSt.severity;
|
||||
strncpy(pold->units, newSt.units, MAX_UNITS_SIZE);
|
||||
@@ -679,7 +682,7 @@ int dbChannel_get_count(
|
||||
pold->upper_ctrl_limit = newSt.upper_ctrl_limit;
|
||||
pold->lower_ctrl_limit = newSt.lower_ctrl_limit;
|
||||
options = 0;
|
||||
status = dbChannelGetField(chan, DBR_UCHAR, &pold->value, &options,
|
||||
status = dbChannelGet(chan, DBR_UCHAR, &pold->value, &options,
|
||||
nRequest, pfl);
|
||||
}
|
||||
break;
|
||||
@@ -696,7 +699,7 @@ int dbChannel_get_count(
|
||||
|
||||
options = DBR_STATUS | DBR_UNITS | DBR_GR_LONG | DBR_CTRL_LONG |
|
||||
DBR_AL_LONG;
|
||||
status = dbChannelGetField(chan, DBR_LONG, &newSt, &options, &zero, pfl);
|
||||
status = dbChannelGet(chan, DBR_LONG, &newSt, &options, &zero, pfl);
|
||||
pold->status = newSt.status;
|
||||
pold->severity = newSt.severity;
|
||||
strncpy(pold->units, newSt.units, MAX_UNITS_SIZE);
|
||||
@@ -710,7 +713,7 @@ int dbChannel_get_count(
|
||||
pold->upper_ctrl_limit = newSt.upper_ctrl_limit;
|
||||
pold->lower_ctrl_limit = newSt.lower_ctrl_limit;
|
||||
options = 0;
|
||||
status = dbChannelGetField(chan, DBR_LONG, &pold->value, &options,
|
||||
status = dbChannelGet(chan, DBR_LONG, &pold->value, &options,
|
||||
nRequest, pfl);
|
||||
}
|
||||
break;
|
||||
@@ -728,7 +731,7 @@ int dbChannel_get_count(
|
||||
|
||||
options = DBR_STATUS | DBR_UNITS | DBR_PRECISION | DBR_GR_DOUBLE |
|
||||
DBR_CTRL_DOUBLE | DBR_AL_DOUBLE;
|
||||
status = dbChannelGetField(chan, DBR_DOUBLE, &newSt, &options, &zero, pfl);
|
||||
status = dbChannelGet(chan, DBR_DOUBLE, &newSt, &options, &zero, pfl);
|
||||
pold->status = newSt.status;
|
||||
pold->severity = newSt.severity;
|
||||
pold->precision = (dbr_short_t) newSt.precision.dp;
|
||||
@@ -743,7 +746,7 @@ int dbChannel_get_count(
|
||||
pold->upper_ctrl_limit = newSt.upper_ctrl_limit;
|
||||
pold->lower_ctrl_limit = newSt.lower_ctrl_limit;
|
||||
options = 0;
|
||||
status = dbChannelGetField(chan, DBR_DOUBLE, &pold->value, &options,
|
||||
status = dbChannelGet(chan, DBR_DOUBLE, &pold->value, &options,
|
||||
nRequest, pfl);
|
||||
}
|
||||
break;
|
||||
@@ -756,13 +759,13 @@ int dbChannel_get_count(
|
||||
} newSt;
|
||||
|
||||
options = DBR_STATUS;
|
||||
status = dbChannelGetField(chan, DBR_STRING, &newSt, &options, &zero, pfl);
|
||||
status = dbChannelGet(chan, DBR_STRING, &newSt, &options, &zero, pfl);
|
||||
pold->status = newSt.status;
|
||||
pold->severity = newSt.severity;
|
||||
pold->ackt = newSt.ackt;
|
||||
pold->acks = newSt.acks;
|
||||
options = 0;
|
||||
status = dbChannelGetField(chan, DBR_STRING, pold->value,
|
||||
status = dbChannelGet(chan, DBR_STRING, pold->value,
|
||||
&options, nRequest, pfl);
|
||||
}
|
||||
break;
|
||||
@@ -791,8 +794,12 @@ int dbChannel_get_count(
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return -1;
|
||||
status = -1;
|
||||
break;
|
||||
}
|
||||
|
||||
dbScanUnlock(dbChannelRecord(chan));
|
||||
|
||||
if (status) return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -19,7 +19,8 @@
|
||||
#ifndef INCLdb_field_logh
|
||||
#define INCLdb_field_logh
|
||||
|
||||
#include "epicsTime.h"
|
||||
#include <epicsTime.h>
|
||||
#include <epicsTypes.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
@@ -32,18 +33,20 @@ extern "C" {
|
||||
* priority task pending on the event queue wakes up). Strings would slow down
|
||||
* events for more reasonable size values. DB fields of native type string
|
||||
* will most likely change infrequently.
|
||||
*
|
||||
*
|
||||
* Strings can be added to the set of types for which updates will be queued
|
||||
* by defining the macro DB_EVENT_LOG_STRINGS. The code in db_add_event()
|
||||
* will adjust automatically, it just compares field sizes.
|
||||
*/
|
||||
union native_value {
|
||||
short dbf_int;
|
||||
short dbf_short;
|
||||
float dbf_float;
|
||||
short dbf_enum;
|
||||
char dbf_char;
|
||||
long dbf_long;
|
||||
double dbf_double;
|
||||
epicsInt8 dbf_char;
|
||||
epicsInt16 dbf_short;
|
||||
epicsEnum16 dbf_enum;
|
||||
epicsInt32 dbf_long;
|
||||
epicsFloat32 dbf_float;
|
||||
epicsFloat64 dbf_double;
|
||||
#ifdef DB_EVENT_LOG_STRINGS
|
||||
char dbf_string[MAX_STRING_SIZE];
|
||||
char dbf_string[MAX_STRING_SIZE];
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
@@ -132,7 +132,7 @@ const char *initHookName(int state)
|
||||
"initHookAfterInterruptAccept",
|
||||
"initHookAtEnd"
|
||||
};
|
||||
if (state < 0 || state > NELEMENTS(stateName)) {
|
||||
if (state < 0 || state >= NELEMENTS(stateName)) {
|
||||
return "Not an initHookState";
|
||||
}
|
||||
return stateName[state];
|
||||
|
||||
@@ -309,11 +309,11 @@ static void getMaxRangeValues(short field_type, double *pupper_limit,
|
||||
{
|
||||
switch(field_type){
|
||||
case DBF_CHAR:
|
||||
*pupper_limit = -128.0;
|
||||
*plower_limit = 127.0;
|
||||
*pupper_limit = (double) CHAR_MAX;
|
||||
*plower_limit = (double) CHAR_MIN;
|
||||
break;
|
||||
case DBF_UCHAR:
|
||||
*pupper_limit = 255.0;
|
||||
*pupper_limit = (double) UCHAR_MAX;
|
||||
*plower_limit = 0.0;
|
||||
break;
|
||||
case DBF_SHORT:
|
||||
|
||||
@@ -12,14 +12,17 @@ include $(TOP)/configure/CONFIG
|
||||
|
||||
TESTLIBRARY = dbTestIoc
|
||||
|
||||
dbTestIoc_SRCS += arrRecord.c
|
||||
dbTestIoc_SRCS += xRecord.c
|
||||
dbTestIoc_SRCS += dbLinkdset.c
|
||||
dbTestIoc_LIBS = dbCore ca Com
|
||||
|
||||
TARGETS += $(COMMON_DIR)/dbTestIoc.dbd
|
||||
DBDDEPENDS_FILES += dbTestIoc.dbd$(DEP)
|
||||
dbTestIoc_DBD += menuGlobal.dbd
|
||||
dbTestIoc_DBD += menuConvert.dbd
|
||||
dbTestIoc_DBD += menuScan.dbd
|
||||
#dbTestIoc_DBD += arrRecord.dbd
|
||||
dbTestIoc_DBD += xRecord.dbd
|
||||
dbTestIoc_DBD += dbLinkdset.dbd
|
||||
TESTFILES += $(COMMON_DIR)/dbTestIoc.dbd ../xRecord.db
|
||||
@@ -76,6 +79,7 @@ TESTS += dbCaStatsTest
|
||||
TESTFILES += ../dbCaStatsTest.db
|
||||
|
||||
TARGETS += $(COMMON_DIR)/scanIoTest.dbd
|
||||
DBDDEPENDS_FILES += scanIoTest.dbd$(DEP)
|
||||
scanIoTest_DBD += menuGlobal.dbd
|
||||
scanIoTest_DBD += menuConvert.dbd
|
||||
scanIoTest_DBD += menuScan.dbd
|
||||
@@ -95,6 +99,17 @@ dbChannelTest_SRCS += dbTestIoc_registerRecordDeviceDriver.cpp
|
||||
testHarness_SRCS += dbChannelTest.c
|
||||
TESTS += dbChannelTest
|
||||
|
||||
TARGETS += $(COMMON_DIR)/dbChArrTest.dbd
|
||||
DBDDEPENDS_FILES += dbChArrTest.dbd$(DEP)
|
||||
dbChArrTest_DBD += arrRecord.dbd
|
||||
TESTPROD_HOST += dbChArrTest
|
||||
dbChArrTest_SRCS += dbChArrTest.cpp
|
||||
dbChArrTest_SRCS += dbChArrTest_registerRecordDeviceDriver.cpp
|
||||
testHarness_SRCS += dbChArrTest.cpp
|
||||
testHarness_SRCS += dbChArrTest_registerRecordDeviceDriver.cpp
|
||||
TESTFILES += $(COMMON_DIR)/dbChArrTest.dbd ../dbChArrTest.db
|
||||
TESTS += dbChArrTest
|
||||
|
||||
TESTPROD_HOST += chfPluginTest
|
||||
chfPluginTest_SRCS += chfPluginTest.c
|
||||
chfPluginTest_SRCS += dbTestIoc_registerRecordDeviceDriver.cpp
|
||||
@@ -132,7 +147,7 @@ TESTSCRIPTS_HOST += $(TESTS:%=%.t)
|
||||
|
||||
include $(TOP)/configure/RULES
|
||||
|
||||
arrRecord$(DEP): $(COMMON_DIR)/arrRecord.h
|
||||
xRecord$(DEP): $(COMMON_DIR)/xRecord.h
|
||||
dbPutLinkTest$(DEP): $(COMMON_DIR)/xRecord.h
|
||||
scanIoTest$(DEP): $(COMMON_DIR)/yRecord.h
|
||||
|
||||
|
||||
135
src/ioc/db/test/arrRecord.c
Normal file
135
src/ioc/db/test/arrRecord.c
Normal file
@@ -0,0 +1,135 @@
|
||||
/*************************************************************************\
|
||||
* Copyright (c) 2010 Brookhaven National Laboratory.
|
||||
* Copyright (c) 2010 Helmholtz-Zentrum Berlin
|
||||
* fuer Materialien und Energie GmbH.
|
||||
* Copyright (c) 2008 UChicago Argonne LLC, as Operator of Argonne
|
||||
* National Laboratory.
|
||||
* Copyright (c) 2002 The Regents of the University of California, as
|
||||
* Operator of Los Alamos National Laboratory.
|
||||
* EPICS BASE is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
\*************************************************************************/
|
||||
|
||||
/* arrRecord.c - minimal array record for test purposes: no processing */
|
||||
|
||||
/*
|
||||
* Author: Ralph Lange <Ralph.Lange@bessy.de>
|
||||
*
|
||||
* vaguely implemented like parts of recWaveform.c by Bob Dalesio
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include "dbDefs.h"
|
||||
#include "epicsPrint.h"
|
||||
#include "dbAccess.h"
|
||||
#include "dbEvent.h"
|
||||
#include "dbFldTypes.h"
|
||||
#include "recSup.h"
|
||||
#include "recGbl.h"
|
||||
#include "cantProceed.h"
|
||||
#define GEN_SIZE_OFFSET
|
||||
#include "arrRecord.h"
|
||||
#undef GEN_SIZE_OFFSET
|
||||
#include "epicsExport.h"
|
||||
|
||||
/* Create RSET - Record Support Entry Table*/
|
||||
#define report NULL
|
||||
#define initialize NULL
|
||||
static long init_record(arrRecord *, int);
|
||||
static long process(arrRecord *);
|
||||
#define special NULL
|
||||
#define get_value NULL
|
||||
static long cvt_dbaddr(DBADDR *);
|
||||
static long get_array_info(DBADDR *, long *, long *);
|
||||
static long put_array_info(DBADDR *, long);
|
||||
#define get_units NULL
|
||||
#define get_precision NULL
|
||||
#define get_enum_str NULL
|
||||
#define get_enum_strs NULL
|
||||
#define put_enum_str NULL
|
||||
#define get_graphic_double NULL
|
||||
#define get_control_double NULL
|
||||
#define get_alarm_double NULL
|
||||
|
||||
rset arrRSET = {
|
||||
RSETNUMBER,
|
||||
report,
|
||||
initialize,
|
||||
init_record,
|
||||
process,
|
||||
special,
|
||||
get_value,
|
||||
cvt_dbaddr,
|
||||
get_array_info,
|
||||
put_array_info,
|
||||
get_units,
|
||||
get_precision,
|
||||
get_enum_str,
|
||||
get_enum_strs,
|
||||
put_enum_str,
|
||||
get_graphic_double,
|
||||
get_control_double,
|
||||
get_alarm_double
|
||||
};
|
||||
epicsExportAddress(rset, arrRSET);
|
||||
|
||||
static long init_record(arrRecord *prec, int pass)
|
||||
{
|
||||
if (pass == 0) {
|
||||
if (prec->nelm <= 0)
|
||||
prec->nelm = 1;
|
||||
if (prec->ftvl > DBF_ENUM)
|
||||
prec->ftvl = DBF_UCHAR;
|
||||
prec->bptr = callocMustSucceed(prec->nelm, dbValueSize(prec->ftvl),
|
||||
"arr calloc failed");
|
||||
|
||||
if (prec->nelm == 1) {
|
||||
prec->nord = 1;
|
||||
} else {
|
||||
prec->nord = 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long process(arrRecord *prec)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
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);
|
||||
paddr->dbr_field_type = prec->ftvl;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long get_array_info(DBADDR *paddr, long *no_elements, long *offset)
|
||||
{
|
||||
arrRecord *prec = (arrRecord *) paddr->precord;
|
||||
|
||||
*no_elements = prec->nord;
|
||||
*offset = prec->off;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long put_array_info(DBADDR *paddr, long nNew)
|
||||
{
|
||||
arrRecord *prec = (arrRecord *) paddr->precord;
|
||||
|
||||
prec->nord = nNew;
|
||||
if (prec->nord > prec->nelm)
|
||||
prec->nord = prec->nelm;
|
||||
|
||||
return 0;
|
||||
}
|
||||
34
src/ioc/db/test/arrRecord.dbd
Normal file
34
src/ioc/db/test/arrRecord.dbd
Normal file
@@ -0,0 +1,34 @@
|
||||
include "menuGlobal.dbd"
|
||||
include "menuConvert.dbd"
|
||||
include "menuScan.dbd"
|
||||
recordtype(arr) {
|
||||
include "dbCommon.dbd"
|
||||
field(VAL, DBF_NOACCESS) {
|
||||
prompt("Value")
|
||||
special(SPC_DBADDR)
|
||||
pp(TRUE)
|
||||
extra("void *val")
|
||||
}
|
||||
field(NELM, DBF_ULONG) {
|
||||
prompt("Number of Elements")
|
||||
special(SPC_NOMOD)
|
||||
initial("1")
|
||||
}
|
||||
field(FTVL, DBF_MENU) {
|
||||
prompt("Field Type of Value")
|
||||
special(SPC_NOMOD)
|
||||
menu(menuFtype)
|
||||
}
|
||||
field(NORD, DBF_ULONG) {
|
||||
prompt("Number elements read")
|
||||
special(SPC_NOMOD)
|
||||
}
|
||||
field(OFF, DBF_ULONG) {
|
||||
prompt("Offset into array")
|
||||
}
|
||||
field(BPTR, DBF_NOACCESS) {
|
||||
prompt("Buffer Pointer")
|
||||
special(SPC_NOMOD)
|
||||
extra("void *bptr")
|
||||
}
|
||||
}
|
||||
@@ -99,7 +99,7 @@ MAIN(callbackParallelTest)
|
||||
myPvt *pcbt[NCALLBACKS];
|
||||
epicsTimeStamp start;
|
||||
int noCpus = epicsThreadGetCPUs();
|
||||
int i, j;
|
||||
int i, j, slowups, faults;
|
||||
/* Statistics: min/max/sum/sum^2/n for each priority */
|
||||
double setupError[NUM_CALLBACK_PRIORITIES][5];
|
||||
double timeError[NUM_CALLBACK_PRIORITIES][5];
|
||||
@@ -109,7 +109,7 @@ MAIN(callbackParallelTest)
|
||||
for (j = 0; j < 5; j++)
|
||||
setupError[i][j] = timeError[i][j] = defaultError[j];
|
||||
|
||||
testPlan(NCALLBACKS * 2 + 1);
|
||||
testPlan(4);
|
||||
|
||||
testDiag("Starting %d parallel callback threads", noCpus);
|
||||
|
||||
@@ -138,7 +138,7 @@ MAIN(callbackParallelTest)
|
||||
pcbt[NCALLBACKS-1]->delay = TEST_DELAY(NCALLBACKS) + 1.0;
|
||||
pcbt[NCALLBACKS-1]->pass = 0;
|
||||
|
||||
testOk1(epicsTimeGetCurrent(&start)==epicsTimeOK);
|
||||
testOk(epicsTimeGetCurrent(&start)==epicsTimeOK, "Time-of-day clock Ok");
|
||||
|
||||
for (i = 0; i < NCALLBACKS ; i++) {
|
||||
callbackRequest(&pcbt[i]->cb1);
|
||||
@@ -147,28 +147,43 @@ MAIN(callbackParallelTest)
|
||||
testDiag("Waiting %.02f sec", pcbt[NCALLBACKS-1]->delay);
|
||||
|
||||
epicsEventWait(finished);
|
||||
|
||||
slowups = 0;
|
||||
faults = 0;
|
||||
|
||||
for (i = 0; i < NCALLBACKS ; i++) {
|
||||
if(pcbt[i]->resultFail || pcbt[i]->pass!=2)
|
||||
testFail("pass = %d for delay = %f", pcbt[i]->pass, pcbt[i]->delay);
|
||||
testDiag("callback setup fault #%d: pass = %d for delay = %.02f",
|
||||
++faults, pcbt[i]->pass, pcbt[i]->delay);
|
||||
else {
|
||||
double delta = epicsTimeDiffInSeconds(&pcbt[i]->pass1Time, &start);
|
||||
testOk(fabs(delta) < 0.05, "callback %.02f setup time |%f| < 0.05",
|
||||
|
||||
if (fabs(delta) >= 0.05) {
|
||||
slowups++;
|
||||
testDiag("callback %.02f setup time |%f| >= 0.05 seconds",
|
||||
pcbt[i]->delay, delta);
|
||||
}
|
||||
updateStats(setupError[i%NUM_CALLBACK_PRIORITIES], delta);
|
||||
}
|
||||
}
|
||||
testOk(faults == 0, "%d faults during callback setup", faults);
|
||||
testOk(slowups <= 1, "%d slowups during callback setup", slowups);
|
||||
|
||||
slowups = 0;
|
||||
for (i = 0; i < NCALLBACKS ; i++) {
|
||||
double delta, error;
|
||||
|
||||
if(pcbt[i]->resultFail || pcbt[i]->pass!=2)
|
||||
continue;
|
||||
delta = epicsTimeDiffInSeconds(&pcbt[i]->pass2Time, &pcbt[i]->pass1Time);
|
||||
error = delta - pcbt[i]->delay;
|
||||
testOk(fabs(error) < 0.05, "delay %.02f seconds, callback time error |%.04f| < 0.05",
|
||||
if (fabs(error) >= 0.05) {
|
||||
slowups++;
|
||||
testDiag("delay %.02f seconds, delay error |%.04f| >= 0.05",
|
||||
pcbt[i]->delay, error);
|
||||
}
|
||||
updateStats(timeError[i%NUM_CALLBACK_PRIORITIES], error);
|
||||
}
|
||||
testOk(slowups < 5, "%d slowups during callbacks", slowups);
|
||||
|
||||
testDiag("Setup time statistics");
|
||||
printStats(setupError[0], "LOW");
|
||||
@@ -184,7 +199,8 @@ MAIN(callbackParallelTest)
|
||||
free(pcbt[i]);
|
||||
}
|
||||
|
||||
callbackShutdown();
|
||||
callbackStop();
|
||||
callbackCleanup();
|
||||
|
||||
return testDone();
|
||||
}
|
||||
|
||||
@@ -99,7 +99,7 @@ MAIN(callbackTest)
|
||||
{
|
||||
myPvt *pcbt[NCALLBACKS];
|
||||
epicsTimeStamp start;
|
||||
int i, j;
|
||||
int i, j, slowups, faults;
|
||||
/* Statistics: min/max/sum/sum^2/n for each priority */
|
||||
double setupError[NUM_CALLBACK_PRIORITIES][5];
|
||||
double timeError[NUM_CALLBACK_PRIORITIES][5];
|
||||
@@ -109,7 +109,7 @@ MAIN(callbackTest)
|
||||
for (j = 0; j < 5; j++)
|
||||
setupError[i][j] = timeError[i][j] = defaultError[j];
|
||||
|
||||
testPlan(NCALLBACKS * 2 + 1);
|
||||
testPlan(4);
|
||||
|
||||
callbackInit();
|
||||
epicsThreadSleep(1.0);
|
||||
@@ -135,7 +135,7 @@ MAIN(callbackTest)
|
||||
pcbt[NCALLBACKS-1]->delay = TEST_DELAY(NCALLBACKS) + 1.0;
|
||||
pcbt[NCALLBACKS-1]->pass = 0;
|
||||
|
||||
testOk1(epicsTimeGetCurrent(&start)==epicsTimeOK);
|
||||
testOk(epicsTimeGetCurrent(&start)==epicsTimeOK, "Time-of-day clock Ok");
|
||||
|
||||
for (i = 0; i < NCALLBACKS ; i++) {
|
||||
callbackRequest(&pcbt[i]->cb1);
|
||||
@@ -144,28 +144,43 @@ MAIN(callbackTest)
|
||||
testDiag("Waiting %.02f sec", pcbt[NCALLBACKS-1]->delay);
|
||||
|
||||
epicsEventWait(finished);
|
||||
|
||||
slowups = 0;
|
||||
faults = 0;
|
||||
|
||||
for (i = 0; i < NCALLBACKS ; i++) {
|
||||
if(pcbt[i]->resultFail || pcbt[i]->pass!=2)
|
||||
testFail("pass = %d for delay = %f", pcbt[i]->pass, pcbt[i]->delay);
|
||||
testDiag("callback setup fault #%d: pass = %d for delay = %.02f",
|
||||
++faults, pcbt[i]->pass, pcbt[i]->delay);
|
||||
else {
|
||||
double delta = epicsTimeDiffInSeconds(&pcbt[i]->pass1Time, &start);
|
||||
testOk(fabs(delta) < 0.05, "callback %.02f setup time |%f| < 0.05",
|
||||
|
||||
if (fabs(delta) >= 0.05) {
|
||||
slowups++;
|
||||
testDiag("callback %.02f setup time |%f| >= 0.05 seconds",
|
||||
pcbt[i]->delay, delta);
|
||||
}
|
||||
updateStats(setupError[i%NUM_CALLBACK_PRIORITIES], delta);
|
||||
}
|
||||
}
|
||||
testOk(faults == 0, "%d faults during callback setup", faults);
|
||||
testOk(slowups <= 1, "%d slowups during callback setup", slowups);
|
||||
|
||||
slowups = 0;
|
||||
for (i = 0; i < NCALLBACKS ; i++) {
|
||||
double delta, error;
|
||||
|
||||
if(pcbt[i]->resultFail || pcbt[i]->pass!=2)
|
||||
continue;
|
||||
delta = epicsTimeDiffInSeconds(&pcbt[i]->pass2Time, &pcbt[i]->pass1Time);
|
||||
error = delta - pcbt[i]->delay;
|
||||
testOk(fabs(error) < 0.05, "delay %.02f seconds, callback time error |%.04f| < 0.05",
|
||||
if (fabs(error) >= 0.05) {
|
||||
slowups++;
|
||||
testDiag("delay %.02f seconds, delay error |%.04f| >= 0.05",
|
||||
pcbt[i]->delay, error);
|
||||
}
|
||||
updateStats(timeError[i%NUM_CALLBACK_PRIORITIES], error);
|
||||
}
|
||||
testOk(slowups < 5, "%d slowups during callbacks", slowups);
|
||||
|
||||
testDiag("Setup time statistics");
|
||||
printStats(setupError[0], "LOW");
|
||||
@@ -181,7 +196,8 @@ MAIN(callbackTest)
|
||||
free(pcbt[i]);
|
||||
}
|
||||
|
||||
callbackShutdown();
|
||||
callbackStop();
|
||||
callbackCleanup();
|
||||
|
||||
return testDone();
|
||||
}
|
||||
|
||||
@@ -418,7 +418,7 @@ static void channelRegisterPost(dbChannel *chan, void *user,
|
||||
static void channel_report(dbChannel *chan, void *user, int level,
|
||||
const unsigned short indent)
|
||||
{
|
||||
testOk(level == R_LEVEL - 1, "channel_report: level correct");
|
||||
testOk(level == R_LEVEL - 2, "channel_report: level correct %u == %u", level, R_LEVEL-2);
|
||||
if (user == puser1) {
|
||||
testOk(e1 & e_report, "channel_report (1) called");
|
||||
c1 |= e_report;
|
||||
|
||||
246
src/ioc/db/test/dbChArrTest.cpp
Normal file
246
src/ioc/db/test/dbChArrTest.cpp
Normal file
@@ -0,0 +1,246 @@
|
||||
/*************************************************************************\
|
||||
* Copyright (c) 2010 Brookhaven National Laboratory.
|
||||
* Copyright (c) 2010 Helmholtz-Zentrum Berlin
|
||||
* fuer Materialien und Energie GmbH.
|
||||
* Copyright (c) 2016 UChicago Argonne LLC, as Operator of Argonne
|
||||
* National Laboratory.
|
||||
* Copyright (c) 2003 The Regents of the University of California, as
|
||||
* Operator of Los Alamos National Laboratory.
|
||||
* EPICS BASE is distributed subject to the Software License Agreement
|
||||
* found in the file LICENSE that is included with this distribution.
|
||||
\*************************************************************************/
|
||||
|
||||
/*
|
||||
* Authors: Ralph Lange <ralph.lange@gmx.de>,
|
||||
* Andrew Johnson <anj@aps.anl.gov>
|
||||
*/
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "registryFunction.h"
|
||||
#include "epicsThread.h"
|
||||
#include "epicsExit.h"
|
||||
#include "epicsStdio.h"
|
||||
#include "epicsString.h"
|
||||
#include "envDefs.h"
|
||||
#include "dbStaticLib.h"
|
||||
#include "dbmf.h"
|
||||
#include "registry.h"
|
||||
#include "dbAddr.h"
|
||||
#include "dbAccess.h"
|
||||
#include "asDbLib.h"
|
||||
#include "iocInit.h"
|
||||
#include "iocsh.h"
|
||||
#include "dbChannel.h"
|
||||
#include "epicsUnitTest.h"
|
||||
#include "testMain.h"
|
||||
#include "osiFileName.h"
|
||||
|
||||
extern "C" {
|
||||
int dbChArrTest_registerRecordDeviceDriver(struct dbBase *pdbbase);
|
||||
}
|
||||
|
||||
#define CA_SERVER_PORT "65535"
|
||||
|
||||
const char *server_port = CA_SERVER_PORT;
|
||||
|
||||
static void createAndOpen(const char *name, dbChannel**pch)
|
||||
{
|
||||
testOk(!!(*pch = dbChannelCreate(name)), "dbChannel %s created", name);
|
||||
testOk(!(dbChannelOpen(*pch)), "dbChannel opened");
|
||||
testOk((ellCount(&(*pch)->pre_chain) == 0), "no filters in pre chain");
|
||||
testOk((ellCount(&(*pch)->post_chain) == 0), "no filters in post chain");
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
static void freeArray(db_field_log *pfl) {
|
||||
if (pfl->type == dbfl_type_ref) {
|
||||
free(pfl->u.r.field);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void testHead (const char *title, const char *typ = "") {
|
||||
const char *line = "------------------------------------------------------------------------------";
|
||||
testDiag("%s", line);
|
||||
testDiag(title, typ);
|
||||
testDiag("%s", line);
|
||||
}
|
||||
|
||||
static void check(short dbr_type) {
|
||||
dbChannel *pch;
|
||||
db_field_log *pfl;
|
||||
dbAddr valaddr;
|
||||
dbAddr offaddr;
|
||||
const char *offname = NULL, *valname = NULL, *typname = NULL;
|
||||
epicsInt32 buf[26];
|
||||
long off, req;
|
||||
int i;
|
||||
|
||||
switch (dbr_type) {
|
||||
case DBR_LONG:
|
||||
offname = "i32.OFF";
|
||||
valname = "i32.VAL";
|
||||
typname = "long";
|
||||
break;
|
||||
case DBR_DOUBLE:
|
||||
offname = "f64.OFF";
|
||||
valname = "f64.VAL";
|
||||
typname = "double";
|
||||
break;
|
||||
case DBR_STRING:
|
||||
offname = "c40.OFF";
|
||||
valname = "c40.VAL";
|
||||
typname = "string";
|
||||
break;
|
||||
default:
|
||||
testDiag("Invalid data type %d", dbr_type);
|
||||
}
|
||||
|
||||
(void) dbNameToAddr(offname, &offaddr);
|
||||
(void) dbNameToAddr(valname, &valaddr);
|
||||
|
||||
testHead("Ten %s elements", typname);
|
||||
|
||||
/* Fill the record's array field with data, 10..19 */
|
||||
|
||||
epicsInt32 ar[10] = {10,11,12,13,14,15,16,17,18,19};
|
||||
(void) dbPutField(&valaddr, DBR_LONG, ar, 10);
|
||||
|
||||
/* Open a channel to it, make sure no filters present */
|
||||
|
||||
createAndOpen(valname, &pch);
|
||||
testOk(pch->final_type == valaddr.field_type,
|
||||
"final type unchanged (%d->%d)", valaddr.field_type, pch->final_type);
|
||||
testOk(pch->final_no_elements == valaddr.no_elements,
|
||||
"final no_elements unchanged (%ld->%ld)", valaddr.no_elements, pch->final_no_elements);
|
||||
|
||||
/* TEST1 sets the record's OFF field, then requests 10 elements from the channel,
|
||||
* passing in a transparent db_field_log and converting the data to LONG on the way in.
|
||||
* It checks that it got back the expected data and the right number of elements.
|
||||
*/
|
||||
|
||||
#define TEST1(Size, Offset, Text, Expected) \
|
||||
testDiag("Reading from offset = %d (%s)", Offset, Text); \
|
||||
off = Offset; req = 10; \
|
||||
memset(buf, 0, sizeof(buf)); \
|
||||
(void) dbPutField(&offaddr, DBR_LONG, &off, 1); \
|
||||
pfl = db_create_read_log(pch); \
|
||||
testOk(pfl && pfl->type == dbfl_type_rec, "Valid pfl, type = rec"); \
|
||||
testOk(!dbChannelGetField(pch, DBR_LONG, buf, NULL, &req, pfl), "Got Field value"); \
|
||||
testOk(req == Size, "Got %ld elements (expected %d)", req, Size); \
|
||||
if (!testOk(!memcmp(buf, Expected, sizeof(Expected)), "Data correct")) \
|
||||
for (i=0; i<Size; i++) \
|
||||
testDiag("Element %d expected %d got %d", i, Expected[i], buf[i]); \
|
||||
db_delete_field_log(pfl);
|
||||
|
||||
const epicsInt32 res_10_0[] = {10,11,12,13,14,15,16,17,18,19};
|
||||
TEST1(10, 0, "no offset", res_10_0);
|
||||
|
||||
const epicsInt32 res_10_4[] = {14,15,16,17,18,19,10,11,12,13};
|
||||
TEST1(10, 4, "wrapped", res_10_4);
|
||||
|
||||
/* Partial array */
|
||||
|
||||
testHead("Five %s elements", typname);
|
||||
off = 0; /* Reset offset for writing the next buffer */
|
||||
(void) dbPutField(&offaddr, DBR_LONG, &off, 1);
|
||||
(void) dbPutField(&valaddr, DBR_LONG, &ar[5], 5);
|
||||
|
||||
createAndOpen(valname, &pch);
|
||||
testOk(pch->final_type == valaddr.field_type,
|
||||
"final type unchanged (%d->%d)", valaddr.field_type, pch->final_type);
|
||||
testOk(pch->final_no_elements == valaddr.no_elements,
|
||||
"final no_elements unchanged (%ld->%ld)", valaddr.no_elements, pch->final_no_elements);
|
||||
|
||||
const epicsInt32 res_5_0[] = {15,16,17,18,19};
|
||||
TEST1(5, 0, "no offset", res_5_0);
|
||||
|
||||
const epicsInt32 res_5_3[] = {18,19,15,16,17};
|
||||
TEST1(5, 3, "wrapped", res_5_3);
|
||||
|
||||
/* TEST2 sets the record's OFF field, then requests 15 elements from the channel
|
||||
* but passes in a db_field_log with alternate data, converting that data to LONG.
|
||||
* It checks that it got back the expected data and the right number of elements.
|
||||
*/
|
||||
|
||||
#define TEST2(Size, Offset, Text, Expected) \
|
||||
testDiag("Reading from offset = %d (%s)", Offset, Text); \
|
||||
off = Offset; req = 15; \
|
||||
memset(buf, 0, sizeof(buf)); \
|
||||
(void) dbPutField(&offaddr, DBR_LONG, &off, 1); \
|
||||
pfl = db_create_read_log(pch); \
|
||||
pfl->type = dbfl_type_ref; \
|
||||
pfl->field_type = DBF_CHAR; \
|
||||
pfl->field_size = 1; \
|
||||
pfl->no_elements = 26; \
|
||||
pfl->u.r.dtor = freeArray; \
|
||||
pfl->u.r.field = epicsStrDup("abcdefghijklmnopqrsstuvwxyz"); \
|
||||
testOk(!dbChannelGetField(pch, DBR_LONG, buf, NULL, &req, pfl), "Got Field value"); \
|
||||
testOk(req == Size, "Got %ld elements (expected %d)", req, Size); \
|
||||
if (!testOk(!memcmp(buf, Expected, sizeof(Expected)), "Data correct")) \
|
||||
for (i=0; i<Size; i++) \
|
||||
testDiag("Element %d expected '%c' got '%c'", i, Expected[i], buf[i]); \
|
||||
db_delete_field_log(pfl);
|
||||
|
||||
testHead("Fifteen letters from field-log instead of %s", typname);
|
||||
|
||||
const epicsInt32 res_15[] = {'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l',
|
||||
'm', 'n', 'o'};
|
||||
TEST2(15, 0, "no offset", res_15);
|
||||
TEST2(15, 10, "ignored", res_15);
|
||||
|
||||
dbChannelDelete(pch);
|
||||
}
|
||||
|
||||
static dbEventCtx evtctx;
|
||||
|
||||
extern "C" {
|
||||
static void dbChArrTestCleanup(void* junk)
|
||||
{
|
||||
dbFreeBase(pdbbase);
|
||||
registryFree();
|
||||
pdbbase=0;
|
||||
|
||||
db_close_events(evtctx);
|
||||
|
||||
dbmfFreeChunks();
|
||||
}
|
||||
}
|
||||
|
||||
MAIN(dbChArrTest)
|
||||
{
|
||||
testPlan(102);
|
||||
|
||||
/* Prepare the IOC */
|
||||
|
||||
epicsEnvSet("EPICS_CA_SERVER_PORT", server_port);
|
||||
|
||||
if (dbReadDatabase(&pdbbase, "dbChArrTest.dbd",
|
||||
"." OSI_PATH_LIST_SEPARATOR ".." OSI_PATH_LIST_SEPARATOR
|
||||
"../O.Common" OSI_PATH_LIST_SEPARATOR "O.Common", NULL))
|
||||
testAbort("Database description not loaded");
|
||||
|
||||
dbChArrTest_registerRecordDeviceDriver(pdbbase);
|
||||
|
||||
if (dbReadDatabase(&pdbbase, "dbChArrTest.db",
|
||||
"." OSI_PATH_LIST_SEPARATOR "..", NULL))
|
||||
testAbort("Test database not loaded");
|
||||
|
||||
epicsAtExit(&dbChArrTestCleanup,NULL);
|
||||
|
||||
/* Start the IOC */
|
||||
|
||||
iocInit();
|
||||
evtctx = db_init_events();
|
||||
|
||||
check(DBR_LONG);
|
||||
check(DBR_DOUBLE);
|
||||
check(DBR_STRING);
|
||||
|
||||
return testDone();
|
||||
}
|
||||
15
src/ioc/db/test/dbChArrTest.db
Normal file
15
src/ioc/db/test/dbChArrTest.db
Normal file
@@ -0,0 +1,15 @@
|
||||
record(arr, "i32") {
|
||||
field(DESC, "test array record")
|
||||
field(NELM, "10")
|
||||
field(FTVL, "LONG")
|
||||
}
|
||||
record(arr, "f64") {
|
||||
field(DESC, "test array record")
|
||||
field(NELM, "10")
|
||||
field(FTVL, "DOUBLE")
|
||||
}
|
||||
record(arr, "c40") {
|
||||
field(DESC, "test array record")
|
||||
field(NELM, "10")
|
||||
field(FTVL, "STRING")
|
||||
}
|
||||
@@ -242,7 +242,7 @@ MAIN(testDbChannel) /* dbChannelTest is an API routine... */
|
||||
testOk1(!!(pch = dbChannelCreate("x.{\"scalar\":null}")));
|
||||
|
||||
e = e_report;
|
||||
dbChannelShow(pch, 1, 2);
|
||||
dbChannelShow(pch, 2, 2);
|
||||
|
||||
e = e_close;
|
||||
if (pch) dbChannelDelete(pch);
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
record(ai, "somename") {}
|
||||
@@ -165,8 +165,9 @@ typedef struct dbBase {
|
||||
ELLLIST functionList;
|
||||
ELLLIST variableList;
|
||||
ELLLIST bptList;
|
||||
ELLLIST filterList;
|
||||
void *pathPvt;
|
||||
ELLLIST filterList;
|
||||
ELLLIST guiGroupList;
|
||||
void *pathPvt;
|
||||
struct dbPvd *ppvd;
|
||||
struct gphPvt *pgpHash;
|
||||
short ignoreMissingMenus;
|
||||
|
||||
@@ -73,15 +73,20 @@ static int yyreset(void)
|
||||
{whitespace} ;
|
||||
|
||||
{doublequote}({stringchar}|{escape})*{newline} { /* bad string */
|
||||
yyerror("Newline in string, closing quote missing");
|
||||
yyerrorAbort("Newline in string, closing quote missing");
|
||||
}
|
||||
|
||||
. {
|
||||
char message[40];
|
||||
YY_BUFFER_STATE *dummy=0;
|
||||
|
||||
sprintf(message,"Invalid character '%c'",yytext[0]);
|
||||
yyerror(message);
|
||||
if (isprint((int) yytext[0])) {
|
||||
sprintf(message, "Invalid character '%c'", yytext[0]);
|
||||
}
|
||||
else {
|
||||
sprintf(message, "Invalid character 0x%2.2x", yytext[0]);
|
||||
}
|
||||
yyerrorAbort(message);
|
||||
/*The following suppresses compiler warning messages*/
|
||||
if(FALSE) yyunput('c',(unsigned char *) message);
|
||||
if(FALSE) yy_switch_to_buffer(*dummy);
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
|
||||
/*The routines in this module are serially reusable NOT reentrant*/
|
||||
|
||||
#include <ctype.h>
|
||||
#include <epicsStdlib.h>
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
@@ -33,7 +34,6 @@
|
||||
#include "dbStaticLib.h"
|
||||
#include "dbStaticPvt.h"
|
||||
#include "epicsExport.h"
|
||||
#include "guigroup.h"
|
||||
#include "link.h"
|
||||
#include "special.h"
|
||||
|
||||
@@ -48,6 +48,9 @@ epicsExportAddress(int,dbRecordsOnceOnly);
|
||||
epicsShareDef int dbBptNotMonotonic=0;
|
||||
epicsExportAddress(int,dbBptNotMonotonic);
|
||||
|
||||
epicsShareDef int dbQuietMacroWarnings=0;
|
||||
epicsExportAddress(int,dbQuietMacroWarnings);
|
||||
|
||||
/*private routines */
|
||||
static void yyerrorAbort(char *str);
|
||||
static void allocTemp(void *pvoid);
|
||||
@@ -67,6 +70,7 @@ static void dbRecordtypeEmpty(void);
|
||||
static void dbRecordtypeBody(void);
|
||||
static void dbRecordtypeFieldHead(char *name,char *type);
|
||||
static void dbRecordtypeFieldItem(char *name,char *value);
|
||||
static short findOrAddGuiGroup(const char *name);
|
||||
|
||||
static void dbDevice(char *recordtype,char *linktype,
|
||||
char *dsetname,char *choicestring);
|
||||
@@ -229,6 +233,7 @@ static long dbReadCOM(DBBASE **ppdbbase,const char *filename, FILE *fp,
|
||||
free((void *)macPairs);
|
||||
mac_input_buffer = dbCalloc(MY_BUFFER_SIZE,sizeof(char));
|
||||
}
|
||||
macSuppressWarning(macHandle,dbQuietMacroWarnings);
|
||||
}
|
||||
pinputFile = dbCalloc(1,sizeof(inputFile));
|
||||
if(filename) {
|
||||
@@ -319,10 +324,9 @@ static int db_yyinput(char *buf, int max_size)
|
||||
if(fgetsRtn) {
|
||||
int exp = macExpandString(macHandle,mac_input_buffer,
|
||||
my_buffer,MY_BUFFER_SIZE);
|
||||
if(exp < 0) {
|
||||
errPrintf(0,__FILE__, __LINE__,
|
||||
"macExpandString failed for file %s",
|
||||
pinputFileNow->filename);
|
||||
if (exp < 0) {
|
||||
fprintf(stderr, "Warning: '%s' line %d has undefined macros\n",
|
||||
pinputFileNow->filename, pinputFileNow->line_num+1);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -491,7 +495,23 @@ static void dbRecordtypeFieldHead(char *name,char *type)
|
||||
yyerrorAbort("Illegal Field Type");
|
||||
pdbFldDes->field_type = i;
|
||||
}
|
||||
|
||||
|
||||
static short findOrAddGuiGroup(const char *name)
|
||||
{
|
||||
dbGuiGroup *pdbGuiGroup;
|
||||
GPHENTRY *pgphentry;
|
||||
pgphentry = gphFind(pdbbase->pgpHash, name, &pdbbase->guiGroupList);
|
||||
if (!pgphentry) {
|
||||
pdbGuiGroup = dbCalloc(1,sizeof(dbGuiGroup));
|
||||
pdbGuiGroup->name = epicsStrDup(name);
|
||||
ellAdd(&pdbbase->guiGroupList, &pdbGuiGroup->node);
|
||||
pdbGuiGroup->key = ellCount(&pdbbase->guiGroupList);
|
||||
pgphentry = gphAdd(pdbbase->pgpHash, pdbGuiGroup->name, &pdbbase->guiGroupList);
|
||||
pgphentry->userPvt = pdbGuiGroup;
|
||||
}
|
||||
return ((dbGuiGroup *)pgphentry->userPvt)->key;
|
||||
}
|
||||
|
||||
static void dbRecordtypeFieldItem(char *name,char *value)
|
||||
{
|
||||
dbFldDes *pdbFldDes;
|
||||
@@ -513,14 +533,7 @@ static void dbRecordtypeFieldItem(char *name,char *value)
|
||||
return;
|
||||
}
|
||||
if(strcmp(name,"promptgroup")==0) {
|
||||
int i;
|
||||
for(i=0; i<GUI_NTYPES; i++) {
|
||||
if(strcmp(value,pamapguiGroup[i].strvalue)==0) {
|
||||
pdbFldDes->promptgroup = pamapguiGroup[i].value;
|
||||
return;
|
||||
}
|
||||
}
|
||||
yyerror("Illegal promptgroup. See guigroup.h for legal values");
|
||||
pdbFldDes->promptgroup = findOrAddGuiGroup(value);
|
||||
return;
|
||||
}
|
||||
if(strcmp(name,"prompt")==0) {
|
||||
|
||||
@@ -31,7 +31,6 @@
|
||||
#include "postfix.h"
|
||||
|
||||
#define DBFLDTYPES_GBLSOURCE
|
||||
#define GUIGROUPS_GBLSOURCE
|
||||
#define SPECIAL_GBLSOURCE
|
||||
|
||||
#define epicsExportSharedSymbols
|
||||
@@ -41,13 +40,12 @@
|
||||
#include "dbStaticPvt.h"
|
||||
#include "devSup.h"
|
||||
#include "drvSup.h"
|
||||
#include "guigroup.h"
|
||||
#include "link.h"
|
||||
#include "special.h"
|
||||
|
||||
int dbStaticDebug = 0;
|
||||
static char *pNullString = "";
|
||||
#define messagesize 100
|
||||
#define messagesize 276
|
||||
#define RPCL_LEN INFIX_TO_POSTFIX_SIZE(80)
|
||||
|
||||
static char *ppstring[5]={"NPP","PP","CA","CP","CPP"};
|
||||
@@ -417,6 +415,7 @@ dbBase * dbAllocBase(void)
|
||||
ellInit(&pdbbase->variableList);
|
||||
ellInit(&pdbbase->bptList);
|
||||
ellInit(&pdbbase->filterList);
|
||||
ellInit(&pdbbase->guiGroupList);
|
||||
gphInitPvt(&pdbbase->pgpHash,256);
|
||||
dbPvdInitPvt(pdbbase);
|
||||
return (pdbbase);
|
||||
@@ -442,8 +441,10 @@ void dbFreeBase(dbBase *pdbbase)
|
||||
drvSup *pdrvSupNext;
|
||||
brkTable *pbrkTable;
|
||||
brkTable *pbrkTableNext;
|
||||
chFilterPlugin *pfilt;
|
||||
chFilterPlugin *pfiltNext;
|
||||
chFilterPlugin *pfilt;
|
||||
chFilterPlugin *pfiltNext;
|
||||
dbGuiGroup *pguiGroup;
|
||||
dbGuiGroup *pguiGroupNext;
|
||||
int i;
|
||||
DBENTRY dbentry;
|
||||
|
||||
@@ -584,6 +585,15 @@ void dbFreeBase(dbBase *pdbbase)
|
||||
free(pfilt);
|
||||
pfilt = pfiltNext;
|
||||
}
|
||||
pguiGroup = (dbGuiGroup *)ellFirst(&pdbbase->guiGroupList);
|
||||
while (pguiGroup) {
|
||||
pguiGroupNext = (dbGuiGroup *)ellNext(&pguiGroup->node);
|
||||
gphDelete(pdbbase->pgpHash, pguiGroup->name, &pdbbase->guiGroupList);
|
||||
ellDelete(&pdbbase->guiGroupList, &pguiGroup->node);
|
||||
free(pguiGroup->name);
|
||||
free((void *)pguiGroup);
|
||||
pguiGroup = pguiGroupNext;
|
||||
}
|
||||
gphFreeMem(pdbbase->pgpHash);
|
||||
dbPvdFreeMem(pdbbase);
|
||||
dbFreePath(pdbbase);
|
||||
@@ -604,7 +614,10 @@ DBENTRY * dbAllocEntry(dbBase *pdbbase)
|
||||
|
||||
void dbFreeEntry(DBENTRY *pdbentry)
|
||||
{
|
||||
if(pdbentry->message) free((void *)pdbentry->message);
|
||||
if (!pdbentry)
|
||||
return;
|
||||
if (pdbentry->message)
|
||||
free((void *)pdbentry->message);
|
||||
dbmfFree(pdbentry);
|
||||
}
|
||||
|
||||
@@ -735,6 +748,31 @@ static long dbAddOnePath (DBBASE *pdbbase, const char *path, unsigned length)
|
||||
return 0;
|
||||
}
|
||||
|
||||
char *dbGetPromptGroupNameFromKey(DBBASE *pdbbase, const short key)
|
||||
{
|
||||
dbGuiGroup *pdbGuiGroup;
|
||||
|
||||
if (!pdbbase) return NULL;
|
||||
for (pdbGuiGroup = (dbGuiGroup *)ellFirst(&pdbbase->guiGroupList);
|
||||
pdbGuiGroup; pdbGuiGroup = (dbGuiGroup *)ellNext(&pdbGuiGroup->node)) {
|
||||
if (pdbGuiGroup->key == key) return pdbGuiGroup->name;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
short dbGetPromptGroupKeyFromName(DBBASE *pdbbase, const char *name)
|
||||
{
|
||||
GPHENTRY *pgphentry;
|
||||
|
||||
if (!pdbbase) return 0;
|
||||
pgphentry = gphFind(pdbbase->pgpHash, name, &pdbbase->guiGroupList);
|
||||
if (!pgphentry) {
|
||||
return 0;
|
||||
} else {
|
||||
return ((dbGuiGroup*)pgphentry->userPvt)->key;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
long dbWriteRecord(DBBASE *ppdbbase,const char *filename,
|
||||
const char *precordTypename,int level)
|
||||
@@ -934,16 +972,11 @@ long dbWriteRecordTypeFP(
|
||||
fprintf(fp,"\t\tprompt(\"%s\")\n",pdbFldDes->prompt);
|
||||
if(pdbFldDes->initial)
|
||||
fprintf(fp,"\t\tinitial(\"%s\")\n",pdbFldDes->initial);
|
||||
if(pdbFldDes->promptgroup) {
|
||||
for(j=0; j<GUI_NTYPES; j++) {
|
||||
if(pamapguiGroup[j].value == pdbFldDes->promptgroup) {
|
||||
fprintf(fp,"\t\tpromptgroup(%s)\n",
|
||||
pamapguiGroup[j].strvalue);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(pdbFldDes->special) {
|
||||
if (pdbFldDes->promptgroup) {
|
||||
fprintf(fp,"\t\tpromptgroup(\"%s\")\n",
|
||||
dbGetPromptGroupNameFromKey(pdbbase, pdbFldDes->promptgroup));
|
||||
}
|
||||
if(pdbFldDes->special) {
|
||||
if(pdbFldDes->special >= SPC_NTYPES) {
|
||||
fprintf(fp,"\t\tspecial(%d)\n",pdbFldDes->special);
|
||||
} else for(j=0; j<SPC_NTYPES; j++) {
|
||||
@@ -1198,7 +1231,7 @@ long dbPutRecordAttribute(
|
||||
|
||||
pnew = dbCalloc(1,sizeof(dbRecordAttribute));
|
||||
if(pattribute) {
|
||||
ellInsert(&precordType->attributeList,&pattribute->node,
|
||||
ellInsert(&precordType->attributeList,pattribute->node.previous,
|
||||
&pnew->node);
|
||||
} else {
|
||||
ellAdd(&precordType->attributeList,&pnew->node);
|
||||
@@ -1226,17 +1259,27 @@ long dbGetAttributePart(DBENTRY *pdbentry, const char **ppname)
|
||||
const char *pname = *ppname;
|
||||
dbRecordAttribute *pattribute;
|
||||
|
||||
if (!precordType) return S_dbLib_recordTypeNotFound;
|
||||
if (!precordType)
|
||||
return S_dbLib_recordTypeNotFound;
|
||||
|
||||
pattribute = (dbRecordAttribute *)ellFirst(&precordType->attributeList);
|
||||
while (pattribute) {
|
||||
size_t nameLen = strlen(pattribute->name);
|
||||
int compare = strncmp(pattribute->name, pname, nameLen);
|
||||
int ch = pname[nameLen];
|
||||
if (compare == 0 && !(ch == '_' || isalnum(ch))) {
|
||||
pdbentry->pflddes = pattribute->pdbFldDes;
|
||||
pdbentry->pfield = pattribute->value;
|
||||
*ppname = &pname[nameLen];
|
||||
return 0;
|
||||
|
||||
if (compare == 0) {
|
||||
int ch = pname[nameLen];
|
||||
|
||||
if (ch != '_' && !isalnum(ch)) {
|
||||
/* Any other character can't be in the attribute name */
|
||||
pdbentry->pflddes = pattribute->pdbFldDes;
|
||||
pdbentry->pfield = pattribute->value;
|
||||
*ppname = &pname[nameLen];
|
||||
return 0;
|
||||
}
|
||||
if (strlen(pname) > nameLen) {
|
||||
compare = -1;
|
||||
}
|
||||
}
|
||||
if (compare >= 0) break;
|
||||
pattribute = (dbRecordAttribute *)ellNext(&pattribute->node);
|
||||
@@ -2038,7 +2081,10 @@ long dbPutString(DBENTRY *pdbentry,const char *pstring)
|
||||
switch (pflddes->field_type) {
|
||||
case DBF_STRING:
|
||||
if(!pfield) return(S_dbLib_fieldNotFound);
|
||||
strncpy((char *)pfield, pstring,pflddes->size);
|
||||
if(strlen(pstring) >= (size_t)pflddes->size) return S_dbLib_strLen;
|
||||
strncpy((char *)pfield, pstring, pflddes->size-1);
|
||||
((char *)pfield)[pflddes->size-1] = 0;
|
||||
|
||||
if((pflddes->special == SPC_CALC) && !stringHasMacro) {
|
||||
char rpcl[RPCL_LEN];
|
||||
short err;
|
||||
@@ -2049,7 +2095,6 @@ long dbPutString(DBENTRY *pdbentry,const char *pstring)
|
||||
calcErrorStr(err), pstring);
|
||||
}
|
||||
}
|
||||
if((short)strlen(pstring) >= pflddes->size) status = S_dbLib_strLen;
|
||||
break;
|
||||
|
||||
case DBF_CHAR:
|
||||
@@ -3168,14 +3213,9 @@ void dbDumpField(
|
||||
if(!pdbFldDes->promptgroup) {
|
||||
printf("\t promptgroup: %d\n",pdbFldDes->promptgroup);
|
||||
} else {
|
||||
for(j=0; j<GUI_NTYPES; j++) {
|
||||
if(pamapguiGroup[j].value == pdbFldDes->promptgroup) {
|
||||
printf("\t promptgroup: %s\n",
|
||||
pamapguiGroup[j].strvalue);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
printf("\t promptgroup: %s\n",
|
||||
dbGetPromptGroupNameFromKey(pdbbase, pdbFldDes->promptgroup));
|
||||
}
|
||||
printf("\t interest: %hd\n", pdbFldDes->interest);
|
||||
printf("\t as_level: %d\n",pdbFldDes->as_level);
|
||||
printf("\t initial: %s\n",
|
||||
|
||||
@@ -81,6 +81,10 @@ epicsShareFunc long dbReadDatabaseFP(DBBASE **ppdbbase,
|
||||
FILE *fp, const char *path, const char *substitutions);
|
||||
epicsShareFunc long dbPath(DBBASE *pdbbase, const char *path);
|
||||
epicsShareFunc long dbAddPath(DBBASE *pdbbase, const char *path);
|
||||
epicsShareFunc char * dbGetPromptGroupNameFromKey(DBBASE *pdbbase,
|
||||
const short key);
|
||||
epicsShareFunc short dbGetPromptGroupKeyFromName(DBBASE *pdbbase,
|
||||
const char *name);
|
||||
epicsShareFunc long dbWriteRecord(DBBASE *ppdbbase,
|
||||
const char *filename, const char *precordTypename, int level);
|
||||
epicsShareFunc long dbWriteRecordFP(DBBASE *ppdbbase,
|
||||
|
||||
@@ -42,6 +42,13 @@ typedef struct dbPathNode {
|
||||
char *directory;
|
||||
} dbPathNode;
|
||||
|
||||
/* Element of the global gui group list */
|
||||
typedef struct dbGuiGroup {
|
||||
ELLNODE node;
|
||||
short key;
|
||||
char *name;
|
||||
} dbGuiGroup;
|
||||
|
||||
/*The following are in dbPvdLib.c*/
|
||||
/*directory*/
|
||||
typedef struct{
|
||||
|
||||
@@ -16,68 +16,11 @@
|
||||
#ifndef __gui_group_h__
|
||||
#define __gui_group_h__
|
||||
|
||||
#define GUI_COMMON 1
|
||||
#define GUI_ALARMS 2
|
||||
#define GUI_BITS1 3
|
||||
#define GUI_BITS2 4
|
||||
#define GUI_CALC 5
|
||||
#define GUI_CLOCK 6
|
||||
#define GUI_COMPRESS 7
|
||||
#define GUI_CONVERT 8
|
||||
#define GUI_DISPLAY 9
|
||||
#define GUI_HIST 10
|
||||
#define GUI_INPUTS 11
|
||||
#define GUI_LINKS 12
|
||||
#define GUI_MBB 13
|
||||
#define GUI_MOTOR 14
|
||||
#define GUI_OUTPUT 15
|
||||
#define GUI_PID 16
|
||||
#define GUI_PULSE 17
|
||||
#define GUI_SELECT 18
|
||||
#define GUI_SEQ1 19
|
||||
#define GUI_SEQ2 20
|
||||
#define GUI_SEQ3 21
|
||||
#define GUI_SUB 22
|
||||
#define GUI_TIMER 23
|
||||
#define GUI_WAVE 24
|
||||
#define GUI_SCAN 25
|
||||
#define GUI_NTYPES 25
|
||||
|
||||
typedef struct mapguiGroup{
|
||||
char *strvalue;
|
||||
int value;
|
||||
}mapguiGroup;
|
||||
|
||||
#ifndef GUIGROUPS_GBLSOURCE
|
||||
extern mapguiGroup pamapguiGroup[];
|
||||
#else
|
||||
mapguiGroup pamapguiGroup[GUI_NTYPES] = {
|
||||
{"GUI_COMMON",GUI_COMMON},
|
||||
{"GUI_ALARMS",GUI_ALARMS},
|
||||
{"GUI_BITS1",GUI_BITS1},
|
||||
{"GUI_BITS2",GUI_BITS2},
|
||||
{"GUI_CALC",GUI_CALC},
|
||||
{"GUI_CLOCK",GUI_CLOCK},
|
||||
{"GUI_COMPRESS",GUI_COMPRESS},
|
||||
{"GUI_CONVERT",GUI_CONVERT},
|
||||
{"GUI_DISPLAY",GUI_DISPLAY},
|
||||
{"GUI_HIST",GUI_HIST},
|
||||
{"GUI_INPUTS",GUI_INPUTS},
|
||||
{"GUI_LINKS",GUI_LINKS},
|
||||
{"GUI_MBB",GUI_MBB},
|
||||
{"GUI_MOTOR",GUI_MOTOR},
|
||||
{"GUI_OUTPUT",GUI_OUTPUT},
|
||||
{"GUI_PID",GUI_PID},
|
||||
{"GUI_PULSE",GUI_PULSE},
|
||||
{"GUI_SELECT",GUI_SELECT},
|
||||
{"GUI_SEQ1",GUI_SEQ1},
|
||||
{"GUI_SEQ2",GUI_SEQ2},
|
||||
{"GUI_SEQ3",GUI_SEQ3},
|
||||
{"GUI_SUB",GUI_SUB},
|
||||
{"GUI_TIMER",GUI_TIMER},
|
||||
{"GUI_WAVE",GUI_WAVE},
|
||||
{"GUI_SCAN",GUI_SCAN}
|
||||
};
|
||||
#endif /*GUIGROUPS_GBLSOURCE*/
|
||||
#error As of Base 3.15.4, the promptgroup implementation has changed. \
|
||||
This header file (guigroup.h) is invalid and will be removed shortly. \
|
||||
Instead, you should include dbStaticLib.h, parse the DBD, \
|
||||
and use dbGetPromptGroupNameFromKey() and dbGetPromptGroupKeyFromName() \
|
||||
that have been added to dbStaticLib. \
|
||||
More details in the 3.15.4 release notes and the AppDev Guide.
|
||||
|
||||
#endif /*__gui_group_h__*/
|
||||
|
||||
@@ -30,7 +30,7 @@ typedef struct rset { /* record support entry table */
|
||||
RECSUPFUN init_record; /*init record */
|
||||
RECSUPFUN process; /*process record */
|
||||
RECSUPFUN special; /*special processing */
|
||||
RECSUPFUN get_value; /*get value field */
|
||||
RECSUPFUN get_value; /*no longer used */
|
||||
RECSUPFUN cvt_dbaddr; /*cvt dbAddr */
|
||||
RECSUPFUN get_array_info;
|
||||
RECSUPFUN put_array_info;
|
||||
@@ -52,21 +52,12 @@ typedef struct rset { /* record support entry table */
|
||||
#define S_rec_outMem (M_recSup| 3) /*Out of Memory*/
|
||||
|
||||
|
||||
/* Definition os structure for routine get_value */
|
||||
|
||||
typedef struct valueDes {
|
||||
long field_type;
|
||||
long no_elements;
|
||||
void * pvalue;
|
||||
}valueDes;
|
||||
|
||||
/************************************************************************
|
||||
* report(FILE fp,void *precord);
|
||||
* init();
|
||||
* init_record(precord,pass);
|
||||
* process(void *precord);
|
||||
* special(struct dbAddr *paddr, after);
|
||||
* get_value(precord,struct valueDes *p);
|
||||
* cvt_dbaddr(struct dbAddr *paddr);
|
||||
* get_array_info(paddr,long *no_elements,long *offset);
|
||||
* put_array_info(paddr,nNew);
|
||||
|
||||
@@ -14,8 +14,12 @@ variable(asCaDebug,int)
|
||||
# Static database access variables
|
||||
variable(dbRecordsOnceOnly,int)
|
||||
variable(dbBptNotMonotonic,int)
|
||||
variable(dbQuietMacroWarnings,int)
|
||||
|
||||
# dbLoadTemplate settings
|
||||
variable(dbTemplateMaxVars,int)
|
||||
# Default number of parallel callback threads
|
||||
variable(callbackParallelThreadsDefault,int)
|
||||
|
||||
# Real-time operation
|
||||
variable(dbThreadRealtimeLock,int)
|
||||
|
||||
@@ -36,7 +36,7 @@
|
||||
|
||||
#include "caeventmask.h"
|
||||
|
||||
#define epicsExportSharedSymbols
|
||||
#include "epicsExport.h" /* defines epicsExportSharedSymbols */
|
||||
#include "alarm.h"
|
||||
#include "asDbLib.h"
|
||||
#include "callback.h"
|
||||
@@ -86,7 +86,9 @@ static void initDatabase(void);
|
||||
static void initialProcess(void);
|
||||
static void exitDatabase(void *dummy);
|
||||
|
||||
|
||||
int dbThreadRealtimeLock = 1;
|
||||
epicsExportAddress(int, dbThreadRealtimeLock);
|
||||
|
||||
/*
|
||||
* Initialize EPICS on the IOC.
|
||||
*/
|
||||
@@ -186,6 +188,10 @@ int iocBuild(void)
|
||||
rsrv_init();
|
||||
|
||||
status = iocBuild_3();
|
||||
|
||||
if (dbThreadRealtimeLock)
|
||||
epicsThreadRealtimeLock();
|
||||
|
||||
if (!status) iocBuildMode = buildRSRV;
|
||||
return status;
|
||||
}
|
||||
@@ -607,7 +613,8 @@ static void initialProcess(void)
|
||||
|
||||
|
||||
/*
|
||||
* Shutdown processing.
|
||||
* set DB_LINK and CA_LINK to PV_LINK
|
||||
* Delete record scans
|
||||
*/
|
||||
static void doCloseLinks(dbRecordType *pdbRecordType, dbCommon *precord,
|
||||
void *user)
|
||||
@@ -678,8 +685,15 @@ int iocShutdown(void)
|
||||
if (iocState == iocVirgin || iocState == iocStopped) return 0;
|
||||
iterateRecords(doCloseLinks, NULL);
|
||||
if (iocBuildMode==buildIsolated) {
|
||||
scanShutdown();
|
||||
callbackShutdown();
|
||||
/* stop and "join" threads */
|
||||
scanStop();
|
||||
callbackStop();
|
||||
}
|
||||
dbCaShutdown();
|
||||
if (iocBuildMode==buildIsolated) {
|
||||
/* free resources */
|
||||
scanCleanup();
|
||||
callbackCleanup();
|
||||
iterateRecords(doFreeRecord, NULL);
|
||||
dbLockCleanupRecords(pdbbase);
|
||||
asShutdown();
|
||||
|
||||
@@ -7,6 +7,8 @@
|
||||
* in file LICENSE that is included with this distribution.
|
||||
\*************************************************************************/
|
||||
|
||||
#include "envDefs.h"
|
||||
#include "epicsVersion.h"
|
||||
#include "iocsh.h"
|
||||
#include "libComRegister.h"
|
||||
|
||||
@@ -21,10 +23,29 @@
|
||||
#include "registryIocRegister.h"
|
||||
#include "rsrvIocRegister.h"
|
||||
|
||||
#define quote(v) #v
|
||||
#define str(v) quote(v)
|
||||
|
||||
void iocshRegisterCommon(void)
|
||||
{
|
||||
const char *targetArch = envGetConfigParamPtr(&EPICS_BUILD_TARGET_ARCH);
|
||||
iocshPpdbbase = &pdbbase;
|
||||
|
||||
/* This uses a config param so the user can override it */
|
||||
if (targetArch) {
|
||||
epicsEnvSet("ARCH", targetArch);
|
||||
}
|
||||
|
||||
/* Base build version variables */
|
||||
epicsEnvSet("EPICS_VERSION_MAJOR", str(EPICS_VERSION));
|
||||
epicsEnvSet("EPICS_VERSION_MIDDLE", str(EPICS_REVISION));
|
||||
epicsEnvSet("EPICS_VERSION_MINOR", str(EPICS_MODIFICATION));
|
||||
epicsEnvSet("EPICS_VERSION_PATCH", str(EPICS_PATCH_LEVEL));
|
||||
epicsEnvSet("EPICS_VERSION_SNAPSHOT", EPICS_DEV_SNAPSHOT);
|
||||
epicsEnvSet("EPICS_VERSION_SITE", EPICS_SITE_VERSION);
|
||||
epicsEnvSet("EPICS_VERSION_SHORT", EPICS_VERSION_SHORT);
|
||||
epicsEnvSet("EPICS_VERSION_FULL", EPICS_VERSION_FULL);
|
||||
|
||||
dbStaticIocRegister();
|
||||
registryIocRegister();
|
||||
dbIocRegister();
|
||||
|
||||
@@ -1112,7 +1112,7 @@ unsigned cid
|
||||
* casAccessRightsCB()
|
||||
*
|
||||
* If access right state changes then inform the client.
|
||||
*
|
||||
* asLock is held
|
||||
*/
|
||||
static void casAccessRightsCB(ASCLIENTPVT ascpvt, asClientStatus type)
|
||||
{
|
||||
@@ -1126,7 +1126,7 @@ static void casAccessRightsCB(ASCLIENTPVT ascpvt, asClientStatus type)
|
||||
pclient = pciu->client;
|
||||
assert(pclient);
|
||||
|
||||
if(pclient == prsrv_cast_client){
|
||||
if(pclient->proto==IPPROTO_UDP){
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1193,7 +1193,7 @@ static void access_rights_reply ( struct channel_in_use * pciu )
|
||||
int v41;
|
||||
int status;
|
||||
|
||||
assert ( pciu->client != prsrv_cast_client );
|
||||
assert ( pciu->client->proto!=IPPROTO_UDP );
|
||||
|
||||
/*
|
||||
* noop if this is an old client
|
||||
@@ -1321,7 +1321,7 @@ static int claim_ciu_action ( caHdrLargeArray *mp,
|
||||
}
|
||||
}
|
||||
else {
|
||||
epicsMutexMustLock(prsrv_cast_client->chanListLock);
|
||||
epicsMutexMustLock(client->chanListLock);
|
||||
/*
|
||||
* clients which dont claim their
|
||||
* channel in use block prior to
|
||||
@@ -1331,7 +1331,7 @@ static int claim_ciu_action ( caHdrLargeArray *mp,
|
||||
if(!pciu){
|
||||
errlogPrintf("CAS: client timeout disconnect id=%d\n",
|
||||
mp->m_cid);
|
||||
epicsMutexUnlock(prsrv_cast_client->chanListLock);
|
||||
epicsMutexUnlock(client->chanListLock);
|
||||
SEND_LOCK(client);
|
||||
send_err(
|
||||
mp,
|
||||
@@ -1342,24 +1342,6 @@ static int claim_ciu_action ( caHdrLargeArray *mp,
|
||||
return RSRV_ERROR;
|
||||
}
|
||||
|
||||
/*
|
||||
* duplicate claim message are unacceptable
|
||||
* (so we disconnect the client)
|
||||
*/
|
||||
if (pciu->client!=prsrv_cast_client) {
|
||||
errlogPrintf("CAS: duplicate claim disconnect id=%d\n",
|
||||
mp->m_cid);
|
||||
epicsMutexUnlock(prsrv_cast_client->chanListLock);
|
||||
SEND_LOCK(client);
|
||||
send_err(
|
||||
mp,
|
||||
ECA_INTERNAL,
|
||||
client,
|
||||
"duplicate claim in old connect protocol");
|
||||
SEND_UNLOCK(client);
|
||||
return RSRV_ERROR;
|
||||
}
|
||||
|
||||
/*
|
||||
* remove channel in use block from
|
||||
* the UDP client where it could time
|
||||
@@ -1367,9 +1349,9 @@ static int claim_ciu_action ( caHdrLargeArray *mp,
|
||||
* who is claiming it
|
||||
*/
|
||||
ellDelete(
|
||||
&prsrv_cast_client->chanList,
|
||||
&client->chanList,
|
||||
&pciu->node);
|
||||
epicsMutexUnlock(prsrv_cast_client->chanListLock);
|
||||
epicsMutexUnlock(client->chanListLock);
|
||||
|
||||
epicsMutexMustLock(client->chanListLock);
|
||||
pciu->state = rsrvCS_pendConnectResp;
|
||||
@@ -1586,6 +1568,9 @@ static void sendAllUpdateAS ( struct client *client )
|
||||
else if ( pciu->state == rsrvCS_inServiceUpdatePendAR ) {
|
||||
access_rights_reply ( pciu );
|
||||
}
|
||||
else if ( pciu->state == rsrvCS_shutdown ) {
|
||||
/* no-op */
|
||||
}
|
||||
else {
|
||||
errlogPrintf (
|
||||
"%s at %d: corrupt channel state detected durring AR update\n",
|
||||
@@ -2065,10 +2050,15 @@ static int clear_channel_reply ( caHdrLargeArray *mp,
|
||||
if ( pciu->state == rsrvCS_inService ||
|
||||
pciu->state == rsrvCS_pendConnectResp ) {
|
||||
ellDelete ( &client->chanList, &pciu->node );
|
||||
pciu->state = rsrvCS_shutdown;
|
||||
}
|
||||
else if ( pciu->state == rsrvCS_inServiceUpdatePendAR ||
|
||||
pciu->state == rsrvCS_pendConnectRespUpdatePendAR ) {
|
||||
ellDelete ( &client->chanPendingUpdateARList, &pciu->node );
|
||||
pciu->state = rsrvCS_shutdown;
|
||||
}
|
||||
else if ( pciu->state == rsrvCS_shutdown ) {
|
||||
/* no-op */
|
||||
}
|
||||
else {
|
||||
epicsMutexUnlock( client->chanListLock );
|
||||
@@ -2515,12 +2505,7 @@ int camessage ( struct client *client )
|
||||
unsigned bytes_left;
|
||||
int status = RSRV_ERROR;
|
||||
|
||||
if ( ! pCaBucket ) {
|
||||
pCaBucket = bucketCreate(CAS_HASH_TABLE_SIZE);
|
||||
if(!pCaBucket){
|
||||
return RSRV_ERROR;
|
||||
}
|
||||
}
|
||||
assert(pCaBucket);
|
||||
|
||||
/* drain remnents of large messages that will not fit */
|
||||
if ( client->recvBytesToDrain ) {
|
||||
@@ -2623,7 +2608,7 @@ int camessage ( struct client *client )
|
||||
if ( CASDEBUG > 2 )
|
||||
log_header (NULL, client, &msg, pBody, nmsg);
|
||||
|
||||
if ( client == prsrv_cast_client ) {
|
||||
if ( client->proto==IPPROTO_UDP ) {
|
||||
if ( msg.m_cmmd < NELEMENTS ( udpJumpTable ) ) {
|
||||
status = ( *udpJumpTable[msg.m_cmmd] )( &msg, pBody, client );
|
||||
if (status!=RSRV_OK) {
|
||||
|
||||
@@ -222,7 +222,7 @@ void cas_send_dg_msg ( struct client * pclient )
|
||||
/*
|
||||
* add placeholder for the first version message should it be needed
|
||||
*/
|
||||
rsrv_version_reply ( prsrv_cast_client );
|
||||
rsrv_version_reply ( pclient );
|
||||
|
||||
SEND_UNLOCK(pclient);
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -54,7 +54,7 @@
|
||||
/*
|
||||
* clean_addrq
|
||||
*/
|
||||
static void clean_addrq(void)
|
||||
static void clean_addrq(struct client *client)
|
||||
{
|
||||
struct channel_in_use * pciu;
|
||||
struct channel_in_use * pnextciu;
|
||||
@@ -67,9 +67,9 @@ static void clean_addrq(void)
|
||||
|
||||
epicsTimeGetCurrent ( ¤t );
|
||||
|
||||
epicsMutexMustLock ( prsrv_cast_client->chanListLock );
|
||||
epicsMutexMustLock ( client->chanListLock );
|
||||
pnextciu = (struct channel_in_use *)
|
||||
prsrv_cast_client->chanList.node.next;
|
||||
client->chanList.node.next;
|
||||
|
||||
while( (pciu = pnextciu) ) {
|
||||
pnextciu = (struct channel_in_use *)pciu->node.next;
|
||||
@@ -77,7 +77,7 @@ static void clean_addrq(void)
|
||||
delay = epicsTimeDiffInSeconds(¤t,&pciu->time_at_creation);
|
||||
if (delay > timeout) {
|
||||
|
||||
ellDelete(&prsrv_cast_client->chanList, &pciu->node);
|
||||
ellDelete(&client->chanList, &pciu->node);
|
||||
LOCK_CLIENTQ;
|
||||
s = bucketRemoveItemUnsignedId (
|
||||
pCaBucket,
|
||||
@@ -96,7 +96,7 @@ static void clean_addrq(void)
|
||||
if(delay>maxdelay) maxdelay = delay;
|
||||
}
|
||||
}
|
||||
epicsMutexUnlock ( prsrv_cast_client->chanListLock );
|
||||
epicsMutexUnlock ( client->chanListLock );
|
||||
|
||||
# ifdef DEBUG
|
||||
if(ndelete){
|
||||
@@ -115,70 +115,19 @@ static void clean_addrq(void)
|
||||
*/
|
||||
void cast_server(void *pParm)
|
||||
{
|
||||
osiSockAddrNode *paddrNode;
|
||||
struct sockaddr_in sin;
|
||||
rsrv_iface_config *conf = pParm;
|
||||
int status;
|
||||
int count=0;
|
||||
int mysocket=0;
|
||||
struct sockaddr_in new_recv_addr;
|
||||
osiSocklen_t recv_addr_size;
|
||||
osiSockIoctl_t nchars;
|
||||
SOCKET recv_sock, reply_sock;
|
||||
struct client *client;
|
||||
|
||||
recv_addr_size = sizeof(new_recv_addr);
|
||||
|
||||
if( IOC_cast_sock!=0 && IOC_cast_sock!=INVALID_SOCKET ) {
|
||||
epicsSocketDestroy ( IOC_cast_sock );
|
||||
}
|
||||
|
||||
/*
|
||||
* Open the socket.
|
||||
* Use ARPA Internet address format and datagram socket.
|
||||
*/
|
||||
|
||||
if ( ( IOC_cast_sock = epicsSocketCreate (AF_INET, SOCK_DGRAM, 0) ) == INVALID_SOCKET ) {
|
||||
epicsPrintf ("CAS: cast socket creation error\n");
|
||||
epicsThreadSuspendSelf ();
|
||||
}
|
||||
|
||||
/*
|
||||
* some concern that vxWorks will run out of mBuf's
|
||||
* if this change is made
|
||||
*
|
||||
* joh 11-10-98
|
||||
*/
|
||||
#if 0
|
||||
{
|
||||
/*
|
||||
*
|
||||
* this allows for faster connects by queuing
|
||||
* additional incomming UDP search frames
|
||||
*
|
||||
* this allocates a 32k buffer
|
||||
* (uses a power of two)
|
||||
*/
|
||||
int size = 1u<<15u;
|
||||
status = setsockopt (IOC_cast_sock, SOL_SOCKET,
|
||||
SO_RCVBUF, (char *)&size, sizeof(size));
|
||||
if (status<0) {
|
||||
epicsPrintf ("CAS: unable to set cast socket size\n");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
epicsSocketEnableAddressUseForDatagramFanout ( IOC_cast_sock );
|
||||
|
||||
paddrNode = (osiSockAddrNode *) ellFirst ( &casIntfAddrList );
|
||||
|
||||
memcpy(&sin, &paddrNode->addr.ia, sizeof (sin));
|
||||
|
||||
/* get server's Internet address */
|
||||
if( bind(IOC_cast_sock, (struct sockaddr *)&sin, sizeof (sin)) < 0){
|
||||
char sockErrBuf[64];
|
||||
epicsSocketConvertErrnoToString (
|
||||
sockErrBuf, sizeof ( sockErrBuf ) );
|
||||
epicsPrintf ("CAS: UDP server port bind error was \"%s\"\n", sockErrBuf );
|
||||
epicsSocketDestroy ( IOC_cast_sock );
|
||||
epicsThreadSuspendSelf ();
|
||||
}
|
||||
reply_sock = conf->udp;
|
||||
|
||||
/*
|
||||
* setup new client structure but reuse old structure if
|
||||
@@ -186,27 +135,39 @@ void cast_server(void *pParm)
|
||||
*
|
||||
*/
|
||||
while ( TRUE ) {
|
||||
prsrv_cast_client = create_client ( IOC_cast_sock, IPPROTO_UDP );
|
||||
if ( prsrv_cast_client ) {
|
||||
client = create_client ( reply_sock, IPPROTO_UDP );
|
||||
if ( client ) {
|
||||
break;
|
||||
}
|
||||
epicsThreadSleep(300.0);
|
||||
}
|
||||
if (conf->startbcast) {
|
||||
recv_sock = conf->udpbcast;
|
||||
conf->bclient = client;
|
||||
}
|
||||
else {
|
||||
recv_sock = conf->udp;
|
||||
conf->client = client;
|
||||
}
|
||||
client->udpRecv = recv_sock;
|
||||
|
||||
casAttachThreadToClient ( prsrv_cast_client );
|
||||
casAttachThreadToClient ( client );
|
||||
|
||||
/*
|
||||
* add placeholder for the first version message should it be needed
|
||||
*/
|
||||
rsrv_version_reply ( prsrv_cast_client );
|
||||
rsrv_version_reply ( client );
|
||||
|
||||
/* these pointers become invalid after signaling casudp_startStopEvent */
|
||||
conf = NULL;
|
||||
|
||||
epicsEventSignal(casudp_startStopEvent);
|
||||
|
||||
while (TRUE) {
|
||||
status = recvfrom (
|
||||
IOC_cast_sock,
|
||||
prsrv_cast_client->recv.buf,
|
||||
prsrv_cast_client->recv.maxstk,
|
||||
recv_sock,
|
||||
client->recv.buf,
|
||||
client->recv.maxstk,
|
||||
0,
|
||||
(struct sockaddr *)&new_recv_addr,
|
||||
&recv_addr_size);
|
||||
@@ -219,71 +180,90 @@ void cast_server(void *pParm)
|
||||
sockErrBuf);
|
||||
epicsThreadSleep(1.0);
|
||||
}
|
||||
}
|
||||
else if (casudp_ctl == ctlRun) {
|
||||
prsrv_cast_client->recv.cnt = (unsigned) status;
|
||||
prsrv_cast_client->recv.stk = 0ul;
|
||||
epicsTimeGetCurrent(&prsrv_cast_client->time_at_last_recv);
|
||||
|
||||
prsrv_cast_client->minor_version_number = 0;
|
||||
prsrv_cast_client->seqNoOfReq = 0;
|
||||
} else {
|
||||
size_t idx;
|
||||
for(idx=0; casIgnoreAddrs[idx]; idx++)
|
||||
{
|
||||
if(new_recv_addr.sin_addr.s_addr==casIgnoreAddrs[idx]) {
|
||||
status = -1; /* ignore */
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (status >= 0 && casudp_ctl == ctlRun) {
|
||||
client->recv.cnt = (unsigned) status;
|
||||
client->recv.stk = 0ul;
|
||||
epicsTimeGetCurrent(&client->time_at_last_recv);
|
||||
|
||||
client->minor_version_number = 0;
|
||||
client->seqNoOfReq = 0;
|
||||
|
||||
/*
|
||||
* If we are talking to a new client flush to the old one
|
||||
* in case we are holding UDP messages waiting to
|
||||
* see if the next message is for this same client.
|
||||
*/
|
||||
if (prsrv_cast_client->send.stk>sizeof(caHdr)) {
|
||||
status = memcmp(&prsrv_cast_client->addr,
|
||||
if (client->send.stk>sizeof(caHdr)) {
|
||||
status = memcmp(&client->addr,
|
||||
&new_recv_addr, recv_addr_size);
|
||||
if(status){
|
||||
/*
|
||||
* if the address is different
|
||||
*/
|
||||
cas_send_dg_msg(prsrv_cast_client);
|
||||
prsrv_cast_client->addr = new_recv_addr;
|
||||
cas_send_dg_msg(client);
|
||||
client->addr = new_recv_addr;
|
||||
}
|
||||
}
|
||||
else {
|
||||
prsrv_cast_client->addr = new_recv_addr;
|
||||
client->addr = new_recv_addr;
|
||||
}
|
||||
|
||||
if (CASDEBUG>1) {
|
||||
char buf[40];
|
||||
|
||||
ipAddrToDottedIP (&prsrv_cast_client->addr, buf, sizeof(buf));
|
||||
ipAddrToDottedIP (&client->addr, buf, sizeof(buf));
|
||||
errlogPrintf ("CAS: cast server msg of %d bytes from addr %s\n",
|
||||
prsrv_cast_client->recv.cnt, buf);
|
||||
client->recv.cnt, buf);
|
||||
}
|
||||
|
||||
if (CASDEBUG>2)
|
||||
count = ellCount (&prsrv_cast_client->chanList);
|
||||
count = ellCount (&client->chanList);
|
||||
|
||||
status = camessage ( prsrv_cast_client );
|
||||
status = camessage ( client );
|
||||
if(status == RSRV_OK){
|
||||
if(prsrv_cast_client->recv.cnt !=
|
||||
prsrv_cast_client->recv.stk){
|
||||
if(client->recv.cnt !=
|
||||
client->recv.stk){
|
||||
char buf[40];
|
||||
|
||||
ipAddrToDottedIP (&prsrv_cast_client->addr, buf, sizeof(buf));
|
||||
|
||||
ipAddrToDottedIP (&client->addr, buf, sizeof(buf));
|
||||
|
||||
epicsPrintf ("CAS: partial (damaged?) UDP msg of %d bytes from %s ?\n",
|
||||
prsrv_cast_client->recv.cnt-prsrv_cast_client->recv.stk, buf);
|
||||
client->recv.cnt - client->recv.stk, buf);
|
||||
|
||||
epicsTimeToStrftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S",
|
||||
&client->time_at_last_recv);
|
||||
epicsPrintf ("CAS: message received at %s\n", buf);
|
||||
}
|
||||
}
|
||||
else {
|
||||
char buf[40];
|
||||
|
||||
ipAddrToDottedIP (&prsrv_cast_client->addr, buf, sizeof(buf));
|
||||
|
||||
ipAddrToDottedIP (&client->addr, buf, sizeof(buf));
|
||||
|
||||
epicsPrintf ("CAS: invalid (damaged?) UDP request from %s ?\n", buf);
|
||||
|
||||
epicsTimeToStrftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S",
|
||||
&client->time_at_last_recv);
|
||||
epicsPrintf ("CAS: message received at %s\n", buf);
|
||||
}
|
||||
|
||||
if (CASDEBUG>2) {
|
||||
if ( ellCount (&prsrv_cast_client->chanList) ) {
|
||||
if ( ellCount (&client->chanList) ) {
|
||||
errlogPrintf ("CAS: Fnd %d name matches (%d tot)\n",
|
||||
ellCount(&prsrv_cast_client->chanList)-count,
|
||||
ellCount(&prsrv_cast_client->chanList));
|
||||
ellCount(&client->chanList)-count,
|
||||
ellCount(&client->chanList));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -292,15 +272,22 @@ void cast_server(void *pParm)
|
||||
* allow messages to batch up if more are comming
|
||||
*/
|
||||
nchars = 0; /* supress purify warning */
|
||||
status = socket_ioctl(IOC_cast_sock, FIONREAD, &nchars);
|
||||
status = socket_ioctl(recv_sock, FIONREAD, &nchars);
|
||||
if (status<0) {
|
||||
errlogPrintf ("CA cast server: Unable to fetch N characters pending\n");
|
||||
cas_send_dg_msg (prsrv_cast_client);
|
||||
clean_addrq ();
|
||||
cas_send_dg_msg (client);
|
||||
clean_addrq (client);
|
||||
}
|
||||
else if (nchars == 0) {
|
||||
cas_send_dg_msg (prsrv_cast_client);
|
||||
clean_addrq ();
|
||||
cas_send_dg_msg (client);
|
||||
clean_addrq (client);
|
||||
}
|
||||
}
|
||||
|
||||
/* ATM never reached, just a placeholder */
|
||||
|
||||
if(!mysocket)
|
||||
client->sock = INVALID_SOCKET; /* only one cast_server should destroy the reply socket */
|
||||
destroy_client(client);
|
||||
epicsSocketDestroy(recv_sock);
|
||||
}
|
||||
|
||||
@@ -35,46 +35,19 @@
|
||||
#define epicsExportSharedSymbols
|
||||
#include "server.h"
|
||||
|
||||
/*
|
||||
* forcePort ()
|
||||
*/
|
||||
static void forcePort (ELLLIST *pList, unsigned short port)
|
||||
{
|
||||
osiSockAddrNode *pNode;
|
||||
|
||||
pNode = (osiSockAddrNode *) ellFirst ( pList );
|
||||
while ( pNode ) {
|
||||
if ( pNode->addr.sa.sa_family == AF_INET ) {
|
||||
pNode->addr.ia.sin_port = htons ( port );
|
||||
}
|
||||
pNode = (osiSockAddrNode *) ellNext ( &pNode->node );
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* RSRV_ONLINE_NOTIFY_TASK
|
||||
*/
|
||||
void rsrv_online_notify_task(void *pParm)
|
||||
{
|
||||
unsigned priorityOfSelf = epicsThreadGetPrioritySelf ();
|
||||
osiSockAddrNode *pNode;
|
||||
double delay;
|
||||
double maxdelay;
|
||||
long longStatus;
|
||||
double maxPeriod;
|
||||
caHdr msg;
|
||||
int status;
|
||||
SOCKET sock;
|
||||
int intTrue = TRUE;
|
||||
unsigned short port;
|
||||
ca_uint32_t beaconCounter = 0;
|
||||
char * pStr;
|
||||
int autoBeaconAddr;
|
||||
ELLLIST autoAddrList;
|
||||
char buf[16];
|
||||
unsigned priorityOfUDP;
|
||||
epicsThreadBooleanStatus tbs;
|
||||
epicsThreadId tid;
|
||||
|
||||
taskwdInsert (epicsThreadGetIdSelf(),NULL,NULL);
|
||||
|
||||
@@ -94,189 +67,34 @@ void rsrv_online_notify_task(void *pParm)
|
||||
|
||||
delay = 0.02; /* initial beacon period in sec */
|
||||
maxdelay = maxPeriod;
|
||||
|
||||
/*
|
||||
* Open the socket.
|
||||
* Use ARPA Internet address format and datagram socket.
|
||||
* Format described in <sys/socket.h>.
|
||||
*/
|
||||
if ( (sock = epicsSocketCreate (AF_INET, SOCK_DGRAM, 0)) == INVALID_SOCKET) {
|
||||
errlogPrintf ("CAS: online socket creation error\n");
|
||||
epicsThreadSuspendSelf ();
|
||||
}
|
||||
|
||||
status = setsockopt (sock, SOL_SOCKET, SO_BROADCAST,
|
||||
(char *)&intTrue, sizeof(intTrue));
|
||||
if (status<0) {
|
||||
errlogPrintf ("CAS: online socket set up error\n");
|
||||
epicsThreadSuspendSelf ();
|
||||
}
|
||||
|
||||
{
|
||||
/*
|
||||
* this connect is to supress a warning message on Linux
|
||||
* when we shutdown the read side of the socket. If it
|
||||
* fails (and it will on old ip kernels) we just ignore
|
||||
* the failure.
|
||||
*/
|
||||
osiSockAddr sockAddr;
|
||||
sockAddr.ia.sin_family = AF_UNSPEC;
|
||||
sockAddr.ia.sin_port = htons ( 0 );
|
||||
sockAddr.ia.sin_addr.s_addr = htonl (0);
|
||||
connect ( sock, & sockAddr.sa, sizeof ( sockAddr.sa ) );
|
||||
shutdown ( sock, SHUT_RD );
|
||||
}
|
||||
|
||||
memset((char *)&msg, 0, sizeof msg);
|
||||
msg.m_cmmd = htons (CA_PROTO_RSRV_IS_UP);
|
||||
msg.m_count = htons (ca_server_port);
|
||||
msg.m_dataType = htons (CA_MINOR_PROTOCOL_REVISION);
|
||||
|
||||
ellInit ( & beaconAddrList );
|
||||
ellInit ( & autoAddrList );
|
||||
|
||||
pStr = envGetConfigParam(&EPICS_CAS_AUTO_BEACON_ADDR_LIST, sizeof(buf), buf);
|
||||
if ( ! pStr ) {
|
||||
pStr = envGetConfigParam(&EPICS_CA_AUTO_ADDR_LIST, sizeof(buf), buf);
|
||||
}
|
||||
if (pStr) {
|
||||
if (strstr(pStr,"no")||strstr(pStr,"NO")) {
|
||||
autoBeaconAddr = FALSE;
|
||||
}
|
||||
else if (strstr(pStr,"yes")||strstr(pStr,"YES")) {
|
||||
autoBeaconAddr = TRUE;
|
||||
}
|
||||
else {
|
||||
fprintf(stderr,
|
||||
"CAS: EPICS_CA(S)_AUTO_ADDR_LIST = \"%s\"? Assuming \"YES\"\n", pStr);
|
||||
autoBeaconAddr = TRUE;
|
||||
}
|
||||
}
|
||||
else {
|
||||
autoBeaconAddr = TRUE;
|
||||
}
|
||||
|
||||
/*
|
||||
* load user and auto configured
|
||||
* broadcast address list
|
||||
*/
|
||||
if (envGetConfigParamPtr(&EPICS_CAS_BEACON_PORT)) {
|
||||
port = envGetInetPortConfigParam (&EPICS_CAS_BEACON_PORT,
|
||||
(unsigned short) CA_REPEATER_PORT );
|
||||
}
|
||||
else {
|
||||
port = envGetInetPortConfigParam (&EPICS_CA_REPEATER_PORT,
|
||||
(unsigned short) CA_REPEATER_PORT );
|
||||
}
|
||||
|
||||
/*
|
||||
* discover beacon addresses associated with this interface
|
||||
*/
|
||||
if ( autoBeaconAddr ) {
|
||||
osiSockAddr addr;
|
||||
ELLLIST tmpList;
|
||||
|
||||
ellInit ( &tmpList );
|
||||
addr.ia.sin_family = AF_UNSPEC;
|
||||
osiSockDiscoverBroadcastAddresses (&tmpList, sock, &addr);
|
||||
forcePort ( &tmpList, port );
|
||||
removeDuplicateAddresses ( &autoAddrList, &tmpList, 1 );
|
||||
}
|
||||
|
||||
/*
|
||||
* by default use EPICS_CA_ADDR_LIST for the
|
||||
* beacon address list
|
||||
*/
|
||||
{
|
||||
const ENV_PARAM *pParam;
|
||||
|
||||
if (envGetConfigParamPtr(&EPICS_CAS_INTF_ADDR_LIST) ||
|
||||
envGetConfigParamPtr(&EPICS_CAS_BEACON_ADDR_LIST)) {
|
||||
pParam = &EPICS_CAS_BEACON_ADDR_LIST;
|
||||
}
|
||||
else {
|
||||
pParam = &EPICS_CA_ADDR_LIST;
|
||||
}
|
||||
|
||||
/*
|
||||
* add in the configured addresses
|
||||
*/
|
||||
addAddrToChannelAccessAddressList (
|
||||
&autoAddrList, pParam, port, pParam == &EPICS_CA_ADDR_LIST );
|
||||
}
|
||||
|
||||
removeDuplicateAddresses ( &beaconAddrList, &autoAddrList, 0 );
|
||||
|
||||
if ( ellCount ( &beaconAddrList ) == 0 ) {
|
||||
errlogPrintf ("The CA server's beacon address list was empty after initialization?\n");
|
||||
}
|
||||
|
||||
# ifdef DEBUG
|
||||
printChannelAccessAddressList (&beaconAddrList);
|
||||
# endif
|
||||
|
||||
tbs = epicsThreadHighestPriorityLevelBelow ( priorityOfSelf, &priorityOfUDP );
|
||||
if ( tbs != epicsThreadBooleanStatusSuccess ) {
|
||||
priorityOfUDP = priorityOfSelf;
|
||||
}
|
||||
|
||||
casudp_startStopEvent = epicsEventMustCreate(epicsEventEmpty);
|
||||
casudp_ctl = ctlPause;
|
||||
|
||||
tid = epicsThreadCreate ( "CAS-UDP", priorityOfUDP,
|
||||
epicsThreadGetStackSize (epicsThreadStackMedium),
|
||||
cast_server, 0 );
|
||||
if ( tid == 0 ) {
|
||||
epicsPrintf ( "CAS: unable to start UDP daemon thread\n" );
|
||||
}
|
||||
|
||||
epicsEventMustWait(casudp_startStopEvent);
|
||||
epicsEventSignal(beacon_startStopEvent);
|
||||
|
||||
while (TRUE) {
|
||||
pNode = (osiSockAddrNode *) ellFirst (&beaconAddrList);
|
||||
while (pNode) {
|
||||
char buf[64];
|
||||
ELLNODE *cur;
|
||||
|
||||
status = connect (sock, &pNode->addr.sa,
|
||||
sizeof(pNode->addr.sa));
|
||||
if (status<0) {
|
||||
/* send beacon to each interface */
|
||||
for(cur=ellFirst(&beaconAddrList); cur; cur=ellNext(cur))
|
||||
{
|
||||
osiSockAddrNode *pAddr = CONTAINER(cur, osiSockAddrNode, node);
|
||||
status = sendto (beaconSocket, (char *)&msg, sizeof(msg), 0,
|
||||
&pAddr->addr.sa, sizeof(pAddr->addr));
|
||||
if (status < 0) {
|
||||
char sockErrBuf[64];
|
||||
epicsSocketConvertErrnoToString (
|
||||
sockErrBuf, sizeof ( sockErrBuf ) );
|
||||
ipAddrToDottedIP (&pNode->addr.ia, buf, sizeof(buf));
|
||||
errlogPrintf ( "%s: CA beacon routing (connect to \"%s\") error was \"%s\"\n",
|
||||
epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) );
|
||||
ipAddrToDottedIP (&pAddr->addr.ia, buf, sizeof(buf));
|
||||
errlogPrintf ( "%s: CA beacon (send to \"%s\") error was \"%s\"\n",
|
||||
__FILE__, buf, sockErrBuf);
|
||||
}
|
||||
else {
|
||||
struct sockaddr_in if_addr;
|
||||
|
||||
osiSocklen_t size = sizeof (if_addr);
|
||||
status = getsockname (sock, (struct sockaddr *) &if_addr, &size);
|
||||
if (status<0) {
|
||||
char sockErrBuf[64];
|
||||
epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) );
|
||||
errlogPrintf ( "%s: CA beacon routing (getsockname) error was \"%s\"\n",
|
||||
__FILE__, sockErrBuf);
|
||||
}
|
||||
else if (if_addr.sin_family==AF_INET) {
|
||||
msg.m_available = if_addr.sin_addr.s_addr;
|
||||
msg.m_cid = htonl ( beaconCounter );
|
||||
|
||||
status = send (sock, (char *)&msg, sizeof(msg), 0);
|
||||
if (status < 0) {
|
||||
char sockErrBuf[64];
|
||||
epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) );
|
||||
ipAddrToDottedIP (&pNode->addr.ia, buf, sizeof(buf));
|
||||
errlogPrintf ( "%s: CA beacon (send to \"%s\") error was \"%s\"\n",
|
||||
__FILE__, buf, sockErrBuf);
|
||||
}
|
||||
else {
|
||||
assert (status == sizeof(msg));
|
||||
}
|
||||
}
|
||||
assert (status == sizeof(msg));
|
||||
}
|
||||
pNode = (osiSockAddrNode *) pNode->node.next;
|
||||
}
|
||||
|
||||
epicsThreadSleep(delay);
|
||||
@@ -287,7 +105,7 @@ void rsrv_online_notify_task(void *pParm)
|
||||
}
|
||||
}
|
||||
|
||||
beaconCounter++; /* expected to overflow */
|
||||
msg.m_cid = htonl ( beaconCounter++ ); /* expected to overflow */
|
||||
|
||||
while (beacon_ctl == ctlPause) {
|
||||
epicsThreadSleep(0.1);
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
#include "ellLib.h"
|
||||
#include "epicsTime.h"
|
||||
#include "epicsAssert.h"
|
||||
#include "osiSock.h"
|
||||
|
||||
#ifdef rsrvRestore_epicsExportSharedSymbols
|
||||
#define epicsExportSharedSymbols
|
||||
@@ -88,7 +89,7 @@ typedef struct client {
|
||||
char *pUserName;
|
||||
char *pHostName;
|
||||
epicsEventId blockSem; /* used whenever the client blocks */
|
||||
SOCKET sock;
|
||||
SOCKET sock, udpRecv;
|
||||
int proto;
|
||||
epicsThreadId tid;
|
||||
unsigned minor_version_number;
|
||||
@@ -98,12 +99,25 @@ typedef struct client {
|
||||
char disconnect; /* disconnect detected */
|
||||
} client;
|
||||
|
||||
/* Channel state shows which struct client list a
|
||||
* channel_in_us::node is in.
|
||||
*
|
||||
* client::chanList
|
||||
* rsrvCS_pendConnectResp, rsrvCS_inService
|
||||
* client::chanPendingUpdateARList
|
||||
* rsrvCS_pendConnectRespUpdatePendAR, rsrvCS_inServiceUpdatePendAR
|
||||
* Not in any list
|
||||
* rsrvCS_shutdown
|
||||
*
|
||||
* rsrvCS_invalid is not used
|
||||
*/
|
||||
enum rsrvChanState {
|
||||
rsrvCS_invalid,
|
||||
rsrvCS_pendConnectResp,
|
||||
rsrvCS_inService,
|
||||
rsrvCS_pendConnectRespUpdatePendAR,
|
||||
rsrvCS_inServiceUpdatePendAR
|
||||
rsrvCS_inServiceUpdatePendAR,
|
||||
rsrvCS_shutdown
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -137,6 +151,16 @@ struct event_ext {
|
||||
char modified; /* mod & ev flw ctrl enbl */
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
ELLNODE node;
|
||||
osiSockAddr tcpAddr, /* TCP listener endpoint */
|
||||
udpAddr, /* UDP name unicast receiver endpoint */
|
||||
udpbcastAddr; /* UDP name broadcast receiver endpoint */
|
||||
SOCKET tcp, udp, udpbcast;
|
||||
struct client *client, *bclient;
|
||||
|
||||
unsigned int startbcast:1;
|
||||
} rsrv_iface_config;
|
||||
|
||||
enum ctl {ctlInit, ctlRun, ctlPause, ctlExit};
|
||||
|
||||
@@ -160,15 +184,16 @@ enum ctl {ctlInit, ctlRun, ctlPause, ctlExit};
|
||||
#endif
|
||||
|
||||
GLBLTYPE int CASDEBUG;
|
||||
GLBLTYPE SOCKET IOC_sock;
|
||||
GLBLTYPE SOCKET IOC_cast_sock;
|
||||
GLBLTYPE unsigned short ca_server_port;
|
||||
GLBLTYPE ELLLIST clientQ; /* locked by clientQlock */
|
||||
GLBLTYPE unsigned short ca_server_port, ca_udp_port, ca_beacon_port;
|
||||
GLBLTYPE ELLLIST clientQ; /* (TCP clients) locked by clientQlock */
|
||||
GLBLTYPE ELLLIST clientQudp; /* locked by clientQlock */
|
||||
GLBLTYPE ELLLIST servers; /* rsrv_iface_config::node, read-only after rsrv_init() */
|
||||
GLBLTYPE ELLLIST beaconAddrList;
|
||||
GLBLTYPE ELLLIST casIntfAddrList;
|
||||
GLBLTYPE SOCKET beaconSocket;
|
||||
GLBLTYPE ELLLIST casIntfAddrList, casMCastAddrList;
|
||||
GLBLTYPE epicsUInt32 *casIgnoreAddrs;
|
||||
GLBLTYPE epicsMutexId clientQlock;
|
||||
GLBLTYPE struct client *prsrv_cast_client;
|
||||
GLBLTYPE BUCKET *pCaBucket;
|
||||
GLBLTYPE BUCKET *pCaBucket; /* locked by clientQlock */
|
||||
GLBLTYPE void *rsrvClientFreeList;
|
||||
GLBLTYPE void *rsrvChanFreeList;
|
||||
GLBLTYPE void *rsrvEventFreeList;
|
||||
@@ -176,7 +201,7 @@ GLBLTYPE void *rsrvSmallBufFreeListTCP;
|
||||
GLBLTYPE void *rsrvLargeBufFreeListTCP;
|
||||
GLBLTYPE unsigned rsrvSizeofLargeBufTCP;
|
||||
GLBLTYPE void *rsrvPutNotifyFreeList;
|
||||
GLBLTYPE unsigned rsrvChannelCount;
|
||||
GLBLTYPE unsigned rsrvChannelCount; /* locked by clientQlock */
|
||||
|
||||
GLBLTYPE epicsEventId casudp_startStopEvent;
|
||||
GLBLTYPE epicsEventId beacon_startStopEvent;
|
||||
@@ -185,6 +210,7 @@ GLBLTYPE volatile enum ctl casudp_ctl;
|
||||
GLBLTYPE volatile enum ctl beacon_ctl;
|
||||
GLBLTYPE volatile enum ctl castcp_ctl;
|
||||
|
||||
GLBLTYPE unsigned int threadPrios[5];
|
||||
|
||||
#define CAS_HASH_TABLE_SIZE 4096
|
||||
|
||||
|
||||
@@ -45,6 +45,11 @@ Com_SYS_LIBS_WIN32 = ws2_32 advapi32 user32
|
||||
|
||||
Com_RCS = Com.rc
|
||||
|
||||
ifeq ($(T_A),$(EPICS_HOST_ARCH))
|
||||
# Antelope & flex are needed to finish libCom
|
||||
DELAY_INSTALL_LIBS = YES
|
||||
endif
|
||||
|
||||
include $(TOP)/configure/RULES
|
||||
|
||||
include $(LIBCOM)/as/RULES
|
||||
|
||||
@@ -529,6 +529,12 @@ Init (rtems_task_argument ignored)
|
||||
putenv ("TERM=xterm");
|
||||
putenv ("IOCSH_HISTSIZE=20");
|
||||
|
||||
/*
|
||||
* Display some OS information
|
||||
*/
|
||||
printf("\n***** RTEMS Version: %s *****\n",
|
||||
rtems_get_version_string());
|
||||
|
||||
/*
|
||||
* Start network
|
||||
*/
|
||||
|
||||
@@ -486,7 +486,7 @@ epicsShareFunc int epicsShareAPI bucketShow(BUCKET *pb)
|
||||
unsigned count;
|
||||
unsigned maxEntries;
|
||||
|
||||
printf( "Bucket entries in use = %d bytes in use = %ld\n",
|
||||
printf( " Bucket entries in use = %d bytes in use = %ld\n",
|
||||
pb->nInUse,
|
||||
(long) (sizeof(*pb)+(pb->hashIdMask+1)*
|
||||
sizeof(ITEM *)+pb->nInUse*sizeof(ITEM)));
|
||||
@@ -511,7 +511,7 @@ epicsShareFunc int epicsShareAPI bucketShow(BUCKET *pb)
|
||||
|
||||
mean = X/nElem;
|
||||
stdDev = sqrt(XX/nElem - mean*mean);
|
||||
printf( "Bucket entries/hash id - mean = %f std dev = %f max = %d\n",
|
||||
printf( " Bucket entries/hash id - mean = %f std dev = %f max = %d\n",
|
||||
mean,
|
||||
stdDev,
|
||||
maxEntries);
|
||||
|
||||
@@ -33,6 +33,10 @@ static int cond_search(const char **ppinst, int match);
|
||||
#define PI 3.14159265358979323
|
||||
#endif
|
||||
|
||||
/* Turn off global optimization for 64-bit MSVC builds */
|
||||
#if defined(_WIN32) && defined(_M_X64) && !defined(_MINGW)
|
||||
# pragma optimize("g", off)
|
||||
#endif
|
||||
|
||||
/* calcPerform
|
||||
*
|
||||
@@ -45,6 +49,7 @@ epicsShareFunc long
|
||||
double *ptop; /* stack pointer */
|
||||
double top; /* value from top of stack */
|
||||
epicsInt32 itop; /* integer from top of stack */
|
||||
epicsUInt32 utop; /* unsigned integer from top of stack */
|
||||
int op;
|
||||
int nargs;
|
||||
|
||||
@@ -150,7 +155,7 @@ epicsShareFunc long
|
||||
break;
|
||||
|
||||
case ABS_VAL:
|
||||
if (*ptop < 0.0) *ptop = - *ptop;
|
||||
*ptop = fabs(*ptop);
|
||||
break;
|
||||
|
||||
case EXP:
|
||||
@@ -262,7 +267,7 @@ epicsShareFunc long
|
||||
|
||||
case NINT:
|
||||
top = *ptop;
|
||||
*ptop = (double)(epicsInt32)(top >= 0 ? top + 0.5 : top - 0.5);
|
||||
*ptop = (epicsInt32) (top >= 0 ? top + 0.5 : top - 0.5);
|
||||
break;
|
||||
|
||||
case RANDOM:
|
||||
@@ -283,34 +288,45 @@ epicsShareFunc long
|
||||
*ptop = ! *ptop;
|
||||
break;
|
||||
|
||||
/* For bitwise operations on values with bit 31 set, double values
|
||||
* must first be cast to unsigned to correctly set that bit; the
|
||||
* double value must be negative in that case. The result must be
|
||||
* cast to a signed integer before converting to the double result.
|
||||
*/
|
||||
|
||||
case BIT_OR:
|
||||
itop = (epicsInt32) *ptop--;
|
||||
*ptop = (epicsInt32) *ptop | itop;
|
||||
utop = *ptop--;
|
||||
*ptop = (epicsInt32) ((epicsUInt32) *ptop | utop);
|
||||
break;
|
||||
|
||||
case BIT_AND:
|
||||
itop = (epicsInt32) *ptop--;
|
||||
*ptop = (epicsInt32) *ptop & itop;
|
||||
utop = *ptop--;
|
||||
*ptop = (epicsInt32) ((epicsUInt32) *ptop & utop);
|
||||
break;
|
||||
|
||||
case BIT_EXCL_OR:
|
||||
itop = (epicsInt32) *ptop--;
|
||||
*ptop = (epicsInt32) *ptop ^ itop;
|
||||
utop = *ptop--;
|
||||
*ptop = (epicsInt32) ((epicsUInt32) *ptop ^ utop);
|
||||
break;
|
||||
|
||||
case BIT_NOT:
|
||||
itop = (epicsInt32) *ptop;
|
||||
*ptop = ~itop;
|
||||
utop = *ptop;
|
||||
*ptop = (epicsInt32) ~utop;
|
||||
break;
|
||||
|
||||
/* The shift operators use signed integers, so a right-shift will
|
||||
* extend the sign bit into the left-hand end of the value. The
|
||||
* double-casting through unsigned here is important, see above.
|
||||
*/
|
||||
|
||||
case RIGHT_SHIFT:
|
||||
itop = (epicsInt32) *ptop--;
|
||||
*ptop = (epicsInt32) *ptop >> itop;
|
||||
utop = *ptop--;
|
||||
*ptop = ((epicsInt32) (epicsUInt32) *ptop) >> (utop & 31);
|
||||
break;
|
||||
|
||||
case LEFT_SHIFT:
|
||||
itop = (epicsInt32) *ptop--;
|
||||
*ptop = (epicsInt32) *ptop << itop;
|
||||
utop = *ptop--;
|
||||
*ptop = ((epicsInt32) (epicsUInt32) *ptop) << (utop & 31);
|
||||
break;
|
||||
|
||||
case NOT_EQ:
|
||||
@@ -367,6 +383,9 @@ epicsShareFunc long
|
||||
*presult = *ptop;
|
||||
return 0;
|
||||
}
|
||||
#if defined(_WIN32) && defined(_M_X64) && !defined(_MINGW)
|
||||
# pragma optimize("", on)
|
||||
#endif
|
||||
|
||||
|
||||
epicsShareFunc long
|
||||
|
||||
@@ -251,7 +251,7 @@ epicsShareFunc long
|
||||
goto bad;
|
||||
}
|
||||
psrc = pnext;
|
||||
lit_i = (int) lit_d;
|
||||
lit_i = (epicsInt32) lit_d;
|
||||
if (lit_d != (double) lit_i) {
|
||||
*pout++ = pel->code;
|
||||
memcpy(pout, &lit_d, sizeof(double));
|
||||
@@ -272,8 +272,8 @@ epicsShareFunc long
|
||||
}
|
||||
psrc = pnext;
|
||||
*pout++ = LITERAL_INT;
|
||||
memcpy(pout, &lit_ui, sizeof(epicsInt32));
|
||||
pout += sizeof(epicsInt32);
|
||||
memcpy(pout, &lit_ui, sizeof(epicsUInt32));
|
||||
pout += sizeof(epicsUInt32);
|
||||
}
|
||||
|
||||
operand_needed = FALSE;
|
||||
|
||||
@@ -52,9 +52,9 @@ void SingletonUntyped :: incrRefCount ( PBuild pBuild )
|
||||
|
||||
void SingletonUntyped :: decrRefCount ( PDestroy pDestroy )
|
||||
{
|
||||
assert ( _refCount > 0 );
|
||||
epicsGuard < epicsMutex >
|
||||
guard ( *pEPICSSigletonMutex );
|
||||
assert ( _refCount > 0 );
|
||||
_refCount--;
|
||||
if ( _refCount == 0 ) {
|
||||
( *pDestroy ) ( _pInstance );
|
||||
|
||||
6
src/libCom/env/RULES
vendored
6
src/libCom/env/RULES
vendored
@@ -8,5 +8,7 @@
|
||||
# This is a Makefile fragment, see src/libCom/Makefile.
|
||||
|
||||
envData.c: $(LIBCOM)/env/envDefs.h $(LIBCOM)/env/bldEnvData.pl \
|
||||
$(CONFIG)/CONFIG_ENV $(CONFIG)/CONFIG_SITE_ENV
|
||||
$(PERL) $(LIBCOM)/env/bldEnvData.pl $(INSTALL_QUIETLY) $(CONFIG)
|
||||
$(CONFIG)/CONFIG_ENV $(CONFIG)/CONFIG_SITE_ENV \
|
||||
$(wildcard $(CONFIG)/os/CONFIG_SITE_ENV.$(T_A))
|
||||
$(PERL) $(LIBCOM)/env/bldEnvData.pl $(INSTALL_QUIETLY) -t $(T_A) \
|
||||
-c $(CMPLR_CLASS) -s $(OS_CLASS) $(CONFIG)
|
||||
|
||||
57
src/libCom/env/bldEnvData.pl
vendored
57
src/libCom/env/bldEnvData.pl
vendored
@@ -26,13 +26,13 @@ use Text::Wrap;
|
||||
|
||||
my $tool = basename($0);
|
||||
|
||||
our ($opt_h, $opt_q);
|
||||
our ($opt_h, $opt_q, $opt_t, $opt_s, $opt_c);
|
||||
our $opt_o = 'envData.c';
|
||||
|
||||
$Getopt::Std::OUTPUT_HELP_VERSION = 1;
|
||||
$Text::Wrap::columns = 75;
|
||||
|
||||
&HELP_MESSAGE unless getopts('ho:q') && @ARGV == 1;
|
||||
&HELP_MESSAGE unless getopts('ho:qt:s:c:') && @ARGV == 1;
|
||||
&HELP_MESSAGE if $opt_h;
|
||||
|
||||
my $config = AbsPath(shift);
|
||||
@@ -52,16 +52,31 @@ while (<SRC>) {
|
||||
}
|
||||
close SRC;
|
||||
|
||||
# Read the values from the CONFIG_ENV and CONFIG_SITE_ENV files
|
||||
# A list of configure/CONFIG_* files to read
|
||||
#
|
||||
my $config_env = "$config/CONFIG_ENV";
|
||||
my $config_site_env = "$config/CONFIG_SITE_ENV";
|
||||
my @configs = ("$config/CONFIG_ENV", "$config/CONFIG_SITE_ENV");
|
||||
|
||||
my %values;
|
||||
readReleaseFiles($config_env, \%values);
|
||||
readReleaseFiles($config_site_env, \%values);
|
||||
if ($opt_t) {
|
||||
my $config_arch_env = "$config/os/CONFIG_SITE_ENV.$opt_t";
|
||||
push @configs, $config_arch_env
|
||||
if -f $config_arch_env;
|
||||
}
|
||||
|
||||
# Warn about any vars with no value
|
||||
my @sources = ($env_defs, @configs);
|
||||
|
||||
# Get values from the config files
|
||||
#
|
||||
my (%values, @dummy);
|
||||
readRelease($_, \%values, \@dummy) foreach @configs;
|
||||
expandRelease(\%values);
|
||||
|
||||
# Get values from the command-line
|
||||
#
|
||||
$values{EPICS_BUILD_COMPILER_CLASS} = $opt_c if $opt_c;
|
||||
$values{EPICS_BUILD_OS_CLASS} = $opt_s if $opt_s;
|
||||
$values{EPICS_BUILD_TARGET_ARCH} = $opt_t if $opt_t;
|
||||
|
||||
# Warn about vars with no configured value
|
||||
#
|
||||
my @undefs = grep {!exists $values{$_}} @vars;
|
||||
warn "$tool: No value given for $_\n" foreach @undefs;
|
||||
@@ -73,13 +88,13 @@ print "Generating $opt_o\n" unless $opt_q;
|
||||
open OUT, '>', $opt_o
|
||||
or die "$tool: Cannot create $opt_o: $!\n";
|
||||
|
||||
my $sources = join "\n", map {" * $_"} @sources;
|
||||
|
||||
print OUT << "END";
|
||||
/* Generated file $opt_o
|
||||
*
|
||||
* Created from
|
||||
* $env_defs
|
||||
* $config_env
|
||||
* $config_site_env
|
||||
$sources
|
||||
*/
|
||||
|
||||
#include <stddef.h>
|
||||
@@ -88,18 +103,23 @@ print OUT << "END";
|
||||
|
||||
END
|
||||
|
||||
# Define all parameters, giving variable name and default value
|
||||
# Define a default value for each named parameter
|
||||
#
|
||||
foreach my $var (@vars) {
|
||||
my $default = $values{$var} || '';
|
||||
$default =~ s/^"//;
|
||||
$default =~ s/"$//;
|
||||
my $default = $values{$var};
|
||||
if (defined $default) {
|
||||
$default =~ s/^"//;
|
||||
$default =~ s/"$//;
|
||||
}
|
||||
else {
|
||||
$default = '';
|
||||
}
|
||||
|
||||
print OUT "epicsShareDef const ENV_PARAM $var =\n",
|
||||
" {\"$var\", \"$default\"};\n";
|
||||
}
|
||||
|
||||
# Now create a list of all those parameters
|
||||
# Also provide a list of all defined parameters
|
||||
#
|
||||
print OUT "\n",
|
||||
"epicsShareDef const ENV_PARAM* env_param_list[] = {\n",
|
||||
@@ -112,6 +132,9 @@ sub HELP_MESSAGE {
|
||||
" -h Help: Print this message\n",
|
||||
" -q Quiet: Only print errors\n",
|
||||
" -o file Output filename, default is $opt_o\n",
|
||||
" -t arch Target architecture \$(T_A) name\n",
|
||||
" -s os Operating system \$(OS_CLASS)\n",
|
||||
" -c comp Compiler class \$(CMPLR_CLASS)\n",
|
||||
"\n";
|
||||
|
||||
exit 1;
|
||||
|
||||
7
src/libCom/env/envDefs.h
vendored
7
src/libCom/env/envDefs.h
vendored
@@ -40,7 +40,7 @@ typedef struct envParam {
|
||||
} ENV_PARAM;
|
||||
|
||||
/*
|
||||
* bldEnvData looks for "epicsShareExtern const ENV_PARAM"
|
||||
* bldEnvData.pl looks for "epicsShareExtern const ENV_PARAM <name>;"
|
||||
*/
|
||||
epicsShareExtern const ENV_PARAM EPICS_CA_ADDR_LIST;
|
||||
epicsShareExtern const ENV_PARAM EPICS_CA_CONN_TMO;
|
||||
@@ -58,6 +58,9 @@ epicsShareExtern const ENV_PARAM EPICS_CAS_SERVER_PORT;
|
||||
epicsShareExtern const ENV_PARAM EPICS_CA_BEACON_PERIOD; /* deprecated */
|
||||
epicsShareExtern const ENV_PARAM EPICS_CAS_BEACON_PERIOD;
|
||||
epicsShareExtern const ENV_PARAM EPICS_CAS_BEACON_PORT;
|
||||
epicsShareExtern const ENV_PARAM EPICS_BUILD_COMPILER_CLASS;
|
||||
epicsShareExtern const ENV_PARAM EPICS_BUILD_OS_CLASS;
|
||||
epicsShareExtern const ENV_PARAM EPICS_BUILD_TARGET_ARCH;
|
||||
epicsShareExtern const ENV_PARAM EPICS_TIMEZONE;
|
||||
epicsShareExtern const ENV_PARAM EPICS_TS_NTP_INET;
|
||||
epicsShareExtern const ENV_PARAM EPICS_IOC_LOG_PORT;
|
||||
@@ -88,6 +91,8 @@ epicsShareFunc long epicsShareAPI
|
||||
envGetLongConfigParam(const ENV_PARAM *pParam, long *pLong);
|
||||
epicsShareFunc unsigned short epicsShareAPI envGetInetPortConfigParam
|
||||
(const ENV_PARAM *pEnv, unsigned short defaultPort);
|
||||
epicsShareFunc long epicsShareAPI
|
||||
envGetBoolConfigParam(const ENV_PARAM *pParam, int *pBool);
|
||||
epicsShareFunc long epicsShareAPI epicsPrtEnvParams(void);
|
||||
epicsShareFunc void epicsShareAPI epicsEnvSet (const char *name, const char *value);
|
||||
epicsShareFunc void epicsShareAPI epicsEnvShow (const char *name);
|
||||
|
||||
15
src/libCom/env/envSubr.c
vendored
15
src/libCom/env/envSubr.c
vendored
@@ -43,6 +43,7 @@
|
||||
#define epicsExportSharedSymbols
|
||||
#include "epicsStdlib.h"
|
||||
#include "epicsStdio.h"
|
||||
#include "epicsString.h"
|
||||
#include "errMdef.h"
|
||||
#include "errlog.h"
|
||||
#include "envDefs.h"
|
||||
@@ -319,7 +320,19 @@ long *pLong /* O pointer to place to store value */
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
long epicsShareAPI
|
||||
envGetBoolConfigParam(const ENV_PARAM *pParam, int *pBool)
|
||||
{
|
||||
char text[20];
|
||||
|
||||
if(!envGetConfigParam(pParam, sizeof(text), text))
|
||||
return -1;
|
||||
*pBool = epicsStrCaseCmp(text, "yes")==0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*+/subr**********************************************************************
|
||||
* NAME envPrtConfigParam - print value of a configuration parameter
|
||||
*
|
||||
|
||||
@@ -120,10 +120,12 @@ epicsShareFunc int errlogPrintf(const char *pFormat, ...)
|
||||
isOkToBlock = epicsThreadIsOkToBlock();
|
||||
|
||||
if (pvtData.atExit || (isOkToBlock && pvtData.toConsole)) {
|
||||
FILE *console = pvtData.console ? pvtData.console : stderr;
|
||||
|
||||
va_start(pvar, pFormat);
|
||||
nchar = vfprintf(pvtData.console, pFormat, pvar);
|
||||
nchar = vfprintf(console, pFormat, pvar);
|
||||
va_end (pvar);
|
||||
fflush(pvtData.console);
|
||||
fflush(console);
|
||||
}
|
||||
|
||||
if (pvtData.atExit)
|
||||
@@ -146,6 +148,7 @@ epicsShareFunc int errlogVprintf(
|
||||
int nchar;
|
||||
char *pbuffer;
|
||||
int isOkToBlock;
|
||||
FILE *console;
|
||||
|
||||
if (epicsInterruptIsInterruptContext()) {
|
||||
epicsInterruptContextMessage
|
||||
@@ -160,15 +163,17 @@ epicsShareFunc int errlogVprintf(
|
||||
|
||||
pbuffer = msgbufGetFree(isOkToBlock);
|
||||
if (!pbuffer) {
|
||||
vfprintf(pvtData.console, pFormat, pvar);
|
||||
fflush(pvtData.console);
|
||||
console = pvtData.console ? pvtData.console : stderr;
|
||||
vfprintf(console, pFormat, pvar);
|
||||
fflush(console);
|
||||
return 0;
|
||||
}
|
||||
|
||||
nchar = tvsnPrint(pbuffer, pvtData.maxMsgSize, pFormat?pFormat:"", pvar);
|
||||
if (pvtData.atExit || (isOkToBlock && pvtData.toConsole)) {
|
||||
fprintf(pvtData.console, "%s", pbuffer);
|
||||
fflush(pvtData.console);
|
||||
console = pvtData.console ? pvtData.console : stderr;
|
||||
fprintf(console, "%s", pbuffer);
|
||||
fflush(console);
|
||||
}
|
||||
msgbufSetSize(nchar);
|
||||
return nchar;
|
||||
@@ -243,11 +248,13 @@ epicsShareFunc int errlogSevPrintf(
|
||||
|
||||
isOkToBlock = epicsThreadIsOkToBlock();
|
||||
if (pvtData.atExit || (isOkToBlock && pvtData.toConsole)) {
|
||||
fprintf(pvtData.console, "sevr=%s ", errlogGetSevEnumString(severity));
|
||||
FILE *console = pvtData.console ? pvtData.console : stderr;
|
||||
|
||||
fprintf(console, "sevr=%s ", errlogGetSevEnumString(severity));
|
||||
va_start(pvar, pFormat);
|
||||
vfprintf(pvtData.console, pFormat, pvar);
|
||||
vfprintf(console, pFormat, pvar);
|
||||
va_end(pvar);
|
||||
fflush(pvtData.console);
|
||||
fflush(console);
|
||||
}
|
||||
|
||||
va_start(pvar, pFormat);
|
||||
@@ -359,7 +366,9 @@ epicsShareFunc int epicsShareAPI errlogRemoveListeners(
|
||||
epicsMutexUnlock(pvtData.listenerLock);
|
||||
|
||||
if (count == 0) {
|
||||
fprintf(pvtData.console,
|
||||
FILE *console = pvtData.console ? pvtData.console : stderr;
|
||||
|
||||
fprintf(console,
|
||||
"errlogRemoveListeners: No listeners found\n");
|
||||
}
|
||||
return count;
|
||||
@@ -376,7 +385,7 @@ epicsShareFunc int epicsShareAPI eltc(int yesno)
|
||||
epicsShareFunc int errlogSetConsole(FILE *stream)
|
||||
{
|
||||
errlogInit(0);
|
||||
pvtData.console = stream ? stream : stderr;
|
||||
pvtData.console = stream;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -405,17 +414,19 @@ epicsShareFunc void errPrintf(long status, const char *pFileName,
|
||||
}
|
||||
|
||||
if (pvtData.atExit || (isOkToBlock && pvtData.toConsole)) {
|
||||
FILE *console = pvtData.console ? pvtData.console : stderr;
|
||||
|
||||
if (pFileName)
|
||||
fprintf(pvtData.console, "filename=\"%s\" line number=%d\n",
|
||||
fprintf(console, "filename=\"%s\" line number=%d\n",
|
||||
pFileName, lineno);
|
||||
if (status > 0)
|
||||
fprintf(pvtData.console, "%s ", name);
|
||||
fprintf(console, "%s ", name);
|
||||
|
||||
va_start(pvar, pformat);
|
||||
vfprintf(pvtData.console, pformat, pvar);
|
||||
vfprintf(console, pformat, pvar);
|
||||
va_end(pvar);
|
||||
fputc('\n', pvtData.console);
|
||||
fflush(pvtData.console);
|
||||
fputc('\n', console);
|
||||
fflush(console);
|
||||
}
|
||||
|
||||
if (pvtData.atExit)
|
||||
@@ -473,7 +484,7 @@ static void errlogInitPvt(void *arg)
|
||||
ellInit(&pvtData.listenerList);
|
||||
ellInit(&pvtData.msgQueue);
|
||||
pvtData.toConsole = TRUE;
|
||||
pvtData.console = stderr;
|
||||
pvtData.console = NULL;
|
||||
pvtData.waitForWork = epicsEventMustCreate(epicsEventEmpty);
|
||||
pvtData.listenerLock = epicsMutexMustCreate();
|
||||
pvtData.msgQueueLock = epicsMutexMustCreate();
|
||||
@@ -559,8 +570,10 @@ static void errlogThread(void)
|
||||
while ((pmessage = msgbufGetSend(&noConsoleMessage))) {
|
||||
epicsMutexMustLock(pvtData.listenerLock);
|
||||
if (pvtData.toConsole && !noConsoleMessage) {
|
||||
fprintf(pvtData.console,"%s",pmessage);
|
||||
fflush(pvtData.console);
|
||||
FILE *console = pvtData.console ? pvtData.console : stderr;
|
||||
|
||||
fprintf(console, "%s", pmessage);
|
||||
fflush(console);
|
||||
}
|
||||
|
||||
plistenerNode = (listenerNode *)ellFirst(&pvtData.listenerList);
|
||||
@@ -680,7 +693,9 @@ static void msgbufFreeSend(void)
|
||||
epicsMutexMustLock(pvtData.msgQueueLock);
|
||||
pnextSend = (msgNode *)ellFirst(&pvtData.msgQueue);
|
||||
if (!pnextSend) {
|
||||
fprintf(pvtData.console, "errlog: msgbufFreeSend logic error\n");
|
||||
FILE *console = pvtData.console ? pvtData.console : stderr;
|
||||
|
||||
fprintf(console, "errlog: msgbufFreeSend logic error\n");
|
||||
epicsThreadSuspendSelf();
|
||||
}
|
||||
ellDelete(&pvtData.msgQueue, &pnextSend->node);
|
||||
|
||||
@@ -148,6 +148,10 @@ epicsShareFunc void epicsShareAPI freeListCleanup(void *pvt)
|
||||
epicsShareFunc size_t epicsShareAPI freeListItemsAvail(void *pvt)
|
||||
{
|
||||
FREELISTPVT *pfl = pvt;
|
||||
return pfl->nBlocksAvailable;
|
||||
size_t nBlocksAvailable;
|
||||
epicsMutexMustLock(pfl->lock);
|
||||
nBlocksAvailable = pfl->nBlocksAvailable;
|
||||
epicsMutexUnlock(pfl->lock);
|
||||
return nBlocksAvailable;
|
||||
}
|
||||
|
||||
|
||||
@@ -70,12 +70,12 @@ epicsShareFunc int
|
||||
/* These macros return 1 if successful, 0 on failure.
|
||||
* This is analagous to the return value from sscanf()
|
||||
*/
|
||||
#define epicsScanLong(str, to, base) !epicsParseLong(str, to, base, NULL)
|
||||
#define epicsScanULong(str, to, base) !epicsParseULong(str, to, base, NULL)
|
||||
#define epicsScanLLong(str, to, base) !epicsParseLLong(str, to, base, NULL)
|
||||
#define epicsScanULLong(str, to, base) !epicsParseULLong(str, to, base, NULL)
|
||||
#define epicsScanFloat(str, to) !epicsParseFloat(str, to, NULL)
|
||||
#define epicsScanDouble(str, to) !epicsParseDouble(str, to, NULL)
|
||||
#define epicsScanLong(str, to, base) (!epicsParseLong(str, to, base, NULL))
|
||||
#define epicsScanULong(str, to, base) (!epicsParseULong(str, to, base, NULL))
|
||||
#define epicsScanLLong(str, to, base) (!epicsParseLLong(str, to, base, NULL))
|
||||
#define epicsScanULLong(str, to, base) (!epicsParseULLong(str, to, base, NULL))
|
||||
#define epicsScanFloat(str, to) (!epicsParseFloat(str, to, NULL))
|
||||
#define epicsScanDouble(str, to) (!epicsParseDouble(str, to, NULL))
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
@@ -211,7 +211,7 @@ int testDone(void) {
|
||||
|
||||
/* Our test harness, for RTEMS and vxWorks */
|
||||
|
||||
static void harnessExit(void *dummy) {
|
||||
void testHarnessExit(void *dummy) {
|
||||
epicsTimeStamp ended;
|
||||
int Faulty;
|
||||
|
||||
@@ -248,7 +248,7 @@ static void harnessExit(void *dummy) {
|
||||
|
||||
void testHarness(void) {
|
||||
epicsThreadOnce(&onceFlag, testOnce, NULL);
|
||||
epicsAtExit(harnessExit, NULL);
|
||||
epicsAtExit(testHarnessExit, NULL);
|
||||
Harness = 1;
|
||||
Programs = 0;
|
||||
Tests = 0;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user