Compare commits
248 Commits
R3.15.4
...
calinkplug
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
46e74328dc | ||
|
|
cb35889a20 | ||
|
|
29afb88006 | ||
|
|
bd8977c51d | ||
|
|
32c3231f5d | ||
|
|
33033e6027 | ||
|
|
905c5e3336 | ||
|
|
47b3c68af1 | ||
|
|
4b3bdf31c2 | ||
|
|
3740082b61 | ||
|
|
7a1766279c | ||
|
|
cec0222c5a | ||
|
|
394820318c | ||
|
|
45db78981c | ||
|
|
7d94d05bb7 | ||
|
|
5c93eb6049 | ||
|
|
92bd217ade | ||
|
|
f0b5b52cef | ||
|
|
e0b578aff5 | ||
|
|
0fd07d1632 | ||
|
|
fbf6b6d3e5 | ||
|
|
82042e270e | ||
|
|
32c3f8a44f | ||
|
|
fc4f010972 | ||
|
|
3d7e95b88a | ||
|
|
8fea0117ae | ||
|
|
8392106f59 | ||
|
|
19680d7869 | ||
|
|
4a0ef34771 | ||
|
|
5d42e60745 | ||
|
|
e01d4b14d6 | ||
|
|
791fa1310a | ||
|
|
64cd49410e | ||
|
|
0f6f833e91 | ||
|
|
c08df82898 | ||
|
|
f0c56bcbba | ||
|
|
2be59e985d | ||
|
|
b4fd19e7db | ||
|
|
969b1030b2 | ||
|
|
84880e876d | ||
|
|
45a741ac6f | ||
|
|
0575d3764f | ||
|
|
48da96cce5 | ||
|
|
18d863b918 | ||
|
|
f528f347cd | ||
|
|
ffed8910c0 | ||
|
|
0fb923186a | ||
|
|
ce500bc98c | ||
|
|
6376ae8b1a | ||
|
|
445c0ada8c | ||
|
|
87759d1c1d | ||
|
|
6143520680 | ||
|
|
f814398d77 | ||
|
|
d2e4fcbbac | ||
|
|
4cca4673ca | ||
|
|
840da801fb | ||
|
|
e75f44100e | ||
|
|
e227ae3590 | ||
|
|
ecedd9c362 | ||
|
|
4e7b185977 | ||
|
|
b74ecff2fc | ||
|
|
1255e75828 | ||
|
|
d6eea14fd0 | ||
|
|
9779d6987c | ||
|
|
b7c2815c21 | ||
|
|
29d2b7f6a2 | ||
|
|
ef39f658cd | ||
|
|
e298fb4c27 | ||
|
|
d94840ae6c | ||
|
|
c1f742e741 | ||
|
|
6f0814108a | ||
|
|
770d9ab313 | ||
|
|
7e7cca6164 | ||
|
|
03bdf4effa | ||
|
|
3258927bab | ||
|
|
a8e285067d | ||
|
|
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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -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"
|
||||
@@ -942,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:
|
||||
@@ -969,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 ||
|
||||
@@ -1019,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 */
|
||||
|
||||
@@ -1039,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);
|
||||
|
||||
@@ -1066,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:
|
||||
@@ -1096,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,21 @@ 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;
|
||||
plink->value.pv_link.backend = "ca";
|
||||
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 +333,10 @@ void dbCaRemoveLink(struct link *plink)
|
||||
epicsMutexMustLock(pca->lock);
|
||||
pca->plink = 0;
|
||||
plink->value.pv_link.pvt = 0;
|
||||
plink->value.pv_link.backend = NULL;
|
||||
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 +396,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 +459,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 +469,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 +482,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 +502,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 +518,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 +537,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 +547,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 +559,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 +570,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 +601,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 +617,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 +633,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 +651,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 +663,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 +678,52 @@ 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 = {
|
||||
LSET_API_VERSION,
|
||||
dbCaReportLink,
|
||||
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 +738,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 +774,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 +813,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 +827,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 +844,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 +861,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 +930,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 +961,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 +1017,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 +1054,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 +1108,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" {
|
||||
@@ -23,54 +20,70 @@ extern "C" {
|
||||
typedef void (*dbCaCallback)(void *userPvt);
|
||||
epicsShareFunc void dbCaCallbackProcess(void *usrPvt);
|
||||
|
||||
#ifdef EPICS_DBCA_PRIVATE_API
|
||||
|
||||
epicsShareFunc void dbCaLinkInit(void); /* internal initialization for iocBuild() */
|
||||
epicsShareFunc void dbCaLinkInitIsolated(void); /* internal initialization for iocBuildIsolated() */
|
||||
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;
|
||||
|
||||
epicsShareFunc void dbCaSync(void);
|
||||
epicsShareFunc void dbCaReportLink(const struct link *plink, dbLinkReportInfo *pinfo);
|
||||
epicsShareExtern void (*dbAddLinkHook)(struct link *link, short dbfType);
|
||||
epicsShareFunc void dbSetBrokenLink(struct link *link, short dbfType);
|
||||
#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 */
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
|
||||
#define EPICS_DBCA_PRIVATE_API
|
||||
#include "dbDefs.h"
|
||||
#include "epicsEvent.h"
|
||||
#include "epicsPrint.h"
|
||||
@@ -52,7 +53,61 @@
|
||||
#include "dbLock.h"
|
||||
#include "link.h"
|
||||
|
||||
|
||||
void dbCaReportLink(const struct link *plink, dbLinkReportInfo *pinfo)
|
||||
{
|
||||
caLink * const pca = (caLink *)plink->value.pv_link.pvt;
|
||||
const char * fname = dbGetFieldName(pinfo->pentry),
|
||||
* rname = dbGetRecordName(pinfo->pentry);
|
||||
|
||||
assert(pca);
|
||||
epicsMutexLock(pca->lock);
|
||||
assert(pca->plink==plink);
|
||||
|
||||
pinfo->connected = ca_field_type(pca->chid) != TYPENOTCONN;
|
||||
pinfo->nWriteFail = pca->nNoWrite;
|
||||
pinfo->nDisconnect = pca->nDisconnect;
|
||||
|
||||
if (pinfo->connected) {
|
||||
pinfo->readable = ca_read_access(pca->chid);
|
||||
pinfo->writable = ca_write_access(pca->chid);
|
||||
|
||||
if (pinfo->filter==dbLinkReportAll || pinfo->filter==dbLinkReportConnected) {
|
||||
int rw = pinfo->readable |
|
||||
pinfo->writable << 1;
|
||||
static const char *rights[4] = {
|
||||
"No Access", "Read Only",
|
||||
"Write Only", "Read/Write"
|
||||
};
|
||||
int mask = plink->value.pv_link.pvlMask;
|
||||
printf(LSET_REPORT_INDENT "%28s.%-4s ==> %-28s (%lu, %lu)\n",
|
||||
rname,
|
||||
fname,
|
||||
plink->value.pv_link.pvname,
|
||||
pca->nDisconnect,
|
||||
pca->nNoWrite);
|
||||
printf(LSET_REPORT_INDENT "%21s [%s%s%s%s] host %s, %s\n", "",
|
||||
mask & pvlOptInpNative ? "IN" : " ",
|
||||
mask & pvlOptInpString ? "IS" : " ",
|
||||
mask & pvlOptOutNative ? "ON" : " ",
|
||||
mask & pvlOptOutString ? "OS" : " ",
|
||||
ca_host_name(pca->chid),
|
||||
rights[rw]);
|
||||
}
|
||||
} else {
|
||||
if (pinfo->filter==dbLinkReportAll || pinfo->filter==dbLinkReportDisconnected) {
|
||||
printf(LSET_REPORT_INDENT "%28s.%-4s --> %-28s (%lu, %lu)\n",
|
||||
rname,
|
||||
fname,
|
||||
plink->value.pv_link.pvname,
|
||||
pca->nDisconnect,
|
||||
pca->nNoWrite);
|
||||
}
|
||||
}
|
||||
|
||||
epicsMutexUnlock(pca->lock);
|
||||
}
|
||||
|
||||
|
||||
long dbcar(char *precordname, int level)
|
||||
{
|
||||
DBENTRY dbentry;
|
||||
@@ -68,7 +123,7 @@ long dbcar(char *precordname, int level)
|
||||
int noWriteAccess=0;
|
||||
unsigned long nDisconnect=0;
|
||||
unsigned long nNoWrite=0;
|
||||
caLink *pca;
|
||||
|
||||
int j;
|
||||
|
||||
if (!precordname || precordname[0] == '\0' || !strcmp(precordname, "*")) {
|
||||
@@ -87,56 +142,32 @@ 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]];
|
||||
pdbentry->pflddes = pdbFldDes;
|
||||
plink = (DBLINK *)((char *)precord + pdbFldDes->offset);
|
||||
dbLockSetGblLock();
|
||||
if (plink->type == CA_LINK) {
|
||||
ncalinks++;
|
||||
pca = (caLink *)plink->value.pv_link.pvt;
|
||||
if (pca
|
||||
&& pca->chid
|
||||
&& (ca_field_type(pca->chid) != TYPENOTCONN)) {
|
||||
nconnected++;
|
||||
nDisconnect += pca->nDisconnect;
|
||||
nNoWrite += pca->nNoWrite;
|
||||
if (!ca_read_access(pca->chid)) noReadAccess++;
|
||||
if (!ca_write_access(pca->chid)) noWriteAccess++;
|
||||
if (level>1) {
|
||||
int rw = ca_read_access(pca->chid) |
|
||||
ca_write_access(pca->chid) << 1;
|
||||
static const char *rights[4] = {
|
||||
"No Access", "Read Only",
|
||||
"Write Only", "Read/Write"
|
||||
};
|
||||
int mask = plink->value.pv_link.pvlMask;
|
||||
printf("%28s.%-4s ==> %-28s (%lu, %lu)\n",
|
||||
precord->name,
|
||||
pdbFldDes->name,
|
||||
plink->value.pv_link.pvname,
|
||||
pca->nDisconnect,
|
||||
pca->nNoWrite);
|
||||
printf("%21s [%s%s%s%s] host %s, %s\n", "",
|
||||
mask & pvlOptInpNative ? "IN" : " ",
|
||||
mask & pvlOptInpString ? "IS" : " ",
|
||||
mask & pvlOptOutNative ? "ON" : " ",
|
||||
mask & pvlOptOutString ? "OS" : " ",
|
||||
ca_host_name(pca->chid),
|
||||
rights[rw]);
|
||||
}
|
||||
} else {
|
||||
if (level>0) {
|
||||
printf("%28s.%-4s --> %-28s (%lu, %lu)\n",
|
||||
precord->name,
|
||||
pdbFldDes->name,
|
||||
plink->value.pv_link.pvname,
|
||||
pca->nDisconnect,
|
||||
pca->nNoWrite);
|
||||
}
|
||||
}
|
||||
dbLinkReportInfo linfo;
|
||||
memset(&linfo, 0, sizeof(linfo));
|
||||
linfo.pentry = pdbentry;
|
||||
if(level==0)
|
||||
linfo.filter = dbLinkReportNone;
|
||||
else if(level==1)
|
||||
linfo.filter = dbLinkReportDisconnected;
|
||||
else
|
||||
linfo.filter = dbLinkReportAll;
|
||||
if(level>2)
|
||||
linfo.detailLevel = level-2;
|
||||
dbReportLink(plink, &linfo);
|
||||
nconnected += linfo.connected;
|
||||
nDisconnect += linfo.nDisconnect;
|
||||
noReadAccess += !linfo.readable;
|
||||
noWriteAccess += !linfo.writable;
|
||||
}
|
||||
dbLockSetGblUnlock();
|
||||
}
|
||||
dbScanUnlock(precord);
|
||||
if (precordname) goto done;
|
||||
}
|
||||
status = dbNextRecord(pdbentry);
|
||||
@@ -189,10 +220,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 (dbIsLinkConnected(plink)) {
|
||||
nconnected++;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,7 +19,6 @@
|
||||
|
||||
#include "cantProceed.h"
|
||||
#include "epicsAssert.h"
|
||||
#include "epicsExit.h"
|
||||
#include "epicsString.h"
|
||||
#include "errlog.h"
|
||||
#include "freeList.h"
|
||||
|
||||
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
|
||||
*/
|
||||
@@ -19,6 +18,7 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#define EPICS_DBCA_PRIVATE_API
|
||||
#include "alarm.h"
|
||||
#include "cantProceed.h"
|
||||
#include "cvtFast.h"
|
||||
@@ -45,7 +45,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 +79,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 +97,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 +131,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 +139,25 @@ static long dbConstGetLink(struct link *plink, short dbrType, void *pbuffer,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static lset dbConst_lset = {
|
||||
LSET_API_VERSION,
|
||||
NULL,
|
||||
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 +168,49 @@ 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);
|
||||
plink->value.pv_link.backend = "db";
|
||||
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;
|
||||
plink->value.pv_link.backend = "db";
|
||||
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.backend = NULL;
|
||||
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 +235,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 +399,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 +430,118 @@ 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 = {
|
||||
LSET_API_VERSION,
|
||||
NULL,
|
||||
dbDbRemoveLink,
|
||||
dbDbIsConnected,
|
||||
dbDbGetDBFtype, dbDbGetElements,
|
||||
dbDbGetValue,
|
||||
dbDbGetControlLimits, dbDbGetGraphicLimits, dbDbGetAlarmLimits,
|
||||
dbDbGetPrecision, dbDbGetUnits,
|
||||
dbDbGetAlarm, dbDbGetTimeStamp,
|
||||
dbDbPutValue,
|
||||
dbDbScanFwdLink
|
||||
};
|
||||
|
||||
static void dbBrokenReportLink(const struct link *plink, dbLinkReportInfo *pinfo)
|
||||
{
|
||||
const char * fname = dbGetFieldName(pinfo->pentry),
|
||||
* rname = dbGetRecordName(pinfo->pentry);
|
||||
|
||||
if (pinfo->filter==dbLinkReportAll || pinfo->filter==dbLinkReportDisconnected) {
|
||||
printf("%28s.%-4s --> %-28s <invalid link type>\n",
|
||||
rname,
|
||||
fname,
|
||||
plink->value.pv_link.pvname);
|
||||
}
|
||||
}
|
||||
|
||||
static void dbBrokenRemoveLink(dbLocker *locker, struct link *plink)
|
||||
{
|
||||
assert(!plink->value.pv_link.pvt);
|
||||
plink->value.pv_link.pvt = 0;
|
||||
plink->value.pv_link.backend = NULL;
|
||||
plink->value.pv_link.getCvt = 0;
|
||||
plink->value.pv_link.pvlMask = 0;
|
||||
plink->value.pv_link.lastGetdbrType = 0;
|
||||
plink->type = PV_LINK;
|
||||
plink->lset = NULL;
|
||||
}
|
||||
|
||||
static lset broken_lset = {
|
||||
LSET_API_VERSION,
|
||||
dbBrokenReportLink,
|
||||
dbBrokenRemoveLink
|
||||
};
|
||||
|
||||
/***************************** Generic Link API *****************************/
|
||||
|
||||
void dbInitLink(struct dbCommon *precord, struct link *plink, short dbfType)
|
||||
void dbSetBrokenLink(struct link *plink, short dbfType)
|
||||
{
|
||||
plink->value.pv_link.precord = precord;
|
||||
plink->lset = &broken_lset;
|
||||
plink->type = CA_LINK;
|
||||
plink->value.pv_link.pvt = NULL;
|
||||
plink->value.pv_link.backend = "invalid";
|
||||
}
|
||||
|
||||
static
|
||||
void dbCaAddLinkHook(struct link *plink, short dbfType)
|
||||
{
|
||||
dbCaAddLink(NULL, plink, dbfType);
|
||||
}
|
||||
|
||||
void (*dbAddLinkHook)(struct link *link, short dbfType) = &dbCaAddLinkHook;
|
||||
|
||||
/* initialize CA_LINK with possibly custom lset */
|
||||
static
|
||||
void customlset(struct link *plink, short dbfType)
|
||||
{
|
||||
int oops = 0;
|
||||
plink->lset = NULL;
|
||||
if(dbAddLinkHook)
|
||||
(*dbAddLinkHook)(plink, dbfType);
|
||||
|
||||
if((plink->lset==NULL) ^ (plink->type==PV_LINK)) {
|
||||
oops = 1;
|
||||
errlogPrintf("custom link types must set both type and lset.\n");
|
||||
}
|
||||
if(plink->lset && plink->lset->version!=LSET_API_VERSION) {
|
||||
oops = 1;
|
||||
errlogPrintf("custom link types must set .version to LSET_API_VERSION (%u) not %u\n",
|
||||
LSET_API_VERSION, plink->lset->version);
|
||||
}
|
||||
if(oops)
|
||||
{
|
||||
plink->lset = NULL;
|
||||
plink->type = PV_LINK;
|
||||
plink->value.pv_link.pvt = NULL; /* leaking */
|
||||
}
|
||||
|
||||
if(!plink->lset)
|
||||
dbSetBrokenLink(plink, dbfType); /* install "invalid" lset as fallback */
|
||||
assert(plink->lset);
|
||||
assert(plink->type==CA_LINK);
|
||||
assert(plink->lset->version==LSET_API_VERSION);
|
||||
}
|
||||
|
||||
void dbInitLink(struct link *plink, short dbfType)
|
||||
{
|
||||
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 +556,7 @@ void dbInitLink(struct dbCommon *precord, struct link *plink, short dbfType)
|
||||
if (dbfType == DBF_INLINK)
|
||||
plink->value.pv_link.pvlMask |= pvlOptInpNative;
|
||||
|
||||
dbCaAddLink(plink);
|
||||
customlset(plink, dbfType);
|
||||
if (dbfType == DBF_FWDLINK) {
|
||||
char *pperiod = strrchr(plink->value.pv_link.pvname, '.');
|
||||
|
||||
@@ -442,24 +573,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);
|
||||
customlset(plink, dbfType);
|
||||
if (dbfType == DBF_FWDLINK) {
|
||||
char *pperiod = strrchr(plink->value.pv_link.pvname, '.');
|
||||
|
||||
@@ -470,69 +606,69 @@ 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;
|
||||
}
|
||||
}
|
||||
|
||||
void dbReportLink(const struct link *plink, dbLinkReportInfo *pinfo)
|
||||
{
|
||||
lset *plset = plink->lset;
|
||||
|
||||
if (plset && plset->reportLink) {
|
||||
plset->reportLink(plink, pinfo);
|
||||
}
|
||||
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 +676,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 +690,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 +780,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,50 @@
|
||||
#include "shareLib.h"
|
||||
#include "epicsTypes.h"
|
||||
#include "epicsTime.h"
|
||||
#include "dbAddr.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct dbLocker;
|
||||
struct DBENTRY;
|
||||
|
||||
#define LSET_API_VERSION 1
|
||||
#define LSET_REPORT_INDENT ""
|
||||
|
||||
typedef enum {
|
||||
dbLinkReportNone,
|
||||
dbLinkReportAll,
|
||||
dbLinkReportConnected,
|
||||
dbLinkReportDisconnected,
|
||||
} dbLinkReportFilter;
|
||||
|
||||
typedef struct {
|
||||
/* from caller */
|
||||
dbLinkReportFilter filter;
|
||||
int detailLevel;
|
||||
struct DBENTRY *pentry;
|
||||
unsigned clearstats:1; /* after reporting, zero stat counters */
|
||||
/* callee fills in current state */
|
||||
unsigned connected:1; /* is this link connected to it's underlying data source */
|
||||
unsigned readable:1; /* would a dbGetLink() succeed at this moment */
|
||||
unsigned writable:1; /* would a dbPutLink() succeed at this moment */
|
||||
/* callee fills in statistics */
|
||||
unsigned nDisconnect; /* number of times this link has entered a not connected state */
|
||||
unsigned nEvents; /* number of times new data has been received from the underlying data source */
|
||||
unsigned nWriteFail; /* number of times dbPutLink() has failed for this link */
|
||||
} dbLinkReportInfo;
|
||||
|
||||
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);
|
||||
unsigned version; /* must be set to LSET_API_VERSION */
|
||||
void (*reportLink)(const struct link *plink, dbLinkReportInfo *pinfo);
|
||||
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 +74,21 @@ 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 void dbReportLink(const struct link *plink, dbLinkReportInfo *pinfo);
|
||||
|
||||
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 +107,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 * const *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 * const *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
|
||||
|
||||
@@ -48,6 +48,14 @@ typedef enum {
|
||||
initHookAfterDatabasePaused,
|
||||
initHookAfterIocPaused, /* End of iocPause command */
|
||||
|
||||
initHookAtIocShutdown, /* Start of iocShutdown */
|
||||
initHookAfterCaLinkClose,
|
||||
initHookAfterScanShutdown,
|
||||
initHookAfterCallbackShutdown,
|
||||
initHookAfterCaServerStopped,
|
||||
initHookAfterDatabaseStopped,
|
||||
initHookAfterIocShutdown, /* End of iocShutdown */
|
||||
|
||||
/* Deprecated states, provided for backwards compatibility.
|
||||
* These states are announced at the same point they were before,
|
||||
* but will not be repeated if the IOC gets paused and restarted.
|
||||
|
||||
@@ -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,11 +10,15 @@ 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
|
||||
@@ -23,6 +27,8 @@ 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
|
||||
|
||||
@@ -30,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
|
||||
@@ -41,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
|
||||
@@ -50,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
|
||||
@@ -75,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
|
||||
@@ -145,7 +168,10 @@ TESTSCRIPTS_HOST += $(TESTS:%=%.t)
|
||||
include $(TOP)/configure/RULES
|
||||
|
||||
arrRecord$(DEP): $(COMMON_DIR)/arrRecord.h
|
||||
xRecord$(DEP): $(COMMON_DIR)/xRecord.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
|
||||
|
||||
|
||||
@@ -97,6 +97,12 @@ static long init_record(arrRecord *prec, int pass)
|
||||
|
||||
static long process(arrRecord *prec)
|
||||
{
|
||||
if(prec->clbk)
|
||||
(*prec->clbk)(prec);
|
||||
prec->pact = TRUE;
|
||||
recGblGetTimeStamp(prec);
|
||||
recGblFwdLink(prec);
|
||||
prec->pact = FALSE;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -31,4 +31,12 @@ recordtype(arr) {
|
||||
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();
|
||||
}
|
||||
|
||||
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());
|
||||
}
|
||||
}
|
||||
613
src/ioc/db/test/dbCaLinkTest.c
Normal file
613
src/ioc/db/test/dbCaLinkTest.c
Normal file
@@ -0,0 +1,613 @@
|
||||
/*************************************************************************\
|
||||
* 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>
|
||||
|
||||
#define EPICS_DBCA_PRIVATE_API
|
||||
#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=))")
|
||||
}
|
||||
@@ -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 */
|
||||
@@ -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 */
|
||||
|
||||
@@ -204,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) {
|
||||
@@ -262,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 */
|
||||
@@ -490,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
@@ -47,7 +47,7 @@ extern "C" {
|
||||
|
||||
typedef dbBase DBBASE;
|
||||
|
||||
typedef struct{
|
||||
typedef struct DBENTRY {
|
||||
DBBASE *pdbbase;
|
||||
dbRecordType *precordType;
|
||||
dbFldDes *pflddes;
|
||||
|
||||
@@ -36,6 +36,39 @@ char *dbRecordName(DBENTRY *pdbentry);
|
||||
char *dbGetStringNum(DBENTRY *pdbentry);
|
||||
long dbPutStringNum(DBENTRY *pdbentry,const char *pstring);
|
||||
|
||||
typedef struct dbLinkInfo {
|
||||
short ltype;
|
||||
|
||||
/* full link string for CONSTANT and PV_LINK,
|
||||
* parm string for HW links*/
|
||||
char *target;
|
||||
|
||||
/* for PV_LINK */
|
||||
short modifiers;
|
||||
|
||||
/* HW links */
|
||||
char hwid[6]; /* one extra element for a nil */
|
||||
int hwnums[5];
|
||||
} dbLinkInfo;
|
||||
|
||||
long dbInitRecordLinks(dbRecordType *rtyp, struct dbCommon *prec);
|
||||
|
||||
/* Parse link string. no record locks needed.
|
||||
* on success caller must free pinfo->target
|
||||
*/
|
||||
epicsShareFunc long dbParseLink(const char *str, short ftype, dbLinkInfo *pinfo);
|
||||
/* Check if link type allow the parsed link value pinfo
|
||||
* to be assigned to the given link.
|
||||
* Record containing plink must be locked.
|
||||
* Frees pinfo->target on failure.
|
||||
*/
|
||||
long dbCanSetLink(DBLINK *plink, dbLinkInfo *pinfo, devSup *devsup);
|
||||
/* Set link field. source record must be locked (target record too
|
||||
* when a DB_LINK is created)
|
||||
* Unconditionally takes ownership of pinfo->target
|
||||
*/
|
||||
long dbSetLink(DBLINK *plink, dbLinkInfo *pinfo, devSup *dset);
|
||||
|
||||
/* The following is for path */
|
||||
typedef struct dbPathNode {
|
||||
ELLNODE node;
|
||||
|
||||
@@ -25,141 +25,12 @@
|
||||
|
||||
#define epicsExportSharedSymbols
|
||||
#include "dbBase.h"
|
||||
#include "dbCommon.h"
|
||||
#include "dbStaticLib.h"
|
||||
#include "dbStaticPvt.h"
|
||||
#include "devSup.h"
|
||||
#include "special.h"
|
||||
|
||||
|
||||
static char hex_digit_to_ascii[16]={'0','1','2','3','4','5','6','7','8','9',
|
||||
'a','b','c','d','e','f'};
|
||||
|
||||
static void ulongToHexString(epicsUInt32 source,char *pdest)
|
||||
{
|
||||
epicsUInt32 val,temp;
|
||||
char digit[10];
|
||||
int i,j;
|
||||
|
||||
if(source==0) {
|
||||
strcpy(pdest,"0x0");
|
||||
return;
|
||||
}
|
||||
*pdest++ = '0'; *pdest++ = 'x';
|
||||
val = source;
|
||||
for(i=0; val!=0; i++) {
|
||||
temp = val/16;
|
||||
digit[i] = hex_digit_to_ascii[val - temp*16];
|
||||
val = temp;
|
||||
}
|
||||
for(j=i-1; j>=0; j--) {
|
||||
*pdest++ = digit[j];
|
||||
}
|
||||
*pdest = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
static double delta[2] = {1e-6, 1e-15};
|
||||
static int precision[2] = {6, 14};
|
||||
static void realToString(double value, char *preturn, int isdouble)
|
||||
{
|
||||
double absvalue;
|
||||
int logval,prec;
|
||||
size_t end;
|
||||
char tstr[30];
|
||||
char *ptstr = &tstr[0];
|
||||
int round;
|
||||
int ise = FALSE;
|
||||
char *loce = NULL;
|
||||
|
||||
if (value == 0) {
|
||||
strcpy(preturn, "0");
|
||||
return;
|
||||
}
|
||||
|
||||
absvalue = value < 0 ? -value : value;
|
||||
if (absvalue < (double)INT_MAX) {
|
||||
epicsInt32 intval = (epicsInt32) value;
|
||||
double diff = value - intval;
|
||||
|
||||
if (diff < 0) diff = -diff;
|
||||
if (diff < absvalue * delta[isdouble]) {
|
||||
cvtLongToString(intval, preturn);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/*Now starts the hard cases*/
|
||||
if (value < 0) {
|
||||
*preturn++ = '-';
|
||||
value = -value;
|
||||
}
|
||||
|
||||
logval = (int)log10(value);
|
||||
if (logval > 6 || logval < -2) {
|
||||
int nout;
|
||||
|
||||
ise = TRUE;
|
||||
prec = precision[isdouble];
|
||||
nout = sprintf(ptstr, "%.*e", prec, value);
|
||||
loce = strchr(ptstr, 'e');
|
||||
|
||||
if (!loce) {
|
||||
ptstr[nout] = 0;
|
||||
strcpy(preturn, ptstr);
|
||||
return;
|
||||
}
|
||||
|
||||
*loce++ = 0;
|
||||
} else {
|
||||
prec = precision[isdouble] - logval;
|
||||
if ( prec < 0) prec = 0;
|
||||
sprintf(ptstr, "%.*f", prec, value);
|
||||
}
|
||||
|
||||
if (prec > 0) {
|
||||
end = strlen(ptstr) - 1;
|
||||
round = FALSE;
|
||||
while (end > 0) {
|
||||
if (tstr[end] == '.') {end--; break;}
|
||||
if (tstr[end] == '0') {end--; continue;}
|
||||
if (!round && end < precision[isdouble]) break;
|
||||
if (!round && tstr[end] < '8') break;
|
||||
if (tstr[end-1] == '.') {
|
||||
if (round) end = end-2;
|
||||
break;
|
||||
}
|
||||
if (tstr[end-1] != '9') break;
|
||||
round = TRUE;
|
||||
end--;
|
||||
}
|
||||
tstr[end+1] = 0;
|
||||
while (round) {
|
||||
if (tstr[end] < '9') {tstr[end]++; break;}
|
||||
if (end == 0) { *preturn++ = '1'; tstr[end] = '0'; break;}
|
||||
tstr[end--] = '0';
|
||||
}
|
||||
}
|
||||
strcpy(preturn, &tstr[0]);
|
||||
if (ise) {
|
||||
if (!(strchr(preturn, '.'))) strcat(preturn, ".0");
|
||||
strcat(preturn, "e");
|
||||
strcat(preturn, loce);
|
||||
}
|
||||
}
|
||||
|
||||
static void floatToString(float value,char *preturn)
|
||||
{
|
||||
realToString((double)value,preturn,0);
|
||||
return;
|
||||
}
|
||||
|
||||
static void doubleToString(double value,char *preturn)
|
||||
{
|
||||
realToString(value,preturn,1);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
static long do_nothing(struct dbCommon *precord) { return 0; }
|
||||
|
||||
/* Dummy DSXT used for soft device supports */
|
||||
@@ -198,7 +69,7 @@ long dbAllocRecord(DBENTRY *pdbentry,const char *precordName)
|
||||
dbRecordNode *precnode = pdbentry->precnode;
|
||||
dbFldDes *pflddes;
|
||||
int i;
|
||||
char *precord;
|
||||
dbCommon *precord;
|
||||
char *pfield;
|
||||
|
||||
if(!pdbRecordType) return(S_dbLib_recordTypeNotFound);
|
||||
@@ -209,24 +80,25 @@ long dbAllocRecord(DBENTRY *pdbentry,const char *precordName)
|
||||
precordName, pdbRecordType->name);
|
||||
return(S_dbLib_noRecSup);
|
||||
}
|
||||
precnode->precord = dbCalloc(1,pdbRecordType->rec_size);
|
||||
precord = (char *)precnode->precord;
|
||||
precord = dbCalloc(1, pdbRecordType->rec_size);
|
||||
precnode->precord = precord;
|
||||
pflddes = pdbRecordType->papFldDes[0];
|
||||
if(!pflddes) {
|
||||
epicsPrintf("dbAllocRecord pflddes for NAME not found\n");
|
||||
return(S_dbLib_flddesNotFound);
|
||||
}
|
||||
if(strlen(precordName)>=pflddes->size) {
|
||||
assert(pflddes->offset == 0);
|
||||
assert(pflddes->size == sizeof(precord->name));
|
||||
if(strlen(precordName) >= sizeof(precord->name)) {
|
||||
epicsPrintf("dbAllocRecord: NAME(%s) too long\n",precordName);
|
||||
return(S_dbLib_nameLength);
|
||||
}
|
||||
pfield = precord + pflddes->offset;
|
||||
strcpy(pfield,precordName);
|
||||
strcpy(precord->name, precordName);
|
||||
for(i=1; i<pdbRecordType->no_fields; i++) {
|
||||
|
||||
pflddes = pdbRecordType->papFldDes[i];
|
||||
if(!pflddes) continue;
|
||||
pfield = precord + pflddes->offset;
|
||||
pfield = (char*)precord + pflddes->offset;
|
||||
pdbentry->pfield = (void *)pfield;
|
||||
pdbentry->pflddes = pflddes;
|
||||
pdbentry->indfield = i;
|
||||
@@ -270,9 +142,9 @@ long dbAllocRecord(DBENTRY *pdbentry,const char *precordName)
|
||||
|
||||
plink->type = CONSTANT;
|
||||
if(pflddes->initial) {
|
||||
plink->value.constantStr =
|
||||
plink->text =
|
||||
dbCalloc(strlen(pflddes->initial)+1,sizeof(char));
|
||||
strcpy(plink->value.constantStr,pflddes->initial);
|
||||
strcpy(plink->text,pflddes->initial);
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -478,95 +350,7 @@ epicsShareFunc int dbIsDefaultValue(DBENTRY *pdbentry)
|
||||
}
|
||||
return(FALSE);
|
||||
}
|
||||
|
||||
char *dbGetStringNum(DBENTRY *pdbentry)
|
||||
{
|
||||
dbFldDes *pflddes = pdbentry->pflddes;
|
||||
void *pfield = pdbentry->pfield;
|
||||
char *message;
|
||||
unsigned char cvttype;
|
||||
|
||||
if(!pdbentry->message) pdbentry->message = dbCalloc(1,50);
|
||||
message = pdbentry->message;
|
||||
cvttype = pflddes->base;
|
||||
switch (pflddes->field_type) {
|
||||
case DBF_CHAR:
|
||||
if(cvttype==CT_DECIMAL)
|
||||
cvtCharToString(*(char*)pfield, message);
|
||||
else
|
||||
ulongToHexString((epicsUInt32)(*(char*)pfield),message);
|
||||
break;
|
||||
case DBF_UCHAR:
|
||||
if(cvttype==CT_DECIMAL)
|
||||
cvtUcharToString(*(unsigned char*)pfield, message);
|
||||
else
|
||||
ulongToHexString((epicsUInt32)(*(unsigned char*)pfield),message);
|
||||
break;
|
||||
case DBF_SHORT:
|
||||
if(cvttype==CT_DECIMAL)
|
||||
cvtShortToString(*(short*)pfield, message);
|
||||
else
|
||||
ulongToHexString((epicsUInt32)(*(short*)pfield),message);
|
||||
break;
|
||||
case DBF_USHORT:
|
||||
case DBF_ENUM:
|
||||
if(cvttype==CT_DECIMAL)
|
||||
cvtUshortToString(*(unsigned short*)pfield, message);
|
||||
else
|
||||
ulongToHexString((epicsUInt32)(*(unsigned short*)pfield),message);
|
||||
break;
|
||||
case DBF_LONG:
|
||||
if(cvttype==CT_DECIMAL)
|
||||
cvtLongToString(*(epicsInt32*)pfield, message);
|
||||
else
|
||||
ulongToHexString((epicsUInt32)(*(epicsInt32*)pfield), message);
|
||||
break;
|
||||
case DBF_ULONG:
|
||||
if(cvttype==CT_DECIMAL)
|
||||
cvtUlongToString(*(epicsUInt32 *)pfield, message);
|
||||
else
|
||||
ulongToHexString(*(epicsUInt32*)pfield, message);
|
||||
break;
|
||||
case DBF_FLOAT:
|
||||
floatToString(*(float *)pfield,message);
|
||||
break;
|
||||
case DBF_DOUBLE:
|
||||
doubleToString(*(double *)pfield,message);
|
||||
break;
|
||||
case DBF_MENU: {
|
||||
dbMenu *pdbMenu = (dbMenu *)pflddes->ftPvt;
|
||||
short choice_ind;
|
||||
char *pchoice;
|
||||
|
||||
if(!pfield) {strcpy(message,"Field not found"); return(message);}
|
||||
choice_ind = *((short *) pdbentry->pfield);
|
||||
if(!pdbMenu || choice_ind<0 || choice_ind>=pdbMenu->nChoice)
|
||||
return(NULL);
|
||||
pchoice = pdbMenu->papChoiceValue[choice_ind];
|
||||
strcpy(message, pchoice);
|
||||
}
|
||||
break;
|
||||
case DBF_DEVICE: {
|
||||
dbDeviceMenu *pdbDeviceMenu;
|
||||
char *pchoice;
|
||||
short choice_ind;
|
||||
|
||||
if(!pfield) {strcpy(message,"Field not found"); return(message);}
|
||||
pdbDeviceMenu = dbGetDeviceMenu(pdbentry);
|
||||
if(!pdbDeviceMenu) return(NULL);
|
||||
choice_ind = *((short *) pdbentry->pfield);
|
||||
if(choice_ind<0 || choice_ind>=pdbDeviceMenu->nChoice)
|
||||
return(NULL);
|
||||
pchoice = pdbDeviceMenu->papChoice[choice_ind];
|
||||
strcpy(message, pchoice);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return(NULL);
|
||||
}
|
||||
return (message);
|
||||
}
|
||||
|
||||
long dbPutStringNum(DBENTRY *pdbentry,const char *pstring)
|
||||
{
|
||||
dbFldDes *pflddes = pdbentry->pflddes;
|
||||
|
||||
@@ -118,7 +118,7 @@ recordtype_field: tokenFIELD recordtype_field_head recordtype_field_body
|
||||
| tokenCDEFS
|
||||
{
|
||||
if(dbStaticDebug>2) printf("recordtype_cdef %s", $1);
|
||||
dbRecordtypeCdef($1);
|
||||
dbRecordtypeCdef($1); dbmfFree($1);
|
||||
}
|
||||
| include ;
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
* Copyright (c) 2002 The Regents of the University of California, as
|
||||
* Operator of Los Alamos National Laboratory.
|
||||
* EPICS BASE is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
* in file LICENSE that is included with this distribution.
|
||||
\*************************************************************************/
|
||||
/* link.h */
|
||||
/* $Id$ */
|
||||
@@ -17,6 +17,7 @@
|
||||
#define INC_link_H
|
||||
|
||||
#include "dbDefs.h"
|
||||
#include "ellLib.h"
|
||||
#include "shareLib.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
@@ -33,7 +34,7 @@ extern "C" {
|
||||
#define BITBUS_IO 6
|
||||
#define MACRO_LINK 7
|
||||
|
||||
#define PN_LINK 9
|
||||
#define PN_LINK 9
|
||||
#define DB_LINK 10
|
||||
#define CA_LINK 11
|
||||
#define INST_IO 12 /* instrument */
|
||||
@@ -42,8 +43,8 @@ extern "C" {
|
||||
#define VXI_IO 15
|
||||
#define LINK_NTYPES 15
|
||||
typedef struct maplinkType {
|
||||
char *strvalue;
|
||||
int value;
|
||||
char *strvalue;
|
||||
int value;
|
||||
} maplinkType;
|
||||
|
||||
epicsShareExtern maplinkType pamaplinkType[];
|
||||
@@ -69,120 +70,125 @@ epicsShareExtern maplinkType pamaplinkType[];
|
||||
#define pvlOptOutString 0x400 /*Output as string*/
|
||||
#define pvlOptTSELisTime 0x800 /*Field TSEL is getting timeStamp*/
|
||||
|
||||
typedef long (*LINKCVT)();
|
||||
|
||||
struct macro_link {
|
||||
char *macroStr;
|
||||
char *macroStr;
|
||||
};
|
||||
|
||||
struct dbCommon;
|
||||
struct pvlet;
|
||||
typedef long (*LINKCVT)();
|
||||
|
||||
struct pv_link {
|
||||
char *pvname; /* pvname link points to */
|
||||
struct dbCommon *precord; /* Address of record owning link */
|
||||
void *pvt; /* CA or DB private */
|
||||
LINKCVT getCvt; /* input conversion function */
|
||||
short pvlMask; /* Options mask */
|
||||
short lastGetdbrType; /* last dbrType for DB or CA get */
|
||||
ELLNODE backlinknode;
|
||||
char *pvname; /* pvname link points to */
|
||||
void *pvt; /* CA or DB private */
|
||||
LINKCVT getCvt; /* input conversion function */
|
||||
short pvlMask; /* Options mask */
|
||||
short lastGetdbrType; /* last dbrType for DB or CA get */
|
||||
const char *backend;/* informational string describing the backend */
|
||||
};
|
||||
|
||||
/* structure of a VME io channel */
|
||||
struct vmeio {
|
||||
short card;
|
||||
short signal;
|
||||
char *parm;
|
||||
short card;
|
||||
short signal;
|
||||
char *parm;
|
||||
};
|
||||
|
||||
/* structure of a CAMAC io channel */
|
||||
struct camacio {
|
||||
short b;
|
||||
short c;
|
||||
short n;
|
||||
short a;
|
||||
short f;
|
||||
char *parm;
|
||||
short b;
|
||||
short c;
|
||||
short n;
|
||||
short a;
|
||||
short f;
|
||||
char *parm;
|
||||
};
|
||||
|
||||
/* structure of a RF io channel */
|
||||
struct rfio {
|
||||
short branch;
|
||||
short cryo;
|
||||
short micro;
|
||||
short dataset;
|
||||
short element;
|
||||
long ext;
|
||||
short branch;
|
||||
short cryo;
|
||||
short micro;
|
||||
short dataset;
|
||||
short element;
|
||||
long ext;
|
||||
};
|
||||
|
||||
/* structure of a Allen-Bradley io channel */
|
||||
struct abio {
|
||||
short link;
|
||||
short adapter;
|
||||
short card;
|
||||
short signal;
|
||||
char *parm;
|
||||
short link;
|
||||
short adapter;
|
||||
short card;
|
||||
short signal;
|
||||
char *parm;
|
||||
};
|
||||
|
||||
/* structure of a gpib io channel */
|
||||
struct gpibio {
|
||||
short link;
|
||||
short addr; /* device address */
|
||||
char *parm;
|
||||
short link;
|
||||
short addr; /* device address */
|
||||
char *parm;
|
||||
};
|
||||
|
||||
/* structure of a bitbus io channel */
|
||||
struct bitbusio {
|
||||
unsigned char link;
|
||||
unsigned char node;
|
||||
unsigned char port;
|
||||
unsigned char signal;
|
||||
char *parm;
|
||||
struct bitbusio {
|
||||
unsigned char link;
|
||||
unsigned char node;
|
||||
unsigned char port;
|
||||
unsigned char signal;
|
||||
char *parm;
|
||||
};
|
||||
|
||||
/* structure of a bitbus to gpib io channel */
|
||||
struct bbgpibio {
|
||||
unsigned char link;
|
||||
unsigned char bbaddr;
|
||||
unsigned char gpibaddr;
|
||||
unsigned char pad;
|
||||
char *parm;
|
||||
struct bbgpibio {
|
||||
unsigned char link;
|
||||
unsigned char bbaddr;
|
||||
unsigned char gpibaddr;
|
||||
unsigned char pad;
|
||||
char *parm;
|
||||
};
|
||||
|
||||
/* structure of an instrument io link */
|
||||
struct instio {
|
||||
char *string;
|
||||
struct instio {
|
||||
char *string;
|
||||
};
|
||||
|
||||
/* structure of a vxi link */
|
||||
struct vxiio{
|
||||
short flag; /* 0 = frame/slot, 1 = SA */
|
||||
short frame;
|
||||
short slot;
|
||||
short la; /* logical address if flag =1 */
|
||||
short signal;
|
||||
char *parm;
|
||||
struct vxiio {
|
||||
short flag; /* 0 = frame/slot, 1 = SA */
|
||||
short frame;
|
||||
short slot;
|
||||
short la; /* logical address if flag =1 */
|
||||
short signal;
|
||||
char *parm;
|
||||
};
|
||||
|
||||
/* union of possible address structures */
|
||||
union value {
|
||||
char *constantStr; /*constant string*/
|
||||
struct macro_link macro_link; /* link containing macro substitution*/
|
||||
struct pv_link pv_link; /* link to process variable*/
|
||||
struct vmeio vmeio; /* vme io point */
|
||||
struct camacio camacio; /* camac io point */
|
||||
struct rfio rfio; /* CEBAF RF buffer interface */
|
||||
struct abio abio; /* allen-bradley io point */
|
||||
struct gpibio gpibio;
|
||||
struct bitbusio bitbusio;
|
||||
struct instio instio; /* instrument io link */
|
||||
struct bbgpibio bbgpibio; /* bitbus to gpib io link */
|
||||
struct vxiio vxiio; /* vxi io */
|
||||
char *constantStr; /*constant string*/
|
||||
struct macro_link macro_link; /* link containing macro substitution*/
|
||||
struct pv_link pv_link; /* link to process variable*/
|
||||
struct vmeio vmeio; /* vme io point */
|
||||
struct camacio camacio; /* camac io point */
|
||||
struct rfio rfio; /* CEBAF RF buffer interface */
|
||||
struct abio abio; /* allen-bradley io point */
|
||||
struct gpibio gpibio;
|
||||
struct bitbusio bitbusio;
|
||||
struct instio instio; /* instrument io link */
|
||||
struct bbgpibio bbgpibio; /* bitbus to gpib io link */
|
||||
struct vxiio vxiio; /* vxi io */
|
||||
};
|
||||
|
||||
struct lset;
|
||||
|
||||
struct link {
|
||||
union value value;
|
||||
short type;
|
||||
char *text; /* original INP/OUT link text */
|
||||
struct dbCommon *precord; /* Pointer to record owning link */
|
||||
short type;
|
||||
short flags;
|
||||
struct lset *lset;
|
||||
char *text; /* Raw link text */
|
||||
union value value;
|
||||
};
|
||||
|
||||
typedef struct link DBLINK;
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
#include <errno.h>
|
||||
#include <limits.h>
|
||||
|
||||
#define EPICS_DBCA_PRIVATE_API
|
||||
#include "dbDefs.h"
|
||||
#include "ellLib.h"
|
||||
#include "envDefs.h"
|
||||
@@ -86,6 +87,14 @@ static void initDatabase(void);
|
||||
static void initialProcess(void);
|
||||
static void exitDatabase(void *dummy);
|
||||
|
||||
/*
|
||||
* Iterate through all record instances (but not aliases),
|
||||
* calling a function for each one.
|
||||
*/
|
||||
typedef void (*recIterFunc)(dbRecordType *rtyp, dbCommon *prec, void *user);
|
||||
|
||||
static void iterateRecords(recIterFunc func, void *user);
|
||||
|
||||
int dbThreadRealtimeLock = 1;
|
||||
epicsExportAddress(int, dbThreadRealtimeLock);
|
||||
|
||||
@@ -128,6 +137,11 @@ static int iocBuild_1(void)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void prepareLinks(dbRecordType *rtyp, dbCommon *prec, void *junk)
|
||||
{
|
||||
dbInitRecordLinks(rtyp, prec);
|
||||
}
|
||||
|
||||
static int iocBuild_2(void)
|
||||
{
|
||||
initHookAnnounce(initHookAfterCaLinkInit);
|
||||
@@ -139,12 +153,14 @@ static int iocBuild_2(void)
|
||||
initHookAnnounce(initHookAfterInitRecSup);
|
||||
|
||||
initDevSup();
|
||||
initHookAnnounce(initHookAfterInitDevSup);
|
||||
initHookAnnounce(initHookAfterInitDevSup); /* used by autosave pass 0 */
|
||||
|
||||
iterateRecords(prepareLinks, NULL);
|
||||
|
||||
dbLockInitRecords(pdbbase);
|
||||
initDatabase();
|
||||
dbBkptInit();
|
||||
initHookAnnounce(initHookAfterInitDatabase);
|
||||
initHookAnnounce(initHookAfterInitDatabase); /* used by autosave pass 1 */
|
||||
|
||||
finishDevSup();
|
||||
initHookAnnounce(initHookAfterFinishDevSup);
|
||||
@@ -425,12 +441,6 @@ static void finishDevSup(void)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Iterate through all record instances (but not aliases),
|
||||
* calling a function for each one.
|
||||
*/
|
||||
typedef void (*recIterFunc)(dbRecordType *rtyp, dbCommon *prec, void *user);
|
||||
|
||||
static void iterateRecords(recIterFunc func, void *user)
|
||||
{
|
||||
@@ -494,10 +504,9 @@ static void doResolveLinks(dbRecordType *pdbRecordType, dbCommon *precord,
|
||||
/* For all the links in the record type... */
|
||||
for (j = 0; j < pdbRecordType->no_links; j++) {
|
||||
dbFldDes *pdbFldDes = papFldDes[link_ind[j]];
|
||||
DBLINK *plink = (DBLINK *)((char *)precord + pdbFldDes->offset);
|
||||
DBLINK *plink = (DBLINK*)((char*)precord + pdbFldDes->offset);
|
||||
|
||||
if (ellCount(&precord->rdes->devList) > 0 &&
|
||||
(strcmp(pdbFldDes->name, "INP") == 0 || strcmp(pdbFldDes->name, "OUT") == 0)) {
|
||||
if (ellCount(&precord->rdes->devList) > 0 && pdbFldDes->isDevLink) {
|
||||
devSup *pdevSup = dbDTYPtoDevSup(pdbRecordType, precord->dtyp);
|
||||
|
||||
if (pdevSup) {
|
||||
@@ -508,8 +517,7 @@ static void doResolveLinks(dbRecordType *pdbRecordType, dbCommon *precord,
|
||||
}
|
||||
}
|
||||
|
||||
if (plink->type == PV_LINK)
|
||||
dbInitLink(precord, plink, pdbFldDes->field_type);
|
||||
dbInitLink(plink, pdbFldDes->field_type);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -634,13 +642,13 @@ static void doCloseLinks(dbRecordType *pdbRecordType, dbCommon *precord,
|
||||
dbScanLock(precord);
|
||||
locked = 1;
|
||||
}
|
||||
dbCaRemoveLink(plink);
|
||||
plink->type = CONSTANT;
|
||||
dbRemoveLink(NULL, plink);
|
||||
|
||||
} else if (plink->type == DB_LINK) {
|
||||
} else if (iocBuildMode==buildIsolated && plink->type == DB_LINK) {
|
||||
/* free link, but don't split lockset like dbDbRemoveLink() */
|
||||
free(plink->value.pv_link.pvt);
|
||||
plink->type = CONSTANT;
|
||||
plink->type = PV_LINK;
|
||||
plink->lset = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -665,11 +673,6 @@ static void doFreeRecord(dbRecordType *pdbRecordType, dbCommon *precord,
|
||||
void *user)
|
||||
{
|
||||
int j;
|
||||
struct rset *prset = pdbRecordType->prset;
|
||||
|
||||
if (!prset) return; /* unlikely */
|
||||
|
||||
epicsMutexDestroy(precord->mlok);
|
||||
|
||||
for (j = 0; j < pdbRecordType->no_links; j++) {
|
||||
dbFldDes *pdbFldDes =
|
||||
@@ -678,18 +681,28 @@ static void doFreeRecord(dbRecordType *pdbRecordType, dbCommon *precord,
|
||||
|
||||
dbFreeLinkContents(plink);
|
||||
}
|
||||
|
||||
epicsMutexDestroy(precord->mlok);
|
||||
free(precord->ppnr); /* may be allocated in dbNotify.c */
|
||||
}
|
||||
|
||||
int iocShutdown(void)
|
||||
{
|
||||
if (iocState == iocVirgin || iocState == iocStopped) return 0;
|
||||
initHookAnnounce(initHookAtIocShutdown); // TODO: iterate hooks in reverse
|
||||
iterateRecords(doCloseLinks, NULL);
|
||||
initHookAnnounce(initHookAfterCaLinkClose);
|
||||
if (iocBuildMode==buildIsolated) {
|
||||
/* stop and "join" threads */
|
||||
scanStop();
|
||||
initHookAnnounce(initHookAfterScanShutdown);
|
||||
callbackStop();
|
||||
initHookAnnounce(initHookAfterCallbackShutdown);
|
||||
}
|
||||
dbCaShutdown();
|
||||
dbCaShutdown(); /* must be before dbFreeRecord and dbChannelExit */
|
||||
initHookAnnounce(initHookAfterCaServerStopped);
|
||||
/* placeholder, RSRV will eventually stop here */
|
||||
initHookAnnounce(initHookAfterDatabaseStopped);
|
||||
if (iocBuildMode==buildIsolated) {
|
||||
/* free resources */
|
||||
scanCleanup();
|
||||
@@ -703,6 +716,7 @@ int iocShutdown(void)
|
||||
}
|
||||
iocState = iocStopped;
|
||||
iocBuildMode = buildRSRV;
|
||||
initHookAnnounce(initHookAfterIocShutdown);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -9,9 +9,14 @@
|
||||
TOP = ../..
|
||||
include $(TOP)/configure/CONFIG
|
||||
|
||||
# Uncomment this to remove the (benign) valgrind helper stubs
|
||||
#USR_CFLAGS += -DNVALGRIND
|
||||
|
||||
SRC = $(TOP)/src
|
||||
LIBCOM = $(SRC)/libCom
|
||||
|
||||
INC += valgrind/valgrind.h
|
||||
|
||||
include $(LIBCOM)/as/Makefile
|
||||
include $(LIBCOM)/bucketLib/Makefile
|
||||
include $(LIBCOM)/calc/Makefile
|
||||
|
||||
@@ -18,8 +18,10 @@
|
||||
#include <time.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/termios.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
@@ -331,10 +333,52 @@ initialize_remote_filesystem(char **argv, int hasLocalFilesystem)
|
||||
argv[1] = abspath;
|
||||
}
|
||||
}
|
||||
errlogPrintf("nfsMount(\"%s\", \"%s\", \"%s\")\n",
|
||||
server_name, server_path, mount_point);
|
||||
nfsMount(server_name, server_path, mount_point);
|
||||
#endif
|
||||
}
|
||||
|
||||
static
|
||||
char rtems_etc_hosts[] = "127.0.0.1 localhost\n";
|
||||
|
||||
/* If it doesn't already exist, create /etc/hosts with an entry for 'localhost' */
|
||||
static
|
||||
void fixup_hosts(void)
|
||||
{
|
||||
FILE *fp;
|
||||
int ret;
|
||||
struct stat STAT;
|
||||
|
||||
ret=stat("/etc/hosts", &STAT);
|
||||
if(ret==0)
|
||||
{
|
||||
return; /* already exists, assume file */
|
||||
} else if(errno!=ENOENT) {
|
||||
perror("error: fixup_hosts stat /etc/hosts");
|
||||
return;
|
||||
}
|
||||
|
||||
ret = mkdir("/etc", 0775);
|
||||
if(ret!=0 && errno!=EEXIST)
|
||||
{
|
||||
perror("error: fixup_hosts create /etc");
|
||||
return;
|
||||
}
|
||||
|
||||
if((fp=fopen("/etc/hosts", "w"))==NULL)
|
||||
{
|
||||
perror("error: fixup_hosts create /etc/hosts");
|
||||
}
|
||||
|
||||
if(fwrite(rtems_etc_hosts, 1, sizeof(rtems_etc_hosts)-1, fp)!=sizeof(rtems_etc_hosts)-1)
|
||||
{
|
||||
perror("error: failed to write /etc/hosts");
|
||||
}
|
||||
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
/*
|
||||
* Get to the startup script directory
|
||||
* The TFTP filesystem requires a trailing '/' on chdir arguments.
|
||||
@@ -361,6 +405,8 @@ set_directory (const char *commandline)
|
||||
directoryPath[l+1] = '\0';
|
||||
if (chdir (directoryPath) < 0)
|
||||
LogFatal ("Can't set initial directory(%s): %s\n", directoryPath, strerror(errno));
|
||||
else
|
||||
errlogPrintf("chdir(\"%s\")\n", directoryPath);
|
||||
free(directoryPath);
|
||||
}
|
||||
|
||||
@@ -546,6 +592,7 @@ Init (rtems_task_argument ignored)
|
||||
printf("\n***** Initializing network *****\n");
|
||||
rtems_bsdnet_initialize_network();
|
||||
initialize_remote_filesystem(argv, initialize_local_filesystem(argv));
|
||||
fixup_hosts();
|
||||
|
||||
/*
|
||||
* More environment: iocsh prompt and hostname
|
||||
@@ -602,11 +649,13 @@ Init (rtems_task_argument ignored)
|
||||
/*
|
||||
* Run the EPICS startup script
|
||||
*/
|
||||
printf ("***** Starting EPICS application *****\n");
|
||||
printf ("***** Preparing EPICS application *****\n");
|
||||
iocshRegisterRTEMS ();
|
||||
set_directory (argv[1]);
|
||||
epicsEnvSet ("IOC_STARTUP_SCRIPT", argv[1]);
|
||||
atexit(exitHandler);
|
||||
errlogFlush();
|
||||
printf ("***** Starting EPICS application *****\n");
|
||||
i = main ((sizeof argv / sizeof argv[0]) - 1, argv);
|
||||
printf ("***** IOC application terminating *****\n");
|
||||
epicsThreadSleep(1.0);
|
||||
|
||||
@@ -19,10 +19,23 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "valgrind/valgrind.h"
|
||||
|
||||
#ifndef NVALGRIND
|
||||
/* buffer around allocations to detect out of bounds access */
|
||||
#define REDZONE sizeof(double)
|
||||
#else
|
||||
#define REDZONE 0
|
||||
#endif
|
||||
|
||||
#define epicsExportSharedSymbols
|
||||
#include "epicsMutex.h"
|
||||
#include "ellLib.h"
|
||||
#include "dbmf.h"
|
||||
/*
|
||||
#define DBMF_FREELIST_DEBUG 1
|
||||
*/
|
||||
#ifndef DBMF_FREELIST_DEBUG
|
||||
|
||||
/*Default values for dblfInit */
|
||||
#define DBMF_SIZE 64
|
||||
@@ -66,13 +79,17 @@ int epicsShareAPI dbmfInit(size_t size, int chunkItems)
|
||||
pdbmfPvt->lock = epicsMutexMustCreate();
|
||||
/*allign to at least a double*/
|
||||
pdbmfPvt->size = size + size%sizeof(double);
|
||||
pdbmfPvt->allocSize = pdbmfPvt->size + sizeof(itemHeader);
|
||||
/* layout is
|
||||
* | itemHeader | REDZONE | size | REDZONE |
|
||||
*/
|
||||
pdbmfPvt->allocSize = pdbmfPvt->size + sizeof(itemHeader) + 2*REDZONE;
|
||||
pdbmfPvt->chunkItems = chunkItems;
|
||||
pdbmfPvt->chunkSize = pdbmfPvt->allocSize * pdbmfPvt->chunkItems;
|
||||
pdbmfPvt->nAlloc = 0;
|
||||
pdbmfPvt->nFree = 0;
|
||||
pdbmfPvt->nGtSize = 0;
|
||||
pdbmfPvt->freeList = NULL;
|
||||
VALGRIND_CREATE_MEMPOOL(pdbmfPvt, REDZONE, 0);
|
||||
return(0);
|
||||
}
|
||||
|
||||
@@ -90,51 +107,53 @@ void* epicsShareAPI dbmfMalloc(size_t size)
|
||||
pfreeList = &pdbmfPvt->freeList;
|
||||
if(*pfreeList == NULL) {
|
||||
int i;
|
||||
size_t nbytesTotal;
|
||||
size_t nbytesTotal;
|
||||
|
||||
if(dbmfDebug) printf("dbmfMalloc allocating new storage\n");
|
||||
nbytesTotal = pdbmfPvt->chunkSize + sizeof(chunkNode);
|
||||
nbytesTotal = pdbmfPvt->chunkSize + sizeof(chunkNode);
|
||||
pmem = (char *)malloc(nbytesTotal);
|
||||
if(!pmem) {
|
||||
if(!pmem) {
|
||||
epicsMutexUnlock(pdbmfPvt->lock);
|
||||
printf("dbmfMalloc malloc failed\n");
|
||||
return(NULL);
|
||||
}
|
||||
pchunkNode = (chunkNode *)(pmem + pdbmfPvt->chunkSize);
|
||||
pchunkNode->pchunk = pmem;
|
||||
pchunkNode->nNotFree=0;
|
||||
ellAdd(&pdbmfPvt->chunkList,&pchunkNode->node);
|
||||
for(i=0; i<pdbmfPvt->chunkItems; i++) {
|
||||
pitemHeader = (itemHeader *)pmem;
|
||||
pitemHeader->pchunkNode = pchunkNode;
|
||||
pnextFree = &pitemHeader->pnextFree;
|
||||
*pnextFree = *pfreeList; *pfreeList = (void *)pmem;
|
||||
pdbmfPvt->nFree++;
|
||||
pmem += pdbmfPvt->allocSize;
|
||||
}
|
||||
printf("dbmfMalloc malloc failed\n");
|
||||
return(NULL);
|
||||
}
|
||||
pchunkNode = (chunkNode *)(pmem + pdbmfPvt->chunkSize);
|
||||
pchunkNode->pchunk = pmem;
|
||||
pchunkNode->nNotFree=0;
|
||||
ellAdd(&pdbmfPvt->chunkList,&pchunkNode->node);
|
||||
for(i=0; i<pdbmfPvt->chunkItems; i++) {
|
||||
pitemHeader = (itemHeader *)pmem;
|
||||
pitemHeader->pchunkNode = pchunkNode;
|
||||
pnextFree = &pitemHeader->pnextFree;
|
||||
*pnextFree = *pfreeList; *pfreeList = (void *)pmem;
|
||||
pdbmfPvt->nFree++;
|
||||
pmem += pdbmfPvt->allocSize;
|
||||
}
|
||||
}
|
||||
if(size<=pdbmfPvt->size) {
|
||||
pnextFree = *pfreeList; *pfreeList = *pnextFree;
|
||||
pmem = (void *)pnextFree;
|
||||
pmem = (void *)pnextFree;
|
||||
pdbmfPvt->nAlloc++; pdbmfPvt->nFree--;
|
||||
pitemHeader = (itemHeader *)pnextFree;
|
||||
pitemHeader->pchunkNode->nNotFree += 1;
|
||||
pitemHeader = (itemHeader *)pnextFree;
|
||||
pitemHeader->pchunkNode->nNotFree += 1;
|
||||
} else {
|
||||
pmem = malloc(sizeof(itemHeader) + size);
|
||||
if(!pmem) {
|
||||
epicsMutexUnlock(pdbmfPvt->lock);
|
||||
printf("dbmfMalloc malloc failed\n");
|
||||
return(NULL);
|
||||
}
|
||||
pdbmfPvt->nAlloc++;
|
||||
pdbmfPvt->nGtSize++;
|
||||
pitemHeader = (itemHeader *)pmem;
|
||||
pitemHeader->pchunkNode = NULL;
|
||||
if(dbmfDebug) printf("dbmfMalloc: size %lu mem %p\n",
|
||||
pmem = malloc(sizeof(itemHeader) + 2*REDZONE + size);
|
||||
if(!pmem) {
|
||||
epicsMutexUnlock(pdbmfPvt->lock);
|
||||
printf("dbmfMalloc malloc failed\n");
|
||||
return(NULL);
|
||||
}
|
||||
pdbmfPvt->nAlloc++;
|
||||
pdbmfPvt->nGtSize++;
|
||||
pitemHeader = (itemHeader *)pmem;
|
||||
pitemHeader->pchunkNode = NULL; /* not part of free list */
|
||||
if(dbmfDebug) printf("dbmfMalloc: size %lu mem %p\n",
|
||||
(unsigned long)size,pmem);
|
||||
}
|
||||
epicsMutexUnlock(pdbmfPvt->lock);
|
||||
return((void *)(pmem + sizeof(itemHeader)));
|
||||
pmem += sizeof(itemHeader) + REDZONE;
|
||||
VALGRIND_MEMPOOL_ALLOC(pdbmfPvt, pmem, size);
|
||||
return((void *)pmem);
|
||||
}
|
||||
|
||||
char * epicsShareAPI dbmfStrdup(unsigned char *str)
|
||||
@@ -153,23 +172,24 @@ void epicsShareAPI dbmfFree(void* mem)
|
||||
|
||||
if(!mem) return;
|
||||
if(!pdbmfPvt) {
|
||||
printf("dbmfFree called but dbmfInit never called\n");
|
||||
return;
|
||||
printf("dbmfFree called but dbmfInit never called\n");
|
||||
return;
|
||||
}
|
||||
pmem -= sizeof(itemHeader);
|
||||
VALGRIND_MEMPOOL_FREE(pdbmfPvt, mem);
|
||||
pmem -= sizeof(itemHeader) + REDZONE;
|
||||
epicsMutexMustLock(pdbmfPvt->lock);
|
||||
pitemHeader = (itemHeader *)pmem;
|
||||
if(!pitemHeader->pchunkNode) {
|
||||
if(dbmfDebug) printf("dbmfGree: mem %p\n",pmem);
|
||||
free((void *)pmem); pdbmfPvt->nAlloc--;
|
||||
if(dbmfDebug) printf("dbmfGree: mem %p\n",pmem);
|
||||
free((void *)pmem); pdbmfPvt->nAlloc--;
|
||||
}else {
|
||||
void **pfreeList = &pdbmfPvt->freeList;
|
||||
void **pnextFree = &pitemHeader->pnextFree;
|
||||
|
||||
pchunkNode = pitemHeader->pchunkNode;
|
||||
pchunkNode->nNotFree--;
|
||||
pchunkNode->nNotFree--;
|
||||
*pnextFree = *pfreeList; *pfreeList = pnextFree;
|
||||
pdbmfPvt->nAlloc--; pdbmfPvt->nFree++;
|
||||
pdbmfPvt->nAlloc--; pdbmfPvt->nFree++;
|
||||
}
|
||||
epicsMutexUnlock(pdbmfPvt->lock);
|
||||
}
|
||||
@@ -216,23 +236,44 @@ void epicsShareAPI dbmfFreeChunks(void)
|
||||
chunkNode *pnext;;
|
||||
|
||||
if(!pdbmfPvt) {
|
||||
printf("dbmfFreeChunks called but dbmfInit never called\n");
|
||||
return;
|
||||
printf("dbmfFreeChunks called but dbmfInit never called\n");
|
||||
return;
|
||||
}
|
||||
epicsMutexMustLock(pdbmfPvt->lock);
|
||||
if(pdbmfPvt->nFree
|
||||
!= (pdbmfPvt->chunkItems * ellCount(&pdbmfPvt->chunkList))) {
|
||||
printf("dbmfFinish: not all free\n");
|
||||
!= (pdbmfPvt->chunkItems * ellCount(&pdbmfPvt->chunkList))) {
|
||||
printf("dbmfFinish: not all free\n");
|
||||
epicsMutexUnlock(pdbmfPvt->lock);
|
||||
return;
|
||||
return;
|
||||
}
|
||||
pchunkNode = (chunkNode *)ellFirst(&pdbmfPvt->chunkList);
|
||||
while(pchunkNode) {
|
||||
pnext = (chunkNode *)ellNext(&pchunkNode->node);
|
||||
ellDelete(&pdbmfPvt->chunkList,&pchunkNode->node);
|
||||
free(pchunkNode->pchunk);
|
||||
pchunkNode = pnext;
|
||||
pnext = (chunkNode *)ellNext(&pchunkNode->node);
|
||||
ellDelete(&pdbmfPvt->chunkList,&pchunkNode->node);
|
||||
free(pchunkNode->pchunk);
|
||||
pchunkNode = pnext;
|
||||
}
|
||||
pdbmfPvt->nFree = 0; pdbmfPvt->freeList = NULL;
|
||||
epicsMutexUnlock(pdbmfPvt->lock);
|
||||
}
|
||||
|
||||
#else /* DBMF_FREELIST_DEBUG */
|
||||
|
||||
int epicsShareAPI dbmfInit(size_t size, int chunkItems)
|
||||
{ return 0; }
|
||||
|
||||
void* epicsShareAPI dbmfMalloc(size_t size)
|
||||
{ return malloc(size); }
|
||||
|
||||
char * epicsShareAPI dbmfStrdup(unsigned char *str)
|
||||
{ return strdup((char*)str); }
|
||||
|
||||
void epicsShareAPI dbmfFree(void* mem)
|
||||
{ free(mem); }
|
||||
|
||||
int epicsShareAPI dbmfShow(int level)
|
||||
{ return 0; }
|
||||
|
||||
void epicsShareAPI dbmfFreeChunks(void) {}
|
||||
|
||||
#endif /* DBMF_FREELIST_DEBUG */
|
||||
|
||||
2
src/libCom/env/RULES
vendored
2
src/libCom/env/RULES
vendored
@@ -9,4 +9,4 @@
|
||||
|
||||
envData.c: $(LIBCOM)/env/envDefs.h $(LIBCOM)/env/bldEnvData.pl \
|
||||
$(CONFIG)/CONFIG_ENV $(CONFIG)/CONFIG_SITE_ENV
|
||||
$(PERL) $(LIBCOM)/env/bldEnvData.pl $(INSTALL_QUIETLY) $(CONFIG)
|
||||
$(PERL) $(LIBCOM)/env/bldEnvData.pl $(QUIET_FLAG) $(CONFIG)
|
||||
|
||||
4
src/libCom/env/bldEnvData.pl
vendored
4
src/libCom/env/bldEnvData.pl
vendored
@@ -32,8 +32,8 @@ our $opt_o = 'envData.c';
|
||||
$Getopt::Std::OUTPUT_HELP_VERSION = 1;
|
||||
$Text::Wrap::columns = 75;
|
||||
|
||||
&HELP_MESSAGE unless getopts('ho:q') && @ARGV == 1;
|
||||
&HELP_MESSAGE if $opt_h;
|
||||
HELP_MESSAGE() unless getopts('ho:q') && @ARGV == 1;
|
||||
HELP_MESSAGE() if $opt_h;
|
||||
|
||||
my $config = AbsPath(shift);
|
||||
my $env_defs = AbsPath('../env/envDefs.h');
|
||||
|
||||
1
src/libCom/env/envDefs.h
vendored
1
src/libCom/env/envDefs.h
vendored
@@ -69,6 +69,7 @@ epicsShareExtern const ENV_PARAM EPICS_CMD_PROTO_PORT;
|
||||
epicsShareExtern const ENV_PARAM EPICS_AR_PORT;
|
||||
epicsShareExtern const ENV_PARAM IOCSH_PS1;
|
||||
epicsShareExtern const ENV_PARAM IOCSH_HISTSIZE;
|
||||
epicsShareExtern const ENV_PARAM IOCSH_HISTEDIT_DISABLE;
|
||||
|
||||
epicsShareExtern const ENV_PARAM *env_param_list[];
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@ Com_SRCS += errSymTbl.c
|
||||
# For bldErrSymTbl
|
||||
#
|
||||
ERR_S_FILES += $(LIBCOM)/osi/devLib.h
|
||||
ERR_S_FILES += $(LIBCOM)/osi/epicsTime.h
|
||||
ERR_S_FILES += $(LIBCOM)/as/asLib.h
|
||||
ERR_S_FILES += $(LIBCOM)/misc/epicsStdlib.h
|
||||
ERR_S_FILES += $(LIBCOM)/pool/epicsThreadPool.h
|
||||
|
||||
@@ -50,6 +50,7 @@ extern "C" {
|
||||
#define M_gddFuncTbl (526 <<16) /*gdd jump table*/
|
||||
#define M_stdlib (527 <<16) /*EPICS Standard library*/
|
||||
#define M_pool (528 <<16) /*Thread pool*/
|
||||
#define M_time (529 <<16) /*epicsTime*/
|
||||
|
||||
epicsShareFunc void epicsShareAPI errSymLookup(long status, char *pBuf, unsigned bufLength);
|
||||
epicsShareFunc void epicsShareAPI errSymTest(unsigned short modnum, unsigned short begErrNum, unsigned short endErrNum);
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
# This is a Makefile fragment, see src/libCom/Makefile.
|
||||
|
||||
SRC_DIRS += $(LIBCOM)/freeList
|
||||
|
||||
INC += freeList.h
|
||||
|
||||
Com_SRCS += freeListLib.c
|
||||
HTMLS += freeList/freeList.html
|
||||
|
||||
@@ -1,85 +0,0 @@
|
||||
/*************************************************************************\
|
||||
* Copyright (c) 2002 The University of Chicago, as Operator of Argonne
|
||||
* National Laboratory.
|
||||
* Copyright (c) 2002 The Regents of the University of California, as
|
||||
* Operator of Los Alamos National Laboratory.
|
||||
* EPICS BASE Versions 3.13.7
|
||||
* and higher are distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
\*************************************************************************/
|
||||
<HTML>
|
||||
<BODY>
|
||||
<PRE>
|
||||
<!-- Manpage converted by man2html 3.0.1 -->
|
||||
|
||||
</PRE>
|
||||
<H2>VERSION $Id: freeList.3,v 1.2 1997/04/10 19:47:35</H2><PRE>
|
||||
|
||||
</PRE>
|
||||
<H2>NAME</H2><PRE>
|
||||
freeList.c - General Purpose memory free list library
|
||||
|
||||
|
||||
</PRE>
|
||||
<H2>SYNOPSIS</H2><PRE>
|
||||
freeListInitPvt - Initialize a free list
|
||||
freeListCalloc - Allocate and initialize to zero a new element
|
||||
freeListMalloc - Allocate a new element
|
||||
freeListFree - Free an element,i.e. put on free list
|
||||
|
||||
|
||||
void freeListInitPvt(void **ppvt,int size,int nmalloc);
|
||||
void *freeListCalloc(void *pvt);
|
||||
void *freeListMalloc(void *pvt);
|
||||
size_t freeListItemsAvail(void *pvt);
|
||||
void freeListFree(void *pvt,void*pmem);
|
||||
|
||||
where :
|
||||
|
||||
pvt - For private use by library. Caller must provide a "void *pvt"
|
||||
size - Size in butes of each element. Note that all elements must be same size
|
||||
nmalloc - Number of elements top allocate when regular malloc must be called.
|
||||
|
||||
|
||||
|
||||
</PRE>
|
||||
<H2>DESCRIPTION</H2><PRE>
|
||||
This library can be used to allocate and free fixed size
|
||||
memory elements. Free elements are maintained on a free
|
||||
list rather then being returned to the heap via calls to
|
||||
free. When it is necessary to call malloc, memory can be
|
||||
allocated in multiples of the element size.
|
||||
|
||||
|
||||
</PRE>
|
||||
<H2>RETURNS</H2><PRE>
|
||||
freeListCalloc and freeListMalloc return address of element allocated
|
||||
or NULL if no more memory could be obtained via call to malloc
|
||||
|
||||
|
||||
</PRE>
|
||||
<H2>INCLUDES</H2><PRE>
|
||||
freeLib.h
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</PRE>
|
||||
<HR>
|
||||
<ADDRESS>
|
||||
Man(1) output converted with
|
||||
<a href="http://www.oac.uci.edu/indiv/ehood/man2html.html">man2html</a>
|
||||
</ADDRESS>
|
||||
</BODY>
|
||||
</HTML>
|
||||
132
src/libCom/freeList/freeListLib.c
Normal file → Executable file
132
src/libCom/freeList/freeListLib.c
Normal file → Executable file
@@ -15,6 +15,15 @@
|
||||
#include <stdlib.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#include "valgrind/valgrind.h"
|
||||
|
||||
#ifndef NVALGRIND
|
||||
/* buffer around allocations to detect out of bounds access */
|
||||
#define REDZONE sizeof(double)
|
||||
#else
|
||||
#define REDZONE 0
|
||||
#endif
|
||||
|
||||
#define epicsExportSharedSymbols
|
||||
#include "cantProceed.h"
|
||||
#include "epicsMutex.h"
|
||||
@@ -47,6 +56,7 @@ epicsShareFunc void epicsShareAPI
|
||||
pfl->nBlocksAvailable = 0u;
|
||||
pfl->lock = epicsMutexMustCreate();
|
||||
*ppvt = (void *)pfl;
|
||||
VALGRIND_CREATE_MEMPOOL(pfl, REDZONE, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -54,13 +64,13 @@ epicsShareFunc void * epicsShareAPI freeListCalloc(void *pvt)
|
||||
{
|
||||
FREELISTPVT *pfl = pvt;
|
||||
# ifdef EPICS_FREELIST_DEBUG
|
||||
return callocMustSucceed(1,pfl->size,"freeList Debug Calloc");
|
||||
return callocMustSucceed(1,pfl->size,"freeList Debug Calloc");
|
||||
# else
|
||||
void *ptemp;
|
||||
void *ptemp;
|
||||
|
||||
ptemp = freeListMalloc(pvt);
|
||||
if(ptemp) memset((char *)ptemp,0,pfl->size);
|
||||
return(ptemp);
|
||||
ptemp = freeListMalloc(pvt);
|
||||
if(ptemp) memset((char *)ptemp,0,pfl->size);
|
||||
return(ptemp);
|
||||
# endif
|
||||
}
|
||||
|
||||
@@ -68,45 +78,56 @@ epicsShareFunc void * epicsShareAPI freeListMalloc(void *pvt)
|
||||
{
|
||||
FREELISTPVT *pfl = pvt;
|
||||
# ifdef EPICS_FREELIST_DEBUG
|
||||
return callocMustSucceed(1,pfl->size,"freeList Debug Malloc");
|
||||
return callocMustSucceed(1,pfl->size,"freeList Debug Malloc");
|
||||
# else
|
||||
void *ptemp;
|
||||
void **ppnext;
|
||||
allocMem *pallocmem;
|
||||
int i;
|
||||
void *ptemp;
|
||||
void **ppnext;
|
||||
allocMem *pallocmem;
|
||||
int i;
|
||||
|
||||
epicsMutexMustLock(pfl->lock);
|
||||
ptemp = pfl->head;
|
||||
epicsMutexMustLock(pfl->lock);
|
||||
ptemp = pfl->head;
|
||||
if(ptemp==0) {
|
||||
/* layout of each block. nmalloc+1 REDZONEs for nmallocs.
|
||||
* The first sizeof(void*) bytes are used to store a pointer
|
||||
* to the next free block.
|
||||
*
|
||||
* | RED | size0 ------ | RED | size1 | ... | RED |
|
||||
* | | next | ----- |
|
||||
*/
|
||||
ptemp = (void *)malloc(pfl->nmalloc*(pfl->size+REDZONE)+REDZONE);
|
||||
if(ptemp==0) {
|
||||
ptemp = (void *)malloc(pfl->nmalloc*pfl->size);
|
||||
if(ptemp==0) {
|
||||
epicsMutexUnlock(pfl->lock);
|
||||
return(0);
|
||||
}
|
||||
pallocmem = (allocMem *)calloc(1,sizeof(allocMem));
|
||||
if(pallocmem==0) {
|
||||
epicsMutexUnlock(pfl->lock);
|
||||
free(ptemp);
|
||||
return(0);
|
||||
}
|
||||
pallocmem->memory = ptemp;
|
||||
if(pfl->mallochead)
|
||||
pallocmem->next = pfl->mallochead;
|
||||
pfl->mallochead = pallocmem;
|
||||
for(i=0; i<pfl->nmalloc; i++) {
|
||||
ppnext = ptemp;
|
||||
*ppnext = pfl->head;
|
||||
pfl->head = ptemp;
|
||||
ptemp = ((char *)ptemp) + pfl->size;
|
||||
}
|
||||
ptemp = pfl->head;
|
||||
pfl->nBlocksAvailable += pfl->nmalloc;
|
||||
epicsMutexUnlock(pfl->lock);
|
||||
return(0);
|
||||
}
|
||||
ppnext = pfl->head;
|
||||
pfl->head = *ppnext;
|
||||
pfl->nBlocksAvailable--;
|
||||
epicsMutexUnlock(pfl->lock);
|
||||
return(ptemp);
|
||||
pallocmem = (allocMem *)calloc(1,sizeof(allocMem));
|
||||
if(pallocmem==0) {
|
||||
epicsMutexUnlock(pfl->lock);
|
||||
free(ptemp);
|
||||
return(0);
|
||||
}
|
||||
pallocmem->memory = ptemp; /* real allocation */
|
||||
ptemp = REDZONE + (char *) ptemp; /* skip first REDZONE */
|
||||
if(pfl->mallochead)
|
||||
pallocmem->next = pfl->mallochead;
|
||||
pfl->mallochead = pallocmem;
|
||||
for(i=0; i<pfl->nmalloc; i++) {
|
||||
ppnext = ptemp;
|
||||
VALGRIND_MEMPOOL_ALLOC(pfl, ptemp, sizeof(void*));
|
||||
*ppnext = pfl->head;
|
||||
pfl->head = ptemp;
|
||||
ptemp = ((char *)ptemp) + pfl->size+REDZONE;
|
||||
}
|
||||
ptemp = pfl->head;
|
||||
pfl->nBlocksAvailable += pfl->nmalloc;
|
||||
}
|
||||
ppnext = pfl->head;
|
||||
pfl->head = *ppnext;
|
||||
pfl->nBlocksAvailable--;
|
||||
epicsMutexUnlock(pfl->lock);
|
||||
VALGRIND_MEMPOOL_FREE(pfl, ptemp);
|
||||
VALGRIND_MEMPOOL_ALLOC(pfl, ptemp, pfl->size);
|
||||
return(ptemp);
|
||||
# endif
|
||||
}
|
||||
|
||||
@@ -114,17 +135,20 @@ epicsShareFunc void epicsShareAPI freeListFree(void *pvt,void*pmem)
|
||||
{
|
||||
FREELISTPVT *pfl = pvt;
|
||||
# ifdef EPICS_FREELIST_DEBUG
|
||||
memset ( pmem, 0xdd, pfl->size );
|
||||
free(pmem);
|
||||
memset ( pmem, 0xdd, pfl->size );
|
||||
free(pmem);
|
||||
# else
|
||||
void **ppnext;
|
||||
void **ppnext;
|
||||
|
||||
epicsMutexMustLock(pfl->lock);
|
||||
ppnext = pmem;
|
||||
*ppnext = pfl->head;
|
||||
pfl->head = pmem;
|
||||
pfl->nBlocksAvailable++;
|
||||
epicsMutexUnlock(pfl->lock);
|
||||
VALGRIND_MEMPOOL_FREE(pvt, pmem);
|
||||
VALGRIND_MEMPOOL_ALLOC(pvt, pmem, sizeof(void*));
|
||||
|
||||
epicsMutexMustLock(pfl->lock);
|
||||
ppnext = pmem;
|
||||
*ppnext = pfl->head;
|
||||
pfl->head = pmem;
|
||||
pfl->nBlocksAvailable++;
|
||||
epicsMutexUnlock(pfl->lock);
|
||||
# endif
|
||||
}
|
||||
|
||||
@@ -134,12 +158,14 @@ epicsShareFunc void epicsShareAPI freeListCleanup(void *pvt)
|
||||
allocMem *phead;
|
||||
allocMem *pnext;
|
||||
|
||||
VALGRIND_DESTROY_MEMPOOL(pvt);
|
||||
|
||||
phead = pfl->mallochead;
|
||||
while(phead) {
|
||||
pnext = phead->next;
|
||||
free(phead->memory);
|
||||
free(phead);
|
||||
phead = pnext;
|
||||
pnext = phead->next;
|
||||
free(phead->memory);
|
||||
free(phead);
|
||||
phead = pnext;
|
||||
}
|
||||
epicsMutexDestroy(pfl->lock);
|
||||
free(pvt);
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
# This is a Makefile fragment, see src/libCom/Makefile.
|
||||
|
||||
SRC_DIRS += $(LIBCOM)/gpHash
|
||||
|
||||
INC += gpHash.h
|
||||
|
||||
Com_SRCS += gpHashLib.c
|
||||
HTMLS += gpHash/gpHash.html
|
||||
|
||||
@@ -1,139 +0,0 @@
|
||||
/*************************************************************************\
|
||||
* Copyright (c) 2002 The University of Chicago, as Operator of Argonne
|
||||
* National Laboratory.
|
||||
* Copyright (c) 2002 The Regents of the University of California, as
|
||||
* Operator of Los Alamos National Laboratory.
|
||||
* EPICS BASE Versions 3.13.7
|
||||
* and higher are distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
\*************************************************************************/
|
||||
<HTML>
|
||||
<BODY>
|
||||
<PRE>
|
||||
<!-- Manpage converted by man2html 3.0.1 -->
|
||||
|
||||
</PRE>
|
||||
<H2>VERSION $Id: gpHash.3,v 1.2 1999/09/13 19:18:04</H2><PRE>
|
||||
|
||||
</PRE>
|
||||
<H2>NAME</H2><PRE>
|
||||
gpHash.c - General Purpose Hash Library
|
||||
|
||||
|
||||
</PRE>
|
||||
<H2>SYNOPSIS</H2><PRE>
|
||||
gphInitPvt - Initialize
|
||||
gphFind - Find an element taht has been hashed
|
||||
gphAdd - Add a new entry
|
||||
gphDelete - Delete an entry
|
||||
gphFreeMem - Free all memory allocated by gpHash
|
||||
gphDump - Dump current members
|
||||
|
||||
|
||||
typedef struct{
|
||||
ELLNODE node;
|
||||
char *name; /*address of name placed in directory*/
|
||||
void *pvtid; /*private name for subsystem user*/
|
||||
void *userPvt; /*private for user*/
|
||||
} GPHENTRY;
|
||||
|
||||
void gphInitPvt(void **ppvt);
|
||||
GPHENTRY *gphFind(void *pvt,const char *name,void *pvtid);
|
||||
GPHENTRY *gphAdd(void *pvt,const char *name,void *pvtid);
|
||||
void gphDelete(void *pvt,const char *name,void *pvtid);
|
||||
void gphFreeMem(void *pvt);
|
||||
void gphDump(void *pvt);
|
||||
|
||||
|
||||
where :
|
||||
|
||||
pvt - For private use by library. Caller must provide a "void *pvt"
|
||||
name - The character string that will be hashed and added to table
|
||||
pvtid - The name plus value of this pointer constitute unique entry
|
||||
|
||||
|
||||
|
||||
</PRE>
|
||||
<H2>DESCRIPTION</H2><PRE>
|
||||
This library provides a general purpose directory of names
|
||||
that is accessed via a hash table. The hash table contains
|
||||
256 entries. Each entry is a list of members that hash to
|
||||
the same value. The user can maintain seperate directories
|
||||
via the same table by having a different pvtid for each
|
||||
directory.
|
||||
|
||||
|
||||
</PRE>
|
||||
<H2>RETURNS</H2><PRE>
|
||||
gphFind returns the address of the GPHENTRY describing the entry or NULL if name was not found.
|
||||
gphAdd returns the address of the new GPHENTRY describing the entry or NULL if name was already
|
||||
present.
|
||||
|
||||
|
||||
</PRE>
|
||||
<H2>INCLUDES</H2><PRE>
|
||||
gpHash.h
|
||||
|
||||
|
||||
|
||||
</PRE>
|
||||
<H2>REFERENCE</H2><PRE>
|
||||
Fast Hashing of Variable Length Text Strings, Peter K. Pear-
|
||||
son, Communications of the ACM, June 1990
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</PRE>
|
||||
<HR>
|
||||
<ADDRESS>
|
||||
Man(1) output converted with
|
||||
<a href="http://www.oac.uci.edu/indiv/ehood/man2html.html">man2html</a>
|
||||
</ADDRESS>
|
||||
</BODY>
|
||||
</HTML>
|
||||
@@ -611,12 +611,13 @@ iocshBody (const char *pathname, const char *commandLine, const char *macros)
|
||||
|
||||
/*
|
||||
* Ignore comment lines other than to echo
|
||||
* them if they came from a script. This
|
||||
* avoids macLib errors from comments.
|
||||
* them if they came from a script (disable echoing
|
||||
* with '#-'). This avoids macLib errors from comments.
|
||||
*/
|
||||
if (c == '#') {
|
||||
if ((prompt == NULL) && (commandLine == NULL))
|
||||
puts(raw);
|
||||
if (raw[icin + 1] != '-')
|
||||
puts(raw);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -635,10 +636,12 @@ iocshBody (const char *pathname, const char *commandLine, const char *macros)
|
||||
}
|
||||
|
||||
/*
|
||||
* Echo non-empty lines read from a script
|
||||
* Echo non-empty lines read from a script.
|
||||
* Comments delineated with '#-' aren't echoed.
|
||||
*/
|
||||
if ((prompt == NULL) && *line && (commandLine == NULL))
|
||||
puts(line);
|
||||
if ((c != '#') || (line[icin + 1] != '-'))
|
||||
puts(line);
|
||||
|
||||
/*
|
||||
* Ignore lines that became a comment or empty after macro expansion
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
# This is a Makefile fragment, see src/libCom/Makefile.
|
||||
|
||||
MAKEVERSION = $(LIBCOM)/misc/makeEpicsVersion.pl
|
||||
MAKEVERSION_FLAGS = -o $(notdir $@) $(INSTALL_QUIETLY)
|
||||
MAKEVERSION_FLAGS = -o $(notdir $@) $(QUIET_FLAG)
|
||||
MAKEVERSION_FLAGS += $(if $(EPICS_SITE_VERSION),-v "$(EPICS_SITE_VERSION)")
|
||||
|
||||
$(COMMON_DIR)/epicsVersion.h: $(CONFIG)/CONFIG_BASE_VERSION \
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user