Compare commits
322 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
82042e270e | ||
|
|
32c3f8a44f | ||
|
|
fc4f010972 | ||
|
|
bd2ada3f8c | ||
|
|
3d7e95b88a | ||
|
|
8fea0117ae | ||
|
|
8392106f59 | ||
|
|
9502841b55 | ||
|
|
19680d7869 | ||
|
|
4a0ef34771 | ||
|
|
5d42e60745 | ||
|
|
e01d4b14d6 | ||
|
|
791fa1310a | ||
|
|
b4404c8266 | ||
|
|
f73f3332fa | ||
|
|
3f4c6abb31 | ||
|
|
64cd49410e | ||
|
|
0f6f833e91 | ||
|
|
c08df82898 | ||
|
|
f0c56bcbba | ||
|
|
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 | ||
|
|
2be59e985d | ||
|
|
b4fd19e7db | ||
|
|
969b1030b2 | ||
|
|
84880e876d | ||
|
|
45a741ac6f | ||
|
|
0575d3764f | ||
|
|
48da96cce5 | ||
|
|
18d863b918 | ||
|
|
61e61aab24 | ||
|
|
d3e046a466 | ||
|
|
f528f347cd | ||
|
|
ffed8910c0 | ||
|
|
99d331e50a | ||
|
|
0fb923186a | ||
|
|
ce500bc98c | ||
|
|
6376ae8b1a | ||
|
|
445c0ada8c | ||
|
|
87759d1c1d | ||
|
|
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 | ||
|
|
6143520680 | ||
|
|
f814398d77 | ||
|
|
d2e4fcbbac | ||
|
|
4cca4673ca | ||
|
|
840da801fb | ||
|
|
e75f44100e | ||
|
|
45c8a173f3 | ||
|
|
e227ae3590 | ||
|
|
ecedd9c362 | ||
|
|
4e7b185977 | ||
|
|
b74ecff2fc | ||
|
|
1255e75828 | ||
|
|
d6eea14fd0 | ||
|
|
98504d1cdc | ||
|
|
568ece9c27 | ||
|
|
f876bdb42c | ||
|
|
605074a580 | ||
|
|
64da181e6f | ||
|
|
823613fb4f | ||
|
|
f837add8c4 | ||
|
|
15307c4db6 | ||
|
|
8d5815ac95 | ||
|
|
25d7d46e08 | ||
|
|
5f31d9d2b1 | ||
|
|
bb67c9db2f | ||
|
|
cb965611ce | ||
|
|
e805abe971 | ||
|
|
c80f71e294 | ||
|
|
692bbafffe | ||
|
|
fd863738be | ||
|
|
2052062324 | ||
|
|
5d74216017 | ||
|
|
991ff308e0 | ||
|
|
9779d6987c | ||
|
|
b7c2815c21 | ||
|
|
77a5f0db77 | ||
|
|
8d6469957e | ||
|
|
ef90d2d3c7 | ||
|
|
29d2b7f6a2 | ||
|
|
ef39f658cd | ||
|
|
e298fb4c27 | ||
|
|
d94840ae6c | ||
|
|
c1f742e741 | ||
|
|
6f0814108a | ||
|
|
770d9ab313 | ||
|
|
7e7cca6164 | ||
|
|
03bdf4effa | ||
|
|
3258927bab | ||
|
|
a8e285067d | ||
|
|
2d9c529f5e | ||
|
|
05d3dbf453 | ||
|
|
695f516cbf | ||
|
|
fda3824ab4 | ||
|
|
b099ea2966 | ||
|
|
c4b128dfff | ||
|
|
a245c3805a | ||
|
|
122b9d3185 | ||
|
|
4081377c4b | ||
|
|
405f50533e | ||
|
|
8f0950288a | ||
|
|
3551513297 | ||
|
|
fc775d76aa | ||
|
|
7195372caf | ||
|
|
655735fa13 | ||
|
|
8aea808b1d | ||
|
|
66e126bae8 | ||
|
|
8ce86bb061 | ||
|
|
f2ff270644 | ||
|
|
895b415968 | ||
|
|
87999c7047 | ||
|
|
9331ca3f44 | ||
|
|
1587c44d79 | ||
|
|
51f0a4509b | ||
|
|
94f00cb0c6 | ||
|
|
4544be090d | ||
|
|
15db30ba79 | ||
|
|
cd79a7b53d | ||
|
|
717d7ff4eb | ||
|
|
696e00eec9 | ||
|
|
a81d123ea5 | ||
|
|
f21fce0b42 | ||
|
|
6fffcf9985 | ||
|
|
d890c80961 | ||
|
|
d49fc94314 | ||
|
|
536c6e91ff | ||
|
|
35c07a958f | ||
|
|
67aa96b957 | ||
|
|
041423092f | ||
|
|
869410d6f6 | ||
|
|
6862ef6580 | ||
|
|
b6aea68304 | ||
|
|
9d00978176 | ||
|
|
b6fbea9610 | ||
|
|
180f40c1f7 | ||
|
|
d5832354e8 | ||
|
|
44980a1dac | ||
|
|
af1daea3e7 | ||
|
|
b2716f0a19 | ||
|
|
050b9f9a48 | ||
|
|
4ab6aa79e3 | ||
|
|
1e39224836 | ||
|
|
f99fbe1964 | ||
|
|
aa04941119 | ||
|
|
46853d6862 | ||
|
|
02ac91aa2a | ||
|
|
f1b403b59a | ||
|
|
877a409de1 | ||
|
|
7395d1d88d | ||
|
|
4d3831ce58 | ||
|
|
1d99ad6cfc | ||
|
|
bd4d8ec26b | ||
|
|
a5e1f367d6 | ||
|
|
eb31fcedeb | ||
|
|
dd6edb103c | ||
|
|
50e6a305b1 | ||
|
|
58602cfa80 | ||
|
|
1e68f3899d | ||
|
|
8b3d37d392 | ||
|
|
ed4bcd831f | ||
|
|
837111296e | ||
|
|
9198428619 | ||
|
|
8f3fcc2787 | ||
|
|
a308fb7c1e | ||
|
|
60823bd2fb | ||
|
|
d0790c3060 | ||
|
|
9f0d34656a | ||
|
|
f6cdbe2693 | ||
|
|
3a54e97758 | ||
|
|
155017bf09 | ||
|
|
961dc450c4 | ||
|
|
53d01f6fb4 | ||
|
|
7fd4ac5a67 | ||
|
|
1240f09797 | ||
|
|
7947807e09 | ||
|
|
3a7cb33234 | ||
|
|
6b6eb5ff6a | ||
|
|
e4f336de94 | ||
|
|
fa23ba8a6a | ||
|
|
ee8bf98f94 | ||
|
|
30b8872150 | ||
|
|
7a8266c419 | ||
|
|
0c16937a5a | ||
|
|
ad6f55d92f | ||
|
|
e852de64ff | ||
|
|
f72ebb9fb9 | ||
|
|
6ac1e050bd | ||
|
|
ff4c88ed05 | ||
|
|
7a5d4cf6cc | ||
|
|
765fb7c63e | ||
|
|
fa4678798c | ||
|
|
127bdfd9aa | ||
|
|
8fea2f5ae6 | ||
|
|
07bb2fbef7 | ||
|
|
58a8a07cc4 | ||
|
|
a78abd0070 | ||
|
|
c26b02c20d | ||
|
|
ee297dc558 | ||
|
|
8ce0ba1e54 | ||
|
|
b8a7da18d2 | ||
|
|
8af3ffb653 | ||
|
|
ffd188bea3 | ||
|
|
af89b716f4 | ||
|
|
adcde46e9e | ||
|
|
c9d889ef3e | ||
|
|
8344b4ed79 | ||
|
|
3a647fb38b | ||
|
|
8bf765365f | ||
|
|
e7037cc519 | ||
|
|
0d41dd0113 | ||
|
|
52fc47dec5 | ||
|
|
e73ed24631 | ||
|
|
40cb21df9f | ||
|
|
40d4d608ed | ||
|
|
77efc973da | ||
|
|
3e8ba7d7cf | ||
|
|
a7a87372aa | ||
|
|
a014314384 | ||
|
|
8ef1e0d177 | ||
|
|
b9cbf7a3ac | ||
|
|
4bd4b93649 | ||
|
|
c120027768 | ||
|
|
cc3a01d460 | ||
|
|
06beb2cafb | ||
|
|
be06b37b42 | ||
|
|
a526d0eb04 | ||
|
|
cd228ccf0e | ||
|
|
bbbf05414d | ||
|
|
d7d45beedd | ||
|
|
a7ad9a1256 | ||
|
|
92641e9160 | ||
|
|
368223f783 | ||
|
|
85e21863dc | ||
|
|
b9b8cde5f6 | ||
|
|
6d7a0327e0 | ||
|
|
d897c9b686 | ||
|
|
ff14d811a7 | ||
|
|
2ba2b90b06 | ||
|
|
7d50f62aed | ||
|
|
4518e8fa7f | ||
|
|
e06b0a87b2 | ||
|
|
c16c4590ec | ||
|
|
b3ad20dec7 | ||
|
|
0f23784b32 | ||
|
|
42a3cf8e11 | ||
|
|
79497c359c | ||
|
|
75321140ef | ||
|
|
95d0d97f6a | ||
|
|
e24d4637c9 | ||
|
|
e5b354708e | ||
|
|
bc9255ba1c | ||
|
|
3f462bd032 | ||
|
|
6a018dce35 | ||
|
|
55cf45364c | ||
|
|
3885c809a6 | ||
|
|
5847f98f89 | ||
|
|
6ed997aacc | ||
|
|
5a8915c051 | ||
|
|
a919d7e09d | ||
|
|
c5da1e3f89 | ||
|
|
33804bd7ea | ||
|
|
200355bc00 | ||
|
|
480797c0e0 | ||
|
|
470a9f9fc9 | ||
|
|
99626f03cd | ||
|
|
8c60e7e2cb |
10
README
10
README
@@ -2,14 +2,14 @@
|
||||
EPICS Base - the central core of a control system toolkit
|
||||
---------------------------------------------------------
|
||||
|
||||
Copyright (c) 1991-2003 The University of Chicago, as Operator
|
||||
of Argonne National Laboratory.
|
||||
Copyright UChicago Argonne LLC, as Operator of Argonne
|
||||
National Laboratory.
|
||||
Copyright (c) 1991-2003 The Regents of the University of
|
||||
California, as Operator of Los Alamos National Laboratory.
|
||||
|
||||
EPICS Base Versions 3.13.7 and higher are distributed
|
||||
subject to a Software License Agreement found in the
|
||||
file LICENSE that is included with this distribution.
|
||||
EPICS Base is distributed subject to a Software License
|
||||
Agreement found in the file LICENSE that is included with
|
||||
this distribution.
|
||||
|
||||
---------------------------------------------------------
|
||||
|
||||
|
||||
@@ -3,13 +3,12 @@
|
||||
# National Laboratory.
|
||||
# Copyright (c) 2002 The Regents of the University of California, as
|
||||
# Operator of Los Alamos National Laboratory.
|
||||
# EPICS BASE Versions 3.13.7
|
||||
# and higher are distributed subject to a Software License Agreement found
|
||||
# EPICS BASE is distributed subject to a Software License Agreement found
|
||||
# in file LICENSE that is included with this distribution.
|
||||
#*************************************************************************
|
||||
|
||||
#---------------------------------------------------------------
|
||||
# Epics base directories
|
||||
# EPICS Base directories
|
||||
|
||||
EPICS_BASE_HOST_BIN = $(EPICS_BASE)/bin/$(EPICS_HOST_ARCH)
|
||||
EPICS_BASE_HOST_LIB = $(EPICS_BASE)/lib/$(EPICS_HOST_ARCH)
|
||||
@@ -19,12 +18,12 @@ ifdef T_A
|
||||
endif
|
||||
|
||||
#---------------------------------------------------------------
|
||||
# Epics base Ioc libraries
|
||||
# EPICS Base Ioc libraries
|
||||
|
||||
EPICS_BASE_IOC_LIBS += dbRecStd dbCore ca Com
|
||||
|
||||
#---------------------------------------------------------------
|
||||
# Epics base Host libraries
|
||||
# EPICS Base Host libraries
|
||||
|
||||
EPICS_BASE_HOST_LIBS += cas gdd
|
||||
EPICS_BASE_HOST_LIBS += ca Com
|
||||
@@ -42,13 +41,11 @@ endif # BASE_TOP
|
||||
#---------------------------------------------------------------
|
||||
# Base c preprocessor flags
|
||||
|
||||
BASE_CPPFLAGS =
|
||||
|
||||
# osithread default stack
|
||||
OSITHREAD_USE_DEFAULT_STACK = NO
|
||||
OSITHREAD_DEFAULT_STACK_FLAGS_YES = -DOSITHREAD_USE_DEFAULT_STACK
|
||||
OSITHREAD_DEFAULT_STACK_FLAGS_NO =
|
||||
BASE_CPPFLAGS += $(OSITHREAD_DEFAULT_STACK_FLAGS_$(OSITHREAD_USE_DEFAULT_STACK))
|
||||
|
||||
BASE_CPPFLAGS = $(OSITHREAD_DEFAULT_STACK_FLAGS_$(OSITHREAD_USE_DEFAULT_STACK))
|
||||
|
||||
#---------------------------------------------------------------
|
||||
# Where to find the installed build tools
|
||||
@@ -59,7 +56,7 @@ TOOLS = $(abspath $(EPICS_BASE_HOST_BIN))
|
||||
FIND_TOOL = $(firstword $(wildcard $(TOOLS)/$(1) $(TOP)/src/tools/$(1)))
|
||||
|
||||
#---------------------------------------------------------------
|
||||
# Epics base build tools and tool flags
|
||||
# EPICS Base build tools and tool flags
|
||||
|
||||
MAKEBPT = $(TOOLS)/makeBpt$(HOSTEXE)
|
||||
DBEXPAND = $(PERL) $(TOOLS)/dbdExpand.pl
|
||||
@@ -68,11 +65,11 @@ DBTOMENUH = $(PERL) $(TOOLS)/dbdToMenuH.pl
|
||||
REGISTERRECORDDEVICEDRIVER = $(PERL) $(TOOLS)/registerRecordDeviceDriver.pl
|
||||
CONVERTRELEASE = $(PERL) $(call FIND_TOOL,convertRelease.pl)
|
||||
FULLPATHNAME = $(PERL) $(TOOLS)/fullPathName.pl
|
||||
GENVERSIONHEADER = $(PERL) $(TOOLS)/genVersionHeader.pl $(QUIET_FLAG)
|
||||
|
||||
#-------------------------------------------------------
|
||||
#---------------------------------------------------------------
|
||||
# tools for installing libraries and products
|
||||
INSTALL_QUIETLY := $(if $(findstring s,$(MAKEFLAGS)),-q,)
|
||||
INSTALL = $(PERL) $(TOOLS)/installEpics.pl $(INSTALL_QUIETLY)
|
||||
INSTALL = $(PERL) $(TOOLS)/installEpics.pl $(QUIET_FLAG)
|
||||
INSTALL_PRODUCT = $(INSTALL)
|
||||
INSTALL_LIBRARY = $(INSTALL)
|
||||
|
||||
@@ -82,7 +79,7 @@ MKMF = $(PERL) $(TOOLS)/mkmf.pl
|
||||
REPLACEVAR = $(PERL) $(TOOLS)/replaceVAR.pl
|
||||
|
||||
#---------------------------------------------------------------
|
||||
# private versions of lex/yacc from EPICS
|
||||
# Our versions of lex (flex) and yacc (antelope)
|
||||
EYACC = $(TOOLS)/antelope$(HOSTEXE)
|
||||
ELEX = $(TOOLS)/e_flex$(HOSTEXE) -S$(EPICS_BASE)/include/flex.skel.static
|
||||
|
||||
@@ -90,28 +87,6 @@ YACC = $(EYACC)
|
||||
LEX = $(ELEX)
|
||||
|
||||
#---------------------------------------------------------------
|
||||
# Our use of msi is incompatible with older versions
|
||||
# The 3.15 version of msi supports new options
|
||||
|
||||
MSI3_15 = $(EPICS_BASE_HOST_BIN)/msi
|
||||
|
||||
#---------------------------------------------------------------
|
||||
# External tools and tool flags - must be in path or defined in application
|
||||
|
||||
ADL2DL ?= adl2dl
|
||||
|
||||
# sch2edif compiler and flags
|
||||
SCH2EDIF = sch2edif
|
||||
SCH2EDIF_PATH =
|
||||
SCH2EDIF_SYSFLAGS = -n -ap -p.+..+$(SCH2EDIF_PATH)+$(CAPFAST_TEMPLATES)/sym+
|
||||
SCH2EDIF_FLAGS =
|
||||
|
||||
# e2db and flags
|
||||
# - again there is an assumption where edb.def is installed.
|
||||
E2DB ?= e2db
|
||||
E2DB_SYSFLAGS = -ate -d $(CAPFAST_TEMPLATES)/edb.def
|
||||
E2DB_FLAGS =
|
||||
|
||||
DBST ?= dbst
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -17,23 +17,34 @@
|
||||
# EPICS_SITE_VERSION is defined in CONFIG_SITE for sites that want a local
|
||||
# version number to be included in the reported version string.
|
||||
|
||||
# In 3.15 we still define BASE_3_14 so "ifdef BASE_3_14" means
|
||||
# 3.14 or later, but "ifeq ($(BASE_3_14),YES)" means 3.14 only.
|
||||
# We define BASE_3_14 and BASE_3_15 as NO and BASE_3_16 as YES, so
|
||||
# ifdef BASE_3_14
|
||||
# true for 3.14 or later
|
||||
# ifdef BASE_3_15
|
||||
# true for 3.15 or later
|
||||
# ifeq ($(BASE_3_14),YES)
|
||||
# true for 3.14.x only
|
||||
# ifeq ($(BASE_3_15),YES)
|
||||
# true for 3.15 only
|
||||
# ifeq ($(BASE_3_16),YES)
|
||||
# true for 3.16 only.
|
||||
|
||||
BASE_3_14 = NO
|
||||
BASE_3_15 = YES
|
||||
BASE_3_15 = NO
|
||||
BASE_3_16 = YES
|
||||
|
||||
# EPICS_VERSION must be a number >0 and <256
|
||||
EPICS_VERSION = 3
|
||||
|
||||
# EPICS_REVISION must be a number >=0 and <256
|
||||
EPICS_REVISION = 15
|
||||
EPICS_REVISION = 16
|
||||
|
||||
# EPICS_MODIFICATION must be a number >=0 and <256
|
||||
EPICS_MODIFICATION = 3
|
||||
EPICS_MODIFICATION = 0
|
||||
|
||||
# EPICS_PATCH_LEVEL must be a number (win32 resource file requirement)
|
||||
# Not included if zero
|
||||
EPICS_PATCH_LEVEL = 0
|
||||
EPICS_PATCH_LEVEL = 1
|
||||
|
||||
# This will end in -DEV between official releases
|
||||
#EPICS_DEV_SNAPSHOT=-DEV
|
||||
|
||||
@@ -40,10 +40,6 @@ BUILD_ARCHS = $(EPICS_HOST_ARCH) $(CROSS1) $(CROSS2)
|
||||
# otherwise override this in os/CONFIG_SITE.<host_arch>.Common
|
||||
PERL = perl -CSD
|
||||
|
||||
#-------------------------------------------------------
|
||||
# dbst based database optimization default
|
||||
DB_OPT = NO
|
||||
|
||||
#-------------------------------------------------------
|
||||
# Check configure/RELEASE file for consistency
|
||||
CHECK_RELEASE_YES = checkRelease
|
||||
@@ -85,6 +81,7 @@ IOCS_APPL_TOP = $(shell $(FULLPATHNAME) $(INSTALL_LOCATION))
|
||||
# Make echo output - suppress echoing if make's '-s' flag is set
|
||||
NOP = :
|
||||
ECHO = @$(if $(findstring s,$(patsubst T_A=%,,$(MAKEFLAGS))),$(NOP),echo)
|
||||
QUIET_FLAG := $(if $(findstring s,$(MAKEFLAGS)),-q,)
|
||||
|
||||
#-------------------------------------------------------
|
||||
ifdef T_A
|
||||
@@ -334,6 +331,14 @@ COMPILE.cpp = $(CCC) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES)
|
||||
# C preprocessor command
|
||||
PREPROCESS.cpp = $(CPP) $(CPPFLAGS) $(INCLUDES) $< > $@
|
||||
|
||||
#--------------------------------------------------
|
||||
# genVersion header defaults
|
||||
|
||||
# C macro name
|
||||
GENVERSIONMACRO = VCSVERSION
|
||||
# C macro default value (empty to use date+time)
|
||||
GENVERSIONDEFAULT =
|
||||
|
||||
#--------------------------------------------------
|
||||
# Header dependency file generation
|
||||
|
||||
|
||||
@@ -57,8 +57,11 @@ EPICS_TS_NTP_INET=
|
||||
# Prompt string
|
||||
# IOCSH_HISTSIZE
|
||||
# Number of lines of command history to keep.
|
||||
# IOCSH_HISTEDIT_DISABLE
|
||||
# Prevents use of readline or equivalent if defined.
|
||||
IOCSH_PS1="epics> "
|
||||
IOCSH_HISTSIZE=50
|
||||
IOCSH_HISTEDIT_DISABLE=
|
||||
|
||||
# Log Server:
|
||||
# EPICS_IOC_LOG_INET
|
||||
|
||||
@@ -8,10 +8,12 @@
|
||||
#*************************************************************************
|
||||
#RULES.Db
|
||||
|
||||
# Set db substitutions file suffix
|
||||
# Set db substitutions and template file suffixes
|
||||
SUBST_SUFFIX ?= .substitutions
|
||||
TEMPL_SUFFIX ?= .template
|
||||
|
||||
##################################################### vpath
|
||||
#---------------------------------------------------------------
|
||||
# vpath
|
||||
|
||||
vpath %.pm $(USR_VPATH) $(SRC_DIRS) $(dir $(DBD))
|
||||
vpath %.pod $(USR_VPATH) $(SRC_DIRS) $(dir $(DBD))
|
||||
@@ -19,12 +21,13 @@ vpath %.dbd $(USR_VPATH) $(SRC_DIRS) $(dir $(DBD))
|
||||
vpath %.db $(USR_VPATH) $(SRC_DIRS) $(dir $(DB))
|
||||
vpath %.vdb $(USR_VPATH) $(SRC_DIRS) $(dir $(DB))
|
||||
vpath %$(SUBST_SUFFIX) $(USR_VPATH) $(SRC_DIRS) $(COMMON_DIR)
|
||||
vpath %.template $(USR_VPATH) $(SRC_DIRS) $(COMMON_DIR)
|
||||
vpath %$(TEMPL_SUFFIX) $(USR_VPATH) $(SRC_DIRS) $(COMMON_DIR)
|
||||
vpath bpt%.data $(USR_VPATH) $(SRC_DIRS) $(COMMON_DIR)
|
||||
vpath %.acf $(USR_VPATH) $(SRC_DIRS) $(COMMON_DIR)
|
||||
vpath %.acs $(USR_VPATH) $(SRC_DIRS) $(COMMON_DIR)
|
||||
|
||||
##################################################### dbflags dbdflags
|
||||
#---------------------------------------------------------------
|
||||
# dbflags dbdflags
|
||||
|
||||
DBD_SEARCH_DIRS = . .. $(COMMON_DIR) $(SRC_DIRS) $(INSTALL_DBD) $(RELEASE_DBD_DIRS)
|
||||
DB_SEARCH_DIRS = . .. $(COMMON_DIR) $(SRC_DIRS) $(INSTALL_DB) $(RELEASE_DB_DIRS)
|
||||
@@ -33,13 +36,15 @@ DBDFLAGS = $(USR_DBDFLAGS) $(CMD_DBDFLAGS) $(addprefix -I,$(DBD_SEARCH_DIRS))
|
||||
DBFLAGS = $($*_DBFLAGS) $(USR_DBFLAGS) $(CMD_DBFLAGS) $(addprefix -I,$(DB_SEARCH_DIRS))
|
||||
REGRDDFLAGS = $(DBDFLAGS) $($*_REGRDDFLAGS) $(USR_REGRDDFLAGS) $(CMD_REGRDDFLAGS)
|
||||
|
||||
##################################################### Targets
|
||||
#---------------------------------------------------------------
|
||||
# Targets
|
||||
|
||||
# ---------------------------------------------------
|
||||
# To allow os specific dbd files AND have the -j option work properly,
|
||||
|
||||
CROSS_TARGET_OS_TYPES = $(sort $(foreach target, \
|
||||
$(EPICS_HOST_ARCH) $(CROSS_COMPILER_TARGET_ARCHS),$(firstword $(subst -, ,$(target)))))
|
||||
CROSS_TARGET_OS_TYPES = $(sort $(foreach target, \
|
||||
$(EPICS_HOST_ARCH) $(CROSS_COMPILER_TARGET_ARCHS), \
|
||||
$(firstword $(subst -, ,$(target)))))
|
||||
DBD += $(foreach type, $(CROSS_TARGET_OS_TYPES), $(DBD_$(type)))
|
||||
|
||||
# Users add os specific dbd files to a Makefile as follows
|
||||
@@ -86,31 +91,28 @@ SOURCE_DB_bbb = $(foreach dir, $(GENERIC_SRC_DIRS), $(SOURCE_DB_aaa) )
|
||||
SOURCE_DB_aaa = $(addsuffix /$(file), $(dir) )
|
||||
|
||||
COMMONS = $(COMMON_DIR)/*.dbd $(COMMON_DIR)/*.db $(COMMON_DIR)/*.h \
|
||||
$(COMMON_DIR)/*$(SUBST_SUFFIX) $(COMMON_DIR)/*.template
|
||||
$(COMMON_DIR)/*$(SUBST_SUFFIX) $(COMMON_DIR)/*$(TEMPL_SUFFIX)
|
||||
|
||||
# Remove trailing numbers (to 99) on stem
|
||||
TEMPLATE1=$(patsubst %0,%,$(patsubst %1,%,$(patsubst %2,%,$(patsubst %3,%,$(patsubst %4,%, \
|
||||
$(patsubst %5,%,$(patsubst %6,%,$(patsubst %7,%,$(patsubst %8,%,$(patsubst %9,%, \
|
||||
$*))))))))))
|
||||
TEMPLATE2=$(patsubst %0,%,$(patsubst %1,%,$(patsubst %2,%,$(patsubst %3,%,$(patsubst %4,%, \
|
||||
$(patsubst %5,%,$(patsubst %6,%,$(patsubst %7,%,$(patsubst %8,%,$(patsubst %9,%, \
|
||||
$(TEMPLATE1)))))))))))
|
||||
TEMPLATE3=$(addsuffix .template,$(addprefix ../,$(TEMPLATE2)))
|
||||
TEMPLATE_FILENAME=$(firstword $(wildcard $($*_TEMPLATE) $(addprefix ../,$($*_TEMPLATE)) ../$*.template $(TEMPLATE3) ../template))
|
||||
|
||||
# dbst based database optimization
|
||||
ifeq '$(DB_OPT)' 'YES'
|
||||
RAW=.raw
|
||||
DBS = $(filter %.db,$(DB)) $(addsuffix $(RAW),$(filter %.db,$(DB)))
|
||||
COMMON_DBS = $(addprefix $(COMMON_DIR)/,$(DBS))
|
||||
endif
|
||||
TEMPLATE1 = $(patsubst %0,%,$(patsubst %1,%,$(patsubst %2,%,$(patsubst %3,%, \
|
||||
$(patsubst %4,%,$(patsubst %5,%,$(patsubst %6,%,$(patsubst %7,%, \
|
||||
$(patsubst %8,%,$(patsubst %9,%,$*))))))))))
|
||||
TEMPLATE2 = $(patsubst %0,%,$(patsubst %1,%,$(patsubst %2,%,$(patsubst %3,%, \
|
||||
$(patsubst %4,%,$(patsubst %5,%,$(patsubst %6,%,$(patsubst %7,%, \
|
||||
$(patsubst %8,%,$(patsubst %9,%,$(TEMPLATE1)))))))))))
|
||||
TEMPLATE3 = $(addsuffix $(TEMPL_SUFFIX),$(addprefix ../,$(TEMPLATE2)))
|
||||
TEMPLATE_FILENAME = $(firstword $(wildcard $($*_TEMPLATE) \
|
||||
$(addprefix ../,$($*_TEMPLATE)) ../$*$(TEMPL_SUFFIX) $(TEMPLATE3) \
|
||||
../template))
|
||||
|
||||
INSTALL_DB_INSTALLS = $(addprefix $(INSTALL_DB)/,$(notdir $(DB_INSTALLS)))
|
||||
INSTALL_DBD_INSTALLS = $(addprefix $(INSTALL_DBD)/,$(notdir $(DBD_INSTALLS)))
|
||||
|
||||
COMMONDEP_TARGET = $(COMMON_DIR)/$(basename $@)
|
||||
|
||||
##################################################### acf files
|
||||
#---------------------------------------------------------------
|
||||
# acf files
|
||||
|
||||
# An access security configuration file, *.acf, can be created from
|
||||
# an *.acs file (has format of acf file plus #include "filename" lines)
|
||||
|
||||
@@ -123,7 +125,8 @@ ACF_INCLUDES = -I. $(TARGET_INCLUDES) $(USR_INCLUDES)\
|
||||
ACFDEPENDS_CMD = $(MKMF) -m $@ $(ACF_INCLUDES) $(COMMONDEP_TARGET) $<
|
||||
ACF_CMD = $(CPP) $(ACF_CPPFLAGS) $(ACF_INCLUDES) $< > $@
|
||||
|
||||
##################################################### dependancies
|
||||
#---------------------------------------------------------------
|
||||
# dependencies
|
||||
|
||||
HINC += $(addsuffix .h,$(DBDINC_NAME))
|
||||
COMMON_DBDINC += $(addprefix $(COMMON_DIR)/,$(HINC))
|
||||
@@ -133,12 +136,12 @@ DBDDEPENDS_FILES += $(addsuffix $(DEP),$(HINC) \
|
||||
$(patsubst $(COMMON_DIR)/%,%, \
|
||||
$(filter-out $(COMMON_DIR)/bpt%.dbd,$(COMMON_DBDS))))
|
||||
|
||||
#####################################################
|
||||
#---------------------------------------------------------------
|
||||
|
||||
ifndef T_A
|
||||
|
||||
DEP = .d
|
||||
TEMPLATE3+=$(addsuffix .template, $(TEMPLATE2))
|
||||
TEMPLATE3 += $(addsuffix $(TEMPL_SUFFIX), $(TEMPLATE2))
|
||||
|
||||
COMMON_DIR = .
|
||||
INSTALL_DBDS =
|
||||
@@ -153,18 +156,13 @@ ACTIONS += install
|
||||
ACTIONS += buildInstall
|
||||
ACTIONS += runtests tapfiles
|
||||
|
||||
actionArchTargets = $(foreach x, $(ACTIONS),\ $(foreach arch,$(BUILD_ARCHS), $(x)$(DIVIDER)$(arch)))
|
||||
actionArchTargets = $(foreach action, $(ACTIONS), \
|
||||
$(foreach arch, $(BUILD_ARCHS), $(action)$(DIVIDER)$(arch)))
|
||||
cleanArchTargets = $(foreach arch, $(BUILD_ARCHS), clean$(DIVIDER)$(arch))
|
||||
|
||||
cleanArchTargets = $(foreach arch,$(BUILD_ARCHS), clean$(DIVIDER)$(arch))
|
||||
-include $(TOP)/configure/CONFIG_APP_INCLUDE
|
||||
|
||||
all: install
|
||||
ifeq ($(EPICS_HOST_ARCH),$T_A)
|
||||
host: install
|
||||
else
|
||||
# Do nothing
|
||||
host:
|
||||
endif
|
||||
|
||||
install: buildInstall
|
||||
|
||||
@@ -172,20 +170,30 @@ buildInstall : build
|
||||
|
||||
rebuild: clean install
|
||||
|
||||
.PHONY: all host $(ACTIONS)
|
||||
.PHONY: all $(ACTIONS)
|
||||
|
||||
$(actionArchTargets) $(BUILD_ARCHS): install
|
||||
$(cleanArchTargets): clean
|
||||
|
||||
.PHONY: $(BUILD_ARCHS) $(actionArchTargets) $(cleanArchTargets)
|
||||
|
||||
endif # T_A defined
|
||||
else
|
||||
# T_A is defined
|
||||
ifeq ($(EPICS_HOST_ARCH),$(T_A))
|
||||
host: install
|
||||
else
|
||||
host:
|
||||
endif
|
||||
|
||||
.PHONY: host
|
||||
endif # T_A
|
||||
|
||||
ifneq (,$(strip $(DBDDEPENDS_FILES)))
|
||||
-include $(DBDDEPENDS_FILES)
|
||||
endif
|
||||
|
||||
##################################################### build dependancies, clean rule
|
||||
#---------------------------------------------------------------
|
||||
# build dependancies, clean rule
|
||||
|
||||
inc : $(COMMON_INC) $(INSTALL_INC)
|
||||
|
||||
@@ -205,7 +213,8 @@ db_clean :
|
||||
|
||||
realclean: clean
|
||||
|
||||
##################################################### Dependency files
|
||||
#---------------------------------------------------------------
|
||||
# Dependency files
|
||||
|
||||
%Record.h$(DEP): $(COMMON_DIR)/%Record.dbd
|
||||
@$(RM) $@
|
||||
@@ -256,19 +265,19 @@ menu%.h$(DEP): ../menu%.dbd
|
||||
@$(DBEXPAND) -D $(DBDFLAGS) -o $(COMMONDEP_TARGET) $($*_DBD) > $@
|
||||
@echo "$(COMMONDEP_TARGET): ../Makefile" >> $@
|
||||
|
||||
%.db$(RAW)$(DEP): %$(SUBST_SUFFIX)
|
||||
%.db$(DEP): %$(SUBST_SUFFIX)
|
||||
@$(RM) $@
|
||||
$(MSI3_15) -D $(DBFLAGS) -o $(COMMONDEP_TARGET) -S$< $(TEMPLATE_FILENAME) > $@
|
||||
|
||||
%.db$(RAW)$(DEP): ../%$(SUBST_SUFFIX)
|
||||
%.db$(DEP): ../%$(SUBST_SUFFIX)
|
||||
@$(RM) $@
|
||||
$(MSI3_15) -D $(DBFLAGS) -o $(COMMONDEP_TARGET) -S$< $(TEMPLATE_FILENAME) > $@
|
||||
|
||||
%.db$(RAW)$(DEP): %.template
|
||||
%.db$(DEP): %$(TEMPL_SUFFIX)
|
||||
@$(RM) $@
|
||||
$(MSI3_15) -D $(DBFLAGS) -o $(COMMONDEP_TARGET) $< > $@
|
||||
|
||||
%.db$(RAW)$(DEP): ../%.template
|
||||
%.db$(DEP): ../%$(TEMPL_SUFFIX)
|
||||
@$(RM) $@
|
||||
$(MSI3_15) -D $(DBFLAGS) -o $(COMMONDEP_TARGET) $< > $@
|
||||
|
||||
@@ -282,14 +291,8 @@ menu%.h$(DEP): ../menu%.dbd
|
||||
|
||||
.PRECIOUS: %$(DEP)
|
||||
|
||||
##################################################### CapFast filter
|
||||
|
||||
$(COMMON_DIR)/%.edf: ../%.sch $(DEPSCHS)
|
||||
@$(RM) $@
|
||||
@if [ ! -f cad.rc -a -r ../cad.rc ] ; then ln -s ../cad.rc ; fi
|
||||
$(SCH2EDIF) $(SCH2EDIF_SYSFLAGS) $(SCH2EDIF_FLAGS) -o $@ $<
|
||||
|
||||
##################################################### Substitution files
|
||||
#---------------------------------------------------------------
|
||||
# Substitution files
|
||||
|
||||
# WARNING: CREATESUBSTITUTIONS script needs output dir on command line
|
||||
|
||||
@@ -310,25 +313,21 @@ $(INSTALL_DB)/%$(SUBST_SUFFIX): ../%$(SUBST_SUFFIX)
|
||||
|
||||
.PRECIOUS: $(COMMON_DIR)/%$(SUBST_SUFFIX)
|
||||
|
||||
##################################################### Template files
|
||||
#---------------------------------------------------------------
|
||||
# Template files
|
||||
|
||||
$(COMMON_DIR)/%.template: $(COMMON_DIR)/%.edf
|
||||
@$(RM) $@
|
||||
$(E2DB) $(E2DB_SYSFLAGS) $(E2DB_FLAGS) -n $@.VAR $<
|
||||
@$(REPLACEVAR) < $@.VAR > $@
|
||||
@$(RM) $@.VAR
|
||||
|
||||
$(INSTALL_DB)/%.template: %.template
|
||||
$(INSTALL_DB)/%$(TEMPL_SUFFIX): %$(TEMPL_SUFFIX)
|
||||
$(ECHO) "Installing template file $@"
|
||||
@$(INSTALL) -d -m $(INSTALL_PERMISSIONS) $< $(@D)
|
||||
|
||||
$(INSTALL_DB)/%.template: ../%.template
|
||||
$(INSTALL_DB)/%$(TEMPL_SUFFIX): ../%$(TEMPL_SUFFIX)
|
||||
$(ECHO) "Installing template file $@"
|
||||
@$(INSTALL) -d -m $(INSTALL_PERMISSIONS) $< $(@D)
|
||||
|
||||
.PRECIOUS: $(COMMON_DIR)/%.template
|
||||
.PRECIOUS: $(COMMON_DIR)/%$(TEMPL_SUFFIX)
|
||||
|
||||
##################################################### INC files
|
||||
#---------------------------------------------------------------
|
||||
# INC files
|
||||
|
||||
$(COMMON_DIR)/%Record.h: $(COMMON_DIR)/%Record.dbd
|
||||
@$(RM) $(notdir $@)
|
||||
@@ -362,7 +361,8 @@ $(COMMON_DIR)/menu%.h: ../menu%.dbd
|
||||
|
||||
.PRECIOUS: $(COMMON_DIR)/%.h
|
||||
|
||||
##################################################### DBD files
|
||||
#---------------------------------------------------------------
|
||||
# DBD files
|
||||
|
||||
$(COMMON_DIR)/bpt%.dbd: bpt%.data
|
||||
@$(RM) $(notdir $@)
|
||||
@@ -425,7 +425,8 @@ $(foreach file, $(DBD_INSTALLS), $(eval $(call DBD_INSTALLS_template, $(file))))
|
||||
|
||||
.PRECIOUS: $(COMMON_DBDS) $(COMMON_DIR)/%.dbd
|
||||
|
||||
##################################################### HTML files
|
||||
#---------------------------------------------------------------
|
||||
# HTML files
|
||||
|
||||
$(COMMON_DIR)/%.html: %.dbd.pod $(TOOLS)/dbdToHtml.pl
|
||||
@$(RM) $(notdir $@)
|
||||
@@ -444,37 +445,44 @@ $(COMMON_DIR)/%.html: %.pm $(TOOLS)/podToHtml.pl
|
||||
|
||||
$(COMMON_DIR)/%.html: ../%.pm $(TOOLS)/podToHtml.pl
|
||||
@$(RM) $(notdir $@)
|
||||
$(PERL) $(TOOLS)/podToHtml.pl -o $(notdir $@) $<
|
||||
$(PERL) $(TOOLS)/podToHtml.pl -s -o $(notdir $@) $<
|
||||
@$(MKDIR) $(dir $@)
|
||||
@$(MV) $(notdir $@) $@
|
||||
|
||||
$(COMMON_DIR)/%.html: ../%.pl $(TOOLS)/podToHtml.pl
|
||||
@$(RM) $(notdir $@)
|
||||
$(PERL) $(TOOLS)/podToHtml.pl -s -o $(notdir $@) $<
|
||||
@$(MV) $(notdir $@) $@
|
||||
|
||||
.PRECIOUS: $(COMMON_DIR)/%.html %.html
|
||||
|
||||
##################################################### DB files
|
||||
#---------------------------------------------------------------
|
||||
# DB files
|
||||
|
||||
$(COMMON_DIR)/%.db$(RAW): $(COMMON_DIR)/%.edf
|
||||
$(COMMON_DIR)/%.db: $(COMMON_DIR)/%.edf
|
||||
$(E2DB) $(E2DB_SYSFLAGS) $(E2DB_FLAGS) -n $*.VAR $<
|
||||
@$(REPLACEVAR) < $*.VAR > $@
|
||||
@$(RM) $*.VAR
|
||||
|
||||
$(COMMON_DIR)/%.db$(RAW): %$(SUBST_SUFFIX)
|
||||
$(COMMON_DIR)/%.db: %$(SUBST_SUFFIX)
|
||||
$(ECHO) "Inflating database from $< $(TEMPLATE_FILENAME)"
|
||||
@$(RM) $(notdir $@)
|
||||
$(MSI3_15) $(DBFLAGS) -o $(notdir $@) -S$< $(TEMPLATE_FILENAME)
|
||||
@$(MV) $(notdir $@) $@
|
||||
|
||||
$(COMMON_DIR)/%.db$(RAW): ../%$(SUBST_SUFFIX)
|
||||
$(COMMON_DIR)/%.db: ../%$(SUBST_SUFFIX)
|
||||
$(ECHO) "Inflating database from $< $(TEMPLATE_FILENAME)"
|
||||
@$(RM) $(notdir $@)
|
||||
$(MSI3_15) $(DBFLAGS) -o $(notdir $@) -S$< $(TEMPLATE_FILENAME)
|
||||
@$(MV) $(notdir $@) $@
|
||||
|
||||
$(COMMON_DIR)/%.db$(RAW): %.template
|
||||
$(COMMON_DIR)/%.db: %$(TEMPL_SUFFIX)
|
||||
$(ECHO) "Inflating database from $<"
|
||||
@$(RM) $(notdir $@)
|
||||
$(MSI3_15) $(DBFLAGS) -o $(notdir $@) $<
|
||||
@$(MV) $(notdir $@) $@
|
||||
|
||||
$(COMMON_DIR)/%.db$(RAW): ../%.template
|
||||
$(COMMON_DIR)/%.db: ../%$(TEMPL_SUFFIX)
|
||||
$(ECHO) "Inflating database from $<"
|
||||
@$(RM) $(notdir $@)
|
||||
$(MSI3_15) $(DBFLAGS) -o $(notdir $@) $<
|
||||
@@ -492,22 +500,6 @@ $(COMMON_DIR)/%.acf: ../%.acs
|
||||
|
||||
.PRECIOUS: $(COMMON_DIR)/%.acf
|
||||
|
||||
# dbst based database optimization
|
||||
ifeq '$(DB_OPT)' 'YES'
|
||||
|
||||
$(COMMON_DIR)/%.db$(RAW): ../%.db
|
||||
@$(RM) $@
|
||||
$(CP) $< $@
|
||||
|
||||
$(COMMON_DIR)/%.db: $(COMMON_DIR)/%.db$(RAW)
|
||||
$(ECHO) "Optimizing database $@"
|
||||
@$(RM) $@
|
||||
$(DBST) . $< -d > $@
|
||||
|
||||
.PRECIOUS: $(COMMON_DIR)/%.db
|
||||
.PRECIOUS: $(DB:%=$(COMMON_DIR)/%$(RAW))
|
||||
else
|
||||
|
||||
$(INSTALL_DB)/%: %
|
||||
$(ECHO) "Installing $@"
|
||||
@$(INSTALL) -d -m $(INSTALL_PERMISSIONS) $< $(@D)
|
||||
@@ -515,7 +507,6 @@ $(INSTALL_DB)/%: %
|
||||
$(INSTALL_DB)/%: ../%
|
||||
$(ECHO) "Installing $@"
|
||||
@$(INSTALL) -d -m $(INSTALL_PERMISSIONS) $< $(@D)
|
||||
endif
|
||||
|
||||
$(INSTALL_DB)/%.db: $(COMMON_DIR)/%.db
|
||||
$(ECHO) "Installing created db file $@"
|
||||
@@ -531,8 +522,8 @@ $(foreach file, $(DB_INSTALLS), $(eval $(call DB_INSTALLS_template, $(file))))
|
||||
.PRECIOUS: $(COMMON_DIR)/%.edf
|
||||
.PRECIOUS: $(COMMON_DBS)
|
||||
|
||||
##################################################### register record,device,driver support
|
||||
|
||||
#---------------------------------------------------------------
|
||||
# register record,device,driver support
|
||||
|
||||
%_registerRecordDeviceDriver.cpp: $(COMMON_DIR)/%.dbd
|
||||
@$(RM) $@
|
||||
@@ -548,4 +539,3 @@ $(foreach file, $(DB_INSTALLS), $(eval $(call DB_INSTALLS_template, $(file))))
|
||||
|
||||
.PRECIOUS: %_registerRecordDeviceDriver.cpp
|
||||
|
||||
##################################################### END OF FILE
|
||||
|
||||
@@ -168,7 +168,7 @@ ifdef RES
|
||||
endif
|
||||
|
||||
$(DIRECTORY_TARGETS) :
|
||||
$(MKDIR) -p $@
|
||||
$(MKDIR) $@
|
||||
|
||||
$(PRODNAME): $(INSTALL_LIB_INSTALLS)
|
||||
|
||||
@@ -346,6 +346,14 @@ tapfiles: $(TESTSCRIPTS) $(TAPFILES)
|
||||
@$(RM) $@
|
||||
$(PERL) $(TOOLS)/makeTestfile.pl $@ $<
|
||||
|
||||
#---------------------------------------------------------------
|
||||
# Generate header with version number from VCS
|
||||
|
||||
ifneq ($(GENVERSION),)
|
||||
$(COMMON_DIR)/$(GENVERSION): FORCE
|
||||
$(GENVERSIONHEADER) -t $(TOP) -N $(GENVERSIONMACRO) -V "$(GENVERSIONDEFAULT)" $@
|
||||
endif
|
||||
|
||||
#---------------------------------------------------------------
|
||||
# Install rules for BIN_INSTALLS and LIB_INSTALLS
|
||||
|
||||
@@ -479,7 +487,7 @@ $(INSTALL_TEMPLATES_SUBDIR)/%: %
|
||||
.PRECIOUS: $(COMMON_INC)
|
||||
|
||||
.PHONY: all host inc build install clean rebuild buildInstall build_clean
|
||||
.PHONY: runtests tapfiles checkRelease warnRelease noCheckRelease
|
||||
.PHONY: runtests tapfiles checkRelease warnRelease noCheckRelease FORCE
|
||||
|
||||
endif # BASE_RULES_BUILD
|
||||
# EOF RULES_BUILD
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
# CONFIG.Common.vxWorksCommon
|
||||
#
|
||||
# $Revision-Id$
|
||||
# This file is maintained by the build community.
|
||||
#
|
||||
# Definitions for vxWorks target archs
|
||||
# Sites may override these definitions in CONFIG_SITE.Common.vxWorksCommon
|
||||
# Override these definitions in CONFIG_SITE.Common.vxWorksCommon
|
||||
# or CONFIG_SITE.<host>.vxWorksCommon
|
||||
#-------------------------------------------------------
|
||||
|
||||
@@ -66,8 +65,6 @@ VXWORKS_MAJOR_VERSION = $(basename $(basename $(VXWORKS_VERSION)))
|
||||
|
||||
# These are needed for vxWorks 6.x; the GNU toolset version number
|
||||
# is in the path to the compiler tools:
|
||||
VX_GNU_VERSION_5.4 = 2.95
|
||||
VX_GNU_VERSION_5.5 = 2.96
|
||||
VX_GNU_VERSION_6.0 = 3.3.2
|
||||
VX_GNU_VERSION_6.1 = 3.3.2
|
||||
VX_GNU_VERSION_6.2 = 3.3.2
|
||||
@@ -83,30 +80,21 @@ VX_GNU_VERSION = $(VX_GNU_VERSION_$(VXWORKS_VERSION))
|
||||
VX_GNU_MAJOR_VERSION = $(basename $(basename $(VX_GNU_VERSION)))
|
||||
|
||||
#--------------------------------------------------
|
||||
# Fix WIND_BASE for vxWorks 6.x on linux
|
||||
# NB: We know the value of WIND_HOST_TYPE here, but not VXWORKS_VERSION
|
||||
# Fix old Linux WIND_HOST_TYPE
|
||||
ifeq ($(WIND_HOST_TYPE),x86-linux)
|
||||
WIND_HOST_TYPE_5 = x86-linux
|
||||
WIND_HOST_TYPE_6 = x86-linux2
|
||||
WIND_HOST_TYPE = $(WIND_HOST_TYPE_$(VXWORKS_MAJOR_VERSION))
|
||||
WIND_HOST_TYPE = x86-linux2
|
||||
endif
|
||||
|
||||
#--------------------------------------------------
|
||||
# vxWorks directory definitions
|
||||
VX_DIR_5 = $(WIND_BASE)
|
||||
VX_DIR_6 = $(WIND_BASE)/vxworks-$(VXWORKS_VERSION)
|
||||
VX_DIR = $(VX_DIR_$(VXWORKS_MAJOR_VERSION))
|
||||
VX_DIR = $(WIND_BASE)/vxworks-$(VXWORKS_VERSION)
|
||||
|
||||
VX_INCLUDE_DIRS_5 = $(VX_DIR)/target/h
|
||||
VX_INCLUDE_DIRS_6 = $(VX_DIR)/target/h $(VX_DIR)/target/h/wrn/coreip
|
||||
GNU_TARGET_INCLUDE_DIR = $(VX_INCLUDE_DIRS_$(VXWORKS_MAJOR_VERSION))
|
||||
GNU_TARGET_INCLUDE_DIR = $(VX_DIR)/target/h $(VX_DIR)/target/h/wrn/coreip
|
||||
|
||||
#--------------------------------------------------
|
||||
# vxWorks GNU directories
|
||||
|
||||
GNU_DIR_5 = $(WIND_BASE)/host/$(WIND_HOST_TYPE)
|
||||
GNU_DIR_6 = $(WIND_BASE)/gnu/$(VX_GNU_VERSION)-vxworks-$(VXWORKS_VERSION)/$(WIND_HOST_TYPE)
|
||||
GNU_DIR = $(GNU_DIR_$(VXWORKS_MAJOR_VERSION))
|
||||
GNU_DIR = $(WIND_BASE)/gnu/$(VX_GNU_VERSION)-vxworks-$(VXWORKS_VERSION)/$(WIND_HOST_TYPE)
|
||||
|
||||
#--------------------------------------------------
|
||||
# Wind River moved nm out of GNU_BIN in some versions
|
||||
@@ -126,9 +114,7 @@ NM = $(NM_DIR)/$(CMPLR_PREFIX)nm$(CMPLR_SUFFIX)$(HOSTEXE)
|
||||
|
||||
#--------------------------------------------------
|
||||
# A linker script is essential for munching from vxWorks 6.6 onwards
|
||||
# (i.e. with versions that use gcc 4.1.2 or later). It can be used
|
||||
# with any vxWorks 5 or 6 version, but apparently should not be used
|
||||
# when compiling for 68K (which isn't supported in vxWorks 6 anyway)
|
||||
# (i.e. with versions that use gcc 4.1.2 or later).
|
||||
MUNCH_LDFLAGS_6 = -T $(VX_DIR)/target/h/tool/gnu/ldscripts/link.OUT
|
||||
MUNCH_LDFLAGS = $(MUNCH_LDFLAGS_$(VXWORKS_MAJOR_VERSION))
|
||||
|
||||
@@ -143,11 +129,10 @@ export TOOL_FAMILY = GNU
|
||||
OP_SYS_CPPFLAGS += -DvxWorks=vxWorks
|
||||
OP_SYS_CFLAGS += -fno-builtin
|
||||
|
||||
# Fix for vxWorks 5 headers that use macros defined in vxWorks.h but
|
||||
# Fix for vxWorks headers that use macros defined in vxWorks.h but
|
||||
# which don't actually include vxWorks.h themselves, for example the
|
||||
# target/h/sys/stat.h file which uses ULONG. This also stops dbDefs.h
|
||||
# from defining the OFFSET macro, which generates lots of warnings in
|
||||
# both vxWorks 5 and 6.
|
||||
# from defining the OFFSET macro, which generates lots of warnings.
|
||||
OP_SYS_INCLUDE_CPPFLAGS += -include $(VX_DIR)/target/h/vxWorks.h
|
||||
|
||||
#--------------------------------------------------
|
||||
@@ -160,7 +145,6 @@ OPT_CXXFLAGS_YES = -O2
|
||||
CODE_CFLAGS =
|
||||
#
|
||||
# For vxWorks versions before 6.3 we need this g++ compiler flag
|
||||
CODE_CXXFLAGS_5 = -fno-implicit-templates
|
||||
CODE_CXXFLAGS_6.0 = -fno-implicit-templates
|
||||
CODE_CXXFLAGS_6.1 = -fno-implicit-templates
|
||||
CODE_CXXFLAGS_6.2 = -fno-implicit-templates
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
CP = cp
|
||||
MV = mv
|
||||
RM = rm -f
|
||||
MKDIR = mkdir
|
||||
MKDIR = mkdir -p
|
||||
RMDIR = rm -rf
|
||||
CAT = cat
|
||||
|
||||
|
||||
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
|
||||
@@ -10,4 +10,4 @@
|
||||
#Include definitions common to unix hosts
|
||||
include $(CONFIG)/os/CONFIG.UnixCommon.Common
|
||||
|
||||
WIND_HOST_TYPE = x86-linux
|
||||
WIND_HOST_TYPE = x86-linux2
|
||||
|
||||
@@ -10,4 +10,4 @@
|
||||
#Include definitions common to unix hosts
|
||||
include $(CONFIG)/os/CONFIG.UnixCommon.Common
|
||||
|
||||
WIND_HOST_TYPE = x86-linux
|
||||
WIND_HOST_TYPE = x86-linux2
|
||||
|
||||
@@ -10,4 +10,4 @@
|
||||
#Include definitions common to unix hosts
|
||||
include $(CONFIG)/os/CONFIG.UnixCommon.Common
|
||||
|
||||
WIND_HOST_TYPE = x86-linux
|
||||
WIND_HOST_TYPE = x86-linux2
|
||||
|
||||
@@ -10,4 +10,4 @@
|
||||
#Include definitions common to unix hosts
|
||||
include $(CONFIG)/os/CONFIG.UnixCommon.Common
|
||||
|
||||
WIND_HOST_TYPE = x86-linux
|
||||
WIND_HOST_TYPE = x86-linux2
|
||||
|
||||
@@ -10,4 +10,4 @@
|
||||
#Include definitions common to unix hosts
|
||||
include $(CONFIG)/os/CONFIG.UnixCommon.Common
|
||||
|
||||
WIND_HOST_TYPE = x86-linux
|
||||
WIND_HOST_TYPE = x86-linux2
|
||||
|
||||
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
|
||||
@@ -10,4 +10,4 @@
|
||||
#Include definitions common to unix hosts
|
||||
include $(CONFIG)/os/CONFIG.UnixCommon.Common
|
||||
|
||||
WIND_HOST_TYPE = x86-linux
|
||||
WIND_HOST_TYPE = x86-linux2
|
||||
|
||||
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,25 +1,17 @@
|
||||
# CONFIG_SITE.Common.vxWorksCommon
|
||||
#
|
||||
# Site specific definitions for vxWorks target builds.
|
||||
# Only the local epics system manager should modify this file
|
||||
|
||||
# Compiler options can vary with the vxWorks version number, so we
|
||||
# need to know that. However don't include any third-level digits
|
||||
# (e.g. the .2 in 5.5.2) because we don't need them.
|
||||
# need to know that. Do not include any third-level digits.
|
||||
|
||||
# Note: vxWorks 5.4.x (Tornado 2.0.x) is not supported
|
||||
# Note: vxWorks 5.4.x and 5.5.x (Tornado 2.x) are not supported.
|
||||
# VxWorks 6.0 through 6.5 use older, untested versions of GCC.
|
||||
|
||||
VXWORKS_VERSION = 5.5
|
||||
#VXWORKS_VERSION = 6.0
|
||||
#VXWORKS_VERSION = 6.1
|
||||
#VXWORKS_VERSION = 6.2
|
||||
#VXWORKS_VERSION = 6.3
|
||||
#VXWORKS_VERSION = 6.4
|
||||
#VXWORKS_VERSION = 6.5
|
||||
#VXWORKS_VERSION = 6.6
|
||||
#VXWORKS_VERSION = 6.7
|
||||
#VXWORKS_VERSION = 6.8
|
||||
#VXWORKS_VERSION = 6.9
|
||||
VXWORKS_VERSION = 6.9
|
||||
|
||||
|
||||
# Sites may override the following path for a particular host
|
||||
@@ -27,10 +19,8 @@ VXWORKS_VERSION = 5.5
|
||||
# CONFIG_SITE.$(EPICS_HOST_ARCH).vxWorksCommon file.
|
||||
|
||||
# WIND_BASE is where you installed the Wind River software.
|
||||
# Under vxWorks 6.x this is *not* the same as the old VX_DIR setting
|
||||
|
||||
WIND_BASE = /usr/local/vw/tornado22-$(ARCH_CLASS)
|
||||
#WIND_BASE = /usr/local/vw/vxWorks-$(VXWORKS_VERSION)
|
||||
WIND_BASE = /usr/local/vw/vxWorks-$(VXWORKS_VERSION)
|
||||
#WIND_BASE = /ade/vxWorks/$(VXWORKS_VERSION)
|
||||
|
||||
|
||||
@@ -39,9 +29,9 @@ WIND_BASE = /usr/local/vw/tornado22-$(ARCH_CLASS)
|
||||
#WORKBENCH_VERSION = 2.6
|
||||
#WORKBENCH_VERSION = 3.0
|
||||
#WORKBENCH_VERSION = 3.2
|
||||
#WORKBENCH_VERSION = 3.3
|
||||
WORKBENCH_VERSION = 3.3
|
||||
|
||||
|
||||
# Utilities Version number, required from vxWorks 6.8 and later
|
||||
|
||||
#UTILITIES_VERSION = 1.0
|
||||
UTILITIES_VERSION = 1.0
|
||||
|
||||
@@ -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,40 +4,36 @@
|
||||
<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.3</title>
|
||||
<title>Known Problems in R3.16.0.1</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h1 style="text-align: center">EPICS Base R3.15.3: Known Problems</h1>
|
||||
<h1 style="text-align: center">EPICS Base R3.16.0.1: Known Problems</h1>
|
||||
|
||||
<p>Any patch files linked below should be applied at the root of the
|
||||
base-3.15.3 tree. Download them, then use the GNU Patch program as
|
||||
base-3.16.0.1 tree. Download them, then use the GNU Patch program as
|
||||
follows:</p>
|
||||
|
||||
<blockquote><pre>% <b>cd <i>/path/to/</i>base-3.15.3</b>
|
||||
<blockquote><pre>% <b>cd <i>/path/to/</i>base-3.16.0.1</b>
|
||||
% <b>patch -p0 < <i>/path/to/</i>file.patch</b></pre></blockquote>
|
||||
|
||||
<p>The following significant problems have been reported with this
|
||||
version of EPICS Base:</p>
|
||||
<p>The following problems were known by the developers at the time of this
|
||||
release:</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>
|
||||
<!-- Items added after release should be formatted thusly:
|
||||
<li>YYYY-MM-DD: Description of problem
|
||||
...</li>
|
||||
-->
|
||||
|
||||
<li>Parallel builds ("make -j") on native Windows are not working properly.
|
||||
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>IOCs running on some versions of Cygwin may display warnings at iocInit
|
||||
about duplicate EPICS CA Address list entries. These warnings might be due
|
||||
to a bug in Cygwin; they are benign and can be ignored.</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
|
||||
model that Windows uses for its 64-bit ABI.</li>
|
||||
The code in <tt>src/legacy/gdd</tt> is incompatible with the LLP64 model
|
||||
that Windows uses for its 64-bit ABI.</li>
|
||||
|
||||
</ul>
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
Installation Instructions
|
||||
|
||||
EPICS Base Release 3.15.3
|
||||
EPICS Base Release 3.16.0
|
||||
|
||||
--------------------------------------------------------------------------
|
||||
|
||||
@@ -85,17 +85,17 @@
|
||||
as processes on the host platform.
|
||||
|
||||
vxWorks
|
||||
You must have vxWorks 5.5.x or 6.x installed if any of your target
|
||||
systems are vxWorks systems; the C++ compiler for vxWorks 5.4 is now too
|
||||
old to support. The vxWorks installation provides the cross-compiler and
|
||||
header files needed to build for these targets. The absolute path to and
|
||||
the version number of the vxWorks installation must be set in the
|
||||
You must have vxWorks 6 installed if any of your target systems are
|
||||
vxWorks systems; the C++ compilers for vxWorks 5.x are now too old to
|
||||
support. The vxWorks installation provides the cross-compiler and header
|
||||
files needed to build for these targets. The absolute path to and the
|
||||
version number of the vxWorks installation must be set in the
|
||||
base/configure/os/CONFIG_SITE.Common.vxWorksCommon file or in one of its
|
||||
target-specific overrides.
|
||||
|
||||
Consult the vxWorks 5.x or vxWorks 6.x EPICS web pages about and the
|
||||
vxWorks documentation for information about configuring your vxWorks
|
||||
operating system for use with EPICS.
|
||||
Consult the vxWorks 6.x EPICS web pages and the vxWorks documentation
|
||||
for information about configuring your vxWorks operating system for use
|
||||
with EPICS.
|
||||
|
||||
RTEMS
|
||||
For RTEMS targets, you need RTEMS core and toolset version 4.9.2 or
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
<BODY>
|
||||
<CENTER>
|
||||
<H1>Installation Instructions</H1>
|
||||
<H2>EPICS Base Release 3.15.3</H2><BR>
|
||||
<H2>EPICS Base Release 3.16.0</H2><BR>
|
||||
</CENTER>
|
||||
<HR>
|
||||
<H3> Table of Contents</H3>
|
||||
@@ -90,17 +90,16 @@
|
||||
as processes on the host platform.</P>
|
||||
|
||||
<P><B>vxWorks</B><BR>
|
||||
You must have vxWorks 5.5.x or 6.x installed if any of your target systems are
|
||||
vxWorks systems; the C++ compiler for vxWorks 5.4 is now too old to support.
|
||||
The vxWorks installation provides the cross-compiler and header files needed to
|
||||
You must have vxWorks 6 installed if any of your target systems are vxWorks
|
||||
systems; the C++ compilers for vxWorks 5.x are now too old to support. The
|
||||
vxWorks installation provides the cross-compiler and header files needed to
|
||||
build for these targets. The absolute path to and the version number of the
|
||||
vxWorks installation must be set in the
|
||||
base/configure/os/CONFIG_SITE.Common.vxWorksCommon file or in one of its
|
||||
target-specific overrides.</P>
|
||||
|
||||
<P>Consult the <a href="http://www.aps.anl.gov/epics/base/tornado.php">vxWorks
|
||||
5.x</a> or <a href="http://www.aps.anl.gov/epics/base/vxWorks6.php">vxWorks
|
||||
6.x</a> EPICS web pages about and the vxWorks documentation for information
|
||||
<P>Consult the <a href="http://www.aps.anl.gov/epics/base/vxWorks6.php">vxWorks
|
||||
6.x</a> EPICS web pages and the vxWorks documentation for information
|
||||
about configuring your vxWorks operating system for use with EPICS.</P>
|
||||
|
||||
<P><B>RTEMS</B><BR>
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -39,7 +39,7 @@
|
||||
</blockquote>
|
||||
|
||||
<p>This document describes the procedures and provides a checklist of tasks
|
||||
that should be performed when creating new releases of EPICS Base.</p>
|
||||
that should be performed when creating production releases of EPICS Base.</p>
|
||||
|
||||
<h3>The Release Process</h3>
|
||||
|
||||
@@ -141,17 +141,17 @@ relevent roles unless the Release Manager designates otherwise:</p>
|
||||
<td>Tag the module in Bazaar, using these tag conventions:
|
||||
<ul>
|
||||
<li>
|
||||
<tt>R3.15.3-pre<i>n</i></tt>
|
||||
<tt>R3.16.1-pre<i>n</i></tt>
|
||||
— pre-release tag
|
||||
</li>
|
||||
<li>
|
||||
<tt>R3.15.3-rc<i>n</i></tt>
|
||||
<tt>R3.16.1-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.3-rc<i>n</i>
|
||||
cd ~/base/mirror-3.16<br />
|
||||
bzr tag R3.16.1-rc<i>n</i>
|
||||
</tt></blockquote>
|
||||
</td>
|
||||
</tr>
|
||||
@@ -163,10 +163,10 @@ relevent roles unless the Release Manager designates otherwise:</p>
|
||||
<blockquote><tt>
|
||||
cd ~/base<br />
|
||||
bzr export --keywords=publish
|
||||
--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
|
||||
--root=base-3.16.1-rc<i>n</i>
|
||||
-r tag:R3.16.1-rc<i>n</i>
|
||||
base-3.16.1-rc<i>n</i>.tar.gz
|
||||
mirror-3.16
|
||||
</tt></blockquote>
|
||||
This requires that the Bazaar keywords plugin is installed and
|
||||
configured properly.
|
||||
@@ -279,8 +279,8 @@ relevent roles unless the Release Manager designates otherwise:</p>
|
||||
<td>Release Manager</td>
|
||||
<td>Tag the module in Bazaar:
|
||||
<blockquote><tt>
|
||||
cd ~/base/mirror-3.15<br />
|
||||
bzr tag R3.15.3</i>
|
||||
cd ~/base/mirror-3.16<br />
|
||||
bzr tag R3.16.1</i>
|
||||
</tt></blockquote>
|
||||
</td>
|
||||
</tr>
|
||||
@@ -292,10 +292,10 @@ relevent roles unless the Release Manager designates otherwise:</p>
|
||||
<blockquote><tt>
|
||||
cd ~/base<br />
|
||||
bzr export --keywords=publish
|
||||
--root=base-3.15.3
|
||||
-r tag:R3.15.3
|
||||
base-3.15.3.tar.gz
|
||||
mirror-3.15
|
||||
--root=base-3.16.1
|
||||
-r tag:R3.16.1
|
||||
base-3.16.1.tar.gz
|
||||
mirror-3.16
|
||||
</tt></blockquote>
|
||||
This requires that the Bazaar keywords plugin is installed and
|
||||
configured properly.
|
||||
|
||||
@@ -841,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
|
||||
@@ -868,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>
|
||||
|
||||
|
||||
@@ -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 );
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -66,6 +66,8 @@ void repeaterSubscribeTimer::shutdown (
|
||||
epicsTimerNotify::expireStatus repeaterSubscribeTimer::
|
||||
expire ( const epicsTime & /* currentTime */ )
|
||||
{
|
||||
epicsGuard < epicsMutex > guard ( this->stateMutex );
|
||||
|
||||
static const unsigned nTriesToMsg = 50;
|
||||
if ( this->attempts > nTriesToMsg && ! this->once ) {
|
||||
callbackManager mgr ( this->ctxNotify, this->cbMutex );
|
||||
@@ -92,12 +94,15 @@ epicsTimerNotify::expireStatus repeaterSubscribeTimer::
|
||||
|
||||
void repeaterSubscribeTimer::show ( unsigned /* level */ ) const
|
||||
{
|
||||
epicsGuard < epicsMutex > guard ( this->stateMutex );
|
||||
|
||||
::printf ( "repeater subscribe timer: attempts=%u registered=%u once=%u\n",
|
||||
this->attempts, this->registered, this->once );
|
||||
}
|
||||
|
||||
void repeaterSubscribeTimer::confirmNotify ()
|
||||
{
|
||||
epicsGuard < epicsMutex > guard ( this->stateMutex );
|
||||
this->registered = true;
|
||||
}
|
||||
|
||||
|
||||
@@ -70,6 +70,7 @@ private:
|
||||
repeaterTimerNotify & iiu;
|
||||
epicsMutex & cbMutex;
|
||||
cacContextNotify & ctxNotify;
|
||||
mutable epicsMutex stateMutex;
|
||||
unsigned attempts;
|
||||
bool registered;
|
||||
bool once;
|
||||
|
||||
@@ -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 ) );
|
||||
|
||||
@@ -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 & );
|
||||
|
||||
@@ -354,9 +354,11 @@ caStatus casDGClient::searchFailResponse ( const caHdrLargeArray * mp )
|
||||
status = this->out.copyInHeader ( CA_PROTO_NOT_FOUND, 0,
|
||||
mp->m_dataType, mp->m_count, mp->m_cid, mp->m_available, 0 );
|
||||
|
||||
this->out.commitMsg ();
|
||||
if ( status == S_cas_success ) {
|
||||
this->out.commitMsg ();
|
||||
}
|
||||
|
||||
return S_cas_success;
|
||||
return S_cas_success;
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -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 );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,7 +24,6 @@
|
||||
#include "dbDefs.h"
|
||||
#include "epicsAtomic.h"
|
||||
#include "epicsEvent.h"
|
||||
#include "epicsExit.h"
|
||||
#include "epicsInterrupt.h"
|
||||
#include "epicsRingPointer.h"
|
||||
#include "epicsString.h"
|
||||
@@ -92,7 +91,7 @@ static int priorityValue[NUM_CALLBACK_PRIORITIES] = {0, 1, 2};
|
||||
int callbackSetQueueSize(int size)
|
||||
{
|
||||
if (callbackIsInit) {
|
||||
errlogPrintf("Callback system already initialized\n");
|
||||
fprintf(stderr, "Callback system already initialized\n");
|
||||
return -1;
|
||||
}
|
||||
callbackQueueSize = size;
|
||||
@@ -102,7 +101,7 @@ int callbackSetQueueSize(int size)
|
||||
int callbackParallelThreads(int count, const char *prio)
|
||||
{
|
||||
if (callbackIsInit) {
|
||||
errlogPrintf("Callback system already initialized\n");
|
||||
fprintf(stderr, "Callback system already initialized\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -112,7 +111,7 @@ int callbackParallelThreads(int count, const char *prio)
|
||||
count = callbackParallelThreadsDefault;
|
||||
if (count < 1) count = 1;
|
||||
|
||||
if (!prio || strcmp(prio, "") == 0 || strcmp(prio, "*") == 0) {
|
||||
if (!prio || *prio == 0 || strcmp(prio, "*") == 0) {
|
||||
int i;
|
||||
|
||||
for (i = 0; i < NUM_CALLBACK_PRIORITIES; i++) {
|
||||
@@ -121,30 +120,30 @@ int callbackParallelThreads(int count, const char *prio)
|
||||
}
|
||||
else {
|
||||
dbMenu *pdbMenu;
|
||||
int i;
|
||||
|
||||
if (!pdbbase) {
|
||||
errlogPrintf("callbackParallelThreads: pdbbase not set\n");
|
||||
fprintf(stderr, "callbackParallelThreads: pdbbase not set\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Find prio in menuPriority */
|
||||
pdbMenu = dbFindMenu(pdbbase, "menuPriority");
|
||||
if (pdbMenu) {
|
||||
int i, gotMatch = 0;
|
||||
|
||||
for (i = 0; i < pdbMenu->nChoice; i++) {
|
||||
gotMatch = (epicsStrCaseCmp(prio, pdbMenu->papChoiceValue[i])==0) ? TRUE : FALSE;
|
||||
if (gotMatch)
|
||||
break;
|
||||
}
|
||||
if (gotMatch) {
|
||||
callbackQueue[i].threadsConfigured = count;
|
||||
return 0;
|
||||
}
|
||||
else {
|
||||
errlogPrintf("Unknown priority \"%s\"\n", prio);
|
||||
return -1;
|
||||
}
|
||||
if (!pdbMenu) {
|
||||
fprintf(stderr, "callbackParallelThreads: No Priority menu\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (i = 0; i < pdbMenu->nChoice; i++) {
|
||||
if (epicsStrCaseCmp(prio, pdbMenu->papChoiceValue[i]) == 0)
|
||||
goto found;
|
||||
}
|
||||
fprintf(stderr, "callbackParallelThreads: "
|
||||
"Unknown priority \"%s\"\n", prio);
|
||||
return -1;
|
||||
|
||||
found:
|
||||
callbackQueue[i].threadsConfigured = count;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -47,14 +47,16 @@ typedef struct callbackPvt {
|
||||
|
||||
typedef void (*CALLBACKFUNC)(struct callbackPvt*);
|
||||
|
||||
#define callbackSetCallback(PFUN,PCALLBACK)\
|
||||
( (PCALLBACK)->callback = (PFUN) )
|
||||
#define callbackSetPriority(PRIORITY,PCALLBACK)\
|
||||
( (PCALLBACK)->priority = (PRIORITY) )
|
||||
#define callbackSetUser(USER,PCALLBACK)\
|
||||
( (PCALLBACK)->user = (void *)(USER) )
|
||||
#define callbackGetUser(USER,PCALLBACK)\
|
||||
( (USER) = (void *)((CALLBACK *)(PCALLBACK))->user )
|
||||
#define callbackSetCallback(PFUN, PCALLBACK) \
|
||||
( (PCALLBACK)->callback = (PFUN) )
|
||||
#define callbackSetPriority(PRIORITY, PCALLBACK) \
|
||||
( (PCALLBACK)->priority = (PRIORITY) )
|
||||
#define callbackGetPriority(PRIORITY, PCALLBACK) \
|
||||
( (PRIORITY) = (PCALLBACK)->priority )
|
||||
#define callbackSetUser(USER, PCALLBACK) \
|
||||
( (PCALLBACK)->user = (void *) (USER) )
|
||||
#define callbackGetUser(USER, PCALLBACK) \
|
||||
( (USER) = (PCALLBACK)->user )
|
||||
|
||||
epicsShareFunc void callbackInit(void);
|
||||
epicsShareFunc void callbackStop(void);
|
||||
|
||||
@@ -50,11 +50,12 @@
|
||||
#include "dbFldTypes.h"
|
||||
#include "dbFldTypes.h"
|
||||
#include "dbLink.h"
|
||||
#include "dbLock.h"
|
||||
#include "dbLockPvt.h"
|
||||
#include "dbNotify.h"
|
||||
#include "dbScan.h"
|
||||
#include "dbServer.h"
|
||||
#include "dbStaticLib.h"
|
||||
#include "dbStaticPvt.h"
|
||||
#include "devSup.h"
|
||||
#include "epicsEvent.h"
|
||||
#include "link.h"
|
||||
@@ -812,11 +813,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 +826,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 +860,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 +885,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 +904,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 +919,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:
|
||||
@@ -940,19 +943,25 @@ devSup* dbDSETtoDevSup(dbRecordType *prdes, struct dset *pdset) {
|
||||
static long dbPutFieldLink(DBADDR *paddr,
|
||||
short dbrType, const void *pbuffer, long nRequest)
|
||||
{
|
||||
dbLinkInfo link_info;
|
||||
DBADDR *pdbaddr = NULL;
|
||||
dbCommon *precord = paddr->precord;
|
||||
dbCommon *lockrecs[2];
|
||||
dbLocker locker;
|
||||
dbFldDes *pfldDes = paddr->pfldDes;
|
||||
long special = paddr->special;
|
||||
struct link *plink = (struct link *)paddr->pfield;
|
||||
const char *pstring = (const char *)pbuffer;
|
||||
DBENTRY dbEntry;
|
||||
struct dsxt *old_dsxt = NULL;
|
||||
struct dset *new_dset = NULL;
|
||||
struct dsxt *new_dsxt = NULL;
|
||||
devSup *new_devsup = NULL;
|
||||
long status;
|
||||
int isDevLink;
|
||||
short scan;
|
||||
|
||||
STATIC_ASSERT(DBLOCKER_NALLOC>=2);
|
||||
|
||||
switch (dbrType) {
|
||||
case DBR_CHAR:
|
||||
case DBR_UCHAR:
|
||||
@@ -967,31 +976,57 @@ static long dbPutFieldLink(DBADDR *paddr,
|
||||
return S_db_badDbrtype;
|
||||
}
|
||||
|
||||
dbInitEntry(pdbbase, &dbEntry);
|
||||
status = dbFindRecord(&dbEntry, precord->name);
|
||||
if (!status) status = dbFindField(&dbEntry, pfldDes->name);
|
||||
if (status) goto finish;
|
||||
status = dbParseLink(pstring, pfldDes->field_type, &link_info);
|
||||
if (status)
|
||||
return status;
|
||||
|
||||
if (link_info.ltype == PV_LINK &&
|
||||
(link_info.modifiers & (pvlOptCA | pvlOptCP | pvlOptCPP)) == 0) {
|
||||
DBADDR tempaddr;
|
||||
|
||||
if (dbNameToAddr(link_info.target, &tempaddr)==0) {
|
||||
/* This will become a DB link. */
|
||||
pdbaddr = malloc(sizeof(*pdbaddr));
|
||||
if (!pdbaddr) {
|
||||
status = S_db_noMemory;
|
||||
goto cleanup;
|
||||
}
|
||||
*pdbaddr = tempaddr; /* struct copy */
|
||||
}
|
||||
}
|
||||
|
||||
isDevLink = ellCount(&precord->rdes->devList) > 0 &&
|
||||
(strcmp(pfldDes->name, "INP") == 0 ||
|
||||
strcmp(pfldDes->name, "OUT") == 0);
|
||||
pfldDes->isDevLink;
|
||||
|
||||
dbLockSetGblLock();
|
||||
dbLockSetRecordLock(precord);
|
||||
memset(&locker, 0, sizeof(locker));
|
||||
lockrecs[0] = precord;
|
||||
lockrecs[1] = pdbaddr ? pdbaddr->precord : NULL;
|
||||
dbLockerPrepare(&locker, lockrecs, 2);
|
||||
|
||||
dbScanLockMany(&locker);
|
||||
|
||||
scan = precord->scan;
|
||||
|
||||
if (isDevLink) {
|
||||
devSup *pdevSup = dbDTYPtoDevSup(precord->rdes, precord->dtyp);
|
||||
if (pdevSup) {
|
||||
new_dset = pdevSup->pdset;
|
||||
new_dsxt = pdevSup->pdsxt;
|
||||
new_devsup = dbDTYPtoDevSup(precord->rdes, precord->dtyp);
|
||||
if (new_devsup) {
|
||||
new_dset = new_devsup->pdset;
|
||||
new_dsxt = new_devsup->pdsxt;
|
||||
}
|
||||
}
|
||||
|
||||
if (dbCanSetLink(plink, &link_info, new_devsup)) {
|
||||
/* link type mis-match prevents assignment */
|
||||
status = S_dbLib_badField;
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
if (isDevLink) {
|
||||
if (precord->dset) {
|
||||
pdevSup = dbDSETtoDevSup(precord->rdes, precord->dset);
|
||||
if (pdevSup)
|
||||
old_dsxt = pdevSup->pdsxt;
|
||||
devSup *old_devsup = dbDSETtoDevSup(precord->rdes, precord->dset);
|
||||
|
||||
if (old_devsup)
|
||||
old_dsxt = old_devsup->pdsxt;
|
||||
}
|
||||
|
||||
if (new_dsxt == NULL ||
|
||||
@@ -1017,13 +1052,11 @@ static long dbPutFieldLink(DBADDR *paddr,
|
||||
switch (plink->type) { /* Old link type */
|
||||
case DB_LINK:
|
||||
case CA_LINK:
|
||||
dbRemoveLink(plink);
|
||||
case CONSTANT:
|
||||
dbRemoveLink(&locker, plink); /* link type becomes PV_LINK */
|
||||
break;
|
||||
|
||||
case PV_LINK:
|
||||
case CONSTANT:
|
||||
break; /* do nothing */
|
||||
|
||||
case MACRO_LINK:
|
||||
break; /* should never get here */
|
||||
|
||||
@@ -1037,7 +1070,7 @@ static long dbPutFieldLink(DBADDR *paddr,
|
||||
|
||||
if (special) status = dbPutSpecial(paddr, 0);
|
||||
|
||||
if (!status) status = dbPutString(&dbEntry, pstring);
|
||||
if (!status) status = dbSetLink(plink, &link_info, new_devsup);
|
||||
|
||||
if (!status && special) status = dbPutSpecial(paddr, 1);
|
||||
|
||||
@@ -1064,11 +1097,9 @@ static long dbPutFieldLink(DBADDR *paddr,
|
||||
|
||||
switch (plink->type) { /* New link type */
|
||||
case PV_LINK:
|
||||
dbAddLink(precord, plink, pfldDes->field_type);
|
||||
break;
|
||||
|
||||
case CONSTANT:
|
||||
break; /* do nothing */
|
||||
dbAddLink(&locker, plink, pfldDes->field_type, pdbaddr);
|
||||
break;
|
||||
|
||||
case DB_LINK:
|
||||
case CA_LINK:
|
||||
@@ -1094,9 +1125,10 @@ postScanEvent:
|
||||
if (scan != precord->scan)
|
||||
db_post_events(precord, &precord->scan, DBE_VALUE | DBE_LOG);
|
||||
unlock:
|
||||
dbLockSetGblUnlock();
|
||||
finish:
|
||||
dbFinishEntry(&dbEntry);
|
||||
dbScanUnlockMany(&locker);
|
||||
dbLockerFinalize(&locker);
|
||||
cleanup:
|
||||
free(link_info.target);
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
@@ -205,6 +205,7 @@ private:
|
||||
cacContextNotify & notify;
|
||||
epics_auto_ptr < cacContext > pNetContext;
|
||||
char * pStateNotifyCache;
|
||||
bool isolated;
|
||||
|
||||
cacChannel & createChannel (
|
||||
epicsGuard < epicsMutex > &,
|
||||
|
||||
@@ -6,14 +6,13 @@
|
||||
* EPICS BASE is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
\*************************************************************************/
|
||||
|
||||
/* $Revision-Id$
|
||||
/* dbCa.c
|
||||
*
|
||||
* Original Authors: Bob Dalesio and Marty Kraimer
|
||||
* Date: 26MAR96
|
||||
*
|
||||
*/
|
||||
|
||||
#define EPICS_DBCA_PRIVATE_API
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
@@ -30,6 +29,7 @@
|
||||
#include "epicsPrint.h"
|
||||
#include "epicsString.h"
|
||||
#include "epicsThread.h"
|
||||
#include "epicsAtomic.h"
|
||||
#include "epicsTime.h"
|
||||
#include "errlog.h"
|
||||
#include "errMdef.h"
|
||||
@@ -46,13 +46,17 @@
|
||||
#include "dbCaPvt.h"
|
||||
#include "dbCommon.h"
|
||||
#include "db_convert.h"
|
||||
#include "dbLink.h"
|
||||
#include "dbLock.h"
|
||||
#include "dbScan.h"
|
||||
#include "link.h"
|
||||
#include "recSup.h"
|
||||
|
||||
/* defined in dbContext.cpp
|
||||
* Setup local CA access
|
||||
*/
|
||||
extern void dbServiceIOInit();
|
||||
|
||||
extern int dbServiceIsolate;
|
||||
|
||||
static ELLLIST workList = ELLLIST_INIT; /* Work list for dbCaTask */
|
||||
static epicsMutexId workListLock; /*Mutual exclusions semaphores for workList*/
|
||||
@@ -60,7 +64,7 @@ static epicsEventId workListEvent; /*wakeup event for dbCaTask*/
|
||||
static int removesOutstanding = 0;
|
||||
#define removesOutstandingWarning 10000
|
||||
|
||||
static volatile enum {
|
||||
static volatile enum dbCaCtl_t {
|
||||
ctlInit, ctlRun, ctlPause, ctlExit
|
||||
} dbCaCtl;
|
||||
static epicsEventId startStopEvent;
|
||||
@@ -70,57 +74,47 @@ struct ca_client_context * dbCaClientContext;
|
||||
/* Forward declarations */
|
||||
static void dbCaTask(void *);
|
||||
|
||||
static lset dbCa_lset;
|
||||
|
||||
#define printLinks(pcaLink) \
|
||||
errlogPrintf("%s has DB CA link to %s\n",\
|
||||
pcaLink->plink->value.pv_link.precord->name, pcaLink->pvname)
|
||||
pcaLink->plink->precord->name, pcaLink->pvname)
|
||||
|
||||
static int dbca_chan_count;
|
||||
|
||||
/* caLink locking
|
||||
*
|
||||
* workListLock
|
||||
* This is only used to put request into and take them out of workList.
|
||||
* While this is locked no other locks are taken
|
||||
* Lock ordering:
|
||||
* dbScanLock -> caLink.lock -> workListLock
|
||||
*
|
||||
* dbScanLock
|
||||
* dbCaAddLink and dbCaRemoveLink are only called by dbAccess or iocInit
|
||||
* They are only called by dbAccess when it has a global lock on lock set.
|
||||
* It is assumed that ALL other dbCaxxx calls are made only if dbScanLock
|
||||
* is already active. These routines are intended for use by record/device
|
||||
* support.
|
||||
* workListLock:
|
||||
* Guards access to workList.
|
||||
*
|
||||
* caLink.lock
|
||||
* Any code that use a caLink takes this lock and releases it when done
|
||||
* dbScanLock:
|
||||
* All dbCa* functions operating on a single link may only be called when
|
||||
* the record containing the DBLINK is locked. Including:
|
||||
* dbCaGet*()
|
||||
* isConnected()
|
||||
* dbCaPutLink()
|
||||
* scanForward()
|
||||
* dbCaAddLinkCallback()
|
||||
* dbCaRemoveLink()
|
||||
*
|
||||
* dbCaTask and the channel access callbacks NEVER access anything in the
|
||||
* records except after locking caLink.lock and checking that caLink.plink
|
||||
* is not null. They NEVER call dbScanLock.
|
||||
* Guard the pointer plink.value.pv_link.pvt, but not the struct caLink
|
||||
* which is pointed to.
|
||||
*
|
||||
* The above is necessary to prevent deadlocks and attempts to use a caLink
|
||||
* that has been deleted.
|
||||
* caLink.lock:
|
||||
* Guards the caLink structure (but not the struct DBLINK)
|
||||
*
|
||||
* Just a few words about handling dbCaRemoveLink because this is when
|
||||
* it is essential that nothing tries to use a caLink that has been freed.
|
||||
* The dbCaTask only locks caLink, and must not lock the record (a violation of lock order).
|
||||
*
|
||||
* dbCaRemoveLink is called when links are being modified. This is only
|
||||
* done with the dbScan mechanism guranteeing that nothing from
|
||||
* database access trys to access the record containing the caLink.
|
||||
* During link modification or IOC shutdown the pca->plink pointer (guarded by caLink.lock)
|
||||
* is used as a flag to indicate that a link is no longer active.
|
||||
*
|
||||
* Thus the problem is to make sure that nothing from channel access
|
||||
* accesses a caLink that is deleted. This is done as follows.
|
||||
* References to the struct caLink are owned by the dbCaTask, and any scanOnceCallback()
|
||||
* which is in progress.
|
||||
*
|
||||
* dbCaRemoveLink does the following:
|
||||
* epicsMutexMustLock(pca->lock);
|
||||
* pca->plink = 0;
|
||||
* plink->value.pv_link.pvt = 0;
|
||||
* epicsMutexUnlock(pca->lock);
|
||||
* addAction(pca,CA_CLEAR_CHANNEL);
|
||||
*
|
||||
* 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
|
||||
* is called no other callback for this caLink will be called.
|
||||
* The libca and scanOnceCallback callbacks take no action if pca->plink==NULL.
|
||||
*
|
||||
* dbCaPutLinkCallback causes an additional complication because
|
||||
* when dbCaRemoveLink is called the callback may not have occured.
|
||||
@@ -162,11 +156,23 @@ static void addAction(caLink *pca, short link_action)
|
||||
epicsEventSignal(workListEvent);
|
||||
}
|
||||
|
||||
static void dbCaLinkFree(caLink *pca)
|
||||
static void caLinkInc(caLink *pca)
|
||||
{
|
||||
assert(epicsAtomicGetIntT(&pca->refcount)>0);
|
||||
epicsAtomicIncrIntT(&pca->refcount);
|
||||
}
|
||||
|
||||
static void caLinkDec(caLink *pca)
|
||||
{
|
||||
int cnt;
|
||||
dbCaCallback callback;
|
||||
void *userPvt = 0;
|
||||
|
||||
cnt = epicsAtomicDecrIntT(&pca->refcount);
|
||||
assert(cnt>=0);
|
||||
if(cnt>0)
|
||||
return;
|
||||
|
||||
if (pca->chid) {
|
||||
ca_clear_channel(pca->chid);
|
||||
--dbca_chan_count;
|
||||
@@ -187,10 +193,46 @@ static void dbCaLinkFree(caLink *pca)
|
||||
if (callback) callback(userPvt);
|
||||
}
|
||||
|
||||
/* Block until worker thread has processed all previously queued actions.
|
||||
* Does not prevent additional actions from being queued.
|
||||
*/
|
||||
void dbCaSync(void)
|
||||
{
|
||||
epicsEventId wake;
|
||||
caLink templink;
|
||||
|
||||
/* we only partially initialize templink.
|
||||
* It has no link field and no subscription
|
||||
* so the worker must handle it early
|
||||
*/
|
||||
memset(&templink, 0, sizeof(templink));
|
||||
templink.refcount = 1;
|
||||
|
||||
wake = epicsEventMustCreate(epicsEventEmpty);
|
||||
templink.lock = epicsMutexMustCreate();
|
||||
|
||||
templink.userPvt = wake;
|
||||
|
||||
addAction(&templink, CA_SYNC);
|
||||
|
||||
epicsEventMustWait(wake);
|
||||
/* Worker holds workListLock when calling epicsEventMustTrigger()
|
||||
* we cycle through workListLock to ensure worker call to
|
||||
* epicsEventMustTrigger() returns before we destroy the event.
|
||||
*/
|
||||
epicsMutexMustLock(workListLock);
|
||||
epicsMutexUnlock(workListLock);
|
||||
|
||||
assert(templink.refcount==1);
|
||||
|
||||
epicsMutexDestroy(templink.lock);
|
||||
epicsEventDestroy(wake);
|
||||
}
|
||||
|
||||
void dbCaCallbackProcess(void *userPvt)
|
||||
{
|
||||
struct link *plink = (struct link *)userPvt;
|
||||
dbCommon *pdbCommon = plink->value.pv_link.precord;
|
||||
dbCommon *pdbCommon = plink->precord;
|
||||
|
||||
dbScanLock(pdbCommon);
|
||||
pdbCommon->rset->process(pdbCommon);
|
||||
@@ -199,40 +241,25 @@ void dbCaCallbackProcess(void *userPvt)
|
||||
|
||||
void dbCaShutdown(void)
|
||||
{
|
||||
if (dbCaCtl == ctlRun || dbCaCtl == ctlPause) {
|
||||
dbCaCtl = ctlExit;
|
||||
epicsEventSignal(workListEvent);
|
||||
epicsEventMustWait(startStopEvent);
|
||||
epicsEventDestroy(startStopEvent);
|
||||
} else {
|
||||
/* manually cleanup queue since dbCa thread isn't running
|
||||
* which only happens in unit tests
|
||||
*/
|
||||
caLink *pca;
|
||||
epicsMutexMustLock(workListLock);
|
||||
while((pca=(caLink*)ellGet(&workList))!=NULL) {
|
||||
if(pca->link_action&CA_CLEAR_CHANNEL) {
|
||||
dbCaLinkFree(pca);
|
||||
}
|
||||
}
|
||||
epicsMutexUnlock(workListLock);
|
||||
}
|
||||
enum dbCaCtl_t cur = dbCaCtl;
|
||||
assert(cur == ctlRun || cur == ctlPause);
|
||||
dbCaCtl = ctlExit;
|
||||
epicsEventSignal(workListEvent);
|
||||
epicsEventMustWait(startStopEvent);
|
||||
}
|
||||
|
||||
void dbCaLinkInitIsolated(void)
|
||||
static void dbCaLinkInitImpl(int isolate)
|
||||
{
|
||||
dbServiceIsolate = isolate;
|
||||
dbServiceIOInit();
|
||||
|
||||
if (!workListLock)
|
||||
workListLock = epicsMutexMustCreate();
|
||||
if (!workListEvent)
|
||||
workListEvent = epicsEventMustCreate(epicsEventEmpty);
|
||||
dbCaCtl = ctlExit;
|
||||
}
|
||||
|
||||
void dbCaLinkInit(void)
|
||||
{
|
||||
dbServiceIOInit();
|
||||
dbCaLinkInitIsolated();
|
||||
startStopEvent = epicsEventMustCreate(epicsEventEmpty);
|
||||
if(!startStopEvent)
|
||||
startStopEvent = epicsEventMustCreate(epicsEventEmpty);
|
||||
dbCaCtl = ctlPause;
|
||||
|
||||
epicsThreadCreate("dbCaLink", epicsThreadPriorityMedium,
|
||||
@@ -241,6 +268,16 @@ void dbCaLinkInit(void)
|
||||
epicsEventMustWait(startStopEvent);
|
||||
}
|
||||
|
||||
void dbCaLinkInitIsolated(void)
|
||||
{
|
||||
dbCaLinkInitImpl(1);
|
||||
}
|
||||
|
||||
void dbCaLinkInit(void)
|
||||
{
|
||||
dbCaLinkInitImpl(0);
|
||||
}
|
||||
|
||||
void dbCaRun(void)
|
||||
{
|
||||
if (dbCaCtl == ctlPause) {
|
||||
@@ -265,6 +302,7 @@ void dbCaAddLinkCallback(struct link *plink,
|
||||
assert(!plink->value.pv_link.pvt);
|
||||
|
||||
pca = (caLink *)dbCalloc(1, sizeof(caLink));
|
||||
pca->refcount = 1;
|
||||
pca->lock = epicsMutexMustCreate();
|
||||
pca->plink = plink;
|
||||
pca->pvname = epicsStrDup(plink->value.pv_link.pvname);
|
||||
@@ -273,13 +311,20 @@ void dbCaAddLinkCallback(struct link *plink,
|
||||
pca->userPvt = userPvt;
|
||||
|
||||
epicsMutexMustLock(pca->lock);
|
||||
plink->lset = &dbCa_lset;
|
||||
plink->type = CA_LINK;
|
||||
plink->value.pv_link.pvt = pca;
|
||||
addAction(pca, CA_CONNECT);
|
||||
epicsMutexUnlock(pca->lock);
|
||||
}
|
||||
|
||||
void dbCaRemoveLink(struct link *plink)
|
||||
long dbCaAddLink(struct dbLocker *locker, struct link *plink, short dbfType)
|
||||
{
|
||||
dbCaAddLinkCallback(plink, 0, 0, NULL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void dbCaRemoveLink(struct dbLocker *locker, struct link *plink)
|
||||
{
|
||||
caLink *pca = (caLink *)plink->value.pv_link.pvt;
|
||||
|
||||
@@ -287,6 +332,9 @@ void dbCaRemoveLink(struct link *plink)
|
||||
epicsMutexMustLock(pca->lock);
|
||||
pca->plink = 0;
|
||||
plink->value.pv_link.pvt = 0;
|
||||
plink->value.pv_link.pvlMask = 0;
|
||||
plink->type = PV_LINK;
|
||||
plink->lset = NULL;
|
||||
/* Unlock before addAction or dbCaTask might free first */
|
||||
epicsMutexUnlock(pca->lock);
|
||||
addAction(pca, CA_CLEAR_CHANNEL);
|
||||
@@ -346,15 +394,15 @@ long dbCaGetLink(struct link *plink,short dbrType, void *pdest,
|
||||
assert(pca->pgetNative);
|
||||
status = fConvert(pca->pgetNative, pdest, 0);
|
||||
} else {
|
||||
long ntoget = *nelements;
|
||||
unsigned long ntoget = *nelements;
|
||||
struct dbAddr dbAddr;
|
||||
long (*aConvert)(struct dbAddr *paddr, void *to, long nreq, long nto, long off);
|
||||
|
||||
aConvert = dbGetConvertRoutine[newType][dbrType];
|
||||
assert(pca->pgetNative);
|
||||
|
||||
if (ntoget > pca->nelements)
|
||||
ntoget = pca->nelements;
|
||||
if (ntoget > pca->usedelements)
|
||||
ntoget = pca->usedelements;
|
||||
*nelements = ntoget;
|
||||
|
||||
memset((void *)&dbAddr, 0, sizeof(dbAddr));
|
||||
@@ -409,6 +457,7 @@ long dbCaPutLinkCallback(struct link *plink,short dbrType,
|
||||
if (!pca->pputNative) {
|
||||
pca->pputNative = dbCalloc(pca->nelements,
|
||||
dbr_value_size[ca_field_type(pca->chid)]);
|
||||
pca->putnelements = 0;
|
||||
/* Fixed and disabled by ANJ, see comment above.
|
||||
plink->value.pv_link.pvlMask |= pvlOptOutNative;
|
||||
*/
|
||||
@@ -418,6 +467,7 @@ long dbCaPutLinkCallback(struct link *plink,short dbrType,
|
||||
|
||||
fConvert = dbFastPutConvertRoutine[dbrType][newType];
|
||||
status = fConvert(pbuffer, pca->pputNative, 0);
|
||||
pca->putnelements = 1;
|
||||
} else {
|
||||
struct dbAddr dbAddr;
|
||||
long (*aConvert)(struct dbAddr *paddr, const void *from, long nreq, long nfrom, long off);
|
||||
@@ -430,10 +480,7 @@ long dbCaPutLinkCallback(struct link *plink,short dbrType,
|
||||
if(nRequest>pca->nelements)
|
||||
nRequest = pca->nelements;
|
||||
status = aConvert(&dbAddr, pbuffer, nRequest, pca->nelements, 0);
|
||||
if(nRequest<pca->nelements) {
|
||||
long elemsize = dbr_value_size[ca_field_type(pca->chid)];
|
||||
memset(nRequest*elemsize+(char*)pca->pputNative, 0, (pca->nelements-nRequest)*elemsize);
|
||||
}
|
||||
pca->putnelements = nRequest;
|
||||
}
|
||||
link_action |= CA_WRITE_NATIVE;
|
||||
pca->gotOutNative = TRUE;
|
||||
@@ -453,7 +500,13 @@ long dbCaPutLinkCallback(struct link *plink,short dbrType,
|
||||
return status;
|
||||
}
|
||||
|
||||
int dbCaIsLinkConnected(const struct link *plink)
|
||||
long dbCaPutLink(struct link *plink, short dbrType,
|
||||
const void *pbuffer, long nRequest)
|
||||
{
|
||||
return dbCaPutLinkCallback(plink, dbrType, pbuffer, nRequest, 0, NULL);
|
||||
}
|
||||
|
||||
static int isConnected(const struct link *plink)
|
||||
{
|
||||
caLink *pca;
|
||||
|
||||
@@ -463,7 +516,7 @@ int dbCaIsLinkConnected(const struct link *plink)
|
||||
return pca->isConnected;
|
||||
}
|
||||
|
||||
void dbCaScanFwdLink(struct link *plink) {
|
||||
static void scanForward(struct link *plink) {
|
||||
short fwdLinkValue = 1;
|
||||
|
||||
if (plink->value.pv_link.pvlMask & pvlOptFWD)
|
||||
@@ -482,7 +535,7 @@ void dbCaScanFwdLink(struct link *plink) {
|
||||
return -1; \
|
||||
}
|
||||
|
||||
long dbCaGetNelements(const struct link *plink, long *nelements)
|
||||
static long getElements(const struct link *plink, long *nelements)
|
||||
{
|
||||
caLink *pca;
|
||||
|
||||
@@ -492,7 +545,7 @@ long dbCaGetNelements(const struct link *plink, long *nelements)
|
||||
return 0;
|
||||
}
|
||||
|
||||
long dbCaGetAlarm(const struct link *plink,
|
||||
static long getAlarm(const struct link *plink,
|
||||
epicsEnum16 *pstat, epicsEnum16 *psevr)
|
||||
{
|
||||
caLink *pca;
|
||||
@@ -504,7 +557,7 @@ long dbCaGetAlarm(const struct link *plink,
|
||||
return 0;
|
||||
}
|
||||
|
||||
long dbCaGetTimeStamp(const struct link *plink,
|
||||
static long getTimeStamp(const struct link *plink,
|
||||
epicsTimeStamp *pstamp)
|
||||
{
|
||||
caLink *pca;
|
||||
@@ -515,7 +568,7 @@ long dbCaGetTimeStamp(const struct link *plink,
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dbCaGetLinkDBFtype(const struct link *plink)
|
||||
static int getDBFtype(const struct link *plink)
|
||||
{
|
||||
caLink *pca;
|
||||
int type;
|
||||
@@ -546,7 +599,7 @@ long dbCaGetAttributes(const struct link *plink,
|
||||
return 0;
|
||||
}
|
||||
|
||||
long dbCaGetControlLimits(const struct link *plink,
|
||||
static long getControlLimits(const struct link *plink,
|
||||
double *low, double *high)
|
||||
{
|
||||
caLink *pca;
|
||||
@@ -562,7 +615,7 @@ long dbCaGetControlLimits(const struct link *plink,
|
||||
return gotAttributes ? 0 : -1;
|
||||
}
|
||||
|
||||
long dbCaGetGraphicLimits(const struct link *plink,
|
||||
static long getGraphicLimits(const struct link *plink,
|
||||
double *low, double *high)
|
||||
{
|
||||
caLink *pca;
|
||||
@@ -578,7 +631,7 @@ long dbCaGetGraphicLimits(const struct link *plink,
|
||||
return gotAttributes ? 0 : -1;
|
||||
}
|
||||
|
||||
long dbCaGetAlarmLimits(const struct link *plink,
|
||||
static long getAlarmLimits(const struct link *plink,
|
||||
double *lolo, double *low, double *high, double *hihi)
|
||||
{
|
||||
caLink *pca;
|
||||
@@ -596,7 +649,7 @@ long dbCaGetAlarmLimits(const struct link *plink,
|
||||
return gotAttributes ? 0 : -1;
|
||||
}
|
||||
|
||||
long dbCaGetPrecision(const struct link *plink, short *precision)
|
||||
static long getPrecision(const struct link *plink, short *precision)
|
||||
{
|
||||
caLink *pca;
|
||||
int gotAttributes;
|
||||
@@ -608,7 +661,7 @@ long dbCaGetPrecision(const struct link *plink, short *precision)
|
||||
return gotAttributes ? 0 : -1;
|
||||
}
|
||||
|
||||
long dbCaGetUnits(const struct link *plink,
|
||||
static long getUnits(const struct link *plink,
|
||||
char *units, int unitsSize)
|
||||
{
|
||||
caLink *pca;
|
||||
@@ -623,6 +676,50 @@ long dbCaGetUnits(const struct link *plink,
|
||||
return gotAttributes ? 0 : -1;
|
||||
}
|
||||
|
||||
static void scanComplete(void *raw, dbCommon *prec)
|
||||
{
|
||||
caLink *pca = raw;
|
||||
epicsMutexMustLock(pca->lock);
|
||||
if(!pca->plink) {
|
||||
/* IOC shutdown or link re-targeted. Do nothing. */
|
||||
} else if(pca->scanningOnce==0) {
|
||||
errlogPrintf("dbCa.c complete callback w/ scanningOnce==0\n");
|
||||
} else if(--pca->scanningOnce){
|
||||
/* another scan is queued */
|
||||
if(scanOnceCallback(prec, scanComplete, raw)) {
|
||||
errlogPrintf("dbCa.c failed to re-queue scanOnce\n");
|
||||
} else
|
||||
caLinkInc(pca);
|
||||
}
|
||||
epicsMutexUnlock(pca->lock);
|
||||
caLinkDec(pca);
|
||||
}
|
||||
|
||||
/* must be called with pca->lock held */
|
||||
static void scanLinkOnce(dbCommon *prec, caLink *pca) {
|
||||
if(pca->scanningOnce==0) {
|
||||
if(scanOnceCallback(prec, scanComplete, pca)) {
|
||||
errlogPrintf("dbCa.c failed to queue scanOnce\n");
|
||||
} else
|
||||
caLinkInc(pca);
|
||||
}
|
||||
if(pca->scanningOnce<5)
|
||||
pca->scanningOnce++;
|
||||
/* else too many scans queued */
|
||||
}
|
||||
|
||||
static lset dbCa_lset = {
|
||||
dbCaRemoveLink,
|
||||
isConnected,
|
||||
getDBFtype, getElements,
|
||||
dbCaGetLink,
|
||||
getControlLimits, getGraphicLimits, getAlarmLimits,
|
||||
getPrecision, getUnits,
|
||||
getAlarm, getTimeStamp,
|
||||
dbCaPutLink,
|
||||
scanForward
|
||||
};
|
||||
|
||||
static void connectionCallback(struct connection_handler_args arg)
|
||||
{
|
||||
caLink *pca;
|
||||
@@ -637,13 +734,13 @@ static void connectionCallback(struct connection_handler_args arg)
|
||||
pca->isConnected = (ca_state(arg.chid) == cs_conn);
|
||||
if (!pca->isConnected) {
|
||||
struct pv_link *ppv_link = &plink->value.pv_link;
|
||||
dbCommon *precord = ppv_link->precord;
|
||||
dbCommon *precord = plink->precord;
|
||||
|
||||
pca->nDisconnect++;
|
||||
if (precord &&
|
||||
((ppv_link->pvlMask & pvlOptCP) ||
|
||||
((ppv_link->pvlMask & pvlOptCPP) && precord->scan == 0)))
|
||||
scanOnce(precord);
|
||||
scanLinkOnce(precord, pca);
|
||||
goto done;
|
||||
}
|
||||
pca->hasReadAccess = ca_read_access(arg.chid);
|
||||
@@ -673,6 +770,7 @@ static void connectionCallback(struct connection_handler_args arg)
|
||||
}
|
||||
pca->gotFirstConnection = TRUE;
|
||||
pca->nelements = ca_element_count(arg.chid);
|
||||
pca->usedelements = 0;
|
||||
pca->dbrType = ca_field_type(arg.chid);
|
||||
if ((plink->value.pv_link.pvlMask & pvlOptInpNative) && !pca->pgetNative) {
|
||||
link_action |= CA_MONITOR_NATIVE;
|
||||
@@ -711,7 +809,7 @@ static void eventCallback(struct event_handler_args arg)
|
||||
if (!plink) goto done;
|
||||
monitor = pca->monitor;
|
||||
userPvt = pca->userPvt;
|
||||
precord = plink->value.pv_link.precord;
|
||||
precord = plink->precord;
|
||||
if (arg.status != ECA_NORMAL) {
|
||||
if (precord) {
|
||||
if (arg.status != ECA_NORDACCESS &&
|
||||
@@ -725,6 +823,7 @@ static void eventCallback(struct event_handler_args arg)
|
||||
goto done;
|
||||
}
|
||||
assert(arg.dbr);
|
||||
assert(arg.count<=pca->nelements);
|
||||
size = arg.count * dbr_value_size[arg.type];
|
||||
if (arg.type == DBR_TIME_STRING &&
|
||||
ca_field_type(pca->chid) == DBR_ENUM) {
|
||||
@@ -741,10 +840,12 @@ static void eventCallback(struct event_handler_args arg)
|
||||
case DBR_TIME_DOUBLE:
|
||||
assert(pca->pgetNative);
|
||||
memcpy(pca->pgetNative, dbr_value_ptr(arg.dbr, arg.type), size);
|
||||
pca->usedelements = arg.count;
|
||||
pca->gotInNative = TRUE;
|
||||
break;
|
||||
default:
|
||||
errMessage(-1, "dbCa: eventCallback Logic Error\n");
|
||||
errlogPrintf("dbCa: eventCallback Logic Error. dbr=%ld dbf=%d\n",
|
||||
arg.type, ca_field_type(pca->chid));
|
||||
break;
|
||||
}
|
||||
pdbr_time_double = (struct dbr_time_double *)arg.dbr;
|
||||
@@ -756,7 +857,7 @@ static void eventCallback(struct event_handler_args arg)
|
||||
|
||||
if ((ppv_link->pvlMask & pvlOptCP) ||
|
||||
((ppv_link->pvlMask & pvlOptCPP) && precord->scan == 0))
|
||||
scanOnce(precord);
|
||||
scanLinkOnce(precord, pca);
|
||||
}
|
||||
done:
|
||||
epicsMutexUnlock(pca->lock);
|
||||
@@ -825,11 +926,11 @@ static void accessRightsCallback(struct access_rights_handler_args arg)
|
||||
pca->hasWriteAccess = ca_write_access(arg.chid);
|
||||
if (pca->hasReadAccess && pca->hasWriteAccess) goto done;
|
||||
ppv_link = &plink->value.pv_link;
|
||||
precord = ppv_link->precord;
|
||||
precord = plink->precord;
|
||||
if (precord &&
|
||||
((ppv_link->pvlMask & pvlOptCP) ||
|
||||
((ppv_link->pvlMask & pvlOptCPP) && precord->scan == 0)))
|
||||
scanOnce(precord);
|
||||
scanLinkOnce(precord, pca);
|
||||
done:
|
||||
epicsMutexUnlock(pca->lock);
|
||||
}
|
||||
@@ -856,7 +957,7 @@ static void getAttribEventCallback(struct event_handler_args arg)
|
||||
getAttributes = pca->getAttributes;
|
||||
getAttributesPvt = pca->getAttributesPvt;
|
||||
if (arg.status != ECA_NORMAL) {
|
||||
dbCommon *precord = plink->value.pv_link.precord;
|
||||
dbCommon *precord = plink->precord;
|
||||
if (precord) {
|
||||
errlogPrintf("dbCa: getAttribEventCallback record %s error %s\n",
|
||||
precord->name, ca_message(arg.status));
|
||||
@@ -912,11 +1013,15 @@ static void dbCaTask(void *arg)
|
||||
break; /* workList is empty */
|
||||
}
|
||||
link_action = pca->link_action;
|
||||
if (link_action&CA_SYNC)
|
||||
epicsEventMustTrigger((epicsEventId)pca->userPvt); /* dbCaSync() requires workListLock to be held here */
|
||||
pca->link_action = 0;
|
||||
if (link_action & CA_CLEAR_CHANNEL) --removesOutstanding;
|
||||
epicsMutexUnlock(workListLock); /* Give back immediately */
|
||||
if (link_action&CA_SYNC)
|
||||
continue;
|
||||
if (link_action & CA_CLEAR_CHANNEL) { /* This must be first */
|
||||
dbCaLinkFree(pca);
|
||||
caLinkDec(pca);
|
||||
/* No alarm is raised. Since link is changing so what? */
|
||||
continue; /* No other link_action makes sense */
|
||||
}
|
||||
@@ -945,11 +1050,11 @@ static void dbCaTask(void *arg)
|
||||
assert(pca->pputNative);
|
||||
if (pca->putType == CA_PUT) {
|
||||
status = ca_array_put(
|
||||
pca->dbrType, pca->nelements,
|
||||
pca->dbrType, pca->putnelements,
|
||||
pca->chid, pca->pputNative);
|
||||
} else if (pca->putType==CA_PUT_CALLBACK) {
|
||||
status = ca_array_put_callback(
|
||||
pca->dbrType, pca->nelements,
|
||||
pca->dbrType, pca->putnelements,
|
||||
pca->chid, pca->pputNative,
|
||||
putComplete, pca);
|
||||
} else {
|
||||
@@ -999,15 +1104,15 @@ static void dbCaTask(void *arg)
|
||||
}
|
||||
}
|
||||
if (link_action & CA_MONITOR_NATIVE) {
|
||||
size_t element_size;
|
||||
|
||||
element_size = dbr_value_size[ca_field_type(pca->chid)];
|
||||
|
||||
epicsMutexMustLock(pca->lock);
|
||||
pca->pgetNative = dbCalloc(pca->nelements, element_size);
|
||||
pca->elementSize = dbr_value_size[ca_field_type(pca->chid)];
|
||||
pca->pgetNative = dbCalloc(pca->nelements, pca->elementSize);
|
||||
epicsMutexUnlock(pca->lock);
|
||||
|
||||
status = ca_add_array_event(
|
||||
ca_field_type(pca->chid)+DBR_TIME_STRING,
|
||||
ca_element_count(pca->chid),
|
||||
dbf_type_to_DBR_TIME(ca_field_type(pca->chid)),
|
||||
0, /* dynamic size */
|
||||
pca->chid, eventCallback, pca, 0.0, 0.0, 0.0, 0);
|
||||
if (status != ECA_NORMAL) {
|
||||
errlogPrintf("dbCaTask ca_add_array_event %s\n",
|
||||
|
||||
@@ -1,20 +1,17 @@
|
||||
/*************************************************************************\
|
||||
* Copyright (c) 2002 The University of Chicago, as Operator of Argonne
|
||||
* Copyright (c) 2015 The University of Chicago, as Operator of Argonne
|
||||
* National Laboratory.
|
||||
* Copyright (c) 2002 The Regents of the University of California, as
|
||||
* Operator of Los Alamos National Laboratory.
|
||||
* EPICS BASE Versions 3.13.7
|
||||
* and higher are distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
* EPICS BASE is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
\*************************************************************************/
|
||||
/* dbCa.h */
|
||||
/* dbCa.h */
|
||||
|
||||
#ifndef INCdbCah
|
||||
#define INCdbCah
|
||||
|
||||
#include "shareLib.h"
|
||||
#include "epicsTime.h"
|
||||
#include "link.h"
|
||||
#include "dbLink.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
@@ -29,48 +26,60 @@ epicsShareFunc void dbCaRun(void);
|
||||
epicsShareFunc void dbCaPause(void);
|
||||
epicsShareFunc void dbCaShutdown(void);
|
||||
|
||||
struct dbLocker;
|
||||
epicsShareFunc void dbCaAddLinkCallback(struct link *plink,
|
||||
dbCaCallback connect, dbCaCallback monitor, void *userPvt);
|
||||
#define dbCaAddLink(plink) dbCaAddLinkCallback((plink), 0, 0, 0)
|
||||
epicsShareFunc void dbCaRemoveLink(struct link *plink);
|
||||
epicsShareFunc long dbCaAddLink(struct dbLocker *locker, struct link *plink, short dbfType);
|
||||
epicsShareFunc void dbCaRemoveLink(struct dbLocker *locker, struct link *plink);
|
||||
|
||||
epicsShareFunc long dbCaGetLink(struct link *plink,
|
||||
short dbrType, void *pbuffer, epicsEnum16 *pstat, epicsEnum16 *psevr,
|
||||
long *nRequest);
|
||||
|
||||
epicsShareFunc long dbCaGetAttributes(const struct link *plink,
|
||||
dbCaCallback callback, void *userPvt);
|
||||
|
||||
epicsShareFunc long dbCaPutLinkCallback(struct link *plink,
|
||||
short dbrType, const void *pbuffer,long nRequest,
|
||||
dbCaCallback callback, void *userPvt);
|
||||
#define dbCaPutLink(plink, dbrType, pbuffer, nRequest) \
|
||||
dbCaPutLinkCallback((plink), (dbrType), (pbuffer), (nRequest), 0, 0)
|
||||
epicsShareFunc int dbCaIsLinkConnected(const struct link *plink);
|
||||
epicsShareFunc void dbCaScanFwdLink(struct link *plink);
|
||||
|
||||
/* The following are available after the link is connected*/
|
||||
epicsShareFunc long dbCaGetNelements(const struct link *plink,
|
||||
long *nelements);
|
||||
#define dbCaGetSevr(plink, severity) \
|
||||
dbCaGetAlarm((plink), NULL, (severity))
|
||||
epicsShareFunc long dbCaGetAlarm(const struct link *plink,
|
||||
epicsEnum16 *status, epicsEnum16 *severity);
|
||||
epicsShareFunc long dbCaGetTimeStamp(const struct link *plink,
|
||||
epicsTimeStamp *pstamp);
|
||||
epicsShareFunc int dbCaGetLinkDBFtype(const struct link *plink);
|
||||
|
||||
/*The following are available after attribute request is complete*/
|
||||
epicsShareFunc long dbCaGetAttributes(const struct link *plink,
|
||||
dbCaCallback callback, void *userPvt);
|
||||
epicsShareFunc long dbCaGetControlLimits(const struct link *plink,
|
||||
double *low, double *high);
|
||||
epicsShareFunc long dbCaGetGraphicLimits(const struct link *plink,
|
||||
double *low, double *high);
|
||||
epicsShareFunc long dbCaGetAlarmLimits(const struct link *plink,
|
||||
double *lolo, double *low, double *high, double *hihi);
|
||||
epicsShareFunc long dbCaGetPrecision(const struct link *plink,
|
||||
short *precision);
|
||||
epicsShareFunc long dbCaGetUnits(const struct link *plink,
|
||||
char *units, int unitsSize);
|
||||
epicsShareFunc long dbCaPutLink(struct link *plink,short dbrType,
|
||||
const void *pbuffer,long nRequest);
|
||||
|
||||
extern struct ca_client_context * dbCaClientContext;
|
||||
|
||||
#ifdef EPICS_DBCA_PRIVATE_API
|
||||
epicsShareFunc void dbCaSync(void);
|
||||
#endif
|
||||
|
||||
/* These macros are for backwards compatibility */
|
||||
|
||||
#define dbCaIsLinkConnected(link) \
|
||||
dbIsLinkConnected(link)
|
||||
|
||||
#define dbCaGetLinkDBFtype(link) \
|
||||
dbGetLinkDBFtype(link)
|
||||
#define dbCaGetNelements(link, nelements) \
|
||||
dbGetNelements(link, nelements)
|
||||
#define dbCaGetSevr(link, sevr) \
|
||||
dbGetAlarm(link, NULL, sevr)
|
||||
#define dbCaGetAlarm(link, stat, sevr) \
|
||||
dbGetAlarm(link, stat, sevr)
|
||||
#define dbCaGetTimeStamp(link, pstamp) \
|
||||
dbGetTimeStamp(link, pstamp)
|
||||
#define dbCaGetControlLimits(link, low, high) \
|
||||
dbGetControlLimits(link, low, high)
|
||||
#define dbCaGetGraphicLimits(link, low, high) \
|
||||
dbGetGraphicLimits(link, low, high)
|
||||
#define dbCaGetAlarmLimits(link, lolo, low, high, hihi) \
|
||||
dbGetAlarmLimits(link, lolo, low, high, hihi)
|
||||
#define dbCaGetPrecision(link, prec) \
|
||||
dbGetPrecision(link, prec)
|
||||
#define dbCaGetUnits(link, units, unitSize) \
|
||||
dbGetUnits(link, units, unitSize)
|
||||
|
||||
#define dbCaScanFwdLink(link) \
|
||||
dbScanFwdLink(link)
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -6,14 +6,11 @@
|
||||
* EPICS BASE is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
\*************************************************************************/
|
||||
/* dbCaPvt.h */
|
||||
/****************************************************************
|
||||
*
|
||||
* Current Author: Bob Dalesio
|
||||
* Contributing Author: Marty Kraimer
|
||||
* Date: 08APR96
|
||||
*
|
||||
****************************************************************/
|
||||
/* dbCaPvt.h
|
||||
*
|
||||
* Original Authors: Bob Dalesio, Marty Kraimer
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef INC_dbCaPvt_H
|
||||
#define INC_dbCaPvt_H
|
||||
@@ -32,61 +29,67 @@
|
||||
#define CA_MONITOR_NATIVE 0x10
|
||||
#define CA_MONITOR_STRING 0x20
|
||||
#define CA_GET_ATTRIBUTES 0x40
|
||||
#define CA_SYNC 0x1000
|
||||
/* write type */
|
||||
#define CA_PUT 0x1
|
||||
#define CA_PUT_CALLBACK 0x2
|
||||
|
||||
|
||||
typedef struct caLink
|
||||
{
|
||||
ELLNODE node;
|
||||
epicsMutexId lock;
|
||||
struct link *plink;
|
||||
char *pvname;
|
||||
chid chid;
|
||||
short link_action;
|
||||
/* The following have new values after each data event*/
|
||||
epicsEnum16 sevr;
|
||||
epicsEnum16 stat;
|
||||
epicsTimeStamp timeStamp;
|
||||
/* The following have values after connection*/
|
||||
short dbrType;
|
||||
long nelements;
|
||||
char hasReadAccess;
|
||||
char hasWriteAccess;
|
||||
char isConnected;
|
||||
char gotFirstConnection;
|
||||
/* The following are for dbCaAddLinkCallback */
|
||||
dbCaCallback connect;
|
||||
dbCaCallback monitor;
|
||||
void *userPvt;
|
||||
/* The following are for write request */
|
||||
short putType;
|
||||
dbCaCallback putCallback;
|
||||
void *putUserPvt;
|
||||
/* The following are for access to additional attributes*/
|
||||
char gotAttributes;
|
||||
dbCaCallback getAttributes;
|
||||
void *getAttributesPvt;
|
||||
/* The following have values after getAttribEventCallback*/
|
||||
double controlLimits[2];
|
||||
double displayLimits[2];
|
||||
double alarmLimits[4];
|
||||
short precision;
|
||||
char units[MAX_UNITS_SIZE]; /* units of value */
|
||||
/* The following are for handling data*/
|
||||
void *pgetNative;
|
||||
char *pgetString;
|
||||
void *pputNative;
|
||||
char *pputString;
|
||||
char gotInNative;
|
||||
char gotInString;
|
||||
char gotOutNative;
|
||||
char gotOutString;
|
||||
char newOutNative;
|
||||
char newOutString;
|
||||
/* The following are for dbcar*/
|
||||
unsigned long nDisconnect;
|
||||
unsigned long nNoWrite; /*only modified by dbCaPutLink*/
|
||||
ELLNODE node;
|
||||
int refcount;
|
||||
epicsMutexId lock;
|
||||
struct link *plink;
|
||||
char *pvname;
|
||||
chid chid;
|
||||
short link_action;
|
||||
/* The following have new values after each data event*/
|
||||
epicsEnum16 sevr;
|
||||
epicsEnum16 stat;
|
||||
epicsTimeStamp timeStamp;
|
||||
/* The following have values after connection*/
|
||||
short dbrType;
|
||||
size_t elementSize; /* size of one element in pgetNative */
|
||||
unsigned long nelements; /* PVs max array size */
|
||||
unsigned long usedelements; /* currently used in pgetNative */
|
||||
unsigned long putnelements; /* currently used in pputNative */
|
||||
char hasReadAccess;
|
||||
char hasWriteAccess;
|
||||
char isConnected;
|
||||
char gotFirstConnection;
|
||||
/* The following are for dbCaAddLinkCallback */
|
||||
dbCaCallback connect;
|
||||
dbCaCallback monitor;
|
||||
void *userPvt;
|
||||
/* The following are for write request */
|
||||
short putType;
|
||||
dbCaCallback putCallback;
|
||||
void *putUserPvt;
|
||||
/* The following are for access to additional attributes*/
|
||||
char gotAttributes;
|
||||
dbCaCallback getAttributes;
|
||||
void *getAttributesPvt;
|
||||
/* The following have values after getAttribEventCallback*/
|
||||
double controlLimits[2];
|
||||
double displayLimits[2];
|
||||
double alarmLimits[4];
|
||||
short precision;
|
||||
char units[MAX_UNITS_SIZE]; /* units of value */
|
||||
/* The following are for handling data*/
|
||||
void *pgetNative;
|
||||
char *pgetString;
|
||||
void *pputNative;
|
||||
char *pputString;
|
||||
char gotInNative;
|
||||
char gotInString;
|
||||
char gotOutNative;
|
||||
char gotOutString;
|
||||
char newOutNative;
|
||||
char newOutString;
|
||||
unsigned char scanningOnce;
|
||||
/* The following are for dbcar*/
|
||||
unsigned long nDisconnect;
|
||||
unsigned long nNoWrite; /*only modified by dbCaPutLink*/
|
||||
}caLink;
|
||||
|
||||
#endif /* INC_dbCaPvt_H */
|
||||
|
||||
@@ -87,10 +87,10 @@ long dbcar(char *precordname, int level)
|
||||
!dbIsAlias(pdbentry)) {
|
||||
pdbRecordType = pdbentry->precordType;
|
||||
precord = (dbCommon *)pdbentry->precnode->precord;
|
||||
dbScanLock(precord);
|
||||
for (j=0; j<pdbRecordType->no_links; j++) {
|
||||
pdbFldDes = pdbRecordType->papFldDes[pdbRecordType->link_ind[j]];
|
||||
plink = (DBLINK *)((char *)precord + pdbFldDes->offset);
|
||||
dbLockSetGblLock();
|
||||
if (plink->type == CA_LINK) {
|
||||
ncalinks++;
|
||||
pca = (caLink *)plink->value.pv_link.pvt;
|
||||
@@ -135,8 +135,8 @@ long dbcar(char *precordname, int level)
|
||||
}
|
||||
}
|
||||
}
|
||||
dbLockSetGblUnlock();
|
||||
}
|
||||
dbScanUnlock(precord);
|
||||
if (precordname) goto done;
|
||||
}
|
||||
status = dbNextRecord(pdbentry);
|
||||
@@ -189,10 +189,8 @@ void dbcaStats(int *pchans, int *pdiscon)
|
||||
dbFldDes *pdbFldDes = pdbRecordType->papFldDes[i];
|
||||
plink = (DBLINK *)((char *)precord + pdbFldDes->offset);
|
||||
if (plink->type == CA_LINK) {
|
||||
caLink *pca = (caLink *)plink->value.pv_link.pvt;
|
||||
|
||||
ncalinks++;
|
||||
if (pca && pca->chid && ca_state(pca->chid) == cs_conn) {
|
||||
if (dbCaIsLinkConnected(plink)) {
|
||||
nconnected++;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,7 +19,6 @@
|
||||
|
||||
#include "cantProceed.h"
|
||||
#include "epicsAssert.h"
|
||||
#include "epicsExit.h"
|
||||
#include "epicsString.h"
|
||||
#include "errlog.h"
|
||||
#include "freeList.h"
|
||||
@@ -704,21 +703,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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
118
src/ioc/db/dbChannelNOOP.h
Normal file
118
src/ioc/db/dbChannelNOOP.h
Normal file
@@ -0,0 +1,118 @@
|
||||
#ifndef DBCHANNELNOOP_H
|
||||
#define DBCHANNELNOOP_H
|
||||
|
||||
#include <string.h>
|
||||
#include <string>
|
||||
|
||||
#include "cacIO.h"
|
||||
#include "caerr.h"
|
||||
|
||||
/** @brief A channel which never connects
|
||||
*
|
||||
* Used when dbCa is placed in isolated mode for unittests
|
||||
*/
|
||||
class dbChannelNOOP : public cacChannel
|
||||
{
|
||||
std::string myname;
|
||||
public:
|
||||
dbChannelNOOP(const char *name, cacChannelNotify ¬ify)
|
||||
:cacChannel(notify)
|
||||
,myname(name)
|
||||
{}
|
||||
|
||||
virtual void destroy (
|
||||
CallbackGuard & /*callbackGuard*/,
|
||||
epicsGuard < epicsMutex > & /*mutualExclusionGuard*/ )
|
||||
{
|
||||
delete this; // goodbye cruel world
|
||||
}
|
||||
|
||||
virtual unsigned getName (
|
||||
epicsGuard < epicsMutex > &,
|
||||
char * pBuf, unsigned bufLen ) const throw ()
|
||||
{
|
||||
const char* name = myname.c_str();
|
||||
if(bufLen>myname.size()+1) {
|
||||
bufLen=myname.size()+1;
|
||||
}
|
||||
memcpy(pBuf, name, bufLen);
|
||||
pBuf[--bufLen] = '\0';
|
||||
return bufLen;
|
||||
}
|
||||
|
||||
// !! deprecated, avoid use !!
|
||||
virtual const char * pName (
|
||||
epicsGuard < epicsMutex > & guard ) const throw ()
|
||||
{return myname.c_str();}
|
||||
|
||||
virtual void show (
|
||||
epicsGuard < epicsMutex > &,
|
||||
unsigned level ) const
|
||||
{}
|
||||
|
||||
virtual void initiateConnect (
|
||||
epicsGuard < epicsMutex > & )
|
||||
{}
|
||||
|
||||
virtual unsigned requestMessageBytesPending (
|
||||
epicsGuard < epicsMutex > & /*mutualExclusionGuard*/ )
|
||||
{return 0;}
|
||||
|
||||
virtual void flush (
|
||||
epicsGuard < epicsMutex > & /*mutualExclusionGuard*/ )
|
||||
{}
|
||||
|
||||
virtual ioStatus read (
|
||||
epicsGuard < epicsMutex > &mut,
|
||||
unsigned type, arrayElementCount count,
|
||||
cacReadNotify ¬ify, ioid * = 0 )
|
||||
{
|
||||
notify.exception(mut, ECA_NORDACCESS, "dbChannelNOOP", type, count);
|
||||
return iosSynch;
|
||||
}
|
||||
|
||||
virtual void write (
|
||||
epicsGuard < epicsMutex > &,
|
||||
unsigned type, arrayElementCount count,
|
||||
const void *pValue )
|
||||
{}
|
||||
|
||||
virtual ioStatus write (
|
||||
epicsGuard < epicsMutex > &mut,
|
||||
unsigned type, arrayElementCount count,
|
||||
const void */*pValue*/, cacWriteNotify & notify, ioid * = 0 )
|
||||
{
|
||||
notify.exception(mut, ECA_NOWTACCESS, "dbChannelNOOP", type, count);
|
||||
return iosSynch;
|
||||
}
|
||||
|
||||
virtual void subscribe (
|
||||
epicsGuard < epicsMutex > &mut, unsigned type,
|
||||
arrayElementCount count, unsigned /*mask*/, cacStateNotify & notify,
|
||||
ioid * = 0 )
|
||||
{
|
||||
// should never subscribe
|
||||
notify.exception(mut, ECA_BADMASK, "dbChannelNOOP", type, count);
|
||||
}
|
||||
|
||||
virtual void ioCancel (
|
||||
CallbackGuard & callbackGuard,
|
||||
epicsGuard < epicsMutex > & mutualExclusionGuard,
|
||||
const ioid & )
|
||||
{}
|
||||
|
||||
virtual void ioShow (
|
||||
epicsGuard < epicsMutex > &,
|
||||
const ioid &, unsigned level ) const
|
||||
{}
|
||||
|
||||
virtual short nativeType (
|
||||
epicsGuard < epicsMutex > & ) const
|
||||
{return 0;} // DBR_STRING
|
||||
|
||||
virtual arrayElementCount nativeElementCount (
|
||||
epicsGuard < epicsMutex > & ) const
|
||||
{return 1;}
|
||||
};
|
||||
|
||||
#endif // DBCHANNELNOOP_H
|
||||
@@ -92,6 +92,12 @@
|
||||
interest(4)
|
||||
extra("ELLLIST mlis")
|
||||
}
|
||||
field(BKLNK,DBF_NOACCESS) {
|
||||
prompt("Backwards link tracking")
|
||||
special(SPC_NOMOD)
|
||||
interest(4)
|
||||
extra("ELLLIST bklnk")
|
||||
}
|
||||
field(DISP,DBF_UCHAR) {
|
||||
prompt("Disable putField")
|
||||
}
|
||||
|
||||
@@ -39,6 +39,7 @@
|
||||
#include "dbCAC.h"
|
||||
#include "dbChannel.h"
|
||||
#include "dbChannelIO.h"
|
||||
#include "dbChannelNOOP.h"
|
||||
#include "dbPutNotifyBlocker.h"
|
||||
|
||||
class dbService : public cacService {
|
||||
@@ -61,9 +62,16 @@ cacContext & dbService::contextCreate (
|
||||
mutualExclusion, notify );
|
||||
}
|
||||
|
||||
extern "C" int dbServiceIsolate;
|
||||
int dbServiceIsolate = 0;
|
||||
|
||||
extern "C" void dbServiceIOInit ()
|
||||
{
|
||||
caInstallDefaultService ( dbs );
|
||||
static int init=0;
|
||||
if(!init) {
|
||||
caInstallDefaultService ( dbs );
|
||||
init=1;
|
||||
}
|
||||
}
|
||||
|
||||
dbBaseIO::dbBaseIO () {}
|
||||
@@ -72,7 +80,8 @@ dbContext::dbContext ( epicsMutex & cbMutexIn,
|
||||
epicsMutex & mutexIn, cacContextNotify & notifyIn ) :
|
||||
readNotifyCache ( mutexIn ), ctx ( 0 ),
|
||||
stateNotifyCacheSize ( 0 ), mutex ( mutexIn ), cbMutex ( cbMutexIn ),
|
||||
notify ( notifyIn ), pNetContext ( 0 ), pStateNotifyCache ( 0 )
|
||||
notify ( notifyIn ), pNetContext ( 0 ), pStateNotifyCache ( 0 ),
|
||||
isolated(dbServiceIsolate)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -92,7 +101,10 @@ cacChannel & dbContext::createChannel (
|
||||
|
||||
dbChannel *dbch = dbChannel_create ( pName );
|
||||
if ( ! dbch ) {
|
||||
if ( ! this->pNetContext.get() ) {
|
||||
if ( isolated ) {
|
||||
return *new dbChannelNOOP(pName, notifyIn);
|
||||
|
||||
} else if ( ! this->pNetContext.get() ) {
|
||||
this->pNetContext.reset (
|
||||
& this->notify.createNetworkContext (
|
||||
this->mutex, this->cbMutex ) );
|
||||
@@ -142,7 +154,8 @@ void dbContext::callStateNotify ( struct dbChannel * dbch,
|
||||
const struct db_field_log * pfl,
|
||||
cacStateNotify & notifyIn )
|
||||
{
|
||||
unsigned long size = dbr_size_n ( type, count );
|
||||
long realcount = (count==0)?dbChannelElements(dbch):count;
|
||||
unsigned long size = dbr_size_n ( type, realcount );
|
||||
|
||||
if ( type > INT_MAX ) {
|
||||
epicsGuard < epicsMutex > guard ( this->mutex );
|
||||
@@ -169,8 +182,13 @@ void dbContext::callStateNotify ( struct dbChannel * dbch,
|
||||
this->stateNotifyCacheSize = size;
|
||||
}
|
||||
void *pvfl = (void *) pfl;
|
||||
int status = dbChannel_get ( dbch, static_cast <int> ( type ),
|
||||
this->pStateNotifyCache, static_cast <int> ( count ), pvfl );
|
||||
int status;
|
||||
if(count==0) /* fetch actual number of elements (dynamic array) */
|
||||
status = dbChannel_get_count( dbch, static_cast <int> ( type ),
|
||||
this->pStateNotifyCache, &realcount, pvfl );
|
||||
else /* fetch requested number of elements, truncated or zero padded */
|
||||
status = dbChannel_get( dbch, static_cast <int> ( type ),
|
||||
this->pStateNotifyCache, realcount, pvfl );
|
||||
if ( status ) {
|
||||
epicsGuard < epicsMutex > guard ( this->mutex );
|
||||
notifyIn.exception ( guard, ECA_GETFAIL,
|
||||
@@ -178,7 +196,7 @@ void dbContext::callStateNotify ( struct dbChannel * dbch,
|
||||
}
|
||||
else {
|
||||
epicsGuard < epicsMutex > guard ( this->mutex );
|
||||
notifyIn.current ( guard, type, count, this->pStateNotifyCache );
|
||||
notifyIn.current ( guard, type, realcount, this->pStateNotifyCache );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -61,27 +61,32 @@ void dbContextReadNotifyCache::callReadNotify (
|
||||
return;
|
||||
}
|
||||
|
||||
if ( dbChannelElements(dbch) < 0 ) {
|
||||
const long maxcount = dbChannelElements(dbch);
|
||||
|
||||
if ( maxcount < 0 ) {
|
||||
notify.exception ( guard, ECA_BADCOUNT,
|
||||
"database has negetive element count",
|
||||
type, count);
|
||||
return;
|
||||
}
|
||||
|
||||
if ( count > static_cast < unsigned long > ( dbChannelElements(dbch) ) ) {
|
||||
} else if ( count > (unsigned long)maxcount ) {
|
||||
notify.exception ( guard, ECA_BADCOUNT,
|
||||
"element count out of range (high side)",
|
||||
type, count);
|
||||
return;
|
||||
}
|
||||
|
||||
unsigned long size = dbr_size_n ( type, count );
|
||||
long realcount = (count==0)?maxcount:count;
|
||||
unsigned long size = dbr_size_n ( type, realcount );
|
||||
|
||||
privateAutoDestroyPtr ptr ( _allocator, size );
|
||||
int status;
|
||||
{
|
||||
epicsGuardRelease < epicsMutex > unguard ( guard );
|
||||
status = dbChannel_get ( dbch, static_cast <int> ( type ),
|
||||
ptr.get (), static_cast <long> ( count ), 0 );
|
||||
if ( count==0 )
|
||||
status = dbChannel_get_count ( dbch, (int)type, ptr.get(), &realcount, 0);
|
||||
else
|
||||
status = dbChannel_get ( dbch, (int)type, ptr.get (), realcount, 0 );
|
||||
}
|
||||
if ( status ) {
|
||||
notify.exception ( guard, ECA_GETFAIL,
|
||||
@@ -90,7 +95,7 @@ void dbContextReadNotifyCache::callReadNotify (
|
||||
}
|
||||
else {
|
||||
notify.completion (
|
||||
guard, type, count, ptr.get () );
|
||||
guard, type, realcount, ptr.get () );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
/*************************************************************************\
|
||||
* Copyright (c) 2010 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.
|
||||
\*************************************************************************/
|
||||
/* dbLink.c */
|
||||
/* $Id$ */
|
||||
/*
|
||||
* 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.
|
||||
\*************************************************************************/
|
||||
/* dbLink.c
|
||||
*
|
||||
* Original Authors: Bob Dalesio, Marty Kraimer
|
||||
* Current Author: Andrew Johnson
|
||||
*/
|
||||
@@ -45,7 +44,7 @@
|
||||
#include "dbFldTypes.h"
|
||||
#include "dbFldTypes.h"
|
||||
#include "dbLink.h"
|
||||
#include "dbLock.h"
|
||||
#include "dbLockPvt.h"
|
||||
#include "dbNotify.h"
|
||||
#include "dbScan.h"
|
||||
#include "dbStaticLib.h"
|
||||
@@ -79,7 +78,7 @@ static void inherit_severity(const struct pv_link *ppv_link, dbCommon *pdest,
|
||||
/* How to identify links in error messages */
|
||||
static const char * link_field_name(const struct link *plink)
|
||||
{
|
||||
const struct dbCommon *precord = plink->value.pv_link.precord;
|
||||
const struct dbCommon *precord = plink->precord;
|
||||
const dbRecordType *pdbRecordType = precord->rdes;
|
||||
dbFldDes * const *papFldDes = pdbRecordType->papFldDes;
|
||||
const short *link_ind = pdbRecordType->link_ind;
|
||||
@@ -97,13 +96,28 @@ static const char * link_field_name(const struct link *plink)
|
||||
|
||||
/***************************** Constant Links *****************************/
|
||||
|
||||
/* Forward definition */
|
||||
static lset dbConst_lset;
|
||||
|
||||
static void dbConstInitLink(struct link *plink)
|
||||
{
|
||||
plink->lset = &dbConst_lset;
|
||||
}
|
||||
|
||||
static void dbConstAddLink(struct link *plink)
|
||||
{
|
||||
plink->lset = &dbConst_lset;
|
||||
}
|
||||
|
||||
static long dbConstLoadLink(struct link *plink, short dbrType, void *pbuffer)
|
||||
{
|
||||
if (!plink->value.constantStr)
|
||||
return S_db_badField;
|
||||
|
||||
plink->lset = &dbConst_lset;
|
||||
|
||||
/* Constant strings are always numeric */
|
||||
if (dbrType== DBF_MENU || dbrType == DBF_ENUM || dbrType == DBF_DEVICE)
|
||||
if (dbrType == DBF_MENU || dbrType == DBF_ENUM || dbrType == DBF_DEVICE)
|
||||
dbrType = DBF_USHORT;
|
||||
|
||||
return dbFastPutConvertRoutine[DBR_STRING][dbrType]
|
||||
@@ -116,7 +130,7 @@ static long dbConstGetNelements(const struct link *plink, long *nelements)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long dbConstGetLink(struct link *plink, short dbrType, void *pbuffer,
|
||||
static long dbConstGetValue(struct link *plink, short dbrType, void *pbuffer,
|
||||
epicsEnum16 *pstat, epicsEnum16 *psevr, long *pnRequest)
|
||||
{
|
||||
if (pnRequest)
|
||||
@@ -124,8 +138,23 @@ static long dbConstGetLink(struct link *plink, short dbrType, void *pbuffer,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static lset dbConst_lset = {
|
||||
NULL,
|
||||
NULL,
|
||||
NULL, dbConstGetNelements,
|
||||
dbConstGetValue,
|
||||
NULL, NULL, NULL,
|
||||
NULL, NULL,
|
||||
NULL, NULL,
|
||||
NULL,
|
||||
NULL
|
||||
};
|
||||
|
||||
/***************************** Database Links *****************************/
|
||||
|
||||
/* Forward definition */
|
||||
static lset dbDb_lset;
|
||||
|
||||
static long dbDbInitLink(struct link *plink, short dbfType)
|
||||
{
|
||||
DBADDR dbaddr;
|
||||
@@ -136,45 +165,46 @@ static long dbDbInitLink(struct link *plink, short dbfType)
|
||||
if (status)
|
||||
return status;
|
||||
|
||||
plink->lset = &dbDb_lset;
|
||||
plink->type = DB_LINK;
|
||||
pdbAddr = dbCalloc(1, sizeof(struct dbAddr));
|
||||
*pdbAddr = dbaddr; /* structure copy */
|
||||
plink->value.pv_link.pvt = pdbAddr;
|
||||
dbLockSetMerge(plink->value.pv_link.precord, pdbAddr->precord);
|
||||
ellAdd(&dbaddr.precord->bklnk, &plink->value.pv_link.backlinknode);
|
||||
/* merging into the same lockset is deferred to the caller.
|
||||
* cf. initPVLinks()
|
||||
*/
|
||||
dbLockSetMerge(NULL, plink->precord, dbaddr.precord);
|
||||
assert(plink->precord->lset->plockSet == dbaddr.precord->lset->plockSet);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long dbDbAddLink(struct link *plink, short dbfType)
|
||||
static void dbDbAddLink(dbLocker *locker, struct link *plink, short dbfType, DBADDR *ptarget)
|
||||
{
|
||||
DBADDR dbaddr;
|
||||
long status;
|
||||
DBADDR *pdbAddr;
|
||||
|
||||
status = dbNameToAddr(plink->value.pv_link.pvname, &dbaddr);
|
||||
if (status)
|
||||
return status;
|
||||
|
||||
plink->lset = &dbDb_lset;
|
||||
plink->type = DB_LINK;
|
||||
pdbAddr = dbCalloc(1, sizeof(struct dbAddr));
|
||||
*pdbAddr = dbaddr; /* structure copy */
|
||||
plink->value.pv_link.pvt = pdbAddr;
|
||||
plink->value.pv_link.pvt = ptarget;
|
||||
ellAdd(&ptarget->precord->bklnk, &plink->value.pv_link.backlinknode);
|
||||
|
||||
dbLockSetRecordLock(pdbAddr->precord);
|
||||
dbLockSetMerge(plink->value.pv_link.precord, pdbAddr->precord);
|
||||
return 0;
|
||||
/* target record is already locked in dbPutFieldLink() */
|
||||
dbLockSetMerge(locker, plink->precord, ptarget->precord);
|
||||
}
|
||||
|
||||
static void dbDbRemoveLink(struct link *plink)
|
||||
static void dbDbRemoveLink(dbLocker *locker, struct link *plink)
|
||||
{
|
||||
free(plink->value.pv_link.pvt);
|
||||
DBADDR *pdbAddr = (DBADDR *) plink->value.pv_link.pvt;
|
||||
plink->value.pv_link.pvt = 0;
|
||||
plink->value.pv_link.getCvt = 0;
|
||||
plink->value.pv_link.pvlMask = 0;
|
||||
plink->value.pv_link.lastGetdbrType = 0;
|
||||
plink->type = PV_LINK;
|
||||
dbLockSetSplit(plink->value.pv_link.precord);
|
||||
plink->lset = NULL;
|
||||
ellDelete(&pdbAddr->precord->bklnk, &plink->value.pv_link.backlinknode);
|
||||
dbLockSetSplit(locker, plink->precord, pdbAddr->precord);
|
||||
free(pdbAddr);
|
||||
}
|
||||
|
||||
static int dbDbIsLinkConnected(const struct link *plink)
|
||||
static int dbDbIsConnected(const struct link *plink)
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
@@ -199,7 +229,7 @@ static long dbDbGetValue(struct link *plink, short dbrType, void *pbuffer,
|
||||
{
|
||||
struct pv_link *ppv_link = &plink->value.pv_link;
|
||||
DBADDR *paddr = ppv_link->pvt;
|
||||
dbCommon *precord = plink->value.pv_link.precord;
|
||||
dbCommon *precord = plink->precord;
|
||||
long status;
|
||||
|
||||
/* scan passive records if link is process passive */
|
||||
@@ -363,7 +393,7 @@ static long dbDbPutValue(struct link *plink, short dbrType,
|
||||
const void *pbuffer, long nRequest)
|
||||
{
|
||||
struct pv_link *ppv_link = &plink->value.pv_link;
|
||||
struct dbCommon *psrce = ppv_link->precord;
|
||||
struct dbCommon *psrce = plink->precord;
|
||||
DBADDR *paddr = (DBADDR *) ppv_link->pvt;
|
||||
dbCommon *pdest = paddr->precord;
|
||||
long status = dbPut(paddr, dbrType, pbuffer, nRequest);
|
||||
@@ -394,23 +424,37 @@ static long dbDbPutValue(struct link *plink, short dbrType,
|
||||
|
||||
static void dbDbScanFwdLink(struct link *plink)
|
||||
{
|
||||
dbCommon *precord = plink->value.pv_link.precord;
|
||||
dbCommon *precord = plink->precord;
|
||||
dbAddr *paddr = (dbAddr *) plink->value.pv_link.pvt;
|
||||
|
||||
dbScanPassive(precord, paddr->precord);
|
||||
}
|
||||
|
||||
lset dbDb_lset = { dbDbInitLink, dbDbAddLink, NULL, dbDbRemoveLink,
|
||||
dbDbIsLinkConnected, dbDbGetDBFtype, dbDbGetElements, dbDbGetValue,
|
||||
dbDbGetControlLimits, dbDbGetGraphicLimits, dbDbGetAlarmLimits,
|
||||
dbDbGetPrecision, dbDbGetUnits, dbDbGetAlarm, dbDbGetTimeStamp,
|
||||
dbDbPutValue, dbDbScanFwdLink };
|
||||
static lset dbDb_lset = {
|
||||
dbDbRemoveLink,
|
||||
dbDbIsConnected,
|
||||
dbDbGetDBFtype, dbDbGetElements,
|
||||
dbDbGetValue,
|
||||
dbDbGetControlLimits, dbDbGetGraphicLimits, dbDbGetAlarmLimits,
|
||||
dbDbGetPrecision, dbDbGetUnits,
|
||||
dbDbGetAlarm, dbDbGetTimeStamp,
|
||||
dbDbPutValue,
|
||||
dbDbScanFwdLink
|
||||
};
|
||||
|
||||
/***************************** Generic Link API *****************************/
|
||||
|
||||
void dbInitLink(struct dbCommon *precord, struct link *plink, short dbfType)
|
||||
void dbInitLink(struct link *plink, short dbfType)
|
||||
{
|
||||
plink->value.pv_link.precord = precord;
|
||||
struct dbCommon *precord = plink->precord;
|
||||
|
||||
if (plink->type == CONSTANT) {
|
||||
dbConstInitLink(plink);
|
||||
return;
|
||||
}
|
||||
|
||||
if (plink->type != PV_LINK)
|
||||
return;
|
||||
|
||||
if (plink == &precord->tsel)
|
||||
recGblTSELwasModified(plink);
|
||||
@@ -425,7 +469,7 @@ void dbInitLink(struct dbCommon *precord, struct link *plink, short dbfType)
|
||||
if (dbfType == DBF_INLINK)
|
||||
plink->value.pv_link.pvlMask |= pvlOptInpNative;
|
||||
|
||||
dbCaAddLink(plink);
|
||||
dbCaAddLink(NULL, plink, dbfType);
|
||||
if (dbfType == DBF_FWDLINK) {
|
||||
char *pperiod = strrchr(plink->value.pv_link.pvname, '.');
|
||||
|
||||
@@ -442,24 +486,29 @@ void dbInitLink(struct dbCommon *precord, struct link *plink, short dbfType)
|
||||
}
|
||||
}
|
||||
|
||||
void dbAddLink(struct dbCommon *precord, struct link *plink, short dbfType)
|
||||
void dbAddLink(dbLocker *locker, struct link *plink, short dbfType, DBADDR *ptarget)
|
||||
{
|
||||
plink->value.pv_link.precord = precord;
|
||||
struct dbCommon *precord = plink->precord;
|
||||
|
||||
if (plink->type == CONSTANT) {
|
||||
dbConstAddLink(plink);
|
||||
return;
|
||||
}
|
||||
|
||||
if (plink == &precord->tsel)
|
||||
recGblTSELwasModified(plink);
|
||||
|
||||
if (!(plink->value.pv_link.pvlMask & (pvlOptCA | pvlOptCP | pvlOptCPP))) {
|
||||
/* Can we make it a DB link? */
|
||||
if (!dbDbAddLink(plink, dbfType))
|
||||
return;
|
||||
if (ptarget) {
|
||||
/* It's a DB link */
|
||||
dbDbAddLink(locker, plink, dbfType, ptarget);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Make it a CA link */
|
||||
if (dbfType == DBF_INLINK)
|
||||
plink->value.pv_link.pvlMask |= pvlOptInpNative;
|
||||
|
||||
dbCaAddLink(plink);
|
||||
dbCaAddLink(locker, plink, dbfType);
|
||||
if (dbfType == DBF_FWDLINK) {
|
||||
char *pperiod = strrchr(plink->value.pv_link.pvname, '.');
|
||||
|
||||
@@ -470,69 +519,60 @@ void dbAddLink(struct dbCommon *precord, struct link *plink, short dbfType)
|
||||
|
||||
long dbLoadLink(struct link *plink, short dbrType, void *pbuffer)
|
||||
{
|
||||
switch (plink->type) {
|
||||
case CONSTANT:
|
||||
if (plink->type == CONSTANT)
|
||||
return dbConstLoadLink(plink, dbrType, pbuffer);
|
||||
}
|
||||
|
||||
/* Could pass a type hint to the other link types here */
|
||||
return S_db_notFound;
|
||||
}
|
||||
|
||||
void dbRemoveLink(struct link *plink)
|
||||
void dbRemoveLink(dbLocker *locker, struct link *plink)
|
||||
{
|
||||
switch (plink->type) {
|
||||
case DB_LINK:
|
||||
dbDbRemoveLink(plink);
|
||||
break;
|
||||
case CA_LINK:
|
||||
dbCaRemoveLink(plink);
|
||||
break;
|
||||
default:
|
||||
cantProceed("dbRemoveLink: Unexpected link type %d\n", plink->type);
|
||||
lset *plset = plink->lset;
|
||||
|
||||
if (plset) {
|
||||
if (plset->removeLink)
|
||||
plset->removeLink(locker, plink);
|
||||
plink->lset = NULL;
|
||||
}
|
||||
plink->type = PV_LINK;
|
||||
plink->value.pv_link.pvlMask = 0;
|
||||
}
|
||||
|
||||
int dbIsLinkConnected(const struct link *plink)
|
||||
{
|
||||
switch (plink->type) {
|
||||
case DB_LINK:
|
||||
return dbDbIsLinkConnected(plink);
|
||||
case CA_LINK:
|
||||
return dbCaIsLinkConnected(plink);
|
||||
}
|
||||
return FALSE;
|
||||
lset *plset = plink->lset;
|
||||
|
||||
if (!plset || !plset->isConnected)
|
||||
return FALSE;
|
||||
|
||||
return plset->isConnected(plink);
|
||||
}
|
||||
|
||||
int dbGetLinkDBFtype(const struct link *plink)
|
||||
{
|
||||
switch (plink->type) {
|
||||
case DB_LINK:
|
||||
return dbDbGetDBFtype(plink);
|
||||
case CA_LINK:
|
||||
return dbCaGetLinkDBFtype(plink);
|
||||
}
|
||||
return -1;
|
||||
lset *plset = plink->lset;
|
||||
|
||||
if (!plset || !plset->getDBFtype)
|
||||
return -1;
|
||||
|
||||
return plset->getDBFtype(plink);
|
||||
}
|
||||
|
||||
long dbGetNelements(const struct link *plink, long *nelements)
|
||||
{
|
||||
switch (plink->type) {
|
||||
case CONSTANT:
|
||||
return dbConstGetNelements(plink, nelements);
|
||||
case DB_LINK:
|
||||
return dbDbGetElements(plink, nelements);
|
||||
case CA_LINK:
|
||||
return dbCaGetNelements(plink, nelements);
|
||||
}
|
||||
return S_db_badField;
|
||||
lset *plset = plink->lset;
|
||||
|
||||
if (!plset || !plset->getElements)
|
||||
return S_db_badField;
|
||||
|
||||
return plset->getElements(plink, nelements);
|
||||
}
|
||||
|
||||
long dbGetLink(struct link *plink, short dbrType, void *pbuffer,
|
||||
long *poptions, long *pnRequest)
|
||||
{
|
||||
struct dbCommon *precord = plink->value.pv_link.precord;
|
||||
struct dbCommon *precord = plink->precord;
|
||||
epicsEnum16 sevr = 0, stat = 0;
|
||||
lset *plset = plink->lset;
|
||||
long status;
|
||||
|
||||
if (poptions && *poptions) {
|
||||
@@ -540,21 +580,10 @@ long dbGetLink(struct link *plink, short dbrType, void *pbuffer,
|
||||
*poptions = 0;
|
||||
}
|
||||
|
||||
switch (plink->type) {
|
||||
case CONSTANT:
|
||||
status = dbConstGetLink(plink, dbrType, pbuffer, &stat, &sevr,
|
||||
pnRequest);
|
||||
break;
|
||||
case DB_LINK:
|
||||
status = dbDbGetValue(plink, dbrType, pbuffer, &stat, &sevr, pnRequest);
|
||||
break;
|
||||
case CA_LINK:
|
||||
status = dbCaGetLink(plink, dbrType, pbuffer, &stat, &sevr, pnRequest);
|
||||
break;
|
||||
default:
|
||||
cantProceed("dbGetLinkValue: Illegal link type %d\n", plink->type);
|
||||
status = -1;
|
||||
}
|
||||
if (!plset || !plset->getValue)
|
||||
return -1;
|
||||
|
||||
status = plset->getValue(plink, dbrType, pbuffer, &stat, &sevr, pnRequest);
|
||||
if (status) {
|
||||
recGblSetSevr(precord, LINK_ALARM, INVALID_ALARM);
|
||||
} else {
|
||||
@@ -565,104 +594,88 @@ long dbGetLink(struct link *plink, short dbrType, void *pbuffer,
|
||||
|
||||
long dbGetControlLimits(const struct link *plink, double *low, double *high)
|
||||
{
|
||||
switch (plink->type) {
|
||||
case DB_LINK:
|
||||
return dbDbGetControlLimits(plink, low, high);
|
||||
case CA_LINK:
|
||||
return dbCaGetControlLimits(plink, low, high);
|
||||
}
|
||||
return S_db_notFound;
|
||||
lset *plset = plink->lset;
|
||||
|
||||
if (!plset || !plset->getControlLimits)
|
||||
return S_db_notFound;
|
||||
|
||||
return plset->getControlLimits(plink, low, high);
|
||||
}
|
||||
|
||||
long dbGetGraphicLimits(const struct link *plink, double *low, double *high)
|
||||
{
|
||||
switch (plink->type) {
|
||||
case DB_LINK:
|
||||
return dbDbGetGraphicLimits(plink, low, high);
|
||||
case CA_LINK:
|
||||
return dbCaGetGraphicLimits(plink, low, high);
|
||||
}
|
||||
return S_db_notFound;
|
||||
lset *plset = plink->lset;
|
||||
|
||||
if (!plset || !plset->getGraphicLimits)
|
||||
return S_db_notFound;
|
||||
|
||||
return plset->getGraphicLimits(plink, low, high);
|
||||
}
|
||||
|
||||
long dbGetAlarmLimits(const struct link *plink, double *lolo, double *low,
|
||||
double *high, double *hihi)
|
||||
{
|
||||
switch (plink->type) {
|
||||
case DB_LINK:
|
||||
return dbDbGetAlarmLimits(plink, lolo, low, high, hihi);
|
||||
case CA_LINK:
|
||||
return dbCaGetAlarmLimits(plink, lolo, low, high, hihi);
|
||||
}
|
||||
return S_db_notFound;
|
||||
lset *plset = plink->lset;
|
||||
|
||||
if (!plset || !plset->getAlarmLimits)
|
||||
return S_db_notFound;
|
||||
|
||||
return plset->getAlarmLimits(plink, lolo, low, high, hihi);
|
||||
}
|
||||
|
||||
long dbGetPrecision(const struct link *plink, short *precision)
|
||||
{
|
||||
switch (plink->type) {
|
||||
case DB_LINK:
|
||||
return dbDbGetPrecision(plink, precision);
|
||||
case CA_LINK:
|
||||
return dbCaGetPrecision(plink, precision);
|
||||
}
|
||||
return S_db_notFound;
|
||||
lset *plset = plink->lset;
|
||||
|
||||
if (!plset || !plset->getPrecision)
|
||||
return S_db_notFound;
|
||||
|
||||
return plset->getPrecision(plink, precision);
|
||||
}
|
||||
|
||||
long dbGetUnits(const struct link *plink, char *units, int unitsSize)
|
||||
{
|
||||
switch (plink->type) {
|
||||
case DB_LINK:
|
||||
return dbDbGetUnits(plink, units, unitsSize);
|
||||
case CA_LINK:
|
||||
return dbCaGetUnits(plink, units, unitsSize);
|
||||
}
|
||||
return S_db_notFound;
|
||||
lset *plset = plink->lset;
|
||||
|
||||
if (!plset || !plset->getUnits)
|
||||
return S_db_notFound;
|
||||
|
||||
return plset->getUnits(plink, units, unitsSize);
|
||||
}
|
||||
|
||||
long dbGetAlarm(const struct link *plink, epicsEnum16 *status,
|
||||
epicsEnum16 *severity)
|
||||
{
|
||||
switch (plink->type) {
|
||||
case DB_LINK:
|
||||
return dbDbGetAlarm(plink, status, severity);
|
||||
case CA_LINK:
|
||||
return dbCaGetAlarm(plink, status, severity);
|
||||
}
|
||||
return S_db_notFound;
|
||||
lset *plset = plink->lset;
|
||||
|
||||
if (!plset || !plset->getAlarm)
|
||||
return S_db_notFound;
|
||||
|
||||
return plset->getAlarm(plink, status, severity);
|
||||
}
|
||||
|
||||
long dbGetTimeStamp(const struct link *plink, epicsTimeStamp *pstamp)
|
||||
{
|
||||
switch (plink->type) {
|
||||
case DB_LINK:
|
||||
return dbDbGetTimeStamp(plink, pstamp);
|
||||
case CA_LINK:
|
||||
return dbCaGetTimeStamp(plink, pstamp);
|
||||
}
|
||||
return S_db_notFound;
|
||||
lset *plset = plink->lset;
|
||||
|
||||
if (!plset || !plset->getTimeStamp)
|
||||
return S_db_notFound;
|
||||
|
||||
return plset->getTimeStamp(plink, pstamp);
|
||||
}
|
||||
|
||||
long dbPutLink(struct link *plink, short dbrType, const void *pbuffer,
|
||||
long nRequest)
|
||||
{
|
||||
lset *plset = plink->lset;
|
||||
long status;
|
||||
|
||||
switch (plink->type) {
|
||||
case CONSTANT:
|
||||
status = 0;
|
||||
break;
|
||||
case DB_LINK:
|
||||
status = dbDbPutValue(plink, dbrType, pbuffer, nRequest);
|
||||
break;
|
||||
case CA_LINK:
|
||||
status = dbCaPutLink(plink, dbrType, pbuffer, nRequest);
|
||||
break;
|
||||
default:
|
||||
cantProceed("dbPutLinkValue: Illegal link type %d\n", plink->type);
|
||||
status = -1;
|
||||
}
|
||||
if (!plset || !plset->putValue)
|
||||
return S_db_notFound;
|
||||
|
||||
status = plset->putValue(plink, dbrType, pbuffer, nRequest);
|
||||
if (status) {
|
||||
struct dbCommon *precord = plink->value.pv_link.precord;
|
||||
struct dbCommon *precord = plink->precord;
|
||||
|
||||
recGblSetSevr(precord, LINK_ALARM, INVALID_ALARM);
|
||||
}
|
||||
@@ -671,14 +684,10 @@ long dbPutLink(struct link *plink, short dbrType, const void *pbuffer,
|
||||
|
||||
void dbScanFwdLink(struct link *plink)
|
||||
{
|
||||
switch (plink->type) {
|
||||
case DB_LINK:
|
||||
dbDbScanFwdLink(plink);
|
||||
break;
|
||||
case CA_LINK:
|
||||
dbCaScanFwdLink(plink);
|
||||
break;
|
||||
}
|
||||
lset *plset = plink->lset;
|
||||
|
||||
if (plset && plset->scanForward)
|
||||
plset->scanForward(plink);
|
||||
}
|
||||
|
||||
/* Helper functions for long string support */
|
||||
|
||||
@@ -6,8 +6,7 @@
|
||||
* EPICS BASE is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
\*************************************************************************/
|
||||
/*
|
||||
* dbLink.h
|
||||
/* dbLink.h
|
||||
*
|
||||
* Created on: Mar 21, 2010
|
||||
* Author: Andrew Johnson
|
||||
@@ -20,21 +19,21 @@
|
||||
#include "shareLib.h"
|
||||
#include "epicsTypes.h"
|
||||
#include "epicsTime.h"
|
||||
#include "dbAddr.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct dbLocker;
|
||||
|
||||
typedef struct lset {
|
||||
long (*initLink)(struct link *plink, short dbfType);
|
||||
long (*addLink)(struct link *plink, short dbfType);
|
||||
long (*loadLink)(struct link *plink, short dbrType, void *pbuffer);
|
||||
void (*removeLink)(struct link *plink);
|
||||
int (*isLinkConnected)(const struct link *plink);
|
||||
void (*removeLink)(struct dbLocker *locker, struct link *plink);
|
||||
int (*isConnected)(const struct link *plink);
|
||||
int (*getDBFtype)(const struct link *plink);
|
||||
long (*getElements)(const struct link *plink, long *nelements);
|
||||
long (*getValue)(struct link *plink, short dbrType, void *pbuffer,
|
||||
epicsEnum16 *pstat, epicsEnum16 *psevr, long *pnRequest);
|
||||
epicsEnum16 *pstat, epicsEnum16 *psevr, long *pnRequest);
|
||||
long (*getControlLimits)(const struct link *plink, double *lo, double *hi);
|
||||
long (*getGraphicLimits)(const struct link *plink, double *lo, double *hi);
|
||||
long (*getAlarmLimits)(const struct link *plink, double *lolo, double *lo,
|
||||
@@ -46,19 +45,19 @@ typedef struct lset {
|
||||
long (*getTimeStamp)(const struct link *plink, epicsTimeStamp *pstamp);
|
||||
long (*putValue)(struct link *plink, short dbrType,
|
||||
const void *pbuffer, long nRequest);
|
||||
void (*scanFwdLink)(struct link *plink);
|
||||
void (*scanForward)(struct link *plink);
|
||||
} lset;
|
||||
|
||||
#define dbGetSevr(PLINK, PSEVERITY) \
|
||||
dbGetAlarm((PLINK), NULL, (PSEVERITY))
|
||||
#define dbGetSevr(link, sevr) \
|
||||
dbGetAlarm(link, NULL, sevr)
|
||||
|
||||
epicsShareFunc void dbInitLink(struct dbCommon *precord, struct link *plink,
|
||||
short dbfType);
|
||||
epicsShareFunc void dbAddLink(struct dbCommon *precord, struct link *plink,
|
||||
short dbfType);
|
||||
epicsShareFunc void dbInitLink(struct link *plink, short dbfType);
|
||||
epicsShareFunc void dbAddLink(struct dbLocker *locker, struct link *plink, short dbfType,
|
||||
DBADDR *ptarget);
|
||||
epicsShareFunc long dbLoadLink(struct link *plink, short dbrType,
|
||||
void *pbuffer);
|
||||
epicsShareFunc void dbRemoveLink(struct link *plink);
|
||||
epicsShareFunc void dbRemoveLink(struct dbLocker *locker, struct link *plink);
|
||||
|
||||
epicsShareFunc long dbGetNelements(const struct link *plink, long *nelements);
|
||||
epicsShareFunc int dbIsLinkConnected(const struct link *plink);
|
||||
epicsShareFunc int dbGetLinkDBFtype(const struct link *plink);
|
||||
@@ -77,7 +76,7 @@ epicsShareFunc long dbGetAlarm(const struct link *plink, epicsEnum16 *status,
|
||||
epicsEnum16 *severity);
|
||||
epicsShareFunc long dbGetTimeStamp(const struct link *plink,
|
||||
epicsTimeStamp *pstamp);
|
||||
epicsShareFunc long dbPutLink(struct link *, short dbrType,
|
||||
epicsShareFunc long dbPutLink(struct link *plink, short dbrType,
|
||||
const void *pbuffer, long nRequest);
|
||||
epicsShareFunc void dbScanFwdLink(struct link *plink);
|
||||
|
||||
|
||||
1286
src/ioc/db/dbLock.c
1286
src/ioc/db/dbLock.c
File diff suppressed because it is too large
Load Diff
@@ -13,6 +13,7 @@
|
||||
#ifndef INCdbLockh
|
||||
#define INCdbLockh
|
||||
|
||||
#include "ellLib.h"
|
||||
#include "shareLib.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
@@ -21,21 +22,26 @@ extern "C" {
|
||||
|
||||
struct dbCommon;
|
||||
struct dbBase;
|
||||
typedef struct dbLocker dbLocker;
|
||||
|
||||
epicsShareFunc void dbScanLock(struct dbCommon *precord);
|
||||
epicsShareFunc void dbScanUnlock(struct dbCommon *precord);
|
||||
|
||||
epicsShareFunc dbLocker *dbLockerAlloc(struct dbCommon **precs,
|
||||
size_t nrecs,
|
||||
unsigned int flags);
|
||||
|
||||
epicsShareFunc void dbLockerFree(dbLocker *);
|
||||
|
||||
epicsShareFunc void dbScanLockMany(dbLocker*);
|
||||
epicsShareFunc void dbScanUnlockMany(dbLocker*);
|
||||
|
||||
epicsShareFunc unsigned long dbLockGetLockId(
|
||||
struct dbCommon *precord);
|
||||
|
||||
epicsShareFunc void dbLockInitRecords(struct dbBase *pdbbase);
|
||||
epicsShareFunc void dbLockCleanupRecords(struct dbBase *pdbbase);
|
||||
epicsShareFunc void dbLockSetMerge(
|
||||
struct dbCommon *pfirst,struct dbCommon *psecond);
|
||||
epicsShareFunc void dbLockSetSplit(struct dbCommon *psource);
|
||||
/*The following are for code that modifies lock sets*/
|
||||
epicsShareFunc void dbLockSetGblLock(void);
|
||||
epicsShareFunc void dbLockSetGblUnlock(void);
|
||||
epicsShareFunc void dbLockSetRecordLock(struct dbCommon *precord);
|
||||
|
||||
|
||||
/* Lock Set Report */
|
||||
epicsShareFunc long dblsr(char *recordname,int level);
|
||||
@@ -47,6 +53,10 @@ epicsShareFunc long dbLockShowLocked(int level);
|
||||
/*KLUDGE to support field TPRO*/
|
||||
epicsShareFunc int * dbLockSetAddrTrace(struct dbCommon *precord);
|
||||
|
||||
/* debugging */
|
||||
epicsShareFunc unsigned long dbLockGetRefs(struct dbCommon*);
|
||||
epicsShareFunc unsigned long dbLockCountSets(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
110
src/ioc/db/dbLockPvt.h
Normal file
110
src/ioc/db/dbLockPvt.h
Normal file
@@ -0,0 +1,110 @@
|
||||
/*************************************************************************\
|
||||
* Copyright (c) 2014 Brookhaven Science Assoc., as Operator of Brookhaven
|
||||
* National Laboratory.
|
||||
* EPICS BASE is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
\*************************************************************************/
|
||||
|
||||
#ifndef DBLOCKPVT_H
|
||||
#define DBLOCKPVT_H
|
||||
|
||||
#include "dbLock.h"
|
||||
#include "epicsSpin.h"
|
||||
|
||||
/* Define to enable additional error checking */
|
||||
#undef LOCKSET_DEBUG
|
||||
|
||||
/* Define to disable the free list for lockSets */
|
||||
#undef LOCKSET_NOFREE
|
||||
|
||||
/* Define to disable use of recomputeCnt optimization */
|
||||
#undef LOCKSET_NOCNT
|
||||
|
||||
/* except for refcount (and lock), all members of dbLockSet
|
||||
* are guarded by its lock.
|
||||
*/
|
||||
typedef struct dbLockSet {
|
||||
ELLNODE node;
|
||||
ELLLIST lockRecordList; /* holds lockRecord::node */
|
||||
epicsMutexId lock;
|
||||
unsigned long id;
|
||||
|
||||
int refcount;
|
||||
#ifdef LOCKSET_DEBUG
|
||||
int ownercount;
|
||||
epicsThreadId owner;
|
||||
#endif
|
||||
dbLocker *ownerlocker;
|
||||
ELLNODE lockernode;
|
||||
|
||||
int trace; /*For field TPRO*/
|
||||
} lockSet;
|
||||
|
||||
struct lockRecord;
|
||||
|
||||
/* dbCommon.LSET is a plockRecord.
|
||||
* Except for spin and plockSet, all members of lockRecord are guarded
|
||||
* by the present lockset lock.
|
||||
* plockSet is guarded by spin.
|
||||
*/
|
||||
typedef struct lockRecord {
|
||||
ELLNODE node; /* in lockSet::lockRecordList */
|
||||
/* The association between lockRecord and lockSet
|
||||
* can only be changed while the lockSet is held,
|
||||
* and the lockRecord's spinlock is held.
|
||||
* It may be read which either lock is held.
|
||||
*/
|
||||
lockSet *plockSet;
|
||||
/* the association between lockRecord and dbCommon never changes */
|
||||
dbCommon *precord;
|
||||
epicsSpinId spin;
|
||||
|
||||
/* temp used during lockset split.
|
||||
* lockSet must be locked for access
|
||||
*/
|
||||
ELLNODE compnode;
|
||||
unsigned int compflag;
|
||||
} lockRecord;
|
||||
|
||||
typedef struct {
|
||||
lockRecord *plr;
|
||||
/* the last lock found associated with the ref.
|
||||
* not stable unless lock is locked, or ref spin
|
||||
* is locked.
|
||||
*/
|
||||
lockSet *plockSet;
|
||||
} lockRecordRef;
|
||||
|
||||
#define DBLOCKER_NALLOC 2
|
||||
/* a dbLocker can only be used by a single thread. */
|
||||
struct dbLocker {
|
||||
ELLLIST locked;
|
||||
#ifndef LOCKSET_NOCNT
|
||||
size_t recomp; /* snapshot of recomputeCnt when refs[] cache updated */
|
||||
#endif
|
||||
size_t maxrefs;
|
||||
lockRecordRef refs[DBLOCKER_NALLOC]; /* actual length is maxrefs */
|
||||
};
|
||||
|
||||
/* These are exported for testing only */
|
||||
epicsShareFunc lockSet* dbLockGetRef(lockRecord *lr); /* lookup lockset and increment ref count */
|
||||
epicsShareFunc void dbLockIncRef(lockSet* ls);
|
||||
epicsShareFunc void dbLockDecRef(lockSet *ls);
|
||||
|
||||
/* Calling dbLockerPrepare directly is an internal
|
||||
* optimization used when dbLocker on the stack.
|
||||
* nrecs must be <=DBLOCKER_NALLOC.
|
||||
*/
|
||||
void dbLockerPrepare(struct dbLocker *locker,
|
||||
struct dbCommon **precs,
|
||||
size_t nrecs);
|
||||
void dbLockerFinalize(dbLocker *);
|
||||
|
||||
void dbLockSetMerge(struct dbLocker *locker,
|
||||
struct dbCommon *pfirst,
|
||||
struct dbCommon *psecond);
|
||||
void dbLockSetSplit(struct dbLocker *locker,
|
||||
struct dbCommon *psource,
|
||||
struct dbCommon *psecond);
|
||||
|
||||
#endif /* DBLOCKPVT_H */
|
||||
@@ -25,7 +25,6 @@
|
||||
#include "ellLib.h"
|
||||
#include "epicsAssert.h"
|
||||
#include "epicsEvent.h"
|
||||
#include "epicsExit.h"
|
||||
#include "epicsMutex.h"
|
||||
#include "epicsString.h"
|
||||
#include "epicsThread.h"
|
||||
@@ -109,6 +108,15 @@ static void notifyCallback(CALLBACK *pcallback);
|
||||
(listnode)->isOnList=0; \
|
||||
}
|
||||
|
||||
static void notifyFree(void *raw)
|
||||
{
|
||||
notifyPvt *pnotifyPvt = raw;
|
||||
assert(pnotifyPvt->magic==MAGIC);
|
||||
epicsEventDestroy(pnotifyPvt->cancelEvent);
|
||||
epicsEventDestroy(pnotifyPvt->userCallbackEvent);
|
||||
free(pnotifyPvt);
|
||||
}
|
||||
|
||||
static void notifyInit(processNotify *ppn)
|
||||
{
|
||||
notifyPvt *pnotifyPvt;
|
||||
@@ -301,7 +309,7 @@ static void notifyCallback(CALLBACK *pcallback)
|
||||
|
||||
void dbProcessNotifyExit(void)
|
||||
{
|
||||
assert(ellCount(&pnotifyGlobal->freeList)==0);
|
||||
ellFree2(&pnotifyGlobal->freeList, ¬ifyFree);
|
||||
epicsMutexDestroy(pnotifyGlobal->lock);
|
||||
free(pnotifyGlobal);
|
||||
pnotifyGlobal = NULL;
|
||||
|
||||
@@ -25,11 +25,9 @@
|
||||
#include "dbDefs.h"
|
||||
#include "ellLib.h"
|
||||
#include "epicsEvent.h"
|
||||
#include "epicsExit.h"
|
||||
#include "epicsInterrupt.h"
|
||||
#include "epicsMutex.h"
|
||||
#include "epicsPrint.h"
|
||||
#include "epicsRingPointer.h"
|
||||
#include "epicsRingBytes.h"
|
||||
#include "epicsStdio.h"
|
||||
#include "epicsStdlib.h"
|
||||
#include "epicsString.h"
|
||||
@@ -64,7 +62,7 @@ static volatile enum ctl scanCtl;
|
||||
|
||||
static int onceQueueSize = 1000;
|
||||
static epicsEventId onceSem;
|
||||
static epicsRingPointerId onceQ;
|
||||
static epicsRingBytesId onceQ;
|
||||
static epicsThreadId onceTaskId;
|
||||
static void *exitOnce;
|
||||
|
||||
@@ -176,7 +174,7 @@ void scanCleanup(void)
|
||||
deletePeriodic();
|
||||
ioscanDestroy();
|
||||
|
||||
epicsRingPointerDelete(onceQ);
|
||||
epicsRingBytesDelete(onceQ);
|
||||
|
||||
free(periodicTaskId);
|
||||
papPeriodic = NULL;
|
||||
@@ -457,7 +455,9 @@ event_list *eventNameToHandle(const char *eventname)
|
||||
if (strcmp(pel->event_name, eventname) == 0) break;
|
||||
}
|
||||
if (pel == NULL) {
|
||||
pel = dbCalloc(1, sizeof(event_list));
|
||||
pel = calloc(1, sizeof(event_list));
|
||||
if (!pel)
|
||||
goto done;
|
||||
strcpy(pel->event_name, eventname);
|
||||
for (prio = 0; prio < NUM_CALLBACK_PRIORITIES; prio++) {
|
||||
callbackSetUser(&pel->scan_list[prio], &pel->callback[prio]);
|
||||
@@ -475,6 +475,7 @@ event_list *eventNameToHandle(const char *eventname)
|
||||
pevent_list[e] = pel;
|
||||
}
|
||||
}
|
||||
done:
|
||||
epicsMutexUnlock(event_lock);
|
||||
return pel;
|
||||
}
|
||||
@@ -581,6 +582,28 @@ unsigned int scanIoRequest(IOSCANPVT piosh)
|
||||
return queued;
|
||||
}
|
||||
|
||||
unsigned int scanIoImmediate(IOSCANPVT piosh, int prio)
|
||||
{
|
||||
io_scan_list *piosl;
|
||||
|
||||
if (prio<0 || prio>=NUM_CALLBACK_PRIORITIES)
|
||||
return S_db_errArg;
|
||||
else if (scanCtl != ctlRun)
|
||||
return 0;
|
||||
|
||||
piosl = &piosh->iosl[prio];
|
||||
|
||||
if (ellCount(&piosl->scan_list.list) == 0)
|
||||
return 0;
|
||||
|
||||
scanList(&piosl->scan_list);
|
||||
|
||||
if (piosh->cb)
|
||||
piosh->cb(piosh->arg, piosh, prio);
|
||||
|
||||
return 1 << prio;
|
||||
}
|
||||
|
||||
/* May not be called while a scan request is queued or running */
|
||||
void scanIoSetComplete(IOSCANPVT piosh, io_scan_complete cb, void *arg)
|
||||
{
|
||||
@@ -588,15 +611,27 @@ void scanIoSetComplete(IOSCANPVT piosh, io_scan_complete cb, void *arg)
|
||||
piosh->arg = arg;
|
||||
}
|
||||
|
||||
void scanOnce(struct dbCommon *precord)
|
||||
int scanOnce(struct dbCommon *precord) {
|
||||
return scanOnceCallback(precord, NULL, NULL);
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
struct dbCommon *prec;
|
||||
once_complete cb;
|
||||
void *usr;
|
||||
} onceEntry;
|
||||
|
||||
int scanOnceCallback(struct dbCommon *precord, once_complete cb, void *usr)
|
||||
{
|
||||
static int newOverflow = TRUE;
|
||||
int lockKey;
|
||||
onceEntry ent;
|
||||
int pushOK;
|
||||
|
||||
lockKey = epicsInterruptLock();
|
||||
pushOK = epicsRingPointerPush(onceQ, precord);
|
||||
epicsInterruptUnlock(lockKey);
|
||||
ent.prec = precord;
|
||||
ent.cb = cb;
|
||||
ent.usr = usr;
|
||||
|
||||
pushOK = epicsRingBytesPut(onceQ, (void*)&ent, sizeof(ent));
|
||||
|
||||
if (!pushOK) {
|
||||
if (newOverflow) errlogPrintf("scanOnce: Ring buffer overflow\n");
|
||||
@@ -605,6 +640,8 @@ void scanOnce(struct dbCommon *precord)
|
||||
newOverflow = TRUE;
|
||||
}
|
||||
epicsEventSignal(onceSem);
|
||||
|
||||
return !pushOK;
|
||||
}
|
||||
|
||||
static void onceTask(void *arg)
|
||||
@@ -613,14 +650,24 @@ static void onceTask(void *arg)
|
||||
epicsEventSignal(startStopEvent);
|
||||
|
||||
while (TRUE) {
|
||||
void *precord;
|
||||
|
||||
epicsEventMustWait(onceSem);
|
||||
while ((precord = epicsRingPointerPop(onceQ))) {
|
||||
if (precord == &exitOnce) goto shutdown;
|
||||
dbScanLock(precord);
|
||||
dbProcess(precord);
|
||||
dbScanUnlock(precord);
|
||||
while(1) {
|
||||
onceEntry ent;
|
||||
int bytes = epicsRingBytesGet(onceQ, (void*)&ent, sizeof(ent));
|
||||
if(bytes==0)
|
||||
break;
|
||||
if(bytes!=sizeof(ent)) {
|
||||
errlogPrintf("onceTask: received incomplete %d of %u\n",
|
||||
bytes, (unsigned)sizeof(ent));
|
||||
continue; /* what to do? */
|
||||
} else if (ent.prec == (void*)&exitOnce) goto shutdown;
|
||||
|
||||
dbScanLock(ent.prec);
|
||||
dbProcess(ent.prec);
|
||||
dbScanUnlock(ent.prec);
|
||||
if(ent.cb)
|
||||
ent.cb(ent.usr, ent.prec);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -637,7 +684,7 @@ int scanOnceSetQueueSize(int size)
|
||||
|
||||
static void initOnce(void)
|
||||
{
|
||||
if ((onceQ = epicsRingPointerCreate(onceQueueSize)) == NULL) {
|
||||
if ((onceQ = epicsRingBytesLockedCreate(sizeof(onceEntry)*onceQueueSize)) == NULL) {
|
||||
cantProceed("initOnce: Ring buffer create failed\n");
|
||||
}
|
||||
if(!onceSem)
|
||||
@@ -815,9 +862,11 @@ static void spawnPeriodic(int ind)
|
||||
|
||||
static void ioscanCallback(CALLBACK *pcallback)
|
||||
{
|
||||
ioscan_head *piosh = (ioscan_head *) pcallback->user;
|
||||
int prio = pcallback->priority;
|
||||
ioscan_head *piosh;
|
||||
int prio;
|
||||
|
||||
callbackGetUser(piosh, pcallback);
|
||||
callbackGetPriority(prio, pcallback);
|
||||
scanList(&piosh->iosl[prio].scan_list);
|
||||
if (piosh->cb)
|
||||
piosh->cb(piosh->arg, piosh, prio);
|
||||
|
||||
@@ -39,10 +39,11 @@ struct ioscan_head;
|
||||
typedef struct ioscan_head *IOSCANPVT;
|
||||
typedef struct event_list *EVENTPVT;
|
||||
|
||||
typedef void (*io_scan_complete)(void *usr, IOSCANPVT, int prio);
|
||||
|
||||
struct dbCommon;
|
||||
|
||||
typedef void (*io_scan_complete)(void *usr, IOSCANPVT, int prio);
|
||||
typedef void (*once_complete)(void *usr, struct dbCommon*);
|
||||
|
||||
epicsShareFunc long scanInit(void);
|
||||
epicsShareFunc void scanRun(void);
|
||||
epicsShareFunc void scanPause(void);
|
||||
@@ -55,7 +56,8 @@ epicsShareFunc void post_event(int event) EPICS_DEPRECATED;
|
||||
epicsShareFunc void scanAdd(struct dbCommon *);
|
||||
epicsShareFunc void scanDelete(struct dbCommon *);
|
||||
epicsShareFunc double scanPeriod(int scan);
|
||||
epicsShareFunc void scanOnce(struct dbCommon *);
|
||||
epicsShareFunc int scanOnce(struct dbCommon *);
|
||||
epicsShareFunc int scanOnceCallback(struct dbCommon *, once_complete cb, void *usr);
|
||||
epicsShareFunc int scanOnceSetQueueSize(int size);
|
||||
|
||||
/*print periodic lists*/
|
||||
@@ -69,6 +71,7 @@ epicsShareFunc int scanpiol(void);
|
||||
|
||||
epicsShareFunc void scanIoInit(IOSCANPVT *ppios);
|
||||
epicsShareFunc unsigned int scanIoRequest(IOSCANPVT pios);
|
||||
epicsShareFunc unsigned int scanIoImmediate(IOSCANPVT pios, int prio);
|
||||
epicsShareFunc void scanIoSetComplete(IOSCANPVT, io_scan_complete, void *usr);
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
@@ -15,19 +15,35 @@
|
||||
#include "dbmf.h"
|
||||
#include "epicsUnitTest.h"
|
||||
#include "osiFileName.h"
|
||||
#include "osiUnistd.h"
|
||||
#include "registry.h"
|
||||
#include "epicsEvent.h"
|
||||
|
||||
#define epicsExportSharedSymbols
|
||||
#include "dbAccess.h"
|
||||
#include "dbBase.h"
|
||||
#include "dbChannel.h"
|
||||
#include "dbEvent.h"
|
||||
#include "dbStaticLib.h"
|
||||
#include "dbUnitTest.h"
|
||||
#include "initHooks.h"
|
||||
#include "iocInit.h"
|
||||
|
||||
static dbEventCtx testEvtCtx;
|
||||
static epicsMutexId testEvtLock;
|
||||
static ELLLIST testEvtList; /* holds testMonitor::node */
|
||||
|
||||
struct testMonitor {
|
||||
ELLNODE node;
|
||||
dbEventSubscription sub;
|
||||
epicsEventId event;
|
||||
unsigned count;
|
||||
};
|
||||
|
||||
void testdbPrepare(void)
|
||||
{
|
||||
/* No-op at the moment */
|
||||
if(!testEvtLock)
|
||||
testEvtLock = epicsMutexMustCreate();
|
||||
}
|
||||
|
||||
void testdbReadDatabase(const char* file,
|
||||
@@ -37,19 +53,35 @@ void testdbReadDatabase(const char* file,
|
||||
if(!path)
|
||||
path = "." OSI_PATH_LIST_SEPARATOR ".." OSI_PATH_LIST_SEPARATOR
|
||||
"../O.Common" OSI_PATH_LIST_SEPARATOR "O.Common";
|
||||
if(dbReadDatabase(&pdbbase, file, path, substitutions))
|
||||
testAbort("Failed to load test database\ndbReadDatabase(%s,%s,%s)",
|
||||
file, path, substitutions);
|
||||
if(dbReadDatabase(&pdbbase, file, path, substitutions)) {
|
||||
char buf[100];
|
||||
const char *cwd = getcwd(buf, sizeof(buf));
|
||||
if(!cwd)
|
||||
cwd = "<directory too long>";
|
||||
testAbort("Failed to load test database\ndbReadDatabase(%s,%s,%s)\n from: \"%s\"",
|
||||
file, path, substitutions, cwd);
|
||||
}
|
||||
}
|
||||
|
||||
void testIocInitOk(void)
|
||||
{
|
||||
if(iocBuildIsolated() || iocRun())
|
||||
testAbort("Failed to start up test database");
|
||||
if(!(testEvtCtx=db_init_events()))
|
||||
testAbort("Failed to initialize test dbEvent context");
|
||||
if(DB_EVENT_OK!=db_start_events(testEvtCtx, "CAS-test", NULL, NULL, epicsThreadPriorityCAServerLow))
|
||||
testAbort("Failed to start test dbEvent context");
|
||||
}
|
||||
|
||||
void testIocShutdownOk(void)
|
||||
{
|
||||
epicsMutexMustLock(testEvtLock);
|
||||
if(ellCount(&testEvtList))
|
||||
testDiag("Warning, testing monitors still active at testIocShutdownOk()");
|
||||
epicsMutexUnlock(testEvtLock);
|
||||
|
||||
db_close_events(testEvtCtx);
|
||||
testEvtCtx = NULL;
|
||||
if(iocShutdown())
|
||||
testAbort("Failed to shutdown test database");
|
||||
}
|
||||
@@ -199,3 +231,90 @@ dbCommon* testdbRecordPtr(const char* pv)
|
||||
|
||||
return addr.precord;
|
||||
}
|
||||
|
||||
static
|
||||
void testmonupdate(void *user_arg, struct dbChannel *chan,
|
||||
int eventsRemaining, struct db_field_log *pfl)
|
||||
{
|
||||
testMonitor *mon = user_arg;
|
||||
|
||||
epicsMutexMustLock(testEvtLock);
|
||||
mon->count++;
|
||||
epicsMutexUnlock(testEvtLock);
|
||||
epicsEventMustTrigger(mon->event);
|
||||
}
|
||||
|
||||
testMonitor* testMonitorCreate(const char* pvname, unsigned mask, unsigned opt)
|
||||
{
|
||||
long status;
|
||||
testMonitor *mon;
|
||||
dbChannel *chan;
|
||||
assert(testEvtCtx);
|
||||
|
||||
mon = callocMustSucceed(1, sizeof(*mon), "testMonitorCreate");
|
||||
|
||||
mon->event = epicsEventMustCreate(epicsEventEmpty);
|
||||
|
||||
chan = dbChannelCreate(pvname);
|
||||
if(!chan)
|
||||
testAbort("testMonitorCreate - dbChannelCreate(\"%s\") fails", pvname);
|
||||
if(!!(status=dbChannelOpen(chan)))
|
||||
testAbort("testMonitorCreate - dbChannelOpen(\"%s\") fails w/ %ld", pvname, status);
|
||||
|
||||
mon->sub = db_add_event(testEvtCtx, chan, &testmonupdate, mon, mask);
|
||||
if(!mon->sub)
|
||||
testAbort("testMonitorCreate - db_add_event(\"%s\") fails", pvname);
|
||||
|
||||
db_event_enable(mon->sub);
|
||||
|
||||
epicsMutexMustLock(testEvtLock);
|
||||
ellAdd(&testEvtList, &mon->node);
|
||||
epicsMutexUnlock(testEvtLock);
|
||||
|
||||
return mon;
|
||||
}
|
||||
|
||||
void testMonitorDestroy(testMonitor *mon)
|
||||
{
|
||||
if(!mon) return;
|
||||
|
||||
db_event_disable(mon->sub);
|
||||
|
||||
epicsMutexMustLock(testEvtLock);
|
||||
ellDelete(&testEvtList, &mon->node);
|
||||
epicsMutexUnlock(testEvtLock);
|
||||
|
||||
db_cancel_event(mon->sub);
|
||||
|
||||
epicsEventDestroy(mon->event);
|
||||
|
||||
free(mon);
|
||||
}
|
||||
|
||||
void testMonitorWait(testMonitor *mon)
|
||||
{
|
||||
static const double delay = 60.0;
|
||||
|
||||
switch(epicsEventWaitWithTimeout(mon->event, delay))
|
||||
{
|
||||
case epicsEventOK:
|
||||
return;
|
||||
case epicsEventWaitTimeout:
|
||||
default:
|
||||
testAbort("testMonitorWait() exceeded %g second timeout", delay);
|
||||
}
|
||||
}
|
||||
|
||||
unsigned testMonitorCount(testMonitor *mon, unsigned reset)
|
||||
{
|
||||
unsigned count;
|
||||
epicsMutexMustLock(testEvtLock);
|
||||
count = mon->count;
|
||||
if(reset) {
|
||||
mon->count = 0;
|
||||
epicsEventWaitWithTimeout(mon->event, 0); /* clear the event */
|
||||
}
|
||||
epicsMutexUnlock(testEvtLock);
|
||||
return count;
|
||||
}
|
||||
|
||||
|
||||
@@ -57,6 +57,26 @@ epicsShareFunc void testdbVGetFieldEqual(const char* pv, short dbrType, va_list
|
||||
|
||||
epicsShareFunc dbCommon* testdbRecordPtr(const char* pv);
|
||||
|
||||
typedef struct testMonitor testMonitor;
|
||||
|
||||
/* Begin monitoring the named PV for changes */
|
||||
epicsShareFunc testMonitor* testMonitorCreate(const char* pvname, unsigned dbe_mask, unsigned opt);
|
||||
/* End monitoring */
|
||||
epicsShareFunc void testMonitorDestroy(testMonitor*);
|
||||
/* Return immediately if it has been updated since create, last wait,
|
||||
* or reset (count w/ reset=1).
|
||||
* Otherwise, block until the value of the target PV is updated.
|
||||
*/
|
||||
epicsShareFunc void testMonitorWait(testMonitor*);
|
||||
/* Return the number of monitor events which have occured since create,
|
||||
* or a pervious reset (called reset=1).
|
||||
* Calling w/ reset=0 only returns the count.
|
||||
* Calling w/ reset=1 resets the count to zero and ensures that the next
|
||||
* wait will block unless subsequent events occur. Returns the previous
|
||||
* count.
|
||||
*/
|
||||
epicsShareFunc unsigned testMonitorCount(testMonitor*, unsigned reset);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -161,14 +161,9 @@ void recGblGetControlDouble(const struct dbAddr *paddr,
|
||||
&pcd->upper_ctrl_limit, &pcd->lower_ctrl_limit);
|
||||
}
|
||||
|
||||
int recGblInitConstantLink(
|
||||
struct link *plink,short dbftype,void *pdest)
|
||||
int recGblInitConstantLink(struct link *plink, short dbftype, void *pdest)
|
||||
{
|
||||
long status = dbLoadLink(plink, dbftype, pdest);
|
||||
|
||||
if (status)
|
||||
return FALSE;
|
||||
return TRUE;
|
||||
return !dbLoadLink(plink, dbftype, pdest);
|
||||
}
|
||||
|
||||
unsigned short recGblResetAlarms(void *precord)
|
||||
|
||||
@@ -10,17 +10,25 @@ TOP=../../../..
|
||||
|
||||
include $(TOP)/configure/CONFIG
|
||||
|
||||
# Allow access to private headers in db/
|
||||
USR_CPPFLAGS = -I ../..
|
||||
|
||||
TESTLIBRARY = dbTestIoc
|
||||
|
||||
dbTestIoc_SRCS += arrRecord.c
|
||||
dbTestIoc_SRCS += xRecord.c
|
||||
dbTestIoc_SRCS += dbLinkdset.c
|
||||
dbTestIoc_SRCS += devx.c
|
||||
dbTestIoc_LIBS = dbCore ca Com
|
||||
|
||||
TARGETS += $(COMMON_DIR)/dbTestIoc.dbd
|
||||
dbTestIoc_DBD += menuGlobal.dbd
|
||||
dbTestIoc_DBD += menuConvert.dbd
|
||||
dbTestIoc_DBD += menuScan.dbd
|
||||
#dbTestIoc_DBD += arrRecord.dbd
|
||||
dbTestIoc_DBD += xRecord.dbd
|
||||
dbTestIoc_DBD += arrRecord.dbd
|
||||
dbTestIoc_DBD += devx.dbd
|
||||
dbTestIoc_DBD += dbLinkdset.dbd
|
||||
TESTFILES += $(COMMON_DIR)/dbTestIoc.dbd ../xRecord.db
|
||||
|
||||
@@ -28,6 +36,12 @@ testHarness_SRCS += dbTestIoc_registerRecordDeviceDriver.cpp
|
||||
|
||||
PROD_LIBS = dbTestIoc dbCore ca Com
|
||||
|
||||
TESTPROD_HOST += dbScanTest
|
||||
dbScanTest_SRCS += dbScanTest.c
|
||||
dbScanTest_SRCS += dbTestIoc_registerRecordDeviceDriver.cpp
|
||||
testHarness_SRCS += dbScanTest.c
|
||||
TESTS += dbScanTest
|
||||
|
||||
TESTPROD_HOST += dbShutdownTest
|
||||
dbShutdownTest_SRCS += dbShutdownTest.c
|
||||
dbShutdownTest_SRCS += dbTestIoc_registerRecordDeviceDriver.cpp
|
||||
@@ -39,7 +53,7 @@ dbPutLinkTest_SRCS += dbPutLinkTest.c
|
||||
dbPutLinkTest_SRCS += dbTestIoc_registerRecordDeviceDriver.cpp
|
||||
testHarness_SRCS += dbPutLinkTest.c
|
||||
TESTS += dbPutLinkTest
|
||||
TESTFILES += ../dbPutLinkTest.db
|
||||
TESTFILES += ../dbPutLinkTest.db ../dbBadLink.db
|
||||
|
||||
TESTPROD_HOST += dbLockTest
|
||||
dbLockTest_SRCS += dbLockTest.c
|
||||
@@ -48,6 +62,14 @@ testHarness_SRCS += dbLockTest.c
|
||||
TESTS += dbLockTest
|
||||
TESTFILES += ../dbLockTest.db
|
||||
|
||||
TESTPROD_HOST += dbStressTest
|
||||
dbStressTest_SRCS += dbStressLock.c
|
||||
dbStressTest_SRCS += dbTestIoc_registerRecordDeviceDriver.cpp
|
||||
dbStressTest_SYS_LIBS_solaris += rt
|
||||
dbStressTest_SYS_LIBS_Linux += rt
|
||||
TESTS += dbStressTest
|
||||
TESTFILES += ../dbStressLock.db
|
||||
|
||||
TESTPROD_HOST += testdbConvert
|
||||
testdbConvert_SRCS += testdbConvert.c
|
||||
testHarness_SRCS += testdbConvert.c
|
||||
@@ -73,20 +95,23 @@ dbCaStatsTest_SRCS += dbCaStatsTest.c
|
||||
dbCaStatsTest_SRCS += dbTestIoc_registerRecordDeviceDriver.cpp
|
||||
testHarness_SRCS += dbCaStatsTest.c
|
||||
TESTS += dbCaStatsTest
|
||||
TESTFILES += ../dbCaStatsTest.db
|
||||
TESTFILES += ../dbCaStats.db
|
||||
|
||||
TESTPROD_HOST += dbCaLinkTest
|
||||
dbCaLinkTest_SRCS += dbCaLinkTest.c
|
||||
dbCaLinkTest_SRCS += dbCACTest.cpp
|
||||
dbCaLinkTest_SRCS += dbTestIoc_registerRecordDeviceDriver.cpp
|
||||
testHarness_SRCS += dbCaLinkTest.c
|
||||
testHarness_SRCS += dbCACTest.cpp
|
||||
TESTS += dbCaLinkTest
|
||||
TESTFILES += ../dbCaLinkTest1.db ../dbCaLinkTest2.db ../dbCaLinkTest3.db
|
||||
|
||||
TARGETS += $(COMMON_DIR)/scanIoTest.dbd
|
||||
scanIoTest_DBD += menuGlobal.dbd
|
||||
scanIoTest_DBD += menuConvert.dbd
|
||||
scanIoTest_DBD += menuScan.dbd
|
||||
scanIoTest_DBD += yRecord.dbd
|
||||
TESTPROD_HOST += scanIoTest
|
||||
scanIoTest_SRCS += scanIoTest.c
|
||||
scanIoTest_REGRDDFLAGS = -l
|
||||
scanIoTest_SRCS += scanIoTest_registerRecordDeviceDriver.cpp
|
||||
scanIoTest_SRCS += dbTestIoc_registerRecordDeviceDriver.cpp
|
||||
testHarness_SRCS += scanIoTest.c
|
||||
testHarness_SRCS += scanIoTest_registerRecordDeviceDriver.cpp
|
||||
TESTFILES += $(COMMON_DIR)/scanIoTest.dbd ../scanIoTest.db
|
||||
TESTFILES += ../scanIoTest.db
|
||||
TESTS += scanIoTest
|
||||
|
||||
TESTPROD_HOST += dbChannelTest
|
||||
@@ -95,6 +120,16 @@ dbChannelTest_SRCS += dbTestIoc_registerRecordDeviceDriver.cpp
|
||||
testHarness_SRCS += dbChannelTest.c
|
||||
TESTS += dbChannelTest
|
||||
|
||||
TARGETS += $(COMMON_DIR)/dbChArrTest.dbd
|
||||
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 +167,11 @@ TESTSCRIPTS_HOST += $(TESTS:%=%.t)
|
||||
|
||||
include $(TOP)/configure/RULES
|
||||
|
||||
xRecord$(DEP): $(COMMON_DIR)/xRecord.h
|
||||
arrRecord$(DEP): $(COMMON_DIR)/arrRecord.h
|
||||
dbCaLinkTest$(DEP): $(COMMON_DIR)/xRecord.h $(COMMON_DIR)/arrRecord.h
|
||||
dbPutLinkTest$(DEP): $(COMMON_DIR)/xRecord.h
|
||||
scanIoTest$(DEP): $(COMMON_DIR)/yRecord.h
|
||||
dbStressLock$(DEP): $(COMMON_DIR)/xRecord.h
|
||||
devx$(DEP): $(COMMON_DIR)/xRecord.h
|
||||
scanIoTest$(DEP): $(COMMON_DIR)/xRecord.h
|
||||
xRecord$(DEP): $(COMMON_DIR)/xRecord.h
|
||||
|
||||
|
||||
141
src/ioc/db/test/arrRecord.c
Normal file
141
src/ioc/db/test/arrRecord.c
Normal file
@@ -0,0 +1,141 @@
|
||||
/*************************************************************************\
|
||||
* 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)
|
||||
{
|
||||
if(prec->clbk)
|
||||
(*prec->clbk)(prec);
|
||||
prec->pact = TRUE;
|
||||
recGblGetTimeStamp(prec);
|
||||
recGblFwdLink(prec);
|
||||
prec->pact = FALSE;
|
||||
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;
|
||||
}
|
||||
42
src/ioc/db/test/arrRecord.dbd
Normal file
42
src/ioc/db/test/arrRecord.dbd
Normal file
@@ -0,0 +1,42 @@
|
||||
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")
|
||||
}
|
||||
field(INP, DBF_INLINK) {
|
||||
prompt("Input Link")
|
||||
}
|
||||
field(CLBK, DBF_NOACCESS) {
|
||||
prompt("Processing callback")
|
||||
special(SPC_NOMOD)
|
||||
extra("void (*clbk)(struct arrRecord*)")
|
||||
}
|
||||
}
|
||||
@@ -22,7 +22,9 @@
|
||||
#include "dbStaticLib.h"
|
||||
#include "dbAccessDefs.h"
|
||||
#include "registry.h"
|
||||
#include "epicsUnitTest.h"
|
||||
#include "errlog.h"
|
||||
#include "epicsExit.h"
|
||||
#include "dbUnitTest.h"
|
||||
#include "testMain.h"
|
||||
#include "osiFileName.h"
|
||||
|
||||
@@ -85,22 +87,20 @@ MAIN(arrShorthandTest)
|
||||
|
||||
testPlan(26);
|
||||
|
||||
db_init_events();
|
||||
dbChannelInit();
|
||||
testdbPrepare();
|
||||
|
||||
if (dbReadDatabase(&pdbbase, "dbTestIoc.dbd",
|
||||
"." OSI_PATH_LIST_SEPARATOR ".." OSI_PATH_LIST_SEPARATOR
|
||||
"../O.Common" OSI_PATH_LIST_SEPARATOR "O.Common", NULL))
|
||||
testAbort("Database description 'dbTestIoc.dbd' not found");
|
||||
testdbReadDatabase("dbTestIoc.dbd", NULL, NULL);
|
||||
|
||||
dbTestIoc_registerRecordDeviceDriver(pdbbase);
|
||||
if (dbReadDatabase(&pdbbase, "xRecord.db",
|
||||
"." OSI_PATH_LIST_SEPARATOR "..", NULL))
|
||||
testAbort("Test database 'xRecord.db' not found");
|
||||
testdbReadDatabase("xRecord.db", NULL, NULL);
|
||||
|
||||
testHead("Register plugin");
|
||||
testOk(!chfPluginRegister("arr", &myPif, opts), "register fake arr plugin");
|
||||
|
||||
eltc(0);
|
||||
testIocInitOk();
|
||||
eltc(1);
|
||||
|
||||
#define TESTBAD(Title, Expr) \
|
||||
testDiag(Title); \
|
||||
testOk(!(pch = dbChannelCreate("x." Expr)), "dbChannelCreate (" Expr ") fails"); \
|
||||
@@ -129,9 +129,8 @@ MAIN(arrShorthandTest)
|
||||
TESTGOOD("range [s::e]", "[2::4]", 2, 1, 4);
|
||||
TESTGOOD("range with incr [s:i:e]", "[2:3:4]", 2, 3, 4);
|
||||
|
||||
dbFreeBase(pdbbase);
|
||||
registryFree();
|
||||
pdbbase = NULL;
|
||||
testIocShutdownOk();
|
||||
testdbCleanup();
|
||||
|
||||
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;
|
||||
|
||||
84
src/ioc/db/test/dbCACTest.cpp
Normal file
84
src/ioc/db/test/dbCACTest.cpp
Normal file
@@ -0,0 +1,84 @@
|
||||
/*************************************************************************\
|
||||
* Copyright (c) 2015 Brookhaven Science Assoc. as operator of Brookhaven
|
||||
* National Laboratory.
|
||||
* EPICS BASE is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
\*************************************************************************/
|
||||
/*
|
||||
* Part of dbCaLinkTest, compiled seperately to avoid
|
||||
* dbAccess.h vs. db_access.h conflicts
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include <vector>
|
||||
#include <stdexcept>
|
||||
|
||||
#include <epicsEvent.h>
|
||||
|
||||
#include "epicsUnitTest.h"
|
||||
|
||||
#include "cadef.h"
|
||||
|
||||
#define testECA(OP) if((OP)!=ECA_NORMAL) {testAbort("%s", #OP);} else {testPass("%s", #OP);}
|
||||
|
||||
void putgetarray(chid chanid, double first, size_t count)
|
||||
{
|
||||
testDiag("putgetarray(%f,%u)", first, (unsigned)count);
|
||||
|
||||
std::vector<double> buf(count);
|
||||
for(size_t i=0; i<count ;i++)
|
||||
buf[i] = first + i*1.0;
|
||||
|
||||
testDiag("Put");
|
||||
|
||||
testECA(ca_array_put(DBR_DOUBLE, count, chanid, &buf[0]));
|
||||
|
||||
testECA(ca_pend_io(1.0));
|
||||
|
||||
testDiag("Get");
|
||||
|
||||
std::vector<double> buf2(count);
|
||||
|
||||
testECA(ca_array_get(DBR_DOUBLE, count, chanid, &buf2[0]));
|
||||
|
||||
testECA(ca_pend_io(1.0));
|
||||
|
||||
for(size_t i=0; i<count ;i++)
|
||||
testOk(buf[i]==buf2[i], "%f == %f", buf[i], buf2[i]);
|
||||
}
|
||||
|
||||
|
||||
|
||||
struct CATestContext
|
||||
{
|
||||
CATestContext()
|
||||
{
|
||||
if(ca_context_create(ca_enable_preemptive_callback)!=ECA_NORMAL)
|
||||
throw std::runtime_error("Failed to create CA context");
|
||||
}
|
||||
~CATestContext()
|
||||
{
|
||||
ca_context_destroy();
|
||||
}
|
||||
};
|
||||
|
||||
extern "C"
|
||||
void dbCaLinkTest_testCAC(void)
|
||||
{
|
||||
try {
|
||||
CATestContext ctxt;
|
||||
chid chanid = 0;
|
||||
testECA(ca_create_channel("target1", NULL, NULL, 0, &chanid));
|
||||
testECA(ca_pend_io(1.0));
|
||||
putgetarray(chanid, 1.0, 1);
|
||||
putgetarray(chanid, 2.0, 2);
|
||||
// repeat to ensure a cache hit in dbContextReadNotifyCacheAllocator
|
||||
putgetarray(chanid, 2.0, 2);
|
||||
putgetarray(chanid, 5.0, 5);
|
||||
|
||||
testECA(ca_clear_channel(chanid));
|
||||
}catch(std::exception& e){
|
||||
testAbort("Unexpected exception in testCAC: %s", e.what());
|
||||
}
|
||||
}
|
||||
612
src/ioc/db/test/dbCaLinkTest.c
Normal file
612
src/ioc/db/test/dbCaLinkTest.c
Normal file
@@ -0,0 +1,612 @@
|
||||
/*************************************************************************\
|
||||
* Copyright (c) 2015 Brookhaven Science Assoc. as operator of Brookhaven
|
||||
* National Laboratory.
|
||||
* EPICS BASE is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
\*************************************************************************/
|
||||
|
||||
/*
|
||||
* Author: Michael Davidsaver <mdavidsaver@bnl.gov>
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
|
||||
#include "epicsString.h"
|
||||
#include "dbUnitTest.h"
|
||||
#include "epicsThread.h"
|
||||
#include "cantProceed.h"
|
||||
#include "epicsEvent.h"
|
||||
#include "iocInit.h"
|
||||
#include "dbBase.h"
|
||||
#include "link.h"
|
||||
#include "dbAccess.h"
|
||||
#include "epicsStdio.h"
|
||||
#include "dbEvent.h"
|
||||
|
||||
/* Declarations from cadef.h and db_access.h which we can't include here */
|
||||
typedef void * chid;
|
||||
epicsShareExtern const unsigned short dbr_value_size[];
|
||||
epicsShareExtern short epicsShareAPI ca_field_type (chid chan);
|
||||
#define MAX_UNITS_SIZE 8
|
||||
|
||||
#include "dbCaPvt.h"
|
||||
#include "errlog.h"
|
||||
#include "testMain.h"
|
||||
|
||||
#include "xRecord.h"
|
||||
#include "arrRecord.h"
|
||||
|
||||
#define testOp(FMT,A,OP,B) testOk((A)OP(B), #A " ("FMT") " #OP " " #B " ("FMT")", A,B)
|
||||
|
||||
void dbTestIoc_registerRecordDeviceDriver(struct dbBase *);
|
||||
|
||||
static epicsEventId waitEvent;
|
||||
static unsigned waitCounter;
|
||||
|
||||
static
|
||||
void waitCB(void *unused)
|
||||
{
|
||||
if(waitEvent)
|
||||
epicsEventMustTrigger(waitEvent);
|
||||
waitCounter++; /* TODO: atomic */
|
||||
}
|
||||
|
||||
static
|
||||
void startWait(DBLINK *plink)
|
||||
{
|
||||
caLink *pca = plink->value.pv_link.pvt;
|
||||
|
||||
assert(!waitEvent);
|
||||
waitEvent = epicsEventMustCreate(epicsEventEmpty);
|
||||
|
||||
assert(pca);
|
||||
epicsMutexMustLock(pca->lock);
|
||||
assert(!pca->monitor && !pca->userPvt);
|
||||
pca->monitor = &waitCB;
|
||||
epicsMutexUnlock(pca->lock);
|
||||
testDiag("Preparing to wait on pca=%p", pca);
|
||||
}
|
||||
|
||||
static
|
||||
void waitForUpdate(DBLINK *plink)
|
||||
{
|
||||
caLink *pca = plink->value.pv_link.pvt;
|
||||
|
||||
assert(pca);
|
||||
|
||||
testDiag("Waiting on pca=%p", pca);
|
||||
epicsEventMustWait(waitEvent);
|
||||
|
||||
epicsMutexMustLock(pca->lock);
|
||||
pca->monitor = NULL;
|
||||
pca->userPvt = NULL;
|
||||
epicsMutexUnlock(pca->lock);
|
||||
|
||||
epicsEventDestroy(waitEvent);
|
||||
waitEvent = NULL;
|
||||
}
|
||||
|
||||
static
|
||||
void putLink(DBLINK *plink, short dbr, const void*buf, long nReq)
|
||||
{
|
||||
long ret;
|
||||
|
||||
waitEvent = epicsEventMustCreate(epicsEventEmpty);
|
||||
|
||||
ret = dbCaPutLinkCallback(plink, dbr, buf, nReq,
|
||||
&waitCB, NULL);
|
||||
if(ret) {
|
||||
testFail("putLink fails %ld\n", ret);
|
||||
} else {
|
||||
epicsEventMustWait(waitEvent);
|
||||
testPass("putLink ok\n");
|
||||
}
|
||||
epicsEventDestroy(waitEvent);
|
||||
waitEvent = NULL;
|
||||
}
|
||||
|
||||
static void testNativeLink(void)
|
||||
{
|
||||
xRecord *psrc, *ptarg;
|
||||
DBLINK *psrclnk;
|
||||
epicsInt32 temp;
|
||||
long nReq;
|
||||
|
||||
testDiag("Link to a scalar numeric field");
|
||||
testdbPrepare();
|
||||
|
||||
testdbReadDatabase("dbTestIoc.dbd", NULL, NULL);
|
||||
|
||||
dbTestIoc_registerRecordDeviceDriver(pdbbase);
|
||||
|
||||
testdbReadDatabase("dbCaLinkTest1.db", NULL, "TARGET=target CA");
|
||||
|
||||
eltc(0);
|
||||
testIocInitOk();
|
||||
eltc(1);
|
||||
|
||||
psrc = (xRecord*)testdbRecordPtr("source");
|
||||
ptarg= (xRecord*)testdbRecordPtr("target");
|
||||
psrclnk = &psrc->lnk;
|
||||
|
||||
/* ensure this is really a CA link */
|
||||
testOk1(dbLockGetLockId((dbCommon*)psrc)!=dbLockGetLockId((dbCommon*)ptarg));
|
||||
|
||||
testOk1(psrclnk->type==CA_LINK);
|
||||
|
||||
startWait(psrclnk);
|
||||
|
||||
dbScanLock((dbCommon*)ptarg);
|
||||
ptarg->val = 42;
|
||||
db_post_events(ptarg, &ptarg->val, DBE_VALUE|DBE_ALARM|DBE_ARCHIVE);
|
||||
dbScanUnlock((dbCommon*)ptarg);
|
||||
|
||||
waitForUpdate(psrclnk);
|
||||
|
||||
dbScanLock((dbCommon*)psrc);
|
||||
/* local CA_LINK connects immediately */
|
||||
testOk1(dbCaIsLinkConnected(psrclnk));
|
||||
nReq = 422;
|
||||
testOk1(dbCaGetNelements(psrclnk, &nReq)==0);
|
||||
testOp("%ld",nReq,==,1l);
|
||||
|
||||
nReq = 1;
|
||||
temp = 0x0f0f0f0f;
|
||||
testOk1(dbGetLink(psrclnk, DBR_LONG, (void*)&temp, NULL, &nReq)==0);
|
||||
testOp("%d",temp,==,42);
|
||||
dbScanUnlock((dbCommon*)psrc);
|
||||
|
||||
temp = 1010;
|
||||
nReq = 1;
|
||||
putLink(psrclnk, DBR_LONG, (void*)&temp, nReq);
|
||||
|
||||
dbScanLock((dbCommon*)ptarg);
|
||||
testOk1(ptarg->val==1010);
|
||||
dbScanUnlock((dbCommon*)ptarg);
|
||||
|
||||
testIocShutdownOk();
|
||||
|
||||
testdbCleanup();
|
||||
}
|
||||
|
||||
static void testStringLink(void)
|
||||
{
|
||||
xRecord *psrc, *ptarg;
|
||||
DBLINK *psrclnk;
|
||||
char temp[MAX_STRING_SIZE];
|
||||
long nReq;
|
||||
|
||||
testDiag("Link to a string field");
|
||||
testdbPrepare();
|
||||
|
||||
testdbReadDatabase("dbTestIoc.dbd", NULL, NULL);
|
||||
|
||||
dbTestIoc_registerRecordDeviceDriver(pdbbase);
|
||||
|
||||
testdbReadDatabase("dbCaLinkTest1.db", NULL, "TARGET=target.DESC CA");
|
||||
|
||||
eltc(0);
|
||||
testIocInitOk();
|
||||
eltc(1);
|
||||
|
||||
psrc = (xRecord*)testdbRecordPtr("source");
|
||||
ptarg= (xRecord*)testdbRecordPtr("target");
|
||||
psrclnk = &psrc->lnk;
|
||||
|
||||
/* ensure this is really a CA link */
|
||||
testOk1(dbLockGetLockId((dbCommon*)psrc)!=dbLockGetLockId((dbCommon*)ptarg));
|
||||
|
||||
testOk1(psrclnk->type==CA_LINK);
|
||||
|
||||
startWait(psrclnk);
|
||||
|
||||
dbScanLock((dbCommon*)ptarg);
|
||||
strcpy(ptarg->desc, "hello");
|
||||
db_post_events(ptarg, &ptarg->desc, DBE_VALUE|DBE_ALARM|DBE_ARCHIVE);
|
||||
dbScanUnlock((dbCommon*)ptarg);
|
||||
|
||||
waitForUpdate(psrclnk);
|
||||
|
||||
dbScanLock((dbCommon*)psrc);
|
||||
/* local CA_LINK connects immediately */
|
||||
testOk1(dbCaIsLinkConnected(psrclnk));
|
||||
|
||||
nReq = 1;
|
||||
memset(temp, '!', sizeof(temp));
|
||||
testOk1(dbGetLink(psrclnk, DBR_STRING, (void*)&temp, NULL, &nReq)==0);
|
||||
testOk(strcmp(temp, "hello")==0, "%s == hello", temp);
|
||||
dbScanUnlock((dbCommon*)psrc);
|
||||
|
||||
strcpy(temp, "world");
|
||||
putLink(psrclnk, DBR_STRING, (void*)&temp, nReq);
|
||||
|
||||
dbScanLock((dbCommon*)ptarg);
|
||||
testOk(strcmp(ptarg->desc, "world")==0, "%s == world", ptarg->desc);
|
||||
dbScanUnlock((dbCommon*)ptarg);
|
||||
|
||||
testIocShutdownOk();
|
||||
|
||||
testdbCleanup();
|
||||
}
|
||||
|
||||
static void wasproc(xRecord *prec)
|
||||
{
|
||||
waitCB(NULL);
|
||||
}
|
||||
|
||||
static void testCP(void)
|
||||
{
|
||||
xRecord *psrc, *ptarg;
|
||||
|
||||
testDiag("Link CP modifier");
|
||||
testdbPrepare();
|
||||
|
||||
testdbReadDatabase("dbTestIoc.dbd", NULL, NULL);
|
||||
|
||||
dbTestIoc_registerRecordDeviceDriver(pdbbase);
|
||||
|
||||
testdbReadDatabase("dbCaLinkTest1.db", NULL, "TARGET=target CP");
|
||||
|
||||
psrc = (xRecord*)testdbRecordPtr("source");
|
||||
ptarg= (xRecord*)testdbRecordPtr("target");
|
||||
|
||||
/* hook in before IOC init */
|
||||
waitCounter=0;
|
||||
psrc->clbk = &wasproc;
|
||||
|
||||
assert(!waitEvent);
|
||||
waitEvent = epicsEventMustCreate(epicsEventEmpty);
|
||||
|
||||
eltc(0);
|
||||
testIocInitOk();
|
||||
eltc(1);
|
||||
|
||||
epicsEventMustWait(waitEvent);
|
||||
|
||||
dbScanLock((dbCommon*)psrc);
|
||||
testOp("%u",waitCounter,==,1); /* initial processing */
|
||||
dbScanUnlock((dbCommon*)psrc);
|
||||
|
||||
dbScanLock((dbCommon*)ptarg);
|
||||
ptarg->val = 42;
|
||||
db_post_events(ptarg, &ptarg->val, DBE_VALUE|DBE_ALARM|DBE_ARCHIVE);
|
||||
dbScanUnlock((dbCommon*)ptarg);
|
||||
|
||||
epicsEventMustWait(waitEvent);
|
||||
|
||||
dbScanLock((dbCommon*)psrc);
|
||||
testOp("%u",waitCounter,==,2); /* process due to monitor update */
|
||||
dbScanUnlock((dbCommon*)psrc);
|
||||
|
||||
testIocShutdownOk();
|
||||
|
||||
testdbCleanup();
|
||||
|
||||
epicsEventDestroy(waitEvent);
|
||||
waitEvent = NULL;
|
||||
}
|
||||
|
||||
static void fillArray(epicsInt32 *buf, unsigned count, epicsInt32 first)
|
||||
{
|
||||
for(;count;count--,first++)
|
||||
*buf++ = first;
|
||||
}
|
||||
|
||||
static void fillArrayDouble(double *buf, unsigned count, double first)
|
||||
{
|
||||
for(;count;count--,first++)
|
||||
*buf++ = first;
|
||||
}
|
||||
|
||||
static void checkArray(const char *msg,
|
||||
epicsInt32 *buf, epicsInt32 first,
|
||||
unsigned used)
|
||||
{
|
||||
int match = 1;
|
||||
unsigned i;
|
||||
epicsInt32 x, *b;
|
||||
|
||||
for(b=buf,x=first,i=0;i<used;i++,x++,b++)
|
||||
match &= (*b)==x;
|
||||
testOk(match, "%s", msg);
|
||||
if(!match) {
|
||||
for(b=buf,x=first,i=0;i<used;i++,x++,b++)
|
||||
if((*b)!=x)
|
||||
testDiag("%u %u != %u", i, (unsigned)*b, (unsigned)x);
|
||||
}
|
||||
}
|
||||
|
||||
static void checkArrayDouble(const char *msg,
|
||||
double *buf, double first,
|
||||
unsigned used)
|
||||
{
|
||||
int match = 1;
|
||||
unsigned i;
|
||||
double x, *b;
|
||||
|
||||
for(b=buf,x=first,i=0;i<used;i++,x++,b++)
|
||||
match &= (*b)==x;
|
||||
testOk(match, "%s", msg);
|
||||
if(!match) {
|
||||
for(b=buf,x=first,i=0;i<used;i++,x++,b++)
|
||||
if((*b)!=x)
|
||||
testDiag("%u %u != %u", i, (unsigned)*b, (unsigned)x);
|
||||
}
|
||||
}
|
||||
|
||||
static void spoilputbuf(DBLINK *lnk)
|
||||
{
|
||||
caLink *pca = lnk->value.pv_link.pvt;
|
||||
|
||||
if(lnk->type!=CA_LINK || !pca->pputNative)
|
||||
return;
|
||||
epicsMutexMustLock(pca->lock);
|
||||
memset(pca->pputNative, '!',
|
||||
pca->nelements*dbr_value_size[ca_field_type(pca->chid)]);
|
||||
epicsMutexUnlock(pca->lock);
|
||||
}
|
||||
|
||||
static void testArrayLink(unsigned nsrc, unsigned ntarg)
|
||||
{
|
||||
char buf[100];
|
||||
arrRecord *psrc, *ptarg;
|
||||
DBLINK *psrclnk;
|
||||
epicsInt32 *bufsrc, *buftarg, *tmpbuf;
|
||||
long nReq;
|
||||
unsigned num_min, num_max;
|
||||
|
||||
testDiag("Link to a array numeric field");
|
||||
|
||||
/* source.INP = "target CA" */
|
||||
epicsSnprintf(buf, sizeof(buf), "TARGET=target CA,FTVL=LONG,SNELM=%u,TNELM=%u",
|
||||
nsrc, ntarg);
|
||||
testDiag("%s", buf);
|
||||
|
||||
testdbPrepare();
|
||||
|
||||
testdbReadDatabase("dbTestIoc.dbd", NULL, NULL);
|
||||
|
||||
dbTestIoc_registerRecordDeviceDriver(pdbbase);
|
||||
|
||||
testdbReadDatabase("dbCaLinkTest2.db", NULL, buf);
|
||||
|
||||
psrc = (arrRecord*)testdbRecordPtr("source");
|
||||
ptarg= (arrRecord*)testdbRecordPtr("target");
|
||||
psrclnk = &psrc->inp;
|
||||
|
||||
eltc(0);
|
||||
testIocInitOk();
|
||||
eltc(1);
|
||||
|
||||
bufsrc = psrc->bptr;
|
||||
buftarg= ptarg->bptr;
|
||||
|
||||
num_max=num_min=psrc->nelm;
|
||||
if(num_min>ptarg->nelm)
|
||||
num_min=ptarg->nelm;
|
||||
if(num_max<ptarg->nelm)
|
||||
num_max=ptarg->nelm;
|
||||
/* always request more than can possibly be filled */
|
||||
num_max += 2;
|
||||
|
||||
tmpbuf = callocMustSucceed(num_max, sizeof(*tmpbuf), "tmpbuf");
|
||||
|
||||
startWait(psrclnk);
|
||||
|
||||
dbScanLock((dbCommon*)ptarg);
|
||||
fillArray(buftarg, ptarg->nelm, 1);
|
||||
ptarg->nord = ptarg->nelm;
|
||||
db_post_events(ptarg, ptarg->bptr, DBE_VALUE|DBE_ALARM|DBE_ARCHIVE);
|
||||
dbScanUnlock((dbCommon*)ptarg);
|
||||
|
||||
waitForUpdate(psrclnk);
|
||||
|
||||
dbScanLock((dbCommon*)psrc);
|
||||
testDiag("fetch source.INP into source.BPTR");
|
||||
nReq = psrc->nelm;
|
||||
if(dbGetLink(psrclnk, DBR_LONG, bufsrc, NULL, &nReq)==0) {
|
||||
testPass("dbGetLink");
|
||||
testOp("%ld",nReq,==,(long)num_min);
|
||||
checkArray("array update", bufsrc, 1, nReq);
|
||||
} else {
|
||||
testFail("dbGetLink");
|
||||
testSkip(2, "dbGetLink fails");
|
||||
}
|
||||
testDiag("fetch source.INP into temp buffer w/ larger capacity");
|
||||
nReq = num_max;
|
||||
if(dbGetLink(psrclnk, DBR_LONG, tmpbuf, NULL, &nReq)==0) {
|
||||
testPass("dbGetLink");
|
||||
testOp("%ld",nReq,==,(long)ntarg);
|
||||
checkArray("array update", tmpbuf, 1, nReq);
|
||||
} else {
|
||||
testFail("dbGetLink");
|
||||
testSkip(2, "dbGetLink fails");
|
||||
}
|
||||
dbScanUnlock((dbCommon*)psrc);
|
||||
|
||||
fillArray(bufsrc, psrc->nelm, 2);
|
||||
/* write buffer allocated on first put */
|
||||
putLink(psrclnk, DBR_LONG, bufsrc, psrc->nelm);
|
||||
|
||||
dbScanLock((dbCommon*)ptarg);
|
||||
testOp("%ld",(long)ptarg->nord,==,(long)num_min);
|
||||
|
||||
dbScanUnlock((dbCommon*)ptarg);
|
||||
|
||||
/* write again to ensure that buffer is completely updated */
|
||||
spoilputbuf(psrclnk);
|
||||
fillArray(bufsrc, psrc->nelm, 3);
|
||||
putLink(psrclnk, DBR_LONG, bufsrc, psrc->nelm);
|
||||
|
||||
dbScanLock((dbCommon*)ptarg);
|
||||
testOp("%ld",(long)ptarg->nord,==,(long)num_min);
|
||||
checkArray("array update", buftarg, 3, num_min);
|
||||
dbScanUnlock((dbCommon*)ptarg);
|
||||
|
||||
testIocShutdownOk();
|
||||
|
||||
testdbCleanup();
|
||||
|
||||
free(tmpbuf);
|
||||
/* records don't cleanup after themselves
|
||||
* so do here to silence valgrind
|
||||
*/
|
||||
free(bufsrc);
|
||||
free(buftarg);
|
||||
}
|
||||
|
||||
|
||||
static void softarr(arrRecord *prec)
|
||||
{
|
||||
long nReq = prec->nelm;
|
||||
long status = dbGetLink(&prec->inp, DBR_DOUBLE, prec->bptr, NULL, &nReq);
|
||||
|
||||
if(status) {
|
||||
testFail("dbGetLink() -> %ld", status);
|
||||
} else {
|
||||
testPass("dbGetLink() succeeds");
|
||||
prec->nord = nReq;
|
||||
if(nReq>0)
|
||||
testDiag("%s.VAL[0] - %f", prec->name, *(double*)prec->bptr);
|
||||
}
|
||||
waitCB(NULL);
|
||||
}
|
||||
|
||||
static void testreTargetTypeChange(void)
|
||||
{
|
||||
arrRecord *psrc, *ptarg1, *ptarg2;
|
||||
double *bufsrc, *buftarg1;
|
||||
epicsInt32 *buftarg2;
|
||||
|
||||
testDiag("Retarget an link to a PV with a different type DOUBLE->LONG");
|
||||
testdbPrepare();
|
||||
|
||||
testdbReadDatabase("dbTestIoc.dbd", NULL, NULL);
|
||||
|
||||
dbTestIoc_registerRecordDeviceDriver(pdbbase);
|
||||
|
||||
testdbReadDatabase("dbCaLinkTest3.db", NULL, "NELM=5,TARGET=target1 CP");
|
||||
|
||||
psrc = (arrRecord*)testdbRecordPtr("source");
|
||||
ptarg1= (arrRecord*)testdbRecordPtr("target1");
|
||||
ptarg2= (arrRecord*)testdbRecordPtr("target2");
|
||||
|
||||
/* hook in before IOC init */
|
||||
waitCounter=0;
|
||||
psrc->clbk = &softarr;
|
||||
|
||||
assert(!waitEvent);
|
||||
waitEvent = epicsEventMustCreate(epicsEventEmpty);
|
||||
|
||||
eltc(0);
|
||||
testIocInitOk();
|
||||
eltc(1);
|
||||
|
||||
epicsEventMustWait(waitEvent); /* wait for initial processing */
|
||||
|
||||
bufsrc = psrc->bptr;
|
||||
buftarg1= ptarg1->bptr;
|
||||
buftarg2= ptarg2->bptr;
|
||||
|
||||
testDiag("Update one with original target");
|
||||
|
||||
dbScanLock((dbCommon*)ptarg2);
|
||||
fillArray(buftarg2, ptarg2->nelm, 2);
|
||||
ptarg2->nord = ptarg2->nelm;
|
||||
dbScanUnlock((dbCommon*)ptarg2);
|
||||
|
||||
/* initialize buffers */
|
||||
dbScanLock((dbCommon*)ptarg1);
|
||||
fillArrayDouble(buftarg1, ptarg1->nelm, 1);
|
||||
ptarg1->nord = ptarg1->nelm;
|
||||
db_post_events(ptarg1, ptarg1->bptr, DBE_VALUE|DBE_ALARM|DBE_ARCHIVE);
|
||||
dbScanUnlock((dbCommon*)ptarg1);
|
||||
|
||||
epicsEventMustWait(waitEvent); /* wait for update */
|
||||
|
||||
dbScanLock((dbCommon*)psrc);
|
||||
testOp("%ld",(long)psrc->nord,==,(long)5);
|
||||
checkArrayDouble("array update", bufsrc, 1, 5);
|
||||
dbScanUnlock((dbCommon*)psrc);
|
||||
|
||||
testDiag("Retarget");
|
||||
testdbPutFieldOk("source.INP", DBR_STRING, "target2 CP");
|
||||
|
||||
epicsEventMustWait(waitEvent); /* wait for update */
|
||||
|
||||
dbScanLock((dbCommon*)psrc);
|
||||
testOp("%ld",(long)psrc->nord,==,(long)5);
|
||||
checkArrayDouble("array update", bufsrc, 2, 5);
|
||||
dbScanUnlock((dbCommon*)psrc);
|
||||
|
||||
testIocShutdownOk();
|
||||
|
||||
testdbCleanup();
|
||||
|
||||
/* records don't cleanup after themselves
|
||||
* so do here to silence valgrind
|
||||
*/
|
||||
free(bufsrc);
|
||||
free(buftarg1);
|
||||
free(buftarg2);
|
||||
}
|
||||
|
||||
void dbCaLinkTest_testCAC(void);
|
||||
|
||||
static void testCAC(void)
|
||||
{
|
||||
arrRecord *psrc, *ptarg1, *ptarg2;
|
||||
double *bufsrc, *buftarg1;
|
||||
epicsInt32 *buftarg2;
|
||||
|
||||
testDiag("Check local CA through libca");
|
||||
testdbPrepare();
|
||||
|
||||
testdbReadDatabase("dbTestIoc.dbd", NULL, NULL);
|
||||
|
||||
dbTestIoc_registerRecordDeviceDriver(pdbbase);
|
||||
|
||||
testdbReadDatabase("dbCaLinkTest3.db", NULL, "NELM=5,TARGET=target1 CP");
|
||||
|
||||
psrc = (arrRecord*)testdbRecordPtr("source");
|
||||
ptarg1= (arrRecord*)testdbRecordPtr("target1");
|
||||
ptarg2= (arrRecord*)testdbRecordPtr("target2");
|
||||
|
||||
eltc(0);
|
||||
testIocInitOk();
|
||||
eltc(1);
|
||||
|
||||
bufsrc = psrc->bptr;
|
||||
buftarg1= ptarg1->bptr;
|
||||
buftarg2= ptarg2->bptr;
|
||||
|
||||
dbCaLinkTest_testCAC();
|
||||
|
||||
testIocShutdownOk();
|
||||
|
||||
testdbCleanup();
|
||||
|
||||
/* records don't cleanup after themselves
|
||||
* so do here to silence valgrind
|
||||
*/
|
||||
free(bufsrc);
|
||||
free(buftarg1);
|
||||
free(buftarg2);
|
||||
}
|
||||
|
||||
MAIN(dbCaLinkTest)
|
||||
{
|
||||
testPlan(99);
|
||||
testNativeLink();
|
||||
testStringLink();
|
||||
testCP();
|
||||
testArrayLink(1,1);
|
||||
testArrayLink(10,1);
|
||||
testArrayLink(1,10);
|
||||
testArrayLink(10,10);
|
||||
testreTargetTypeChange();
|
||||
testCAC();
|
||||
return testDone();
|
||||
}
|
||||
5
src/ioc/db/test/dbCaLinkTest1.db
Normal file
5
src/ioc/db/test/dbCaLinkTest1.db
Normal file
@@ -0,0 +1,5 @@
|
||||
record(x, "target") {}
|
||||
|
||||
record(x, "source") {
|
||||
field(LNK, "$(TARGET)")
|
||||
}
|
||||
10
src/ioc/db/test/dbCaLinkTest2.db
Normal file
10
src/ioc/db/test/dbCaLinkTest2.db
Normal file
@@ -0,0 +1,10 @@
|
||||
record(arr, "target") {
|
||||
field(FTVL, "$(TFTVL=$(FTVL=))")
|
||||
field(NELM, "$(TNELM=$(NELM=))")
|
||||
}
|
||||
|
||||
record(arr, "source") {
|
||||
field(INP, "$(TARGET)")
|
||||
field(FTVL, "$(SFTVL=$(FTVL=))")
|
||||
field(NELM, "$(SNELM=$(NELM=))")
|
||||
}
|
||||
14
src/ioc/db/test/dbCaLinkTest3.db
Normal file
14
src/ioc/db/test/dbCaLinkTest3.db
Normal file
@@ -0,0 +1,14 @@
|
||||
record(arr, "target1") {
|
||||
field(FTVL, "DOUBLE")
|
||||
field(NELM, "$(TNELM=$(NELM=))")
|
||||
}
|
||||
record(arr, "target2") {
|
||||
field(FTVL, "LONG")
|
||||
field(NELM, "$(TNELM=$(NELM=))")
|
||||
}
|
||||
|
||||
record(arr, "source") {
|
||||
field(INP, "$(TARGET)")
|
||||
field(FTVL, "DOUBLE")
|
||||
field(NELM, "$(SNELM=$(NELM=))")
|
||||
}
|
||||
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")
|
||||
}
|
||||
@@ -18,7 +18,7 @@
|
||||
#include "dbAccessDefs.h"
|
||||
#include "registry.h"
|
||||
#include "recSup.h"
|
||||
#include "epicsUnitTest.h"
|
||||
#include "dbUnitTest.h"
|
||||
#include "testMain.h"
|
||||
#include "osiFileName.h"
|
||||
#include "errlog.h"
|
||||
@@ -158,17 +158,16 @@ MAIN(testDbChannel) /* dbChannelTest is an API routine... */
|
||||
|
||||
testPlan(76);
|
||||
|
||||
if (dbReadDatabase(&pdbbase, "dbTestIoc.dbd",
|
||||
"." OSI_PATH_LIST_SEPARATOR ".." OSI_PATH_LIST_SEPARATOR
|
||||
"../O.Common" OSI_PATH_LIST_SEPARATOR "O.Common", NULL))
|
||||
testAbort("Database description 'dbTestIoc.dbd' not found");
|
||||
testdbPrepare();
|
||||
|
||||
testdbReadDatabase("dbTestIoc.dbd", NULL, NULL);
|
||||
|
||||
dbTestIoc_registerRecordDeviceDriver(pdbbase);
|
||||
if (dbReadDatabase(&pdbbase, "xRecord.db",
|
||||
"." OSI_PATH_LIST_SEPARATOR "..", NULL))
|
||||
testAbort("Test database 'xRecord.db' not found");
|
||||
testdbReadDatabase("xRecord.db", NULL, NULL);
|
||||
|
||||
dbChannelInit();
|
||||
eltc(0);
|
||||
testIocInitOk();
|
||||
eltc(1);
|
||||
|
||||
r = e = 0;
|
||||
/* dbChannelTest() checks record and field names */
|
||||
@@ -242,7 +241,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);
|
||||
@@ -267,9 +266,8 @@ MAIN(testDbChannel) /* dbChannelTest is an API routine... */
|
||||
e = e_start | e_start_map | e_abort;
|
||||
testOk1(!dbChannelCreate("x.{\"scalar\":{}}"));
|
||||
|
||||
dbFreeBase(pdbbase);
|
||||
registryFree();
|
||||
pdbbase = NULL;
|
||||
testIocShutdownOk();
|
||||
testdbCleanup();
|
||||
|
||||
return testDone();
|
||||
}
|
||||
|
||||
@@ -12,9 +12,10 @@ long link_test_extend(struct dbCommon *junk)
|
||||
static dsxt xrecextend = {&link_test_extend, &link_test_extend};
|
||||
|
||||
static
|
||||
long link_test_init(int junk)
|
||||
long link_test_init(int pass)
|
||||
{
|
||||
devExtend(&xrecextend);
|
||||
if (pass == 0)
|
||||
devExtend(&xrecextend);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -28,7 +29,6 @@ long link_test_noop(void *junk)
|
||||
static dset devxLTest ## LTYPE = {4, NULL, &link_test_init, &link_test_noop, &link_test_noop}; \
|
||||
epicsExportAddress(dset, devxLTest ## LTYPE);
|
||||
|
||||
DEFDSET(Soft)
|
||||
DEFDSET(VME_IO)
|
||||
DEFDSET(CAMAC_IO)
|
||||
DEFDSET(AB_IO)
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
device(x, CONSTANT,devxLTestSoft,"Soft Channel")
|
||||
|
||||
device(x, VME_IO, devxLTestVME_IO, "Unit Test VME_IO")
|
||||
device(x, CAMAC_IO, devxLTestCAMAC_IO, "Unit Test CAMAC_IO")
|
||||
device(x, AB_IO, devxLTestAB_IO, "Unit Test AB_IO")
|
||||
|
||||
@@ -9,7 +9,15 @@
|
||||
* Author: Michael Davidsaver <mdavidsaver@bnl.gov>
|
||||
*/
|
||||
|
||||
#include "dbLock.h"
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "epicsSpin.h"
|
||||
#include "epicsMutex.h"
|
||||
#include "dbCommon.h"
|
||||
#include "epicsThread.h"
|
||||
|
||||
#include "dbLockPvt.h"
|
||||
#include "dbStaticLib.h"
|
||||
|
||||
#include "dbUnitTest.h"
|
||||
#include "testMain.h"
|
||||
@@ -28,15 +36,19 @@ void compareSets(int match, const char *A, const char *B)
|
||||
rA = testdbRecordPtr(A);
|
||||
rB = testdbRecordPtr(B);
|
||||
|
||||
actual = dbLockGetLockId(rA)==dbLockGetLockId(rB);
|
||||
actual = rA->lset->plockSet==rB->lset->plockSet;
|
||||
|
||||
testOk(match==actual, "dbLockGetLockId(\"%s\")%c=dbLockGetLockId(\"%s\")",
|
||||
A, match?'=':'!', B);
|
||||
}
|
||||
|
||||
#define testIntOk1(A, OP, B) testOk((A) OP (B), "%s (%d) %s %s (%d)", #A, A, #OP, #B, B);
|
||||
#define testPtrOk1(A, OP, B) testOk((A) OP (B), "%s (%p) %s %s (%p)", #A, A, #OP, #B, B);
|
||||
|
||||
static
|
||||
void testSets(void) {
|
||||
testDiag("Check initial creation of DB links");
|
||||
DBENTRY entry;
|
||||
long status;
|
||||
|
||||
testdbPrepare();
|
||||
|
||||
@@ -48,6 +60,27 @@ void testSets(void) {
|
||||
testIocInitOk();
|
||||
eltc(1);
|
||||
|
||||
testDiag("Check that all records have initialized lockRecord and lockSet");
|
||||
|
||||
dbInitEntry(pdbbase, &entry);
|
||||
for(status = dbFirstRecordType(&entry);
|
||||
!status;
|
||||
status = dbNextRecordType(&entry)) {
|
||||
for(status = dbFirstRecord(&entry);
|
||||
!status;
|
||||
status = dbNextRecord(&entry)) {
|
||||
dbCommon *prec = entry.precnode->precord;
|
||||
testOk(prec->lset!=NULL, "%s.LSET != NULL", prec->name);
|
||||
if(prec->lset!=NULL)
|
||||
testOk(prec->lset->plockSet!=NULL, "%s.LSET.plockSet != NULL", prec->name);
|
||||
else
|
||||
testSkip(1, "lockRecord missing");
|
||||
}
|
||||
}
|
||||
dbFinishEntry(&entry);
|
||||
|
||||
testDiag("Check initial creation of DB links");
|
||||
|
||||
/* reca is by itself */
|
||||
compareSets(0, "reca", "recb");
|
||||
compareSets(0, "reca", "recc");
|
||||
@@ -71,6 +104,310 @@ void testSets(void) {
|
||||
|
||||
compareSets(1, "rece", "recf");
|
||||
|
||||
testOk1(testdbRecordPtr("reca")->lset->plockSet->refcount==1);
|
||||
testOk1(testdbRecordPtr("recb")->lset->plockSet->refcount==2);
|
||||
testOk1(testdbRecordPtr("recd")->lset->plockSet->refcount==3);
|
||||
|
||||
testIocShutdownOk();
|
||||
|
||||
testdbCleanup();
|
||||
}
|
||||
|
||||
static void testSingleLock(void)
|
||||
{
|
||||
dbCommon *prec;
|
||||
testDiag("testing dbScanLock()/dbScanUnlock()");
|
||||
|
||||
testdbPrepare();
|
||||
|
||||
testdbReadDatabase("dbTestIoc.dbd", NULL, NULL);
|
||||
dbTestIoc_registerRecordDeviceDriver(pdbbase);
|
||||
testdbReadDatabase("dbLockTest.db", NULL, NULL);
|
||||
|
||||
eltc(0);
|
||||
testIocInitOk();
|
||||
eltc(1);
|
||||
|
||||
prec = testdbRecordPtr("reca");
|
||||
testOk1(prec->lset->plockSet->refcount==1);
|
||||
dbScanLock(prec);
|
||||
/* scan lock does not keep a reference to the lockSet */
|
||||
testOk1(prec->lset->plockSet->refcount==1);
|
||||
dbScanUnlock(prec);
|
||||
|
||||
dbScanLock(prec);
|
||||
dbScanLock(prec);
|
||||
/* scan lock can be recursive */
|
||||
testOk1(prec->lset->plockSet->refcount==1);
|
||||
dbScanUnlock(prec);
|
||||
dbScanUnlock(prec);
|
||||
|
||||
testIocShutdownOk();
|
||||
|
||||
testdbCleanup();
|
||||
}
|
||||
|
||||
static void testMultiLock(void)
|
||||
{
|
||||
dbCommon *prec[8];
|
||||
dbLocker *plockA;
|
||||
#ifdef LOCKSET_DEBUG
|
||||
epicsThreadId myself = epicsThreadGetIdSelf();
|
||||
#endif
|
||||
|
||||
testDiag("Test multi-locker function (lock everything)");
|
||||
|
||||
testdbPrepare();
|
||||
|
||||
testdbReadDatabase("dbTestIoc.dbd", NULL, NULL);
|
||||
dbTestIoc_registerRecordDeviceDriver(pdbbase);
|
||||
testdbReadDatabase("dbLockTest.db", NULL, NULL);
|
||||
|
||||
eltc(0);
|
||||
testIocInitOk();
|
||||
eltc(1);
|
||||
|
||||
prec[0] = testdbRecordPtr("reca");
|
||||
prec[1] = testdbRecordPtr("recb");
|
||||
prec[2] = testdbRecordPtr("recc");
|
||||
prec[3] = NULL;
|
||||
prec[4] = testdbRecordPtr("recd");
|
||||
prec[5] = testdbRecordPtr("rece");
|
||||
prec[6] = testdbRecordPtr("recf");
|
||||
prec[7] = testdbRecordPtr("recg");
|
||||
|
||||
testDiag("Test init refcounts");
|
||||
testIntOk1(testdbRecordPtr("reca")->lset->plockSet->refcount,==,1);
|
||||
testIntOk1(testdbRecordPtr("recb")->lset->plockSet->refcount,==,2);
|
||||
testIntOk1(testdbRecordPtr("recd")->lset->plockSet->refcount,==,3);
|
||||
testIntOk1(testdbRecordPtr("recg")->lset->plockSet->refcount,==,1);
|
||||
|
||||
plockA = dbLockerAlloc(prec, 8, 0);
|
||||
if(!plockA)
|
||||
testAbort("dbLockerAlloc() failed");
|
||||
|
||||
|
||||
testDiag("After locker created");
|
||||
/* locker takes 7 references, one for each lockRecord. */
|
||||
testIntOk1(testdbRecordPtr("reca")->lset->plockSet->refcount,==,2);
|
||||
testIntOk1(testdbRecordPtr("recb")->lset->plockSet->refcount,==,4);
|
||||
testIntOk1(testdbRecordPtr("recd")->lset->plockSet->refcount,==,6);
|
||||
testIntOk1(testdbRecordPtr("recg")->lset->plockSet->refcount,==,2);
|
||||
#ifdef LOCKSET_DEBUG
|
||||
testPtrOk1(testdbRecordPtr("reca")->lset->plockSet->owner,==,NULL);
|
||||
testPtrOk1(testdbRecordPtr("recb")->lset->plockSet->owner,==,NULL);
|
||||
testPtrOk1(testdbRecordPtr("recd")->lset->plockSet->owner,==,NULL);
|
||||
testPtrOk1(testdbRecordPtr("recg")->lset->plockSet->owner,==,NULL);
|
||||
#endif
|
||||
|
||||
dbScanLockMany(plockA);
|
||||
|
||||
testDiag("After locker locked");
|
||||
/* locker takes 4 references, one for each lockSet. */
|
||||
testIntOk1(ellCount(&plockA->locked),==,4);
|
||||
testIntOk1(testdbRecordPtr("reca")->lset->plockSet->refcount,==,3);
|
||||
testIntOk1(testdbRecordPtr("recb")->lset->plockSet->refcount,==,5);
|
||||
testIntOk1(testdbRecordPtr("recd")->lset->plockSet->refcount,==,7);
|
||||
testIntOk1(testdbRecordPtr("recg")->lset->plockSet->refcount,==,3);
|
||||
#ifdef LOCKSET_DEBUG
|
||||
testPtrOk1(testdbRecordPtr("reca")->lset->plockSet->owner,==,myself);
|
||||
testPtrOk1(testdbRecordPtr("recb")->lset->plockSet->owner,==,myself);
|
||||
testPtrOk1(testdbRecordPtr("recd")->lset->plockSet->owner,==,myself);
|
||||
testPtrOk1(testdbRecordPtr("recg")->lset->plockSet->owner,==,myself);
|
||||
#endif
|
||||
|
||||
/* recursive locking of individual records is allowed */
|
||||
dbScanLock(prec[0]);
|
||||
testIntOk1(testdbRecordPtr("reca")->lset->plockSet->refcount,==,3);
|
||||
dbScanUnlock(prec[0]);
|
||||
|
||||
/* recursive locking with dbScanLockMany() isn't
|
||||
* dbScanLockMany(plockA); <-- would fail
|
||||
*/
|
||||
|
||||
dbScanUnlockMany(plockA);
|
||||
|
||||
testDiag("After locker unlocked");
|
||||
testIntOk1(testdbRecordPtr("reca")->lset->plockSet->refcount,==,2);
|
||||
testIntOk1(testdbRecordPtr("recb")->lset->plockSet->refcount,==,4);
|
||||
testIntOk1(testdbRecordPtr("recd")->lset->plockSet->refcount,==,6);
|
||||
testIntOk1(testdbRecordPtr("recg")->lset->plockSet->refcount,==,2);
|
||||
#ifdef LOCKSET_DEBUG
|
||||
testPtrOk1(testdbRecordPtr("reca")->lset->plockSet->owner,==,NULL);
|
||||
testPtrOk1(testdbRecordPtr("recb")->lset->plockSet->owner,==,NULL);
|
||||
testPtrOk1(testdbRecordPtr("recd")->lset->plockSet->owner,==,NULL);
|
||||
testPtrOk1(testdbRecordPtr("recg")->lset->plockSet->owner,==,NULL);
|
||||
#endif
|
||||
|
||||
dbLockerFree(plockA);
|
||||
|
||||
testDiag("After locker free'd");
|
||||
testIntOk1(testdbRecordPtr("reca")->lset->plockSet->refcount,==,1);
|
||||
testIntOk1(testdbRecordPtr("recb")->lset->plockSet->refcount,==,2);
|
||||
testIntOk1(testdbRecordPtr("recd")->lset->plockSet->refcount,==,3);
|
||||
testIntOk1(testdbRecordPtr("recg")->lset->plockSet->refcount,==,1);
|
||||
|
||||
testIocShutdownOk();
|
||||
|
||||
testdbCleanup();
|
||||
}
|
||||
|
||||
static void testLinkBreak(void)
|
||||
{
|
||||
dbCommon *precB, *precC;
|
||||
testDiag("Test break link");
|
||||
|
||||
testdbPrepare();
|
||||
|
||||
testdbReadDatabase("dbTestIoc.dbd", NULL, NULL);
|
||||
dbTestIoc_registerRecordDeviceDriver(pdbbase);
|
||||
testdbReadDatabase("dbLockTest.db", NULL, NULL);
|
||||
|
||||
eltc(0);
|
||||
testIocInitOk();
|
||||
eltc(1);
|
||||
|
||||
precB = testdbRecordPtr("recb");
|
||||
precC = testdbRecordPtr("recc");
|
||||
|
||||
testOk1(precB->lset->plockSet==precC->lset->plockSet);
|
||||
testOk1(precB->lset->plockSet->refcount==2);
|
||||
|
||||
/* break the link between B and C */
|
||||
testdbPutFieldOk("recb.SDIS", DBR_STRING, "");
|
||||
|
||||
testOk1(precB->lset->plockSet!=precC->lset->plockSet);
|
||||
testIntOk1(precB->lset->plockSet->refcount, ==, 1);
|
||||
testIntOk1(precC->lset->plockSet->refcount, ==, 1);
|
||||
|
||||
testIocShutdownOk();
|
||||
|
||||
testdbCleanup();
|
||||
}
|
||||
|
||||
static void testLinkMake(void)
|
||||
{
|
||||
dbCommon *precA, *precG;
|
||||
lockSet *lA, *lG;
|
||||
testDiag("Test make link");
|
||||
|
||||
testdbPrepare();
|
||||
|
||||
testdbReadDatabase("dbTestIoc.dbd", NULL, NULL);
|
||||
dbTestIoc_registerRecordDeviceDriver(pdbbase);
|
||||
testdbReadDatabase("dbLockTest.db", NULL, NULL);
|
||||
|
||||
eltc(0);
|
||||
testIocInitOk();
|
||||
eltc(1);
|
||||
|
||||
precA = testdbRecordPtr("reca");
|
||||
lA = dbLockGetRef(precA->lset);
|
||||
precG = testdbRecordPtr("recg");
|
||||
lG = dbLockGetRef(precG->lset);
|
||||
|
||||
testPtrOk1(precA->lset->plockSet, !=, precG->lset->plockSet);
|
||||
testIntOk1(precA->lset->plockSet->refcount, ==, 2);
|
||||
testIntOk1(precG->lset->plockSet->refcount, ==, 2);
|
||||
|
||||
/* make a link between A and G */
|
||||
testdbPutFieldOk("reca.SDIS", DBR_STRING, "recg");
|
||||
|
||||
testPtrOk1(precA->lset->plockSet, ==, precG->lset->plockSet);
|
||||
testIntOk1(precA->lset->plockSet->refcount, ==, 3);
|
||||
|
||||
if(precA->lset->plockSet==lG) {
|
||||
testIntOk1(lA->refcount, ==, 1);
|
||||
testIntOk1(lG->refcount, ==, 3);
|
||||
} else {
|
||||
testIntOk1(lA->refcount, ==, 3);
|
||||
testIntOk1(lG->refcount, ==, 1);
|
||||
}
|
||||
dbLockDecRef(lG);
|
||||
dbLockDecRef(lA);
|
||||
|
||||
testIocShutdownOk();
|
||||
|
||||
testdbCleanup();
|
||||
}
|
||||
|
||||
static void testLinkChange(void)
|
||||
{
|
||||
dbCommon *precB, *precC, *precG;
|
||||
lockSet *lB, *lG;
|
||||
testDiag("Test re-target link");
|
||||
|
||||
testdbPrepare();
|
||||
|
||||
testdbReadDatabase("dbTestIoc.dbd", NULL, NULL);
|
||||
dbTestIoc_registerRecordDeviceDriver(pdbbase);
|
||||
testdbReadDatabase("dbLockTest.db", NULL, NULL);
|
||||
|
||||
eltc(0);
|
||||
testIocInitOk();
|
||||
eltc(1);
|
||||
|
||||
precB = testdbRecordPtr("recb");
|
||||
precC = testdbRecordPtr("recc");
|
||||
precG = testdbRecordPtr("recg");
|
||||
|
||||
lB = dbLockGetRef(precB->lset);
|
||||
lG = dbLockGetRef(precG->lset);
|
||||
|
||||
testPtrOk1(lB,==,precC->lset->plockSet);
|
||||
testPtrOk1(lB,!=,lG);
|
||||
testIntOk1(lB->refcount,==,3);
|
||||
testIntOk1(lG->refcount,==,2);
|
||||
|
||||
/* break the link between B and C and replace it
|
||||
* with a link between B and G
|
||||
*/
|
||||
testdbPutFieldOk("recb.SDIS", DBR_STRING, "recg");
|
||||
|
||||
testPtrOk1(precB->lset->plockSet,==,lB);
|
||||
testPtrOk1(precG->lset->plockSet,==,lB);
|
||||
testPtrOk1(precC->lset->plockSet,!=,lB);
|
||||
testPtrOk1(precC->lset->plockSet,!=,lG);
|
||||
|
||||
testIntOk1(lB->refcount,==,3);
|
||||
testIntOk1(lG->refcount,==,1);
|
||||
testIntOk1(precC->lset->plockSet->refcount,==,1);
|
||||
|
||||
dbLockDecRef(lB);
|
||||
dbLockDecRef(lG);
|
||||
|
||||
testIocShutdownOk();
|
||||
|
||||
testdbCleanup();
|
||||
}
|
||||
|
||||
static void testLinkNOP(void)
|
||||
{
|
||||
dbCommon *precB, *precC;
|
||||
testDiag("Test re-target link to the same destination");
|
||||
|
||||
testdbPrepare();
|
||||
|
||||
testdbReadDatabase("dbTestIoc.dbd", NULL, NULL);
|
||||
dbTestIoc_registerRecordDeviceDriver(pdbbase);
|
||||
testdbReadDatabase("dbLockTest.db", NULL, NULL);
|
||||
|
||||
eltc(0);
|
||||
testIocInitOk();
|
||||
eltc(1);
|
||||
|
||||
precB = testdbRecordPtr("recb");
|
||||
precC = testdbRecordPtr("recc");
|
||||
|
||||
testOk1(precB->lset->plockSet==precC->lset->plockSet);
|
||||
testOk1(precB->lset->plockSet->refcount==2);
|
||||
|
||||
/* renew link between B and C */
|
||||
testdbPutFieldOk("recb.SDIS", DBR_STRING, "recc");
|
||||
|
||||
testOk1(precB->lset->plockSet==precC->lset->plockSet);
|
||||
testOk1(precB->lset->plockSet->refcount==2);
|
||||
|
||||
testIocShutdownOk();
|
||||
|
||||
testdbCleanup();
|
||||
@@ -78,7 +415,17 @@ void testSets(void) {
|
||||
|
||||
MAIN(dbLockTest)
|
||||
{
|
||||
testPlan(15);
|
||||
#ifdef LOCKSET_DEBUG
|
||||
testPlan(100);
|
||||
#else
|
||||
testPlan(88);
|
||||
#endif
|
||||
testSets();
|
||||
testSingleLock();
|
||||
testMultiLock();
|
||||
testLinkBreak();
|
||||
testLinkMake();
|
||||
testLinkChange();
|
||||
testLinkNOP();
|
||||
return testDone();
|
||||
}
|
||||
|
||||
@@ -19,3 +19,6 @@ record(x, "rece") {
|
||||
|
||||
record(x, "recf") {
|
||||
}
|
||||
|
||||
record(x, "recg") {
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
#include "dbAccess.h"
|
||||
#include "registry.h"
|
||||
#include "dbStaticLib.h"
|
||||
#include "dbStaticPvt.h"
|
||||
#include "osiFileName.h"
|
||||
#include "dbmf.h"
|
||||
#include "errlog.h"
|
||||
@@ -28,8 +29,124 @@
|
||||
|
||||
#include "testMain.h"
|
||||
|
||||
static
|
||||
int testStrcmp(int expect, const char *A, const char *B) {
|
||||
static const char op[] = "<=>";
|
||||
int ret = strcmp(A,B);
|
||||
testOk(ret==expect, "\"%s\" %c= \"%s\"",
|
||||
A, op[expect+1], B);
|
||||
return ret==expect;
|
||||
}
|
||||
|
||||
void dbTestIoc_registerRecordDeviceDriver(struct dbBase *);
|
||||
|
||||
#define TEST_CONSTANT(SET, EXPECT) {SET, {CONSTANT, EXPECT}}
|
||||
#define TEST_PV_LINK(SET, PV, MOD) {SET, {PV_LINK, PV, MOD}}
|
||||
|
||||
static const struct testParseDataT {
|
||||
const char * const str;
|
||||
dbLinkInfo info;
|
||||
} testParseData[] = {
|
||||
TEST_CONSTANT("", ""),
|
||||
TEST_CONSTANT("0.1", "0.1"),
|
||||
TEST_CONSTANT(" 0.2\t ", "0.2"),
|
||||
|
||||
TEST_PV_LINK("0.1a", "0.1a", 0),
|
||||
TEST_PV_LINK(" hello ", "hello", 0),
|
||||
TEST_PV_LINK(" hellox MSI", "hellox", pvlOptMSI),
|
||||
TEST_PV_LINK(" world MSICP", "world", pvlOptMSI|pvlOptCP),
|
||||
|
||||
{"#C14 S145 @testing", {VME_IO, "testing", 0, "CS", {14, 145}}},
|
||||
{"#B11 C12 N13 A14 F15 @cparam", {CAMAC_IO, "cparam", 0, "BCNAF", {11, 12, 13, 14, 15}}},
|
||||
{" #B111 C112 N113 @cparam", {CAMAC_IO, "cparam", 0, "BCN", {111, 112, 113}}},
|
||||
{" @hello world ", {INST_IO, "hello world", 0, "", /*{}*/}},
|
||||
{NULL}
|
||||
};
|
||||
|
||||
static void testLinkParse(void)
|
||||
{
|
||||
const struct testParseDataT *td = testParseData;
|
||||
dbLinkInfo info;
|
||||
testDiag("link parsing");
|
||||
testdbPrepare();
|
||||
|
||||
testdbReadDatabase("dbTestIoc.dbd", NULL, NULL);
|
||||
|
||||
dbTestIoc_registerRecordDeviceDriver(pdbbase);
|
||||
|
||||
testdbReadDatabase("dbPutLinkTest.db", NULL, NULL);
|
||||
|
||||
eltc(0);
|
||||
testIocInitOk();
|
||||
eltc(1);
|
||||
|
||||
for(;td->str; td++) {
|
||||
int i, N;
|
||||
testDiag("Parse \"%s\"", td->str);
|
||||
testOk1(dbParseLink(td->str, DBF_INLINK, &info)==0);
|
||||
testOk1(info.ltype==td->info.ltype);
|
||||
if(td->info.target)
|
||||
testStrcmp(0, info.target, td->info.target);
|
||||
if(info.ltype==td->info.ltype) {
|
||||
switch(info.ltype) {
|
||||
case PV_LINK:
|
||||
testOk1(info.modifiers==td->info.modifiers);
|
||||
break;
|
||||
case VME_IO:
|
||||
testStrcmp(0, info.hwid, td->info.hwid);
|
||||
N = strlen(td->info.hwid);
|
||||
for(i=0; i<N; i++)
|
||||
testOk(info.hwnums[i]==td->info.hwnums[i], "%d == %d",
|
||||
info.hwnums[i], td->info.hwnums[i]);
|
||||
}
|
||||
}
|
||||
free(info.target);
|
||||
}
|
||||
|
||||
testIocShutdownOk();
|
||||
|
||||
testdbCleanup();
|
||||
}
|
||||
|
||||
static const char *testParseFailData[] = {
|
||||
"#",
|
||||
"#S",
|
||||
"#ABC",
|
||||
"#A0 B",
|
||||
"#A0 B @",
|
||||
"#A0 B C @",
|
||||
"#R1 M2 D3 E4 @oops", /* RF_IO has no parm */
|
||||
"#C1 S2", /* VME_IO needs parm */
|
||||
NULL
|
||||
};
|
||||
|
||||
static void testLinkFailParse(void)
|
||||
{
|
||||
const char * const *td = testParseFailData;
|
||||
dbLinkInfo info;
|
||||
testDiag("link parsing of invalid input");
|
||||
testdbPrepare();
|
||||
|
||||
testdbReadDatabase("dbTestIoc.dbd", NULL, NULL);
|
||||
|
||||
dbTestIoc_registerRecordDeviceDriver(pdbbase);
|
||||
|
||||
testdbReadDatabase("dbPutLinkTest.db", NULL, NULL);
|
||||
|
||||
eltc(0);
|
||||
testIocInitOk();
|
||||
eltc(1);
|
||||
|
||||
for(;*td; td++) {
|
||||
testDiag("Expect failure \"%s\"", *td);
|
||||
testOk1(dbParseLink(*td, DBF_INLINK, &info)==S_dbLib_badField);
|
||||
}
|
||||
|
||||
testIocShutdownOk();
|
||||
|
||||
testdbCleanup();
|
||||
}
|
||||
|
||||
static const struct testDataT {
|
||||
const char * const linkstring;
|
||||
short linkType;
|
||||
@@ -39,6 +156,7 @@ static const struct testDataT {
|
||||
{"", CONSTANT, 0},
|
||||
{"0", CONSTANT, 0},
|
||||
{"42", CONSTANT, 0},
|
||||
{"0x1", CONSTANT, 0},
|
||||
|
||||
{"x1", DB_LINK, 0, "x1 NPP NMS"},
|
||||
{"x1.VAL", DB_LINK, 0, "x1.VAL NPP NMS"},
|
||||
@@ -77,17 +195,17 @@ static void testCADBSet(void)
|
||||
prec = (xRecord*)testdbRecordPtr("x1");
|
||||
plink = &prec->lnk;
|
||||
|
||||
for(;td->linkstring;td++) {
|
||||
for (;td->linkstring;td++) {
|
||||
testDiag("x1.LNK <- \"%s\"", td->linkstring);
|
||||
|
||||
testdbPutFieldOk("x1.LNK", DBF_STRING, td->linkstring);
|
||||
if(td->linkback)
|
||||
if (td->linkback)
|
||||
testdbGetFieldEqual("x1.LNK", DBF_STRING, td->linkback);
|
||||
else
|
||||
testdbGetFieldEqual("x1.LNK", DBF_STRING, td->linkstring);
|
||||
testOk1(plink->type==td->linkType);
|
||||
|
||||
if(plink->type==td->linkType) {
|
||||
if (plink->type==td->linkType) {
|
||||
switch(td->linkType) {
|
||||
case CONSTANT:
|
||||
if(plink->value.constantStr)
|
||||
@@ -327,7 +445,7 @@ static void testLinkInitFail(void)
|
||||
testOk1(plink->type==VME_IO);
|
||||
testOk1(plink->value.vmeio.parm!=NULL);
|
||||
|
||||
testdbGetFieldEqual("eVME_IO2.INP", DBR_STRING, "#C200 S0 @");
|
||||
testdbGetFieldEqual("eVME_IO2.INP", DBR_STRING, "#C0 S0 @");
|
||||
|
||||
prec = (xRecord*)testdbRecordPtr("eVME_IO2");
|
||||
plink = &prec->inp;
|
||||
@@ -364,9 +482,8 @@ static void testLinkFail(void)
|
||||
/* INST_IO doesn't accept empty string */
|
||||
testdbPutFieldFail(S_dbLib_badField, "rINST_IO.INP", DBR_STRING, "");
|
||||
|
||||
/* INST_IO accepts invalid input as empty string */
|
||||
testdbPutFieldOk("rINST_IO.INP", DBR_STRING, "abc");
|
||||
testdbGetFieldEqual("rINST_IO.INP", DBR_STRING, "@");
|
||||
/* INST_IO doesn't accept empty string */
|
||||
testdbPutFieldFail(S_dbLib_badField, "rINST_IO.INP", DBR_STRING, "abc");
|
||||
|
||||
/* syntax errors */
|
||||
testdbPutFieldFail(S_dbLib_badField, "rVME_IO.INP", DBR_STRING, "#S201 C200 @another VME_IO");
|
||||
@@ -387,7 +504,9 @@ static void testLinkFail(void)
|
||||
|
||||
MAIN(dbPutLinkTest)
|
||||
{
|
||||
testPlan(200);
|
||||
testPlan(251);
|
||||
testLinkParse();
|
||||
testLinkFailParse();
|
||||
testCADBSet();
|
||||
testHWInitSet();
|
||||
testHWMod();
|
||||
|
||||
73
src/ioc/db/test/dbScanTest.c
Normal file
73
src/ioc/db/test/dbScanTest.c
Normal file
@@ -0,0 +1,73 @@
|
||||
/*************************************************************************\
|
||||
* Copyright (c) 2015 Brookhaven Science Assoc. as operator of Brookhaven
|
||||
* National Laboratory.
|
||||
* EPICS BASE is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
\*************************************************************************/
|
||||
|
||||
/*
|
||||
* Author: Michael Davidsaver <mdavidsaver@bnl.gov>
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "dbScan.h"
|
||||
#include "epicsEvent.h"
|
||||
|
||||
#include "dbUnitTest.h"
|
||||
#include "testMain.h"
|
||||
|
||||
#include "dbAccess.h"
|
||||
#include "errlog.h"
|
||||
|
||||
void dbTestIoc_registerRecordDeviceDriver(struct dbBase *);
|
||||
|
||||
static epicsEventId waiter;
|
||||
static int called;
|
||||
static dbCommon *prec;
|
||||
|
||||
static void onceComp(void *junk, dbCommon *prec)
|
||||
{
|
||||
testOk1(junk==(void*)&waiter);
|
||||
testOk1(strcmp(prec->name, "reca")==0);
|
||||
called = 1;
|
||||
epicsEventMustTrigger(waiter);
|
||||
}
|
||||
|
||||
static void testOnce(void)
|
||||
{
|
||||
testDiag("check scanOnceCallback() callback");
|
||||
waiter = epicsEventMustCreate(epicsEventEmpty);
|
||||
|
||||
testdbPrepare();
|
||||
|
||||
testdbReadDatabase("dbTestIoc.dbd", NULL, NULL);
|
||||
dbTestIoc_registerRecordDeviceDriver(pdbbase);
|
||||
testdbReadDatabase("dbLockTest.db", NULL, NULL);
|
||||
|
||||
eltc(0);
|
||||
testIocInitOk();
|
||||
eltc(1);
|
||||
|
||||
prec = testdbRecordPtr("reca");
|
||||
|
||||
testDiag("scanOnce %s", prec->name);
|
||||
scanOnceCallback(prec, onceComp, &waiter);
|
||||
testDiag("Waiting");
|
||||
epicsEventMustWait(waiter);
|
||||
testOk1(called==1);
|
||||
if(!called)
|
||||
testSkip(2, "callback failed to run");
|
||||
|
||||
testIocShutdownOk();
|
||||
|
||||
testdbCleanup();
|
||||
epicsEventDestroy(waiter);
|
||||
}
|
||||
|
||||
MAIN(dbScanTest)
|
||||
{
|
||||
testPlan(3);
|
||||
testOnce();
|
||||
return testDone();
|
||||
}
|
||||
358
src/ioc/db/test/dbStressLock.c
Normal file
358
src/ioc/db/test/dbStressLock.c
Normal file
@@ -0,0 +1,358 @@
|
||||
/*************************************************************************\
|
||||
* Copyright (c) 2014 Brookhaven Science Assoc. as operator of Brookhaven
|
||||
* National Laboratory.
|
||||
* EPICS BASE is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
\*************************************************************************/
|
||||
|
||||
/*
|
||||
* Lockset stress test.
|
||||
*
|
||||
* The test stratagy is for N threads to contend for M records.
|
||||
* Each thread will perform one of three operations:
|
||||
* 1) Lock a single record.
|
||||
* 2) Lock several records.
|
||||
* 3) Retarget the TSEL link of a record
|
||||
*
|
||||
* Author: Michael Davidsaver <mdavidsaver@bnl.gov>
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
|
||||
#include "envDefs.h"
|
||||
#include "epicsEvent.h"
|
||||
#include "epicsStdlib.h"
|
||||
#include "epicsSpin.h"
|
||||
#include "epicsThread.h"
|
||||
#include "epicsMutex.h"
|
||||
#include "dbCommon.h"
|
||||
|
||||
#include "dbLockPvt.h"
|
||||
#include "dbStaticLib.h"
|
||||
|
||||
#include "dbUnitTest.h"
|
||||
#include "testMain.h"
|
||||
|
||||
#include "dbAccess.h"
|
||||
#include "errlog.h"
|
||||
|
||||
#include "xRecord.h"
|
||||
|
||||
#if defined(CLOCK_REALTIME) && defined(CLOCK_MONOTONIC) && !defined(_WIN32)
|
||||
# define TIME_STATS
|
||||
#endif
|
||||
|
||||
#define testIntOk1(A, OP, B) testOk((A) OP (B), "%s (%d) %s %s (%d)", #A, A, #OP, #B, B);
|
||||
#define testPtrOk1(A, OP, B) testOk((A) OP (B), "%s (%p) %s %s (%p)", #A, A, #OP, #B, B);
|
||||
|
||||
void dbTestIoc_registerRecordDeviceDriver(struct dbBase *);
|
||||
|
||||
/* number of seconds for the test to run */
|
||||
static double runningtime = 18.0;
|
||||
|
||||
/* number of worker threads */
|
||||
static unsigned int nworkers = 5;
|
||||
|
||||
static unsigned int nrecords;
|
||||
|
||||
#define MAXLOCK 20
|
||||
|
||||
static dbCommon **precords;
|
||||
|
||||
typedef struct {
|
||||
int id;
|
||||
unsigned long N[3];
|
||||
#ifdef TIME_STATS
|
||||
double X[3];
|
||||
double X2[3];
|
||||
#endif
|
||||
|
||||
unsigned int done;
|
||||
epicsEventId donevent;
|
||||
|
||||
dbCommon *prec[MAXLOCK];
|
||||
} workerPriv;
|
||||
|
||||
/* hopefully a uniform random number in [0.0, 1.0] */
|
||||
static
|
||||
double getRand(void)
|
||||
{
|
||||
return rand()/(double)RAND_MAX;
|
||||
}
|
||||
|
||||
static
|
||||
void doSingle(workerPriv *p)
|
||||
{
|
||||
size_t recn = (size_t)(getRand()*(nrecords-1));
|
||||
dbCommon *prec = precords[recn];
|
||||
xRecord *px = (xRecord*)prec;
|
||||
|
||||
dbScanLock(prec);
|
||||
px->val++;
|
||||
dbScanUnlock(prec);
|
||||
}
|
||||
|
||||
static volatile int bitbucket;
|
||||
|
||||
static
|
||||
void doMulti(workerPriv *p)
|
||||
{
|
||||
int sum = 0;
|
||||
size_t i;
|
||||
size_t nlock = 2 + (size_t)(getRand()*(MAXLOCK-3));
|
||||
size_t nrec = (size_t)(getRand()*(nrecords-1));
|
||||
dbLocker *locker;
|
||||
|
||||
assert(nlock>=2);
|
||||
assert(nlock<nrecords);
|
||||
|
||||
for(i=0; i<nlock; i++, nrec=(nrec+1)%nrecords) {
|
||||
p->prec[i] = precords[nrec];
|
||||
}
|
||||
|
||||
locker = dbLockerAlloc(p->prec, nlock, 0);
|
||||
if(!locker)
|
||||
testAbort("locker allocation fails");
|
||||
|
||||
dbScanLockMany(locker);
|
||||
for(i=0; i<nlock; i++) {
|
||||
xRecord *px = (xRecord*)p->prec[i];
|
||||
sum += px->val;
|
||||
}
|
||||
dbScanUnlockMany(locker);
|
||||
|
||||
dbLockerFree(locker);
|
||||
}
|
||||
|
||||
static
|
||||
void doreTarget(workerPriv *p)
|
||||
{
|
||||
char scratchsrc[60];
|
||||
char scratchdst[MAX_STRING_SIZE];
|
||||
long ret;
|
||||
DBADDR dbaddr;
|
||||
double action = getRand();
|
||||
size_t nsrc = (size_t)(getRand()*(nrecords-1));
|
||||
size_t ntarg = (size_t)(getRand()*(nrecords-1));
|
||||
xRecord *psrc = (xRecord*)precords[nsrc];
|
||||
xRecord *ptarg = (xRecord*)precords[ntarg];
|
||||
|
||||
strcpy(scratchsrc, psrc->name);
|
||||
strcat(scratchsrc, ".TSEL");
|
||||
|
||||
ret = dbNameToAddr(scratchsrc, &dbaddr);
|
||||
if(ret)
|
||||
testAbort("bad record name? %ld", ret);
|
||||
|
||||
if(action<=0.6) {
|
||||
scratchdst[0] = '\0';
|
||||
} else {
|
||||
strcpy(scratchdst, ptarg->name);
|
||||
}
|
||||
|
||||
ret = dbPutField(&dbaddr, DBR_STRING, ptarg->name, 1);
|
||||
if(ret)
|
||||
testAbort("put fails with %ld", ret);
|
||||
}
|
||||
|
||||
static
|
||||
void worker(void *raw)
|
||||
{
|
||||
#ifdef TIME_STATS
|
||||
struct timespec before;
|
||||
#endif
|
||||
workerPriv *priv = raw;
|
||||
|
||||
testDiag("worker %d is %p", priv->id, epicsThreadGetIdSelf());
|
||||
|
||||
#ifdef TIME_STATS
|
||||
clock_gettime(CLOCK_MONOTONIC, &before);
|
||||
#endif
|
||||
|
||||
while(!priv->done) {
|
||||
double sel = getRand();
|
||||
#ifdef TIME_STATS
|
||||
struct timespec after;
|
||||
double duration;
|
||||
#endif
|
||||
|
||||
int act;
|
||||
if(sel<0.33) {
|
||||
doSingle(priv);
|
||||
act = 0;
|
||||
} else if(sel<0.66) {
|
||||
doMulti(priv);
|
||||
act = 1;
|
||||
} else {
|
||||
doreTarget(priv);
|
||||
act = 2;
|
||||
}
|
||||
|
||||
#ifdef TIME_STATS
|
||||
clock_gettime(CLOCK_MONOTONIC, &after);
|
||||
|
||||
duration = (double)((long)after.tv_nsec - (long)before.tv_nsec);
|
||||
duration *= 1e-9;
|
||||
duration += (double)(after.tv_sec - before.tv_sec);
|
||||
#endif
|
||||
|
||||
priv->N[act]++;
|
||||
#ifdef TIME_STATS
|
||||
priv->X[act] += duration;
|
||||
priv->X2[act] += duration*duration;
|
||||
#endif
|
||||
}
|
||||
|
||||
epicsEventMustTrigger(priv->donevent);
|
||||
}
|
||||
|
||||
MAIN(dbStressTest)
|
||||
{
|
||||
DBENTRY ent;
|
||||
long status;
|
||||
unsigned int i;
|
||||
workerPriv *priv;
|
||||
char *nwork=getenv("NWORK");
|
||||
epicsTimeStamp seed;
|
||||
|
||||
epicsTimeGetCurrent(&seed);
|
||||
|
||||
srand(seed.nsec);
|
||||
|
||||
if(nwork) {
|
||||
long val = 0;
|
||||
epicsParseLong(nwork, &val, 0, NULL);
|
||||
if(val>2)
|
||||
nworkers = val;
|
||||
}
|
||||
|
||||
testPlan(80+nworkers*3);
|
||||
|
||||
priv = callocMustSucceed(nworkers, sizeof(*priv), "no memory");
|
||||
|
||||
testDiag("lock set stress test");
|
||||
|
||||
testdbPrepare();
|
||||
|
||||
testdbReadDatabase("dbTestIoc.dbd", NULL, NULL);
|
||||
dbTestIoc_registerRecordDeviceDriver(pdbbase);
|
||||
testdbReadDatabase("dbStressLock.db", NULL, NULL);
|
||||
|
||||
eltc(0);
|
||||
testIocInitOk();
|
||||
eltc(1);
|
||||
|
||||
/* collect an array of all records */
|
||||
dbInitEntry(pdbbase, &ent);
|
||||
for(status = dbFirstRecordType(&ent);
|
||||
!status;
|
||||
status = dbNextRecordType(&ent))
|
||||
{
|
||||
for(status = dbFirstRecord(&ent);
|
||||
!status;
|
||||
status = dbNextRecord(&ent))
|
||||
{
|
||||
if(ent.precnode->flags&DBRN_FLAGS_ISALIAS)
|
||||
continue;
|
||||
nrecords++;
|
||||
}
|
||||
|
||||
}
|
||||
if(nrecords<2)
|
||||
testAbort("where are the records!");
|
||||
precords = callocMustSucceed(nrecords, sizeof(*precords), "no mem");
|
||||
for(status = dbFirstRecordType(&ent), i = 0;
|
||||
!status;
|
||||
status = dbNextRecordType(&ent))
|
||||
{
|
||||
for(status = dbFirstRecord(&ent);
|
||||
!status;
|
||||
status = dbNextRecord(&ent))
|
||||
{
|
||||
if(ent.precnode->flags&DBRN_FLAGS_ISALIAS)
|
||||
continue;
|
||||
precords[i++] = ent.precnode->precord;
|
||||
}
|
||||
|
||||
}
|
||||
dbFinishEntry(&ent);
|
||||
|
||||
testDiag("Running with %u workers and %u records",
|
||||
nworkers, nrecords);
|
||||
|
||||
for(i=0; i<nworkers; i++) {
|
||||
priv[i].id = i;
|
||||
priv[i].donevent = epicsEventMustCreate(epicsEventEmpty);
|
||||
}
|
||||
|
||||
for(i=0; i<nworkers; i++) {
|
||||
epicsThreadMustCreate("runner", epicsThreadPriorityMedium,
|
||||
epicsThreadGetStackSize(epicsThreadStackSmall),
|
||||
&worker, &priv[i]);
|
||||
}
|
||||
|
||||
testDiag("All started. Will run for %f sec", runningtime);
|
||||
|
||||
epicsThreadSleep(runningtime);
|
||||
|
||||
testDiag("Stopping");
|
||||
|
||||
for(i=0; i<nworkers; i++) {
|
||||
priv[i].done = 1;
|
||||
}
|
||||
|
||||
for(i=0; i<nworkers; i++) {
|
||||
epicsEventMustWait(priv[i].donevent);
|
||||
epicsEventDestroy(priv[i].donevent);
|
||||
}
|
||||
|
||||
testDiag("All stopped");
|
||||
|
||||
testDiag("Validate lockSet ref counts");
|
||||
dbInitEntry(pdbbase, &ent);
|
||||
for(status = dbFirstRecordType(&ent);
|
||||
!status;
|
||||
status = dbNextRecordType(&ent))
|
||||
{
|
||||
for(status = dbFirstRecord(&ent);
|
||||
!status;
|
||||
status = dbNextRecord(&ent))
|
||||
{
|
||||
dbCommon *prec = ent.precnode->precord;
|
||||
lockSet *ls;
|
||||
if(ent.precnode->flags&DBRN_FLAGS_ISALIAS)
|
||||
continue;
|
||||
ls = prec->lset->plockSet;
|
||||
testOk(ellCount(&ls->lockRecordList)==ls->refcount, "%s only lockRecords hold refs. %d == %d",
|
||||
prec->name,ellCount(&ls->lockRecordList),ls->refcount);
|
||||
testOk1(ls->ownerlocker==NULL);
|
||||
}
|
||||
|
||||
}
|
||||
dbFinishEntry(&ent);
|
||||
|
||||
testDiag("Statistics");
|
||||
for(i=0; i<nworkers; i++) {
|
||||
testDiag("Worker %u", i);
|
||||
testDiag("N = %lu %lu %lu", priv[i].N[0], priv[i].N[1], priv[i].N[2]);
|
||||
#ifdef TIME_STATS
|
||||
testDiag("X = %g %g %g", priv[i].X[0], priv[i].X[1], priv[i].X[2]);
|
||||
testDiag("X2= %g %g %g", priv[i].X2[0], priv[i].X2[1], priv[i].X2[2]);
|
||||
#endif
|
||||
|
||||
testOk1(priv[i].N[0]>0);
|
||||
testOk1(priv[i].N[1]>0);
|
||||
testOk1(priv[i].N[2]>0);
|
||||
}
|
||||
|
||||
testIocShutdownOk();
|
||||
|
||||
testdbCleanup();
|
||||
|
||||
free(priv);
|
||||
free(precords);
|
||||
|
||||
return testDone();
|
||||
}
|
||||
40
src/ioc/db/test/dbStressLock.db
Normal file
40
src/ioc/db/test/dbStressLock.db
Normal file
@@ -0,0 +1,40 @@
|
||||
record(x, "rec01") {}
|
||||
record(x, "rec02") {}
|
||||
record(x, "rec03") {}
|
||||
record(x, "rec04") {}
|
||||
record(x, "rec05") {}
|
||||
record(x, "rec06") {}
|
||||
record(x, "rec07") {}
|
||||
record(x, "rec08") {}
|
||||
record(x, "rec09") {}
|
||||
record(x, "rec10") {}
|
||||
record(x, "rec11") {}
|
||||
record(x, "rec12") {}
|
||||
record(x, "rec13") {}
|
||||
record(x, "rec14") {}
|
||||
record(x, "rec15") {}
|
||||
record(x, "rec16") {}
|
||||
record(x, "rec17") {}
|
||||
record(x, "rec18") {}
|
||||
record(x, "rec19") {}
|
||||
record(x, "rec20") {}
|
||||
record(x, "rec21") {}
|
||||
record(x, "rec22") {}
|
||||
record(x, "rec23") {}
|
||||
record(x, "rec24") {}
|
||||
record(x, "rec25") {}
|
||||
record(x, "rec26") {}
|
||||
record(x, "rec27") {}
|
||||
record(x, "rec28") {}
|
||||
record(x, "rec29") {}
|
||||
record(x, "rec30") {}
|
||||
record(x, "rec31") {}
|
||||
record(x, "rec32") {}
|
||||
record(x, "rec33") {}
|
||||
record(x, "rec34") {}
|
||||
record(x, "rec35") {}
|
||||
record(x, "rec36") {}
|
||||
record(x, "rec37") {}
|
||||
record(x, "rec38") {}
|
||||
record(x, "rec39") {}
|
||||
record(x, "rec40") {}
|
||||
163
src/ioc/db/test/devx.c
Normal file
163
src/ioc/db/test/devx.c
Normal file
@@ -0,0 +1,163 @@
|
||||
/*************************************************************************\
|
||||
* Copyright (c) 2014 Brookhaven Science Assoc. as Operator of Brookhaven
|
||||
* National Lab
|
||||
* EPICS BASE is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
\*************************************************************************/
|
||||
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include <epicsAssert.h>
|
||||
#include <cantProceed.h>
|
||||
#include <ellLib.h>
|
||||
#include <dbDefs.h>
|
||||
#include <dbAccess.h>
|
||||
#include <dbScan.h>
|
||||
#include <devSup.h>
|
||||
#include <recGbl.h>
|
||||
#include <link.h>
|
||||
#include <dbLink.h>
|
||||
#include <xRecord.h>
|
||||
|
||||
#include <epicsExport.h>
|
||||
|
||||
#include "devx.h"
|
||||
|
||||
/* xRecord DTYP="Scan I/O"
|
||||
*
|
||||
* dset to test I/O Intr scanning.
|
||||
* INP="@drvname" names a "driver" which
|
||||
* provides a IOSCANPVT.
|
||||
* The driver also defines a callback function which
|
||||
* is invoked when the record is processed.
|
||||
*/
|
||||
|
||||
struct ELLLIST xdrivers;
|
||||
|
||||
/* Add a new "driver" with the given group id
|
||||
* and processing callback
|
||||
*/
|
||||
xdrv* xdrv_add(int group, xdrvcb cb, void *arg)
|
||||
{
|
||||
xdrv *drv=callocMustSucceed(1, sizeof(*drv), "xdrv_add");
|
||||
drv->cb = cb;
|
||||
drv->arg = arg;
|
||||
drv->group = group;
|
||||
scanIoInit(&drv->scan);
|
||||
ellAdd(&xdrivers, &drv->drvnode);
|
||||
return drv;
|
||||
}
|
||||
|
||||
/* Trigger the named "driver" group to scan */
|
||||
xdrv *xdrv_get(int group)
|
||||
{
|
||||
ELLNODE *cur;
|
||||
for(cur=ellFirst(&xdrivers); cur; cur=ellNext(cur)) {
|
||||
xdrv *curd = CONTAINER(cur, xdrv, drvnode);
|
||||
if(curd->group==group) {
|
||||
return curd;
|
||||
}
|
||||
}
|
||||
cantProceed("xdrv_get() for non-existant group");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Free all "driver" groups and record private structures.
|
||||
* Call after testdbCleanup()
|
||||
*/
|
||||
void xdrv_reset()
|
||||
{
|
||||
ELLNODE *cur;
|
||||
while((cur=ellGet(&xdrivers))!=NULL) {
|
||||
ELLNODE *cur2;
|
||||
xdrv *curd = CONTAINER(cur, xdrv, drvnode);
|
||||
while((cur2=ellGet(&curd->privlist))!=NULL) {
|
||||
xpriv *priv = CONTAINER(cur2, xpriv, privnode);
|
||||
free(priv);
|
||||
}
|
||||
free(curd);
|
||||
}
|
||||
}
|
||||
|
||||
static long xscanio_init_record(xRecord *prec)
|
||||
{
|
||||
ELLNODE *cur;
|
||||
xpriv *priv;
|
||||
xdrv *drv = NULL;
|
||||
int group, member;
|
||||
assert(prec->inp.type==INST_IO);
|
||||
|
||||
if(sscanf(prec->inp.value.instio.string, "%d %d",
|
||||
&group, &member)!=2)
|
||||
cantProceed("xscanio_init_record invalid INP string");
|
||||
|
||||
for(cur=ellFirst(&xdrivers); cur; cur=ellNext(cur)) {
|
||||
xdrv *curd = CONTAINER(cur, xdrv, drvnode);
|
||||
if(curd->group==group) {
|
||||
drv = curd;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
assert(drv!=NULL);
|
||||
priv = mallocMustSucceed(sizeof(*priv), "xscanio_init_record");
|
||||
priv->prec = prec;
|
||||
priv->drv = drv;
|
||||
priv->member = member;
|
||||
ellAdd(&drv->privlist, &priv->privnode);
|
||||
prec->dpvt = priv;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long xscanio_get_ioint_info(int cmd, xRecord *prec, IOSCANPVT *ppvt)
|
||||
{
|
||||
xpriv *priv = prec->dpvt;
|
||||
if(!priv || !priv->drv)
|
||||
return 0;
|
||||
*ppvt = priv->drv->scan;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long xscanio_read(xRecord *prec)
|
||||
{
|
||||
xpriv *priv = prec->dpvt;
|
||||
if(!priv || !priv->drv)
|
||||
return 0;
|
||||
if(priv->drv->cb)
|
||||
(*priv->drv->cb)(priv, priv->drv->arg);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static xdset devxScanIO = {
|
||||
5, NULL, NULL,
|
||||
&xscanio_init_record,
|
||||
&xscanio_get_ioint_info,
|
||||
&xscanio_read
|
||||
};
|
||||
epicsExportAddress(dset, devxScanIO);
|
||||
|
||||
/* basic DTYP="Soft Channel" */
|
||||
static long xsoft_init_record(xRecord *prec)
|
||||
{
|
||||
if(prec->inp.type==CONSTANT)
|
||||
recGblInitConstantLink(&prec->inp, DBF_LONG, &prec->val);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long xsoft_read(xRecord *prec)
|
||||
{
|
||||
if(prec->inp.type==CONSTANT)
|
||||
return 0;
|
||||
dbGetLink(&prec->inp, DBR_DOUBLE, &prec->val, NULL, NULL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct xdset devxSoft = {
|
||||
5, NULL, NULL,
|
||||
&xsoft_init_record,
|
||||
NULL,
|
||||
&xsoft_read
|
||||
};
|
||||
epicsExportAddress(dset, devxSoft);
|
||||
2
src/ioc/db/test/devx.dbd
Normal file
2
src/ioc/db/test/devx.dbd
Normal file
@@ -0,0 +1,2 @@
|
||||
device(x, CONSTANT, devxSoft, "Soft Channel")
|
||||
device(x, INST_IO , devxScanIO, "Scan I/O")
|
||||
52
src/ioc/db/test/devx.h
Normal file
52
src/ioc/db/test/devx.h
Normal file
@@ -0,0 +1,52 @@
|
||||
/*************************************************************************\
|
||||
* Copyright (c) 2014 Brookhaven Science Assoc. as Operator of Brookhaven
|
||||
* National Lab
|
||||
* EPICS BASE is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
\*************************************************************************/
|
||||
|
||||
#ifndef DEVXSCANIO_H
|
||||
#define DEVXSCANIO_H
|
||||
|
||||
#include <ellLib.h>
|
||||
#include <dbScan.h>
|
||||
|
||||
#include <shareLib.h>
|
||||
|
||||
struct xRecord;
|
||||
struct xpriv;
|
||||
|
||||
epicsShareExtern struct ELLLIST xdrivers;
|
||||
|
||||
typedef void (*xdrvcb)(struct xpriv *, void *);
|
||||
|
||||
typedef struct {
|
||||
ELLNODE drvnode;
|
||||
IOSCANPVT scan;
|
||||
xdrvcb cb;
|
||||
void *arg;
|
||||
ELLLIST privlist;
|
||||
int group;
|
||||
} xdrv;
|
||||
|
||||
typedef struct xpriv {
|
||||
ELLNODE privnode;
|
||||
xdrv *drv;
|
||||
struct xRecord *prec;
|
||||
int member;
|
||||
} xpriv;
|
||||
|
||||
epicsShareFunc xdrv *xdrv_add(int group, xdrvcb cb, void *arg);
|
||||
epicsShareFunc xdrv *xdrv_get(int group);
|
||||
epicsShareFunc void xdrv_reset();
|
||||
|
||||
typedef struct xdset {
|
||||
long number;
|
||||
long (*report)(int);
|
||||
long (*init)(int);
|
||||
long (*init_record)(struct xRecord *);
|
||||
long (*get_ioint_info)(int, struct xRecord*, IOSCANPVT*);
|
||||
long (*process)(struct xRecord *);
|
||||
} xdset;
|
||||
|
||||
#endif /* DEVXSCANIO_H */
|
||||
@@ -22,9 +22,11 @@ int callbackParallelTest(void);
|
||||
int dbStateTest(void);
|
||||
int dbCaStatsTest(void);
|
||||
int dbShutdownTest(void);
|
||||
int dbScanTest(void);
|
||||
int scanIoTest(void);
|
||||
int dbLockTest(void);
|
||||
int dbPutLinkTest(void);
|
||||
int dbCaLinkTest(void);
|
||||
int testDbChannel(void);
|
||||
int chfPluginTest(void);
|
||||
int arrShorthandTest(void);
|
||||
@@ -40,9 +42,11 @@ void epicsRunDbTests(void)
|
||||
runTest(dbStateTest);
|
||||
runTest(dbCaStatsTest);
|
||||
runTest(dbShutdownTest);
|
||||
runTest(dbScanTest);
|
||||
runTest(scanIoTest);
|
||||
runTest(dbLockTest);
|
||||
runTest(dbPutLinkTest);
|
||||
runTest(dbCaLinkTest);
|
||||
runTest(testDbChannel);
|
||||
runTest(arrShorthandTest);
|
||||
runTest(recGblCheckDeadbandTest);
|
||||
|
||||
@@ -12,6 +12,8 @@
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "epicsEvent.h"
|
||||
#include "epicsMessageQueue.h"
|
||||
@@ -26,9 +28,6 @@
|
||||
#include "dbLock.h"
|
||||
#include "dbUnitTest.h"
|
||||
#include "dbCommon.h"
|
||||
#include "registry.h"
|
||||
#include "registryRecordType.h"
|
||||
#include "registryDeviceSupport.h"
|
||||
#include "recSup.h"
|
||||
#include "devSup.h"
|
||||
#include "iocInit.h"
|
||||
@@ -38,482 +37,272 @@
|
||||
#include "testMain.h"
|
||||
#include "osiFileName.h"
|
||||
|
||||
#define GEN_SIZE_OFFSET
|
||||
#include "yRecord.h"
|
||||
|
||||
#include "epicsExport.h"
|
||||
|
||||
#define ONE_THREAD_LOOPS 101
|
||||
#define PAR_THREAD_LOOPS 53
|
||||
#define CB_THREAD_LOOPS 13
|
||||
#include "devx.h"
|
||||
#include "xRecord.h"
|
||||
|
||||
#define NO_OF_THREADS 7
|
||||
#define NO_OF_MEMBERS 5
|
||||
#define NO_OF_GROUPS 11
|
||||
STATIC_ASSERT(NUM_CALLBACK_PRIORITIES==3);
|
||||
|
||||
#define NO_OF_MID_THREADS 3
|
||||
void dbTestIoc_registerRecordDeviceDriver(struct dbBase *);
|
||||
|
||||
static int noOfGroups = NO_OF_GROUPS;
|
||||
static int noOfIoscans = NO_OF_GROUPS;
|
||||
|
||||
static IOSCANPVT *ioscanpvt; /* Soft interrupt sources */
|
||||
static ELLLIST *pvtList; /* Per group private part lists */
|
||||
|
||||
static int executionOrder;
|
||||
static int orderFail;
|
||||
static int testNo;
|
||||
static epicsMessageQueueId *mq; /* Per group message queue */
|
||||
static epicsEventId *barrier; /* Per group barrier event */
|
||||
static int *cbCounter;
|
||||
|
||||
struct pvtY {
|
||||
ELLNODE node;
|
||||
yRecord *prec;
|
||||
int group;
|
||||
int member;
|
||||
int count;
|
||||
int processed;
|
||||
int callback;
|
||||
};
|
||||
|
||||
/* test2: priority and ioscan index for each group
|
||||
* used priorities are expressed in the bit pattern of (ioscan index + 1) */
|
||||
struct groupItem {
|
||||
int prio;
|
||||
int ioscan;
|
||||
} groupTable[12] = {
|
||||
{ 0, 0 },
|
||||
{ 1, 1 },
|
||||
{ 0, 2 }, { 1, 2 },
|
||||
{ 2, 3 },
|
||||
{ 0, 4 }, { 2, 4 },
|
||||
{ 1, 5 }, { 2, 5 },
|
||||
{ 0, 6 }, { 1, 6 }, { 2, 6 }
|
||||
};
|
||||
static int recsProcessed = 1;
|
||||
static int noDoubleCallback = 1;
|
||||
|
||||
void scanIoTest_registerRecordDeviceDriver(struct dbBase *);
|
||||
|
||||
long count_bits(long n) {
|
||||
unsigned int c; /* c accumulates the total bits set in v */
|
||||
for (c = 0; n; c++)
|
||||
n &= n - 1; /* clear the least significant bit set */
|
||||
return c;
|
||||
}
|
||||
|
||||
/*************************************************************************\
|
||||
* yRecord: minimal record needed to test I/O Intr scanning
|
||||
\*************************************************************************/
|
||||
|
||||
static long get_ioint_info(int cmd, yRecord *prec, IOSCANPVT *ppvt)
|
||||
static void loadRecord(int group, int member, const char *prio)
|
||||
{
|
||||
struct pvtY *pvt = (struct pvtY *)(prec->dpvt);
|
||||
|
||||
if (testNo == 2)
|
||||
*ppvt = ioscanpvt[groupTable[pvt->group].ioscan];
|
||||
else
|
||||
*ppvt = ioscanpvt[pvt->group];
|
||||
return 0;
|
||||
char buf[40];
|
||||
sprintf(buf, "GROUP=%d,MEMBER=%d,PRIO=%s",
|
||||
group, member, prio);
|
||||
testdbReadDatabase("scanIoTest.db", NULL, buf);
|
||||
}
|
||||
|
||||
struct ydset {
|
||||
long number;
|
||||
DEVSUPFUN report;
|
||||
DEVSUPFUN init;
|
||||
DEVSUPFUN init_record;
|
||||
DEVSUPFUN get_ioint_info;
|
||||
DEVSUPFUN process;
|
||||
} devY = {
|
||||
5,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
get_ioint_info,
|
||||
NULL
|
||||
};
|
||||
epicsExportAddress(dset, devY);
|
||||
typedef struct {
|
||||
int hasprocd[NUM_CALLBACK_PRIORITIES];
|
||||
int getcomplete[NUM_CALLBACK_PRIORITIES];
|
||||
epicsEventId wait[NUM_CALLBACK_PRIORITIES];
|
||||
epicsEventId wake[NUM_CALLBACK_PRIORITIES];
|
||||
} testsingle;
|
||||
|
||||
static long init_record(yRecord *prec, int pass)
|
||||
static void testcb(xpriv *priv, void *raw)
|
||||
{
|
||||
struct pvtY *pvt;
|
||||
testsingle *td = raw;
|
||||
int prio = priv->prec->prio;
|
||||
|
||||
if (pass == 0) return 0;
|
||||
|
||||
pvt = (struct pvtY *) calloc(1, sizeof(struct pvtY));
|
||||
prec->dpvt = pvt;
|
||||
|
||||
pvt->prec = prec;
|
||||
sscanf(prec->name, "g%dm%d", &pvt->group, &pvt->member);
|
||||
ellAdd(&pvtList[pvt->group], &pvt->node);
|
||||
|
||||
return 0;
|
||||
testOk1(td->hasprocd[prio]==0);
|
||||
td->hasprocd[prio] = 1;
|
||||
}
|
||||
|
||||
static long process(yRecord *prec)
|
||||
static void testcomp(void *raw, IOSCANPVT scan, int prio)
|
||||
{
|
||||
struct pvtY *pvt = (struct pvtY *)(prec->dpvt);
|
||||
testsingle *td = raw;
|
||||
|
||||
if (testNo == 0) {
|
||||
/* Single callback thread */
|
||||
if (executionOrder != pvt->member) {
|
||||
orderFail = 1;
|
||||
}
|
||||
pvt->count++;
|
||||
if (++executionOrder == NO_OF_MEMBERS) executionOrder = 0;
|
||||
} else {
|
||||
pvt->count++;
|
||||
if (pvt->member == 0) {
|
||||
epicsMessageQueueSend(mq[pvt->group], NULL, 0);
|
||||
epicsEventMustWait(barrier[pvt->group]);
|
||||
}
|
||||
}
|
||||
pvt->processed = 1;
|
||||
return 0;
|
||||
testOk1(td->hasprocd[prio]==1);
|
||||
testOk1(td->getcomplete[prio]==0);
|
||||
td->getcomplete[prio] = 1;
|
||||
epicsEventMustTrigger(td->wait[prio]);
|
||||
epicsEventMustWait(td->wake[prio]);
|
||||
}
|
||||
|
||||
rset yRSET={
|
||||
4,
|
||||
NULL, /* report */
|
||||
NULL, /* initialize */
|
||||
init_record,
|
||||
process
|
||||
};
|
||||
epicsExportAddress(rset, yRSET);
|
||||
|
||||
static void startMockIoc(void) {
|
||||
char substitutions[256];
|
||||
int i, j;
|
||||
char *prio[] = { "LOW", "MEDIUM", "HIGH" };
|
||||
|
||||
if (testNo == 2) {
|
||||
noOfGroups = 12;
|
||||
noOfIoscans = 7;
|
||||
}
|
||||
ioscanpvt = calloc(noOfIoscans, sizeof(IOSCANPVT));
|
||||
mq = calloc(noOfGroups, sizeof(epicsMessageQueueId));
|
||||
barrier = calloc(noOfGroups, sizeof(epicsEventId));
|
||||
pvtList = calloc(noOfGroups, sizeof(ELLLIST));
|
||||
cbCounter = calloc(noOfGroups, sizeof(int));
|
||||
|
||||
if (dbReadDatabase(&pdbbase, "scanIoTest.dbd",
|
||||
"." OSI_PATH_LIST_SEPARATOR ".." OSI_PATH_LIST_SEPARATOR
|
||||
"../O.Common" OSI_PATH_LIST_SEPARATOR "O.Common", NULL))
|
||||
testAbort("Error reading database description 'scanIoTest.dbd'");
|
||||
|
||||
callbackParallelThreads(1, "Low");
|
||||
callbackParallelThreads(NO_OF_MID_THREADS, "Medium");
|
||||
callbackParallelThreads(NO_OF_THREADS, "High");
|
||||
|
||||
for (i = 0; i < noOfIoscans; i++) {
|
||||
scanIoInit(&ioscanpvt[i]);
|
||||
}
|
||||
|
||||
for (i = 0; i < noOfGroups; i++) {
|
||||
mq[i] = epicsMessageQueueCreate(NO_OF_MEMBERS, 1);
|
||||
barrier[i] = epicsEventMustCreate(epicsEventEmpty);
|
||||
ellInit(&pvtList[i]);
|
||||
}
|
||||
|
||||
scanIoTest_registerRecordDeviceDriver(pdbbase);
|
||||
for (i = 0; i < noOfGroups; i++) {
|
||||
for (j = 0; j < NO_OF_MEMBERS; j++) {
|
||||
sprintf(substitutions, "GROUP=%d,MEMBER=%d,PRIO=%s", i, j,
|
||||
testNo==0?"LOW":(testNo==1?"HIGH":prio[groupTable[i].prio]));
|
||||
if (dbReadDatabase(&pdbbase, "scanIoTest.db",
|
||||
"." OSI_PATH_LIST_SEPARATOR "..", substitutions))
|
||||
testAbort("Error reading test database 'scanIoTest.db'");
|
||||
}
|
||||
}
|
||||
|
||||
testIocInitOk();
|
||||
}
|
||||
|
||||
static void stopMockIoc(void) {
|
||||
static void testSingleThreading(void)
|
||||
{
|
||||
int i;
|
||||
testsingle data[2];
|
||||
xdrv *drvs[2];
|
||||
|
||||
memset(data, 0, sizeof(data));
|
||||
|
||||
for(i=0; i<2; i++) {
|
||||
int p;
|
||||
for(p=0; p<NUM_CALLBACK_PRIORITIES; p++) {
|
||||
data[i].wake[p] = epicsEventMustCreate(epicsEventEmpty);
|
||||
data[i].wait[p] = epicsEventMustCreate(epicsEventEmpty);
|
||||
}
|
||||
}
|
||||
|
||||
testDiag("Test single-threaded I/O Intr scanning");
|
||||
|
||||
testdbPrepare();
|
||||
testdbReadDatabase("dbTestIoc.dbd", NULL, NULL);
|
||||
dbTestIoc_registerRecordDeviceDriver(pdbbase);
|
||||
|
||||
/* create two scan lists with one record on each of three priorities */
|
||||
|
||||
/* group#, member#, priority */
|
||||
loadRecord(0, 0, "LOW");
|
||||
loadRecord(1, 0, "LOW");
|
||||
loadRecord(0, 1, "MEDIUM");
|
||||
loadRecord(1, 1, "MEDIUM");
|
||||
loadRecord(0, 2, "HIGH");
|
||||
loadRecord(1, 2, "HIGH");
|
||||
|
||||
drvs[0] = xdrv_add(0, &testcb, &data[0]);
|
||||
drvs[1] = xdrv_add(1, &testcb, &data[1]);
|
||||
scanIoSetComplete(drvs[0]->scan, &testcomp, &data[0]);
|
||||
scanIoSetComplete(drvs[1]->scan, &testcomp, &data[1]);
|
||||
|
||||
eltc(0);
|
||||
testIocInitOk();
|
||||
eltc(1);
|
||||
|
||||
testDiag("Scan first list");
|
||||
scanIoRequest(drvs[0]->scan);
|
||||
testDiag("Scan second list");
|
||||
scanIoRequest(drvs[1]->scan);
|
||||
|
||||
testDiag("Wait for first list to complete");
|
||||
for(i=0; i<NUM_CALLBACK_PRIORITIES; i++)
|
||||
epicsEventMustWait(data[0].wait[i]);
|
||||
|
||||
testDiag("Wait one more second");
|
||||
epicsThreadSleep(1.0);
|
||||
|
||||
testOk1(data[0].hasprocd[0]==1);
|
||||
testOk1(data[0].hasprocd[1]==1);
|
||||
testOk1(data[0].hasprocd[2]==1);
|
||||
testOk1(data[0].getcomplete[0]==1);
|
||||
testOk1(data[0].getcomplete[1]==1);
|
||||
testOk1(data[0].getcomplete[2]==1);
|
||||
testOk1(data[1].hasprocd[0]==0);
|
||||
testOk1(data[1].hasprocd[1]==0);
|
||||
testOk1(data[1].hasprocd[2]==0);
|
||||
testOk1(data[1].getcomplete[0]==0);
|
||||
testOk1(data[1].getcomplete[1]==0);
|
||||
testOk1(data[1].getcomplete[2]==0);
|
||||
|
||||
testDiag("Release the first scan and wait for the second");
|
||||
for(i=0; i<NUM_CALLBACK_PRIORITIES; i++)
|
||||
epicsEventMustTrigger(data[0].wake[i]);
|
||||
for(i=0; i<NUM_CALLBACK_PRIORITIES; i++)
|
||||
epicsEventMustWait(data[1].wait[i]);
|
||||
|
||||
testOk1(data[0].hasprocd[0]==1);
|
||||
testOk1(data[0].hasprocd[1]==1);
|
||||
testOk1(data[0].hasprocd[2]==1);
|
||||
testOk1(data[0].getcomplete[0]==1);
|
||||
testOk1(data[0].getcomplete[1]==1);
|
||||
testOk1(data[0].getcomplete[2]==1);
|
||||
testOk1(data[1].hasprocd[0]==1);
|
||||
testOk1(data[1].hasprocd[1]==1);
|
||||
testOk1(data[1].hasprocd[2]==1);
|
||||
testOk1(data[1].getcomplete[0]==1);
|
||||
testOk1(data[1].getcomplete[1]==1);
|
||||
testOk1(data[1].getcomplete[2]==1);
|
||||
|
||||
testDiag("Release the second scan and complete");
|
||||
for(i=0; i<NUM_CALLBACK_PRIORITIES; i++)
|
||||
epicsEventMustTrigger(data[1].wake[i]);
|
||||
|
||||
testIocShutdownOk();
|
||||
epicsThreadSleep(0.1);
|
||||
for (i = 0; i < noOfGroups; i++) {
|
||||
epicsMessageQueueDestroy(mq[i]); mq[i] = NULL;
|
||||
epicsEventDestroy(barrier[i]); barrier[i] = NULL;
|
||||
ellFree(&pvtList[i]);
|
||||
}
|
||||
free(mq);
|
||||
free(barrier);
|
||||
free(pvtList);
|
||||
free(cbCounter);
|
||||
|
||||
testdbCleanup();
|
||||
|
||||
xdrv_reset();
|
||||
|
||||
for(i=0; i<2; i++) {
|
||||
int p;
|
||||
for(p=0; p<NUM_CALLBACK_PRIORITIES; p++) {
|
||||
epicsEventDestroy(data[i].wake[p]);
|
||||
epicsEventDestroy(data[i].wait[p]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void checkProcessed(void *user, IOSCANPVT ioscan, int prio) {
|
||||
struct pvtY *pvt;
|
||||
int group = -1;
|
||||
typedef struct {
|
||||
int hasprocd;
|
||||
int getcomplete;
|
||||
epicsEventId wait;
|
||||
epicsEventId wake;
|
||||
} testmulti;
|
||||
|
||||
static void testcbmulti(xpriv *priv, void *raw)
|
||||
{
|
||||
testmulti *td = raw;
|
||||
td += priv->member;
|
||||
|
||||
testOk1(td->hasprocd==0);
|
||||
td->hasprocd = 1;
|
||||
epicsEventMustTrigger(td->wait);
|
||||
epicsEventMustWait(td->wake);
|
||||
td->getcomplete = 1;
|
||||
}
|
||||
|
||||
static void testcompmulti(void *raw, IOSCANPVT scan, int prio)
|
||||
{
|
||||
int *mask = raw;
|
||||
testOk(((*mask)&(1<<prio))==0, "(0x%x)&(0x%x)==0", *mask, 1<<prio);
|
||||
*mask |= 1<<prio;
|
||||
}
|
||||
|
||||
static void testMultiThreading(void)
|
||||
{
|
||||
int i;
|
||||
int masks[2];
|
||||
testmulti data[6];
|
||||
xdrv *drvs[2];
|
||||
|
||||
for (i = 0; i < noOfGroups; i++) {
|
||||
if (ioscanpvt[groupTable[i].ioscan] == ioscan
|
||||
&& groupTable[i].prio == prio) {
|
||||
group = i;
|
||||
break;
|
||||
}
|
||||
memset(masks, 0, sizeof(masks));
|
||||
memset(data, 0, sizeof(data));
|
||||
|
||||
for(i=0; i<NELEMENTS(data); i++) {
|
||||
data[i].wake = epicsEventMustCreate(epicsEventEmpty);
|
||||
data[i].wait = epicsEventMustCreate(epicsEventEmpty);
|
||||
}
|
||||
if (group == -1)
|
||||
testAbort("invalid ioscanpvt in scanio callback");
|
||||
|
||||
cbCounter[group]++;
|
||||
for (pvt = (struct pvtY *)ellFirst(&pvtList[group]);
|
||||
pvt;
|
||||
pvt = (struct pvtY *)ellNext(&pvt->node)) {
|
||||
if (pvt->callback == 1) {
|
||||
testDiag("callback for rec %s arrived twice\n", pvt->prec->name);
|
||||
noDoubleCallback = 0;
|
||||
}
|
||||
if (pvt->processed == 0) {
|
||||
testDiag("rec %s was not processed\n", pvt->prec->name);
|
||||
recsProcessed = 0;
|
||||
}
|
||||
pvt->callback = 1;
|
||||
testDiag("Test multi-threaded I/O Intr scanning");
|
||||
|
||||
testdbPrepare();
|
||||
testdbReadDatabase("dbTestIoc.dbd", NULL, NULL);
|
||||
dbTestIoc_registerRecordDeviceDriver(pdbbase);
|
||||
|
||||
/* create two scan lists with one record on each of three priorities */
|
||||
|
||||
/* group#, member#, priority */
|
||||
loadRecord(0, 0, "LOW");
|
||||
loadRecord(1, 1, "LOW");
|
||||
loadRecord(0, 2, "MEDIUM");
|
||||
loadRecord(1, 3, "MEDIUM");
|
||||
loadRecord(0, 4, "HIGH");
|
||||
loadRecord(1, 5, "HIGH");
|
||||
|
||||
drvs[0] = xdrv_add(0, &testcbmulti, data);
|
||||
drvs[1] = xdrv_add(1, &testcbmulti, data);
|
||||
scanIoSetComplete(drvs[0]->scan, &testcompmulti, &masks[0]);
|
||||
scanIoSetComplete(drvs[1]->scan, &testcompmulti, &masks[1]);
|
||||
|
||||
/* just enough workers to process all records concurrently */
|
||||
callbackParallelThreads(2, "LOW");
|
||||
callbackParallelThreads(2, "MEDIUM");
|
||||
callbackParallelThreads(2, "HIGH");
|
||||
|
||||
eltc(0);
|
||||
testIocInitOk();
|
||||
eltc(1);
|
||||
|
||||
testDiag("Scan first list");
|
||||
testOk1(scanIoRequest(drvs[0]->scan)==0x7);
|
||||
testDiag("Scan second list");
|
||||
testOk1(scanIoRequest(drvs[1]->scan)==0x7);
|
||||
|
||||
testDiag("Wait for everything to start");
|
||||
for(i=0; i<NELEMENTS(data); i++)
|
||||
epicsEventMustWait(data[i].wait);
|
||||
|
||||
testDiag("Wait one more second");
|
||||
epicsThreadSleep(1.0);
|
||||
|
||||
for(i=0; i<NELEMENTS(data); i++) {
|
||||
testOk(data[i].hasprocd==1, "data[%d].hasprocd==1 (%d)", i, data[i].hasprocd);
|
||||
testOk(data[i].getcomplete==0, "data[%d].getcomplete==0 (%d)", i, data[i].getcomplete);
|
||||
}
|
||||
|
||||
testDiag("Release all and complete");
|
||||
for(i=0; i<NELEMENTS(data); i++)
|
||||
epicsEventMustTrigger(data[i].wake);
|
||||
|
||||
testIocShutdownOk();
|
||||
|
||||
for(i=0; i<NELEMENTS(data); i++) {
|
||||
testOk(data[i].getcomplete==1, "data[%d].getcomplete==0 (%d)", i, data[i].getcomplete);
|
||||
}
|
||||
testOk1(masks[0]==0x7);
|
||||
testOk1(masks[1]==0x7);
|
||||
|
||||
testdbCleanup();
|
||||
|
||||
xdrv_reset();
|
||||
|
||||
for(i=0; i<NELEMENTS(data); i++) {
|
||||
epicsEventDestroy(data[i].wake);
|
||||
epicsEventDestroy(data[i].wait);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************\
|
||||
* scanIoTest: Test I/O Intr scanning
|
||||
* including parallel callback threads and scanio callbacks
|
||||
\*************************************************************************/
|
||||
|
||||
MAIN(scanIoTest)
|
||||
{
|
||||
int i, j;
|
||||
int loop;
|
||||
int max_one, max_one_all;
|
||||
int parallel, parallel_all;
|
||||
int result;
|
||||
int cbCountOk;
|
||||
long waiting;
|
||||
struct pvtY *pvt;
|
||||
|
||||
testPlan(10);
|
||||
|
||||
if (noOfGroups < NO_OF_THREADS)
|
||||
testAbort("ERROR: This test requires number of ioscan sources >= number of parallel threads");
|
||||
|
||||
/**************************************\
|
||||
* Single callback thread
|
||||
\**************************************/
|
||||
|
||||
testNo = 0;
|
||||
startMockIoc();
|
||||
|
||||
testDiag("Testing single callback thread");
|
||||
testDiag(" using %d ioscan sources, %d records for each, and %d loops",
|
||||
noOfGroups, NO_OF_MEMBERS, ONE_THREAD_LOOPS);
|
||||
|
||||
for (j = 0; j < ONE_THREAD_LOOPS; j++) {
|
||||
for (i = 0; i < noOfIoscans; i++) {
|
||||
scanIoRequest(ioscanpvt[i]);
|
||||
}
|
||||
}
|
||||
|
||||
epicsThreadSleep(1.0);
|
||||
|
||||
testOk((orderFail==0), "No out-of-order processing");
|
||||
|
||||
result = 1;
|
||||
for (i = 0; i < noOfGroups; i++) {
|
||||
for (pvt = (struct pvtY *)ellFirst(&pvtList[i]);
|
||||
pvt;
|
||||
pvt = (struct pvtY *)ellNext(&pvt->node)) {
|
||||
if (pvt->count != ONE_THREAD_LOOPS) result = 0;
|
||||
}
|
||||
}
|
||||
|
||||
testOk(result, "All per-record process counters match number of loops");
|
||||
|
||||
stopMockIoc();
|
||||
|
||||
/**************************************\
|
||||
* Multiple parallel callback threads
|
||||
\**************************************/
|
||||
|
||||
testNo = 1;
|
||||
startMockIoc();
|
||||
|
||||
testDiag("Testing multiple parallel callback threads");
|
||||
testDiag(" using %d ioscan sources, %d records for each, %d loops, and %d parallel threads",
|
||||
noOfIoscans, NO_OF_MEMBERS, PAR_THREAD_LOOPS, NO_OF_THREADS);
|
||||
|
||||
for (j = 0; j < PAR_THREAD_LOOPS; j++) {
|
||||
for (i = 0; i < noOfIoscans; i++) {
|
||||
scanIoRequest(ioscanpvt[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/* With parallel cb threads, order and distribution to threads are not guaranteed.
|
||||
* We have stop barrier events for each request (in the first record).
|
||||
* Test schedule:
|
||||
* - After the requests have been put in the queue, NO_OF_THREADS threads should have taken
|
||||
* one request each.
|
||||
* - Each barrier event is given PAR_THREAD_LOOPS times.
|
||||
* - Whenever things stop, there should be four threads waiting, one request each.
|
||||
* - After all loops, each record should have processed PAR_THREAD_LOOPS times.
|
||||
*/
|
||||
|
||||
max_one_all = 1;
|
||||
parallel_all = 1;
|
||||
|
||||
for (loop = 0; loop < (PAR_THREAD_LOOPS * noOfGroups) / NO_OF_THREADS + 1; loop++) {
|
||||
max_one = 1;
|
||||
parallel = 0;
|
||||
waiting = 0;
|
||||
j = 0;
|
||||
do {
|
||||
epicsThreadSleep(0.001);
|
||||
j++;
|
||||
for (i = 0; i < noOfGroups; i++) {
|
||||
int l = epicsMessageQueuePending(mq[i]);
|
||||
while (epicsMessageQueueTryReceive(mq[i], NULL, 0) != -1);
|
||||
if (l == 1) {
|
||||
waiting |= 1 << i;
|
||||
} else if (l > 1) {
|
||||
max_one = 0;
|
||||
}
|
||||
}
|
||||
parallel = count_bits(waiting);
|
||||
} while (j < 5 && parallel < NO_OF_THREADS);
|
||||
|
||||
if (!max_one) max_one_all = 0;
|
||||
if (loop < (PAR_THREAD_LOOPS * noOfGroups) / NO_OF_THREADS) {
|
||||
if (!(parallel == NO_OF_THREADS)) parallel_all = 0;
|
||||
} else {
|
||||
/* In the last run of the loop only the remaining requests are processed */
|
||||
if (!(parallel == PAR_THREAD_LOOPS * noOfGroups % NO_OF_THREADS)) parallel_all = 0;
|
||||
}
|
||||
|
||||
for (i = 0; i < noOfGroups; i++) {
|
||||
if (waiting & (1 << i)) {
|
||||
epicsEventTrigger(barrier[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
testOk(max_one_all, "No thread took more than one request per loop");
|
||||
testOk(parallel_all, "Correct number of requests were being processed in parallel in each loop");
|
||||
|
||||
epicsThreadSleep(0.1);
|
||||
|
||||
result = 1;
|
||||
for (i = 0; i < noOfGroups; i++) {
|
||||
for (pvt = (struct pvtY *)ellFirst(&pvtList[i]);
|
||||
pvt;
|
||||
pvt = (struct pvtY *)ellNext(&pvt->node)) {
|
||||
if (pvt->count != PAR_THREAD_LOOPS) {
|
||||
testDiag("Process counter for record %s (%d) does not match loop count (%d)",
|
||||
pvt->prec->name, pvt->count, PAR_THREAD_LOOPS);
|
||||
result = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
testOk(result, "All per-record process counters match number of loops");
|
||||
|
||||
stopMockIoc();
|
||||
|
||||
/**************************************\
|
||||
* Scanio callback mechanism
|
||||
\**************************************/
|
||||
|
||||
testNo = 2;
|
||||
startMockIoc();
|
||||
|
||||
for (i = 0; i < noOfIoscans; i++) {
|
||||
scanIoSetComplete(ioscanpvt[i], checkProcessed, NULL);
|
||||
}
|
||||
|
||||
testDiag("Testing scanio callback mechanism");
|
||||
testDiag(" using %d ioscan sources, %d records for each, %d loops, and 1 LOW / %d MEDIUM / %d HIGH parallel threads",
|
||||
noOfIoscans, NO_OF_MEMBERS, CB_THREAD_LOOPS, NO_OF_MID_THREADS, NO_OF_THREADS);
|
||||
|
||||
result = 1;
|
||||
for (j = 0; j < CB_THREAD_LOOPS; j++) {
|
||||
for (i = 0; i < noOfIoscans; i++) {
|
||||
int prio_used;
|
||||
prio_used = scanIoRequest(ioscanpvt[i]);
|
||||
if (i+1 != prio_used)
|
||||
result = 0;
|
||||
}
|
||||
}
|
||||
testOk(result, "All requests return the correct priority callback mask (all 7 permutations covered)");
|
||||
|
||||
/* Test schedule:
|
||||
* After the requests have been put in the queue, it is checked
|
||||
* - that each callback arrives exactly once,
|
||||
* - after all records in the group have been processed.
|
||||
*/
|
||||
|
||||
/* loop count times 4 since (worst case) one loop triggers 4 groups for the single LOW thread */
|
||||
for (loop = 0; loop < CB_THREAD_LOOPS * 4; loop++) {
|
||||
max_one = 1;
|
||||
parallel = 0;
|
||||
waiting = 0;
|
||||
j = 0;
|
||||
do {
|
||||
epicsThreadSleep(0.001);
|
||||
j++;
|
||||
for (i = 0; i < noOfGroups; i++) {
|
||||
int l = epicsMessageQueuePending(mq[i]);
|
||||
while (epicsMessageQueueTryReceive(mq[i], NULL, 0) != -1);
|
||||
if (l == 1) {
|
||||
waiting |= 1 << i;
|
||||
} else if (l > 1) {
|
||||
max_one = 0;
|
||||
}
|
||||
}
|
||||
parallel = count_bits(waiting);
|
||||
} while (j < 5);
|
||||
\
|
||||
for (i = 0; i < noOfGroups; i++) {
|
||||
if (waiting & (1 << i)) {
|
||||
for (pvt = (struct pvtY *)ellFirst(&pvtList[i]);
|
||||
pvt;
|
||||
pvt = (struct pvtY *)ellNext(&pvt->node)) {
|
||||
pvt->processed = 0;
|
||||
pvt->callback = 0;
|
||||
/* record processing will set this at the end of process() */
|
||||
}
|
||||
epicsEventTrigger(barrier[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
epicsThreadSleep(0.1);
|
||||
|
||||
testOk(recsProcessed, "Each callback occured after all records in the group were processed");
|
||||
testOk(noDoubleCallback, "No double callbacks occured in any loop");
|
||||
|
||||
result = 1;
|
||||
cbCountOk = 1;
|
||||
for (i = 0; i < noOfGroups; i++) {
|
||||
if (cbCounter[i] != CB_THREAD_LOOPS) {
|
||||
testDiag("Callback counter for group %d (%d) does not match loop count (%d)",
|
||||
i, cbCounter[i], CB_THREAD_LOOPS);
|
||||
cbCountOk = 0;
|
||||
}
|
||||
for (pvt = (struct pvtY *)ellFirst(&pvtList[i]);
|
||||
pvt;
|
||||
pvt = (struct pvtY *)ellNext(&pvt->node)) {
|
||||
if (pvt->count != CB_THREAD_LOOPS) {
|
||||
testDiag("Process counter for record %s (%d) does not match loop count (%d)",
|
||||
pvt->prec->name, pvt->count, CB_THREAD_LOOPS);
|
||||
result = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
testOk(result, "All per-record process counters match number of loops");
|
||||
testOk(cbCountOk, "All per-group callback counters match number of loops");
|
||||
|
||||
stopMockIoc();
|
||||
|
||||
testPlan(152);
|
||||
testSingleThreading();
|
||||
testDiag("run a second time to verify shutdown and restart works");
|
||||
testSingleThreading();
|
||||
testMultiThreading();
|
||||
testDiag("run a second time to verify shutdown and restart works");
|
||||
testMultiThreading();
|
||||
return testDone();
|
||||
}
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
record(y, g$(GROUP)m$(MEMBER)) {
|
||||
record(x, "g$(GROUP)m$(MEMBER)") {
|
||||
field(DTYP, "Scan I/O")
|
||||
field(INP , "@$(GROUP) $(MEMBER)")
|
||||
field(SCAN, "I/O Intr")
|
||||
field(PRIO, "$(PRIO)")
|
||||
}
|
||||
|
||||
@@ -16,22 +16,47 @@
|
||||
#include "dbAccessDefs.h"
|
||||
#include "recSup.h"
|
||||
#include "recGbl.h"
|
||||
#include "devSup.h"
|
||||
#include "dbScan.h"
|
||||
|
||||
#define GEN_SIZE_OFFSET
|
||||
#include "xRecord.h"
|
||||
|
||||
#include <epicsExport.h>
|
||||
|
||||
#include "devx.h"
|
||||
|
||||
static long init_record(xRecord *prec, int pass)
|
||||
{
|
||||
long ret = 0;
|
||||
xdset *xset = (xdset*)prec->dset;
|
||||
if(!pass) return 0;
|
||||
|
||||
if(!xset) {
|
||||
recGblRecordError(S_dev_noDSET, prec, "x: init_record");
|
||||
return S_dev_noDSET;
|
||||
}
|
||||
if(xset->init_record)
|
||||
ret = (*xset->init_record)(prec);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static long process(xRecord *prec)
|
||||
{
|
||||
long ret = 0;
|
||||
xdset *xset = (xdset*)prec->dset;
|
||||
if(prec->clbk)
|
||||
(*prec->clbk)(prec);
|
||||
prec->pact = TRUE;
|
||||
if(xset && xset->process)
|
||||
ret = (*xset->process)(prec);
|
||||
recGblGetTimeStamp(prec);
|
||||
recGblFwdLink(prec);
|
||||
prec->pact = FALSE;
|
||||
return 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static rset xRSET = {
|
||||
RSETNUMBER, NULL, NULL, NULL, process
|
||||
RSETNUMBER, NULL, NULL, init_record, process
|
||||
};
|
||||
epicsExportAddress(rset,xRSET);
|
||||
|
||||
@@ -11,4 +11,9 @@ recordtype(x) {
|
||||
field(INP, DBF_INLINK) {
|
||||
prompt("Input Link")
|
||||
}
|
||||
field(CLBK, DBF_NOACCESS) {
|
||||
prompt("Processing callback")
|
||||
special(SPC_NOMOD)
|
||||
extra("void (*clbk)(struct xRecord*)")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
# This is a minimal I/O scanned record
|
||||
|
||||
recordtype(y) {
|
||||
include "dbCommon.dbd"
|
||||
field(VAL, DBF_LONG) {
|
||||
prompt("Value")
|
||||
}
|
||||
}
|
||||
|
||||
device(y,CONSTANT,devY,"ScanIO Test")
|
||||
@@ -76,8 +76,9 @@ typedef struct dbFldDes{ /* field description */
|
||||
short indRecordType; /*within dbRecordType.papFldDes */
|
||||
short special; /*Special processing requirements */
|
||||
dbfType field_type; /*Field type as defined in dbFldTypes.h */
|
||||
unsigned int process_passive:1;/*should dbPutField process passive */
|
||||
unsigned int prop:1;/*field is a metadata, post DBE_PROPERTY on change*/
|
||||
unsigned int process_passive:1;/*should dbPutField process passive */
|
||||
unsigned int prop:1;/*field is a metadata, post DBE_PROPERTY on change*/
|
||||
unsigned int isDevLink:1; /* true for INP/OUT fields */
|
||||
ctType base; /*base for integer to string conversions*/
|
||||
short promptgroup; /*prompt, i.e. gui group */
|
||||
short interest; /*interest level */
|
||||
|
||||
@@ -49,6 +49,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);
|
||||
@@ -201,6 +204,10 @@ static long dbReadCOM(DBBASE **ppdbbase,const char *filename, FILE *fp,
|
||||
char *penv;
|
||||
char **macPairs;
|
||||
|
||||
if(ellCount(&tempList)) {
|
||||
epicsPrintf("dbReadCOM: Parser stack dirty %d\n", ellCount(&tempList));
|
||||
}
|
||||
|
||||
if(*ppdbbase == 0) *ppdbbase = dbAllocBase();
|
||||
pdbbase = *ppdbbase;
|
||||
if(path && strlen(path)>0) {
|
||||
@@ -230,6 +237,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) {
|
||||
@@ -258,9 +266,10 @@ static long dbReadCOM(DBBASE **ppdbbase,const char *filename, FILE *fp,
|
||||
ellAdd(&inputFileList,&pinputFile->node);
|
||||
status = pvt_yy_parse();
|
||||
|
||||
if (yyAbort)
|
||||
while (ellCount(&tempList))
|
||||
popFirstTemp();
|
||||
if (ellCount(&tempList) && !yyAbort)
|
||||
epicsPrintf("dbReadCOM: Parser stack dirty w/o error. %d\n", ellCount(&tempList));
|
||||
while (ellCount(&tempList))
|
||||
popFirstTemp(); /* Memory leak on parser failure */
|
||||
|
||||
dbFreePath(pdbbase);
|
||||
if(!status) { /*add RTYP and VERS as an attribute */
|
||||
@@ -320,10 +329,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 {
|
||||
@@ -487,6 +495,8 @@ static void dbRecordtypeFieldHead(char *name,char *type)
|
||||
allocTemp(pdbFldDes);
|
||||
pdbFldDes->name = epicsStrDup(name);
|
||||
pdbFldDes->as_level = ASL1;
|
||||
pdbFldDes->isDevLink = strcmp(pdbFldDes->name, "INP")==0 ||
|
||||
strcmp(pdbFldDes->name, "OUT")==0;
|
||||
i = dbFindFieldType(type);
|
||||
if (i < 0)
|
||||
yyerrorAbort("Illegal Field Type");
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user