Compare commits

..

79 Commits

Author SHA1 Message Date
Ralph Lange
65574b5971 Update CONFIG_BASE_VERSION for 3.15.4, remove "not released" from RELEASE_NOTES 2016-05-27 08:54:04 +02:00
Andrew Johnson
4284222b54 Convert GUI_ promptgroup strings to new-style 2016-05-26 16:15:48 +02:00
Ralph Lange
54bab1e2f0 pcas: fix compiler warning (gcc 5.3.1) 2016-05-26 14:14:56 +02:00
Andrew Johnson
df200de27f Fix lsi/lso puts with DBF_STRING data 2016-05-26 13:49:18 +02:00
Ralph Lange
22d6ebe7e1 Merge changes from 3.14 branch up to rev 12642 2016-05-26 13:18:38 +02:00
Ralph Lange
9c3ed1bfe2 catools: fix escape handling when sending long strings 2016-05-26 13:13:29 +02:00
Ralph Lange
23fd4e202b ca: Fix client bug (race condition) in ipAddrToAsciiEngine 2016-05-25 12:26:32 +02:00
Andrew Johnson
a5b8b0b890 Use fabs() in calcPerform 2016-05-24 11:12:25 +02:00
Andrew Johnson
988614ae8a Reset SNAPSHOT after -rc1 tagged 2016-05-22 06:06:53 +02:00
Ralph Lange
fe887b4f83 Update CONFIG_BASE_VERSION for 3.15.4-rc1 2016-05-20 16:37:06 +02:00
Andrew Johnson
7be7ad2768 Merge changes from 3.14 branch, to revno 12640 2016-05-18 10:44:16 -05:00
Andrew Johnson
6c9555310a Update EPICS_TIMEZONE with info for 2016-2021 2016-05-18 10:26:04 -05:00
Andrew Johnson
4ccc2e9d3a Merged my devEnviron branch 2016-05-18 09:48:22 -05:00
Andrew Johnson
122fb6c731 New "getenv" device support for string input types 2016-05-17 19:09:48 -05:00
Andrew Johnson
3b8fd13152 libCom: NTP Time Provider adjusts to OS tick rate changes
Allow the NTP Time provider (used on VxWorks and RTEMS only)
to adapt to changes in the OS clock tick rate after the provider
has been initialized.

Changing the tick rate after iocInit() is not advisable,
other software might still misbehave if initialized before
an OS tick rate change.

Back-ported from the 3.15 branch (commit 12468).
2016-05-17 16:03:27 -05:00
Andrew Johnson
04a9fdb4e3 Clean up warnings from gcc 5.2 2016-05-16 17:02:34 -05:00
Andrew Johnson
3c61880d79 Allow some callback slow-ups before failing tests
The tests occasionally fail on Windows because the server
is busy; this will accept up to 4 slow callbacks without
them causing the build to fail.
2016-05-16 16:55:50 -05:00
Andrew Johnson
8081d3ada4 Merged Till's fix for lp:1581212, modified 2016-05-13 14:48:08 -05:00
Andrew Johnson
6d35ee9c3c Move dbLock.h *after* epicsExportSharedSymbols 2016-05-13 13:40:37 -05:00
Andrew Johnson
b5a0657adc Applied Till's fix for lp:1581212 2016-05-13 13:30:55 -05:00
Andrew Johnson
b87f3eaaee Merged changes from 3.14 branch to revno 12636 2016-05-13 13:12:08 -05:00
Andrew Johnson
6db0e13809 Added CROSS_COMPILER_RUNTEST_ARCHS 2016-05-13 13:00:48 -05:00
Michael Davidsaver
0f6c997288 osdNetIntf: ignore 0.0.0.0 as a broadcast address
It seems in some situations Linux (at least)
sets IFF_BROADCAST bug leaves the bcast address
set to 0.0.0.0.

Fixes lp:1581505
2016-05-13 09:31:26 -04:00
Michael Davidsaver
704c748fbd Revert "libCom/osi: osiSockDiscoverBroadcastAddresses() finds 127.255.255.255"
This reverts commit e805abe971.
2016-05-13 09:14:20 -04:00
Andrew Johnson
82456f83ee Fix Menu declaration test 2016-05-12 16:35:54 -05:00
Andrew Johnson
e98a6bbafa Make <menu>_NUM_CHOICES not an enum tag
Fixes lp:1580972
2016-05-12 15:05:18 -05:00
Andrew Johnson
9b51444fb7 Fix data types and macro name 2016-05-04 18:15:54 -05:00
Andrew Johnson
3be97865b3 Warn instead of building cap5 if xsubpp is missing 2016-05-04 14:41:51 -05:00
Andrew Johnson
67097456e3 Add additional RELEASE file checks
These checks are important for mixing Debian modules with
privately-built applications.
2016-05-03 12:26:40 -05:00
Andrew Johnson
3c8af4c571 Catch bad attribute names/values to prevent a segfault 2016-05-02 11:38:51 -05:00
Michael Davidsaver
26c04844cf add test for lp:1577108 2016-05-01 13:30:45 -04:00
Michael Davidsaver
a3d981ad0a dbLink.c: fix dbGetLink for array of length 1 2016-05-01 13:30:45 -04:00
Ralph Lange
924aa2f93b Set Base version to -pre1-DEV 2016-04-29 12:20:35 +02:00
Ralph Lange
457fb8fa13 Update CONFIG_BASE_VERSION for 3.15.4-pre1 2016-04-29 11:34:05 +02:00
Ralph Lange
b8b259de6f Merge Andrew's libs-prereq branch 2016-04-29 10:42:03 +02:00
Andrew Johnson
7ce8e5ea01 Fix RTEMS Makefile issue 2016-04-28 23:59:39 -05:00
Andrew Johnson
496414c88c Fix for VxWorks 5.5 compiler 2016-04-28 23:28:39 -05:00
Andrew Johnson
2acde8bac7 Merged changes from 3.14 branch to revno 12630 2016-04-28 21:39:55 -05:00
Andrew Johnson
2c69ddbee5 Merged my ioc-arch branch, manually re-based 2016-04-28 19:26:11 -05:00
Andrew Johnson
ba0d5f9443 Disallow single-quoted strings in Perl DBD parser 2016-04-28 19:03:17 -05:00
Andrew Johnson
08fd987c60 Perl script clean-up 2016-04-28 19:00:37 -05:00
Andrew Johnson
5a605fa0c3 Set various EPICS_VERSION_* environment variables
These are set at the same time as the ARCH variable.
Had to add EPICS_VERSION_SHORT and EPICS_VERSION_FULL to epicsVersion.h.

This uses more uniform names, should we change epicsVersion.h names?
We would continue to support the old names in parallel for some time.
2016-04-28 18:58:07 -05:00
Andrew Johnson
0e5dc2a21c More build parameters.
Renamed EPICS_TARGET_ARCH to EPICS_BUILD_TARGET_ARCH
Added EPICS_BUILD_OS_CLASS and EPICS_BUILD_COMPILER_CLASS
2016-04-28 18:56:03 -05:00
Andrew Johnson
230603f4ac Remove ARCH from cdCommands and envPaths files
Set it in iocshRegisterCommon() instead.
Remove ARCH from iocBoot/ioc/Makefile@Common templates.
2016-04-28 18:50:41 -05:00
Andrew Johnson
396ff3c484 Add EPICS_TARGET_ARCH parameter
Set its default value automatically in the generated envData.c
2016-04-28 18:49:10 -05:00
Andrew Johnson
09fbeaf6d3 Merged Ralph's new-promptgroups branch 2016-04-28 18:27:15 -05:00
Andrew Johnson
b4a8a1ac98 Fix dbFreeBase() issue causing segfaults 2016-04-28 18:10:26 -05:00
Andrew Johnson
ee87aecd6e Always have linker search INSTALL_LIB 2016-04-28 14:32:21 -05:00
Andrew Johnson
054a234c70 Parentheses in macro 2016-04-28 13:27:47 -05:00
Andrew Johnson
24ddcd2524 Fix epicsTimeZoneTest.cpp => .c and remove conditional 2016-04-27 12:09:10 -05:00
Michael Davidsaver
2226f3acb9 libCom/test: don't include epicsTimeZoneTest for vxWorks 2016-04-27 12:45:18 -04:00
Andrew Johnson
f0f89b4b1c Fix file paths in comments 2016-04-27 10:28:57 -05:00
Andrew Johnson
fdda079b8f BSD's finite expects a double; make glibc 2.23 happy 2016-04-27 10:28:01 -05:00
Ralph Lange
1f36670175 dbStatic: remove obsolete GUIGROUPS_GBLSOURCE definition 2016-04-27 09:14:51 +02:00
Andrew Johnson
1ede873200 Fixed some space/tab issues in DBD files 2016-04-26 16:20:12 -05:00
Michael Davidsaver
51dd371784 rsrv: add rsrvCS_shutdown to avoid race in destroyAllChannels()
A race can occur between rsrv and cac by way of asLib
whereby casAccessRightsCB() is called after
a channel_in_use has been removed from
chanList/chanPendingUpdateARList.
casAccessRightsCB() would then attempt to remove
the node again, causing corruption.

Create a new rsrvCS_shutdown state when the
channel is not in either list.

Attempt to resolve lp:1571224
2016-04-26 17:06:22 -04:00
Andrew Johnson
72745d7b0c Only make runtests and tapfiles if T_A = EPICS_HOST_ARCH 2016-04-29 16:20:25 -05:00
Andrew Johnson
c933d77963 Add rule to install libraries before linking executables
The rule must be disabled for building libCom and gdd though.
2016-04-26 15:18:31 -05:00
Michael Davidsaver
93597e20d5 add epicsTimeZoneTest to libCom test harness 2016-04-26 09:59:22 -04:00
Michael Davidsaver
f0453faf36 libCom/test: add test for HST time zone
Test US/Hawaii zone which does not observe daylight saving time.
2016-04-26 09:28:09 -04:00
Ralph Lange
473992cfb9 Update release notes 2016-04-26 13:47:12 +02:00
Ralph Lange
6a362f467d rec: implement new promptgroup (gui_group) choices 2016-04-26 13:28:28 +02:00
Andrew Johnson
af07e0fd51 Fix oops in last commit 2016-04-25 12:24:05 -05:00
Andrew Johnson
de38b80795 Apply partial solution for Windows builds of Cap5 2016-04-25 11:54:17 -05:00
Ralph Lange
7bb27ad3e7 dbStatic: deprecate guigroup.h; remove from includes 2016-04-22 17:02:21 +02:00
Ralph Lange
1e1799c30e tools: change RE to allow any value for promptgroup in the Perl DBD parser 2016-04-18 09:56:41 +02:00
Andrew Johnson
8358580190 Merged commit 12623 from 3.14 branch. 2016-04-15 17:38:41 -05:00
Andrew Johnson
24e39b252d Print RTEMS version at startup 2016-04-15 17:37:07 -05:00
Ralph Lange
ce7044c711 dbStatic: collect promptgroup enum choices when reading dbd 2016-04-06 15:38:35 +02:00
Andrew Johnson
177090e35e Merged fixes for lp:1558206 and lp:1563191 from 3.14 branch, revno 12622 2016-03-30 11:26:53 -05:00
Andrew Johnson
430da57a35 Release notes for lp:1563191 2016-03-30 11:00:48 -05:00
Andrew Johnson
d2d637d0c2 Prevent string overflow in recGblInitConstantLink() 2016-03-29 12:04:08 -05:00
Michael Davidsaver
106fae3b26 dbStatic: prevent overflow in dbPutString()
The bounds check should be before the string
copy.  Also zero the last element out of
paranoia (should already be zero).

Fix lp:1563191
2016-03-29 17:36:39 +09:00
Andrew Johnson
3179e65791 Clean-up after lp:1558206 fix 2016-03-16 18:15:14 -05:00
Michael Davidsaver
6b9bfb09a5 pWaitRelease in wrong place
waitRelease==false indicates that pThread
has not be delete'd
2016-03-16 17:43:52 -04:00
Michael Davidsaver
4e312b9f64 libCom: exitWait() from thread exit handler corrupts stack
The epicsThreadCallEntryPoint() function stores a pointer
to a local variable in epicsThread::pWaitReleaseFlag.
Calling epicsAtThreadExit::exitWait() from that thread's
epicsAtThreadExit() handler writes to this pointer
after epicsThreadCallEntryPoint() has returned.
Thus corrupting the stack.

Set pWaitReleaseFlag=NULL before return to prevent this.

fixes lp:1558206
2016-03-16 14:40:37 -04:00
Andrew Johnson
59fea64390 Add missing dependencies for Test.dbd files 2016-03-16 13:10:17 -05:00
Andrew Johnson
f260124733 Ensure mbb* MASK calculated properly 2016-03-10 13:05:00 -06:00
Andrew Johnson
113076a009 Allow quotes in DBD argument strings 2016-03-03 11:19:22 -06:00
295 changed files with 7472 additions and 17283 deletions

10
README
View File

@@ -2,14 +2,14 @@
EPICS Base - the central core of a control system toolkit
---------------------------------------------------------
Copyright UChicago Argonne LLC, as Operator of Argonne
National Laboratory.
Copyright (c) 1991-2003 The University of Chicago, 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 is distributed subject to a Software License
Agreement found in the file LICENSE that is included with
this distribution.
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.
---------------------------------------------------------

View File

@@ -3,12 +3,13 @@
# 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
# 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 directories
# Epics base directories
EPICS_BASE_HOST_BIN = $(EPICS_BASE)/bin/$(EPICS_HOST_ARCH)
EPICS_BASE_HOST_LIB = $(EPICS_BASE)/lib/$(EPICS_HOST_ARCH)
@@ -18,12 +19,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
@@ -41,11 +42,13 @@ 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
BASE_CPPFLAGS = $(OSITHREAD_DEFAULT_STACK_FLAGS_$(OSITHREAD_USE_DEFAULT_STACK))
OSITHREAD_DEFAULT_STACK_FLAGS_NO =
BASE_CPPFLAGS += $(OSITHREAD_DEFAULT_STACK_FLAGS_$(OSITHREAD_USE_DEFAULT_STACK))
#---------------------------------------------------------------
# Where to find the installed build tools
@@ -56,7 +59,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
@@ -65,11 +68,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 = $(PERL) $(TOOLS)/installEpics.pl $(QUIET_FLAG)
INSTALL_QUIETLY := $(if $(findstring s,$(MAKEFLAGS)),-q,)
INSTALL = $(PERL) $(TOOLS)/installEpics.pl $(INSTALL_QUIETLY)
INSTALL_PRODUCT = $(INSTALL)
INSTALL_LIBRARY = $(INSTALL)
@@ -79,7 +82,7 @@ MKMF = $(PERL) $(TOOLS)/mkmf.pl
REPLACEVAR = $(PERL) $(TOOLS)/replaceVAR.pl
#---------------------------------------------------------------
# Our versions of lex (flex) and yacc (antelope)
# private versions of lex/yacc from EPICS
EYACC = $(TOOLS)/antelope$(HOSTEXE)
ELEX = $(TOOLS)/e_flex$(HOSTEXE) -S$(EPICS_BASE)/include/flex.skel.static
@@ -87,6 +90,28 @@ YACC = $(EYACC)
LEX = $(ELEX)
#---------------------------------------------------------------
# The 3.15 version of msi supports new options
# Our use of msi is incompatible with older versions
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

View File

@@ -17,37 +17,26 @@
# EPICS_SITE_VERSION is defined in CONFIG_SITE for sites that want a local
# version number to be included in the reported version string.
# 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.
# 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.
BASE_3_14 = NO
BASE_3_15 = NO
BASE_3_16 = YES
BASE_3_15 = YES
# EPICS_VERSION must be a number >0 and <256
EPICS_VERSION = 3
# EPICS_REVISION must be a number >=0 and <256
EPICS_REVISION = 16
EPICS_REVISION = 15
# EPICS_MODIFICATION must be a number >=0 and <256
EPICS_MODIFICATION = 0
EPICS_MODIFICATION = 4
# EPICS_PATCH_LEVEL must be a number (win32 resource file requirement)
# Not included if zero
EPICS_PATCH_LEVEL = 1
EPICS_PATCH_LEVEL = 0
# This will end in -DEV between official releases
EPICS_DEV_SNAPSHOT=-DEV
#EPICS_DEV_SNAPSHOT=-DEV
#EPICS_DEV_SNAPSHOT=-pre1
#EPICS_DEV_SNAPSHOT=-pre1-DEV
#EPICS_DEV_SNAPSHOT=-pre2
@@ -56,7 +45,7 @@ EPICS_DEV_SNAPSHOT=-DEV
#EPICS_DEV_SNAPSHOT=-rc1-DEV
#EPICS_DEV_SNAPSHOT=-rc2
#EPICS_DEV_SNAPSHOT=-rc2-DEV
#EPICS_DEV_SNAPSHOT=
EPICS_DEV_SNAPSHOT=
# No changes should be needed below here

View File

@@ -40,6 +40,10 @@ 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
@@ -81,7 +85,6 @@ 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
@@ -331,14 +334,6 @@ 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

View File

@@ -118,6 +118,13 @@ CROSS_COMPILER_TARGET_ARCHS=
# configure/os/CONFIG_SITE.<host>.Common files instead.
CROSS_COMPILER_HOST_ARCHS=
# The 'make runtests' and 'make tapfiles' build targets normally only run
# self-tests for the EPICS_HOST_ARCH architecture. If the host can execute
# the self-test programs for any other cross-built architectures such as
# a -debug architecture, those architectures can be named here.
#
CROSS_COMPILER_RUNTEST_ARCHS=
# Build shared libraries (DLLs on Windows).
# Must be either YES or NO. Definitions in the target-specific
# os/CONFIG.Common.<target> and os/CONFIG_SITE.Common.<target> files may

View File

@@ -30,10 +30,8 @@
# local timezone info for vxWorks and RTEMS IOCs. The format is
# <name>::<minutesWest>:<start daylight>:<end daylight>
# where the start and end are mmddhh - that is month,day,hour
# e.g. for ANL in 2015: EPICS_TIMEZONE=CST/CDT::360:030802:110102
# e.g. for ANL in 2016: EPICS_TIMEZONE=CST/CDT::360:031302:110602
#
# DST for 2015 US: Mar 08 - Nov 01
# EU: Mar 29 - Oct 25
# DST for 2016 US: Mar 13 - Nov 06
# EU: Mar 27 - Oct 30
# DST for 2017 US: Mar 12 - Nov 05
@@ -42,11 +40,15 @@
# EU: Mar 25 - Oct 28
# DST for 2019 US: Mar 10 - Nov 03
# EU: Mar 31 - Oct 27
# (see: http://www.timeanddate.com/time/map/)
# DST for 2020 US: Mar 08 - Nov 01
# EU: Mar 29 - Oct 25
# DST for 2021 US: Mar 14 - Nov 07
# EU: Mar 28 - Oct 31
# (see: http://www.timeanddate.com/time/dst/2016.html etc. )
#
# These values are for 2015:
EPICS_TIMEZONE=CST/CDT::360:030802:110102
#EPICS_TIMEZONE=CET/CEST::-60:032902:102502
# These values are for 2016:
EPICS_TIMEZONE=CST/CDT::360:031302:110602
#EPICS_TIMEZONE=CET/CEST::-60:032702:103002
# EPICS_TS_NTP_INET
# NTP time server ip address. Uses boot host if not set.
@@ -57,11 +59,8 @@ 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

View File

@@ -8,12 +8,10 @@
#*************************************************************************
#RULES.Db
# Set db substitutions and template file suffixes
# Set db substitutions file suffix
SUBST_SUFFIX ?= .substitutions
TEMPL_SUFFIX ?= .template
#---------------------------------------------------------------
# vpath
##################################################### vpath
vpath %.pm $(USR_VPATH) $(SRC_DIRS) $(dir $(DBD))
vpath %.pod $(USR_VPATH) $(SRC_DIRS) $(dir $(DBD))
@@ -21,13 +19,12 @@ 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 %$(TEMPL_SUFFIX) $(USR_VPATH) $(SRC_DIRS) $(COMMON_DIR)
vpath %.template $(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)
@@ -36,15 +33,13 @@ 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
@@ -91,28 +86,31 @@ 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)/*$(TEMPL_SUFFIX)
$(COMMON_DIR)/*$(SUBST_SUFFIX) $(COMMON_DIR)/*.template
# 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 $(TEMPL_SUFFIX),$(addprefix ../,$(TEMPLATE2)))
TEMPLATE_FILENAME = $(firstword $(wildcard $($*_TEMPLATE) \
$(addprefix ../,$($*_TEMPLATE)) ../$*$(TEMPL_SUFFIX) $(TEMPLATE3) \
../template))
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
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)
@@ -125,8 +123,7 @@ ACF_INCLUDES = -I. $(TARGET_INCLUDES) $(USR_INCLUDES)\
ACFDEPENDS_CMD = $(MKMF) -m $@ $(ACF_INCLUDES) $(COMMONDEP_TARGET) $<
ACF_CMD = $(CPP) $(ACF_CPPFLAGS) $(ACF_INCLUDES) $< > $@
#---------------------------------------------------------------
# dependencies
##################################################### dependancies
HINC += $(addsuffix .h,$(DBDINC_NAME))
COMMON_DBDINC += $(addprefix $(COMMON_DIR)/,$(HINC))
@@ -136,12 +133,12 @@ DBDDEPENDS_FILES += $(addsuffix $(DEP),$(HINC) \
$(patsubst $(COMMON_DIR)/%,%, \
$(filter-out $(COMMON_DIR)/bpt%.dbd,$(COMMON_DBDS))))
#---------------------------------------------------------------
#####################################################
ifndef T_A
DEP = .d
TEMPLATE3 += $(addsuffix $(TEMPL_SUFFIX), $(TEMPLATE2))
TEMPLATE3+=$(addsuffix .template, $(TEMPLATE2))
COMMON_DIR = .
INSTALL_DBDS =
@@ -156,13 +153,18 @@ ACTIONS += install
ACTIONS += buildInstall
ACTIONS += runtests tapfiles
actionArchTargets = $(foreach action, $(ACTIONS), \
$(foreach arch, $(BUILD_ARCHS), $(action)$(DIVIDER)$(arch)))
cleanArchTargets = $(foreach arch, $(BUILD_ARCHS), clean$(DIVIDER)$(arch))
actionArchTargets = $(foreach x, $(ACTIONS),\ $(foreach arch,$(BUILD_ARCHS), $(x)$(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
@@ -170,30 +172,20 @@ buildInstall : build
rebuild: clean install
.PHONY: all $(ACTIONS)
.PHONY: all host $(ACTIONS)
$(actionArchTargets) $(BUILD_ARCHS): install
$(cleanArchTargets): clean
.PHONY: $(BUILD_ARCHS) $(actionArchTargets) $(cleanArchTargets)
else
# T_A is defined
ifeq ($(EPICS_HOST_ARCH),$(T_A))
host: install
else
host:
endif
.PHONY: host
endif # T_A
endif # T_A defined
ifneq (,$(strip $(DBDDEPENDS_FILES)))
-include $(DBDDEPENDS_FILES)
endif
#---------------------------------------------------------------
# build dependancies, clean rule
##################################################### build dependancies, clean rule
inc : $(COMMON_INC) $(INSTALL_INC)
@@ -213,8 +205,7 @@ db_clean :
realclean: clean
#---------------------------------------------------------------
# Dependency files
##################################################### Dependency files
%Record.h$(DEP): $(COMMON_DIR)/%Record.dbd
@$(RM) $@
@@ -265,19 +256,19 @@ menu%.h$(DEP): ../menu%.dbd
@$(DBEXPAND) -D $(DBDFLAGS) -o $(COMMONDEP_TARGET) $($*_DBD) > $@
@echo "$(COMMONDEP_TARGET): ../Makefile" >> $@
%.db$(DEP): %$(SUBST_SUFFIX)
%.db$(RAW)$(DEP): %$(SUBST_SUFFIX)
@$(RM) $@
$(MSI3_15) -D $(DBFLAGS) -o $(COMMONDEP_TARGET) -S$< $(TEMPLATE_FILENAME) > $@
%.db$(DEP): ../%$(SUBST_SUFFIX)
%.db$(RAW)$(DEP): ../%$(SUBST_SUFFIX)
@$(RM) $@
$(MSI3_15) -D $(DBFLAGS) -o $(COMMONDEP_TARGET) -S$< $(TEMPLATE_FILENAME) > $@
%.db$(DEP): %$(TEMPL_SUFFIX)
%.db$(RAW)$(DEP): %.template
@$(RM) $@
$(MSI3_15) -D $(DBFLAGS) -o $(COMMONDEP_TARGET) $< > $@
%.db$(DEP): ../%$(TEMPL_SUFFIX)
%.db$(RAW)$(DEP): ../%.template
@$(RM) $@
$(MSI3_15) -D $(DBFLAGS) -o $(COMMONDEP_TARGET) $< > $@
@@ -291,8 +282,14 @@ menu%.h$(DEP): ../menu%.dbd
.PRECIOUS: %$(DEP)
#---------------------------------------------------------------
# Substitution files
##################################################### 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
# WARNING: CREATESUBSTITUTIONS script needs output dir on command line
@@ -313,21 +310,25 @@ $(INSTALL_DB)/%$(SUBST_SUFFIX): ../%$(SUBST_SUFFIX)
.PRECIOUS: $(COMMON_DIR)/%$(SUBST_SUFFIX)
#---------------------------------------------------------------
# Template files
##################################################### Template files
$(INSTALL_DB)/%$(TEMPL_SUFFIX): %$(TEMPL_SUFFIX)
$(COMMON_DIR)/%.template: $(COMMON_DIR)/%.edf
@$(RM) $@
$(E2DB) $(E2DB_SYSFLAGS) $(E2DB_FLAGS) -n $@.VAR $<
@$(REPLACEVAR) < $@.VAR > $@
@$(RM) $@.VAR
$(INSTALL_DB)/%.template: %.template
$(ECHO) "Installing template file $@"
@$(INSTALL) -d -m $(INSTALL_PERMISSIONS) $< $(@D)
$(INSTALL_DB)/%$(TEMPL_SUFFIX): ../%$(TEMPL_SUFFIX)
$(INSTALL_DB)/%.template: ../%.template
$(ECHO) "Installing template file $@"
@$(INSTALL) -d -m $(INSTALL_PERMISSIONS) $< $(@D)
.PRECIOUS: $(COMMON_DIR)/%$(TEMPL_SUFFIX)
.PRECIOUS: $(COMMON_DIR)/%.template
#---------------------------------------------------------------
# INC files
##################################################### INC files
$(COMMON_DIR)/%Record.h: $(COMMON_DIR)/%Record.dbd
@$(RM) $(notdir $@)
@@ -361,8 +362,7 @@ $(COMMON_DIR)/menu%.h: ../menu%.dbd
.PRECIOUS: $(COMMON_DIR)/%.h
#---------------------------------------------------------------
# DBD files
##################################################### DBD files
$(COMMON_DIR)/bpt%.dbd: bpt%.data
@$(RM) $(notdir $@)
@@ -425,8 +425,7 @@ $(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 $@)
@@ -445,44 +444,37 @@ $(COMMON_DIR)/%.html: %.pm $(TOOLS)/podToHtml.pl
$(COMMON_DIR)/%.html: ../%.pm $(TOOLS)/podToHtml.pl
@$(RM) $(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 $@) $<
$(PERL) $(TOOLS)/podToHtml.pl -o $(notdir $@) $<
@$(MV) $(notdir $@) $@
.PRECIOUS: $(COMMON_DIR)/%.html %.html
#---------------------------------------------------------------
# DB files
##################################################### DB files
$(COMMON_DIR)/%.db: $(COMMON_DIR)/%.edf
$(COMMON_DIR)/%.db$(RAW): $(COMMON_DIR)/%.edf
$(E2DB) $(E2DB_SYSFLAGS) $(E2DB_FLAGS) -n $*.VAR $<
@$(REPLACEVAR) < $*.VAR > $@
@$(RM) $*.VAR
$(COMMON_DIR)/%.db: %$(SUBST_SUFFIX)
$(COMMON_DIR)/%.db$(RAW): %$(SUBST_SUFFIX)
$(ECHO) "Inflating database from $< $(TEMPLATE_FILENAME)"
@$(RM) $(notdir $@)
$(MSI3_15) $(DBFLAGS) -o $(notdir $@) -S$< $(TEMPLATE_FILENAME)
@$(MV) $(notdir $@) $@
$(COMMON_DIR)/%.db: ../%$(SUBST_SUFFIX)
$(COMMON_DIR)/%.db$(RAW): ../%$(SUBST_SUFFIX)
$(ECHO) "Inflating database from $< $(TEMPLATE_FILENAME)"
@$(RM) $(notdir $@)
$(MSI3_15) $(DBFLAGS) -o $(notdir $@) -S$< $(TEMPLATE_FILENAME)
@$(MV) $(notdir $@) $@
$(COMMON_DIR)/%.db: %$(TEMPL_SUFFIX)
$(COMMON_DIR)/%.db$(RAW): %.template
$(ECHO) "Inflating database from $<"
@$(RM) $(notdir $@)
$(MSI3_15) $(DBFLAGS) -o $(notdir $@) $<
@$(MV) $(notdir $@) $@
$(COMMON_DIR)/%.db: ../%$(TEMPL_SUFFIX)
$(COMMON_DIR)/%.db$(RAW): ../%.template
$(ECHO) "Inflating database from $<"
@$(RM) $(notdir $@)
$(MSI3_15) $(DBFLAGS) -o $(notdir $@) $<
@@ -500,6 +492,22 @@ $(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)
@@ -507,6 +515,7 @@ $(INSTALL_DB)/%: %
$(INSTALL_DB)/%: ../%
$(ECHO) "Installing $@"
@$(INSTALL) -d -m $(INSTALL_PERMISSIONS) $< $(@D)
endif
$(INSTALL_DB)/%.db: $(COMMON_DIR)/%.db
$(ECHO) "Installing created db file $@"
@@ -522,8 +531,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) $@
@@ -539,3 +548,4 @@ $(foreach file, $(DB_INSTALLS), $(eval $(call DB_INSTALLS_template, $(file))))
.PRECIOUS: %_registerRecordDeviceDriver.cpp
##################################################### END OF FILE

View File

@@ -24,11 +24,15 @@ else
clean$(DIVIDER)$(ARCH) clean:
endif
cdCommands envPaths dllPath.bat relPaths.sh: \
cdCommands dllPath.bat relPaths.sh: \
$(wildcard $(TOP)/configure/RELEASE*) \
$(wildcard $(TOP)/configure/CONFIG_SITE*) $(INSTALL_BIN)
$(wildcard $(TOP)/configure/CONFIG_SITE*) | $(INSTALL_BIN)
$(CONVERTRELEASE) -a $(ARCH) -t $(IOCS_APPL_TOP) $@
envPaths: $(wildcard $(TOP)/configure/RELEASE*) \
$(wildcard $(TOP)/configure/CONFIG_SITE*) | $(INSTALL_BIN)
$(CONVERTRELEASE) -t $(IOCS_APPL_TOP) $@
realclean:
$(RM) cdCommands envPaths dllPath.bat relPaths.sh

View File

@@ -168,9 +168,15 @@ ifdef RES
endif
$(DIRECTORY_TARGETS) :
$(MKDIR) $@
$(MKDIR) -p $@
$(PRODNAME): $(INSTALL_LIB_INSTALLS)
# Install LIB_INSTALLS libraries before linking executables
$(TESTPRODNAME) $(PRODNAME): | $(INSTALL_LIB_INSTALLS)
# Install built libraries too, unless Makefile says to wait
ifneq ($(DELAY_INSTALL_LIBS),YES)
$(TESTPRODNAME) $(PRODNAME): | $(INSTALL_LIBS) $(INSTALL_DLLSTUB_LIBS)
endif
# RELEASE file consistency checking
checkRelease:
@@ -319,7 +325,9 @@ $(MODNAME): %$(MODEXT): %$(EXE)
# Automated testing
runtests: $(TESTSCRIPTS)
ifneq (,$(findstring $(T_A),$(EPICS_HOST_ARCH) $(CROSS_COMPILER_RUNTEST_ARCHS)))
-$(PERL) -MTest::Harness -e 'runtests @ARGV if @ARGV;' $^
endif
testspec: $(TESTSCRIPTS)
@$(RM) $@
@@ -333,7 +341,9 @@ tapfiles: $(TESTSCRIPTS) $(TAPFILES)
# A .tap file is the output from running the associated test script
%.tap: %.t
ifneq (,$(findstring $(T_A),$(EPICS_HOST_ARCH) $(CROSS_COMPILER_RUNTEST_ARCHS)))
-$(PERL) $< -tap > $@
endif
# If there's a perl test script (.plt) available, use it
%.t: ../%.plt
@@ -346,14 +356,6 @@ 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
@@ -487,7 +489,7 @@ $(INSTALL_TEMPLATES_SUBDIR)/%: %
.PRECIOUS: $(COMMON_INC)
.PHONY: all host inc build install clean rebuild buildInstall build_clean
.PHONY: runtests tapfiles checkRelease warnRelease noCheckRelease FORCE
.PHONY: runtests tapfiles checkRelease warnRelease noCheckRelease
endif # BASE_RULES_BUILD
# EOF RULES_BUILD

View File

@@ -57,7 +57,7 @@ SHRLIB_LDLIBS = $(addprefix -l, $($*_LDLIBS) $(LIB_LIBS) $(USR_LIBS)) \
$(LDLIBS)
SHRLIB_DEPLIB_DIRS = $(foreach word, \
$(sort $(dir $($*_DEPLIBS) $(SHRLIB_DEPLIBS))), \
$(sort $(INSTALL_LIB)/ $(dir $($*_DEPLIBS) $(SHRLIB_DEPLIBS))), \
$(shell $(FULLPATHNAME) $(word)))
SHRLIBDIR_LDFLAGS += $(SHRLIB_DEPLIB_DIRS:%=-L%)
@@ -86,7 +86,7 @@ PROD_LDLIBS += $($(firstword $(LDLIBS_STATIC_$(STATIC_BUILD)) \
$(LDLIBS_SHARED_$(SHARED_LIBRARIES))))
PROD_DEPLIB_DIRS = $(foreach word, \
$(sort $(dir $($*_DEPLIBS) $(PROD_DEPLIBS))), \
$(sort $(INSTALL_LIB)/ $(dir $($*_DEPLIBS) $(PROD_DEPLIBS))), \
$(shell $(FULLPATHNAME) $(word)))
PRODDIR_LDFLAGS += $(PROD_DEPLIB_DIRS:%=-L%)

View File

@@ -1,9 +1,10 @@
# CONFIG.Common.vxWorksCommon
#
# $Revision-Id$
# This file is maintained by the build community.
#
# Definitions for vxWorks target archs
# Override these definitions in CONFIG_SITE.Common.vxWorksCommon
# Sites may override these definitions in CONFIG_SITE.Common.vxWorksCommon
# or CONFIG_SITE.<host>.vxWorksCommon
#-------------------------------------------------------
@@ -65,6 +66,8 @@ 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
@@ -80,21 +83,30 @@ VX_GNU_VERSION = $(VX_GNU_VERSION_$(VXWORKS_VERSION))
VX_GNU_MAJOR_VERSION = $(basename $(basename $(VX_GNU_VERSION)))
#--------------------------------------------------
# Fix old Linux WIND_HOST_TYPE
# Fix WIND_BASE for vxWorks 6.x on linux
# NB: We know the value of WIND_HOST_TYPE here, but not VXWORKS_VERSION
ifeq ($(WIND_HOST_TYPE),x86-linux)
WIND_HOST_TYPE = x86-linux2
WIND_HOST_TYPE_5 = x86-linux
WIND_HOST_TYPE_6 = x86-linux2
WIND_HOST_TYPE = $(WIND_HOST_TYPE_$(VXWORKS_MAJOR_VERSION))
endif
#--------------------------------------------------
# vxWorks directory definitions
VX_DIR = $(WIND_BASE)/vxworks-$(VXWORKS_VERSION)
VX_DIR_5 = $(WIND_BASE)
VX_DIR_6 = $(WIND_BASE)/vxworks-$(VXWORKS_VERSION)
VX_DIR = $(VX_DIR_$(VXWORKS_MAJOR_VERSION))
GNU_TARGET_INCLUDE_DIR = $(VX_DIR)/target/h $(VX_DIR)/target/h/wrn/coreip
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))
#--------------------------------------------------
# vxWorks GNU directories
GNU_DIR = $(WIND_BASE)/gnu/$(VX_GNU_VERSION)-vxworks-$(VXWORKS_VERSION)/$(WIND_HOST_TYPE)
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))
#--------------------------------------------------
# Wind River moved nm out of GNU_BIN in some versions
@@ -114,7 +126,9 @@ 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).
# (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)
MUNCH_LDFLAGS_6 = -T $(VX_DIR)/target/h/tool/gnu/ldscripts/link.OUT
MUNCH_LDFLAGS = $(MUNCH_LDFLAGS_$(VXWORKS_MAJOR_VERSION))
@@ -129,10 +143,11 @@ export TOOL_FAMILY = GNU
OP_SYS_CPPFLAGS += -DvxWorks=vxWorks
OP_SYS_CFLAGS += -fno-builtin
# Fix for vxWorks headers that use macros defined in vxWorks.h but
# Fix for vxWorks 5 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.
# from defining the OFFSET macro, which generates lots of warnings in
# both vxWorks 5 and 6.
OP_SYS_INCLUDE_CPPFLAGS += -include $(VX_DIR)/target/h/vxWorks.h
#--------------------------------------------------
@@ -145,6 +160,7 @@ 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

View File

@@ -11,7 +11,7 @@
CP = cp
MV = mv
RM = rm -f
MKDIR = mkdir -p
MKDIR = mkdir
RMDIR = rm -rf
CAT = cat

View File

@@ -10,4 +10,4 @@
#Include definitions common to unix hosts
include $(CONFIG)/os/CONFIG.UnixCommon.Common
WIND_HOST_TYPE = x86-linux2
WIND_HOST_TYPE = x86-linux

View File

@@ -10,4 +10,4 @@
#Include definitions common to unix hosts
include $(CONFIG)/os/CONFIG.UnixCommon.Common
WIND_HOST_TYPE = x86-linux2
WIND_HOST_TYPE = x86-linux

View File

@@ -10,4 +10,4 @@
#Include definitions common to unix hosts
include $(CONFIG)/os/CONFIG.UnixCommon.Common
WIND_HOST_TYPE = x86-linux2
WIND_HOST_TYPE = x86-linux

View File

@@ -10,4 +10,4 @@
#Include definitions common to unix hosts
include $(CONFIG)/os/CONFIG.UnixCommon.Common
WIND_HOST_TYPE = x86-linux2
WIND_HOST_TYPE = x86-linux

View File

@@ -10,4 +10,4 @@
#Include definitions common to unix hosts
include $(CONFIG)/os/CONFIG.UnixCommon.Common
WIND_HOST_TYPE = x86-linux2
WIND_HOST_TYPE = x86-linux

View File

@@ -10,4 +10,4 @@
#Include definitions common to unix hosts
include $(CONFIG)/os/CONFIG.UnixCommon.Common
WIND_HOST_TYPE = x86-linux2
WIND_HOST_TYPE = x86-linux

View File

@@ -1,22 +1,22 @@
# CONFIG_SITE.Common.cygwin-x86_64
#
# $Revision-Id$
#
# Site Specific definitions for cygwin-x86_64 target
# Only the local epics system manager should modify this file
# If readline is installed uncomment the following line
# to add command-line editing and history support
#COMMANDLINE_LIBRARY = READLINE
# Uncomment the following line if readline has problems
#LDLIBS_READLINE = -lreadline -lcurses
# It makes sense to include debugging symbols even in optimized builds
# in case you want to attach gdb to the process or examine a core-dump.
# This does cost disk space, but not memory as debug symbols are not
# loaded into RAM when the binary is loaded.
OPT_CFLAGS_YES += -g
OPT_CXXFLAGS_YES += -g
# CONFIG_SITE.Common.cygwin-x86_64
#
# $Revision-Id$
#
# Site Specific definitions for cygwin-x86_64 target
# Only the local epics system manager should modify this file
# If readline is installed uncomment the following line
# to add command-line editing and history support
#COMMANDLINE_LIBRARY = READLINE
# Uncomment the following line if readline has problems
#LDLIBS_READLINE = -lreadline -lcurses
# It makes sense to include debugging symbols even in optimized builds
# in case you want to attach gdb to the process or examine a core-dump.
# This does cost disk space, but not memory as debug symbols are not
# loaded into RAM when the binary is loaded.
OPT_CFLAGS_YES += -g
OPT_CXXFLAGS_YES += -g

View File

@@ -1,17 +1,25 @@
# 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. Do not include any third-level digits.
# 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.
# 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.
# Note: vxWorks 5.4.x (Tornado 2.0.x) is not supported
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
@@ -19,8 +27,10 @@ VXWORKS_VERSION = 6.9
# 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/vxWorks-$(VXWORKS_VERSION)
WIND_BASE = /usr/local/vw/tornado22-$(ARCH_CLASS)
#WIND_BASE = /usr/local/vw/vxWorks-$(VXWORKS_VERSION)
#WIND_BASE = /ade/vxWorks/$(VXWORKS_VERSION)
@@ -29,9 +39,9 @@ WIND_BASE = /usr/local/vw/vxWorks-$(VXWORKS_VERSION)
#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

View File

@@ -4,36 +4,40 @@
<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.16.0.1</title>
<title>Known Problems in R3.15.3</title>
</head>
<body>
<h1 style="text-align: center">EPICS Base R3.16.0.1: Known Problems</h1>
<h1 style="text-align: center">EPICS Base R3.15.3: Known Problems</h1>
<p>Any patch files linked below should be applied at the root of the
base-3.16.0.1 tree. Download them, then use the GNU Patch program as
base-3.15.3 tree. Download them, then use the GNU Patch program as
follows:</p>
<blockquote><pre>% <b>cd <i>/path/to/</i>base-3.16.0.1</b>
<blockquote><pre>% <b>cd <i>/path/to/</i>base-3.15.3</b>
% <b>patch -p0 &lt; <i>/path/to/</i>file.patch</b></pre></blockquote>
<p>The following problems were known by the developers at the time of this
release:</p>
<p>The following significant problems have been reported with this
version of EPICS Base:</p>
<ul>
<!-- Items added after release should be formatted thusly:
<li>YYYY-MM-DD: Description of problem
...</li>
-->
<li>See LaunchPad bug
<a href="https://bugs.launchpad.net/epics-base/+bug/1466129">#1466129</a>:
The feature that allows an IOC to bind to a single network interface is not
working correctly. The bug mentioned above contains a patch that has been
tested on Linux-64, a complete fix is expected for 3.15.4. Note that Windows
systems are not affected by this bug.</li>
<li>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>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>64-bit Windows builds of the CAS library may not work with some compilers.
The code in <tt>src/legacy/gdd</tt> is incompatible with the LLP64 model
that Windows uses for its 64-bit ABI.</li>
The code in <tt>src/legacy/gdd</tt> is currently incompatible with the LLP64
model that Windows uses for its 64-bit ABI.</li>
</ul>

View File

@@ -1,6 +1,6 @@
Installation Instructions
EPICS Base Release 3.16.0
EPICS Base Release 3.15.3
--------------------------------------------------------------------------
@@ -85,17 +85,17 @@
as processes on the host platform.
vxWorks
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
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
base/configure/os/CONFIG_SITE.Common.vxWorksCommon file or in one of its
target-specific overrides.
Consult the vxWorks 6.x EPICS web pages and the vxWorks documentation
for information about configuring your vxWorks operating system for use
with EPICS.
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.
RTEMS
For RTEMS targets, you need RTEMS core and toolset version 4.9.2 or

View File

@@ -9,7 +9,7 @@
<BODY>
<CENTER>
<H1>Installation Instructions</H1>
<H2>EPICS Base Release 3.16.0</H2><BR>
<H2>EPICS Base Release 3.15.3</H2><BR>
</CENTER>
<HR>
<H3> Table of Contents</H3>
@@ -90,16 +90,17 @@
as processes on the host platform.</P>
<P><B>vxWorks</B><BR>
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
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
base/configure/os/CONFIG_SITE.Common.vxWorksCommon file or in one of its
target-specific overrides.</P>
<P>Consult the <a href="http://www.aps.anl.gov/epics/base/vxWorks6.php">vxWorks
6.x</a> EPICS web pages and the vxWorks documentation for information
<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
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

View File

@@ -39,7 +39,7 @@
</blockquote>
<p>This document describes the procedures and provides a checklist of tasks
that should be performed when creating production releases of EPICS Base.</p>
that should be performed when creating new 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.16.1-pre<i>n</i></tt>
<tt>R3.15.3-pre<i>n</i></tt>
&mdash; pre-release tag
</li>
<li>
<tt>R3.16.1-rc<i>n</i></tt>
<tt>R3.15.3-rc<i>n</i></tt>
&mdash; release candidate tag, note the <tt>rc</tt> is now
lower-case</li>
</ul>
<blockquote><tt>
cd ~/base/mirror-3.16<br />
bzr tag R3.16.1-rc<i>n</i>
cd ~/base/mirror-3.15<br />
bzr tag R3.15.3-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.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
--root=base-3.15.3-rc<i>n</i>
-r tag:R3.15.3-rc<i>n</i>
base-3.15.3-rc<i>n</i>.tar.gz
mirror-3.15
</tt></blockquote>
This requires that the Bazaar keywords plugin is installed and
configured properly.
@@ -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.16<br />
bzr tag R3.16.1</i>
cd ~/base/mirror-3.15<br />
bzr tag R3.15.3</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.16.1
-r tag:R3.16.1
base-3.16.1.tar.gz
mirror-3.16
--root=base-3.15.3
-r tag:R3.15.3
base-3.15.3.tar.gz
mirror-3.15
</tt></blockquote>
This requires that the Bazaar keywords plugin is installed and
configured properly.

View File

@@ -333,6 +333,12 @@ cac::~cac ()
this->ipToAEngine.release ();
// clean-up the list of un-notified msg objects
while ( msgForMultiplyDefinedPV * msg = this->msgMultiPVList.get() ) {
msg->~msgForMultiplyDefinedPV ();
this->mdpvFreeList.release ( msg );
}
errlogFlush ();
osiSockRelease ();
@@ -606,6 +612,8 @@ void cac::transferChanToVirtCircuit (
msgForMultiplyDefinedPV * pMsg = new ( this->mdpvFreeList )
msgForMultiplyDefinedPV ( this->ipToAEngine,
*this, pChan->pName ( guard ), acc );
// cac keeps a list of these objects for proper clean-up in ~cac
this->msgMultiPVList.add ( *pMsg );
// It is possible for the ioInitiate call below to
// call the callback directly if queue quota is exceeded.
// This callback takes the callback lock and therefore we
@@ -1297,6 +1305,8 @@ void cac::pvMultiplyDefinedNotify ( msgForMultiplyDefinedPV & mfmdpv,
epicsGuard < epicsMutex > guard ( this->mutex );
this->exception ( mgr.cbGuard, guard, ECA_DBLCHNL, buf, __FILE__, __LINE__ );
}
// remove from the list and delete msg object
this->msgMultiPVList.remove ( mfmdpv );
mfmdpv.~msgForMultiplyDefinedPV ();
this->mdpvFreeList.release ( & mfmdpv );
}

View File

@@ -237,6 +237,7 @@ private:
resTable < tcpiiu, caServerID > serverTable;
tsDLList < tcpiiu > circuitList;
tsDLList < SearchDest > searchDestList;
tsDLList < msgForMultiplyDefinedPV > msgMultiPVList;
tsFreeList
< class tcpiiu, 32, epicsMutexNOOP >
freeListVirtualCircuit;

View File

@@ -55,8 +55,10 @@ msgForMultiplyDefinedPV::~msgForMultiplyDefinedPV ()
void msgForMultiplyDefinedPV::transactionComplete ( const char * pHostNameRej )
{
// calls into cac for the notification
// the msg object (= this) is being deleted as part of the notification
this->cb.pvMultiplyDefinedNotify ( *this, this->channel, this->acc, pHostNameRej );
// !! dont touch this pointer after this point because object has been deleted !!
// !! dont touch 'this' pointer after this point because object has been deleted !!
}
void * msgForMultiplyDefinedPV::operator new ( size_t size,

View File

@@ -33,6 +33,7 @@
#include "ipAddrToAsciiAsynchronous.h"
#include "tsFreeList.h"
#include "tsDLList.h"
#include "compilerDependencies.h"
#ifdef msgForMultiplyDefinedPVh_epicsExportSharedSymbols
@@ -47,7 +48,9 @@ public:
const char * pAcc, const char * pRej ) = 0;
};
class msgForMultiplyDefinedPV : public ipAddrToAsciiCallBack {
class msgForMultiplyDefinedPV :
public ipAddrToAsciiCallBack,
public tsDLNode < msgForMultiplyDefinedPV > {
public:
msgForMultiplyDefinedPV ( ipAddrToAsciiEngine & engine,
callbackForMultiplyDefinedPV &, const char * pChannelName,
@@ -62,8 +65,8 @@ private:
ipAddrToAsciiTransaction & dnsTransaction;
callbackForMultiplyDefinedPV & cb;
void transactionComplete ( const char * pHostName );
msgForMultiplyDefinedPV ( const msgForMultiplyDefinedPV & );
msgForMultiplyDefinedPV & operator = ( const msgForMultiplyDefinedPV & );
msgForMultiplyDefinedPV ( const msgForMultiplyDefinedPV & );
msgForMultiplyDefinedPV & operator = ( const msgForMultiplyDefinedPV & );
void operator delete ( void * );
};

View File

@@ -1138,6 +1138,20 @@ void CA_add_exception_event(const char *class, SV *sub) {
static
SV * printf_sub = NULL;
#ifndef va_copy
# ifdef __GNUC__
# define va_copy(d, s) __va_copy(d, s)
# else
# define va_copy(d, s) ((d) = (s))
/* The above macro is NOT PORTABLE but works on Windows when
* stdarg.h doesn't provide va_copy(). Some architectures need
* define va_copy(d, s) ((*d) = (*s))
* while others may be even more complicated, but hopefully the
* system stdarg.h header defines va_copy() in those cases.
*/
# endif
#endif
static
int printf_handler(const char *format, va_list args) {
if (! printf_sub)
@@ -1152,11 +1166,7 @@ int printf_handler(const char *format, va_list args) {
ENTER;
SAVETMPS;
#ifdef __GNUC__
__va_copy(argcopy, args);
#else
va_copy(argcopy, args);
#endif
printf_str = NEWSV(0, strlen(format) + 32);
sv_vsetpvf(printf_str, format, &argcopy);

View File

@@ -20,10 +20,19 @@ ifeq ($(OS_CLASS),Darwin)
LOADABLE_SHRLIB_SUFFIX = .$(shell $(PERL) ../perlConfig.pl dlext)
endif
PERL_VERSION = $(shell $(PERL) ../perlConfig.pl version)
PERL_ARCHNAME = $(shell $(PERL) ../perlConfig.pl archname)
PERL_ARCHPATH = $(PERL_VERSION)/$(PERL_ARCHNAME)
ifdef T_A
PERL_VERSION = $(shell $(PERL) ../perlConfig.pl version)
PERL_ARCHNAME = $(shell $(PERL) ../perlConfig.pl archname)
PERL_ARCHPATH := $(PERL_VERSION)/$(PERL_ARCHNAME)
EXTUTILS := $(shell $(PERL) ../perlConfig.pl privlib)/ExtUtils
PERLBIN := $(shell $(PERL) ../perlConfig.pl bin)
XSUBPP := $(firstword $(wildcard $(PERLBIN)/xsubpp $(EXTUTILS)/xsubpp))
ifeq ($(strip $(XSUBPP)),)
$(warning Perl's xsubpp program was not found.)
$(warning The Perl CA module will not be built.)
else
ifeq ($(T_A),$(EPICS_HOST_ARCH)) # No cross-builds (wrong Perl!)
ifeq ($(findstring $(OS_CLASS),WIN32 cygwin32),) # Doesn't build on WIN32
LOADABLE_LIBRARY_HOST = Cap5
@@ -41,6 +50,8 @@ ifeq ($(findstring $(OS_CLASS),WIN32 cygwin32),) # Doesn't build on WIN32
HTMLS = CA.html
endif
endif
endif
endif
Cap5_SRCS = Cap5.xs
Cap5_LIBS = ca Com
@@ -52,10 +63,6 @@ CLEANS += Cap5.c pod2htmd.tmp pod2htmi.tmp
include $(TOP)/configure/RULES
ifdef T_A
EXTUTILS = $(shell $(PERL) ../perlConfig.pl privlib)/ExtUtils
PERLBIN = $(shell $(PERL) ../perlConfig.pl bin)
XSUBPP = $(firstword $(wildcard $(PERLBIN)/xsubpp $(EXTUTILS)/xsubpp))
%.c: ../%.xs
$(RM) $@ $@_new
$(PERL) $(XSUBPP) -typemap $(EXTUTILS)/typemap $< > $@_new && $(MV) $@_new $@

View File

@@ -3,7 +3,13 @@
# This script is used to extract information about the Perl build
# configuration, so the EPICS build system uses the same settings.
use strict;
use Config;
my $arg = shift;
print $Config{$arg};
my $val = $Config{$arg};
$val =~ s{\\}{/}go
if $^O eq 'MSWin32';
print $val;

View File

@@ -66,8 +66,6 @@ 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 );
@@ -94,15 +92,12 @@ 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;
}

View File

@@ -70,7 +70,6 @@ private:
repeaterTimerNotify & iiu;
epicsMutex & cbMutex;
cacContextNotify & ctxNotify;
mutable epicsMutex stateMutex;
unsigned attempts;
bool registered;
bool once;

View File

@@ -492,14 +492,13 @@ int main (int argc, char *argv[])
} else { /* Not an ENUM */
if (charArrAsStr) {
count = len;
dbrType = DBR_CHAR;
ebuf = calloc(strlen(cbuf)+1, sizeof(char));
ebuf = calloc(len, sizeof(char));
if(!ebuf) {
fprintf(stderr, "Memory allocation failed\n");
return 1;
}
epicsStrnRawFromEscaped(ebuf, strlen(cbuf)+1, cbuf, strlen(cbuf));
count = epicsStrnRawFromEscaped(ebuf, len, cbuf, len-1) + 1;
} else {
for (i = 0; i < count; ++i) {
epicsStrnRawFromEscaped(sbuf[i], sizeof(EpicsStr), *(argv+optind+i), sizeof(EpicsStr));

View File

@@ -69,6 +69,11 @@ CLEANS += $(COMMON_DIR)/aitConvertGenerated.cc
USR_CXXFLAGS_Linux = -fno-strict-aliasing
USR_CXXFLAGS_RTEMS = -fno-strict-aliasing
ifeq ($(T_A),$(EPICS_HOST_ARCH))
# genApps and aitGen are needed to finish libgdd
DELAY_INSTALL_LIBS = YES
endif
include $(TOP)/configure/RULES
# Manual dependencies

View File

@@ -348,17 +348,13 @@ caStatus casDGClient::searchResponse ( const caHdrLargeArray & msg,
//
caStatus casDGClient::searchFailResponse ( const caHdrLargeArray * mp )
{
int status;
epicsGuard < epicsMutex > guard ( this->mutex );
status = this->out.copyInHeader ( CA_PROTO_NOT_FOUND, 0,
this->out.copyInHeader ( CA_PROTO_NOT_FOUND, 0,
mp->m_dataType, mp->m_count, mp->m_cid, mp->m_available, 0 );
if ( status == S_cas_success ) {
this->out.commitMsg ();
}
this->out.commitMsg ();
return S_cas_success;
return S_cas_success;
}
/*

View File

@@ -24,6 +24,7 @@
#include "dbDefs.h"
#include "epicsAtomic.h"
#include "epicsEvent.h"
#include "epicsExit.h"
#include "epicsInterrupt.h"
#include "epicsRingPointer.h"
#include "epicsString.h"
@@ -91,7 +92,7 @@ static int priorityValue[NUM_CALLBACK_PRIORITIES] = {0, 1, 2};
int callbackSetQueueSize(int size)
{
if (callbackIsInit) {
fprintf(stderr, "Callback system already initialized\n");
errlogPrintf("Callback system already initialized\n");
return -1;
}
callbackQueueSize = size;
@@ -101,7 +102,7 @@ int callbackSetQueueSize(int size)
int callbackParallelThreads(int count, const char *prio)
{
if (callbackIsInit) {
fprintf(stderr, "Callback system already initialized\n");
errlogPrintf("Callback system already initialized\n");
return -1;
}
@@ -111,7 +112,7 @@ int callbackParallelThreads(int count, const char *prio)
count = callbackParallelThreadsDefault;
if (count < 1) count = 1;
if (!prio || *prio == 0 || strcmp(prio, "*") == 0) {
if (!prio || strcmp(prio, "") == 0 || strcmp(prio, "*") == 0) {
int i;
for (i = 0; i < NUM_CALLBACK_PRIORITIES; i++) {
@@ -120,30 +121,30 @@ int callbackParallelThreads(int count, const char *prio)
}
else {
dbMenu *pdbMenu;
int i;
if (!pdbbase) {
fprintf(stderr, "callbackParallelThreads: pdbbase not set\n");
errlogPrintf("callbackParallelThreads: pdbbase not set\n");
return -1;
}
/* Find prio in menuPriority */
pdbMenu = dbFindMenu(pdbbase, "menuPriority");
if (!pdbMenu) {
fprintf(stderr, "callbackParallelThreads: No Priority menu\n");
return -1;
}
if (pdbMenu) {
int i, gotMatch = 0;
for (i = 0; i < pdbMenu->nChoice; i++) {
if (epicsStrCaseCmp(prio, pdbMenu->papChoiceValue[i]) == 0)
goto found;
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;
}
}
fprintf(stderr, "callbackParallelThreads: "
"Unknown priority \"%s\"\n", prio);
return -1;
found:
callbackQueue[i].threadsConfigured = count;
}
return 0;
}

View File

@@ -47,16 +47,14 @@ typedef struct callbackPvt {
typedef void (*CALLBACKFUNC)(struct callbackPvt*);
#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 )
#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 )
epicsShareFunc void callbackInit(void);
epicsShareFunc void callbackStop(void);

View File

@@ -50,12 +50,11 @@
#include "dbFldTypes.h"
#include "dbFldTypes.h"
#include "dbLink.h"
#include "dbLockPvt.h"
#include "dbLock.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"
@@ -403,19 +402,29 @@ struct rset * dbGetRset(const struct dbAddr *paddr)
}
long dbPutAttribute(
const char *recordTypename,const char *name,const char*value)
const char *recordTypename, const char *name, const char *value)
{
DBENTRY dbEntry;
DBENTRY *pdbEntry = &dbEntry;
long status=0;
DBENTRY dbEntry;
DBENTRY *pdbEntry = &dbEntry;
long status = 0;
if(!pdbbase) return(S_db_notFound);
dbInitEntry(pdbbase,pdbEntry);
status = dbFindRecordType(pdbEntry,recordTypename);
if(!status) status = dbPutRecordAttribute(pdbEntry,name,value);
dbFinishEntry(pdbEntry);
if(status) errMessage(status,"dbPutAttribute failure");
return(status);
if (!pdbbase)
return S_db_notFound;
if (!name) {
status = S_db_badField;
goto done;
}
if (!value)
value = "";
dbInitEntry(pdbbase, pdbEntry);
status = dbFindRecordType(pdbEntry, recordTypename);
if (!status)
status = dbPutRecordAttribute(pdbEntry, name, value);
dbFinishEntry(pdbEntry);
done:
if (status)
errMessage(status, "dbPutAttribute failure");
return status;
}
int dbIsValueField(const struct dbFldDes *pdbFldDes)
@@ -943,25 +952,19 @@ 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:
@@ -976,57 +979,31 @@ static long dbPutFieldLink(DBADDR *paddr,
return S_db_badDbrtype;
}
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 */
}
}
dbInitEntry(pdbbase, &dbEntry);
status = dbFindRecord(&dbEntry, precord->name);
if (!status) status = dbFindField(&dbEntry, pfldDes->name);
if (status) goto finish;
isDevLink = ellCount(&precord->rdes->devList) > 0 &&
pfldDes->isDevLink;
(strcmp(pfldDes->name, "INP") == 0 ||
strcmp(pfldDes->name, "OUT") == 0);
memset(&locker, 0, sizeof(locker));
lockrecs[0] = precord;
lockrecs[1] = pdbaddr ? pdbaddr->precord : NULL;
dbLockerPrepare(&locker, lockrecs, 2);
dbScanLockMany(&locker);
dbLockSetGblLock();
dbLockSetRecordLock(precord);
scan = precord->scan;
if (isDevLink) {
new_devsup = dbDTYPtoDevSup(precord->rdes, precord->dtyp);
if (new_devsup) {
new_dset = new_devsup->pdset;
new_dsxt = new_devsup->pdsxt;
devSup *pdevSup = dbDTYPtoDevSup(precord->rdes, precord->dtyp);
if (pdevSup) {
new_dset = pdevSup->pdset;
new_dsxt = pdevSup->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) {
devSup *old_devsup = dbDSETtoDevSup(precord->rdes, precord->dset);
if (old_devsup)
old_dsxt = old_devsup->pdsxt;
pdevSup = dbDSETtoDevSup(precord->rdes, precord->dset);
if (pdevSup)
old_dsxt = pdevSup->pdsxt;
}
if (new_dsxt == NULL ||
@@ -1052,11 +1029,13 @@ static long dbPutFieldLink(DBADDR *paddr,
switch (plink->type) { /* Old link type */
case DB_LINK:
case CA_LINK:
case CONSTANT:
dbRemoveLink(&locker, plink); /* link type becomes PV_LINK */
dbRemoveLink(plink);
break;
case PV_LINK:
case CONSTANT:
break; /* do nothing */
case MACRO_LINK:
break; /* should never get here */
@@ -1070,7 +1049,7 @@ static long dbPutFieldLink(DBADDR *paddr,
if (special) status = dbPutSpecial(paddr, 0);
if (!status) status = dbSetLink(plink, &link_info, new_devsup);
if (!status) status = dbPutString(&dbEntry, pstring);
if (!status && special) status = dbPutSpecial(paddr, 1);
@@ -1097,10 +1076,12 @@ static long dbPutFieldLink(DBADDR *paddr,
switch (plink->type) { /* New link type */
case PV_LINK:
case CONSTANT:
dbAddLink(&locker, plink, pfldDes->field_type, pdbaddr);
dbAddLink(precord, plink, pfldDes->field_type);
break;
case CONSTANT:
break; /* do nothing */
case DB_LINK:
case CA_LINK:
case MACRO_LINK:
@@ -1125,10 +1106,9 @@ postScanEvent:
if (scan != precord->scan)
db_post_events(precord, &precord->scan, DBE_VALUE | DBE_LOG);
unlock:
dbScanUnlockMany(&locker);
dbLockerFinalize(&locker);
cleanup:
free(link_info.target);
dbLockSetGblUnlock();
finish:
dbFinishEntry(&dbEntry);
return status;
}

View File

@@ -205,7 +205,6 @@ private:
cacContextNotify & notify;
epics_auto_ptr < cacContext > pNetContext;
char * pStateNotifyCache;
bool isolated;
cacChannel & createChannel (
epicsGuard < epicsMutex > &,

View File

@@ -6,13 +6,14 @@
* EPICS BASE is distributed subject to a Software License Agreement found
* in file LICENSE that is included with this distribution.
\*************************************************************************/
/* dbCa.c
/* $Revision-Id$
*
* Original Authors: Bob Dalesio and Marty Kraimer
* Date: 26MAR96
*
*/
#define EPICS_DBCA_PRIVATE_API
#include <stddef.h>
#include <stdlib.h>
#include <stdio.h>
@@ -29,7 +30,6 @@
#include "epicsPrint.h"
#include "epicsString.h"
#include "epicsThread.h"
#include "epicsAtomic.h"
#include "epicsTime.h"
#include "errlog.h"
#include "errMdef.h"
@@ -46,17 +46,13 @@
#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*/
@@ -64,7 +60,7 @@ static epicsEventId workListEvent; /*wakeup event for dbCaTask*/
static int removesOutstanding = 0;
#define removesOutstandingWarning 10000
static volatile enum dbCaCtl_t {
static volatile enum {
ctlInit, ctlRun, ctlPause, ctlExit
} dbCaCtl;
static epicsEventId startStopEvent;
@@ -74,47 +70,57 @@ 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->precord->name, pcaLink->pvname)
pcaLink->plink->value.pv_link.precord->name, pcaLink->pvname)
static int dbca_chan_count;
/* caLink locking
*
* Lock ordering:
* dbScanLock -> caLink.lock -> workListLock
* workListLock
* This is only used to put request into and take them out of workList.
* While this is locked no other locks are taken
*
* workListLock:
* Guards access to workList.
* 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.
*
* 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()
* caLink.lock
* Any code that use a caLink takes this lock and releases it when done
*
* Guard the pointer plink.value.pv_link.pvt, but not the struct caLink
* which is pointed to.
* 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.
*
* caLink.lock:
* Guards the caLink structure (but not the struct DBLINK)
* The above is necessary to prevent deadlocks and attempts to use a caLink
* that has been deleted.
*
* The dbCaTask only locks caLink, and must not lock the record (a violation of lock order).
* 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.
*
* 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.
* 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.
*
* References to the struct caLink are owned by the dbCaTask, and any scanOnceCallback()
* which is in progress.
* Thus the problem is to make sure that nothing from channel access
* accesses a caLink that is deleted. This is done as follows.
*
* The libca and scanOnceCallback callbacks take no action if pca->plink==NULL.
* 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.
*
* dbCaPutLinkCallback causes an additional complication because
* when dbCaRemoveLink is called the callback may not have occured.
@@ -156,23 +162,11 @@ static void addAction(caLink *pca, short link_action)
epicsEventSignal(workListEvent);
}
static void caLinkInc(caLink *pca)
static void dbCaLinkFree(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;
@@ -193,46 +187,10 @@ static void caLinkDec(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->precord;
dbCommon *pdbCommon = plink->value.pv_link.precord;
dbScanLock(pdbCommon);
pdbCommon->rset->process(pdbCommon);
@@ -241,25 +199,40 @@ void dbCaCallbackProcess(void *userPvt)
void dbCaShutdown(void)
{
enum dbCaCtl_t cur = dbCaCtl;
assert(cur == ctlRun || cur == ctlPause);
dbCaCtl = ctlExit;
epicsEventSignal(workListEvent);
epicsEventMustWait(startStopEvent);
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);
}
}
static void dbCaLinkInitImpl(int isolate)
void dbCaLinkInitIsolated(void)
{
dbServiceIsolate = isolate;
dbServiceIOInit();
if (!workListLock)
workListLock = epicsMutexMustCreate();
if (!workListEvent)
workListEvent = epicsEventMustCreate(epicsEventEmpty);
dbCaCtl = ctlExit;
}
if(!startStopEvent)
startStopEvent = epicsEventMustCreate(epicsEventEmpty);
void dbCaLinkInit(void)
{
dbServiceIOInit();
dbCaLinkInitIsolated();
startStopEvent = epicsEventMustCreate(epicsEventEmpty);
dbCaCtl = ctlPause;
epicsThreadCreate("dbCaLink", epicsThreadPriorityMedium,
@@ -268,16 +241,6 @@ static void dbCaLinkInitImpl(int isolate)
epicsEventMustWait(startStopEvent);
}
void dbCaLinkInitIsolated(void)
{
dbCaLinkInitImpl(1);
}
void dbCaLinkInit(void)
{
dbCaLinkInitImpl(0);
}
void dbCaRun(void)
{
if (dbCaCtl == ctlPause) {
@@ -302,7 +265,6 @@ 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);
@@ -311,21 +273,13 @@ 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);
}
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)
void dbCaRemoveLink(struct link *plink)
{
caLink *pca = (caLink *)plink->value.pv_link.pvt;
@@ -333,10 +287,6 @@ void dbCaRemoveLink(struct dbLocker *locker, 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);
@@ -396,15 +346,15 @@ long dbCaGetLink(struct link *plink,short dbrType, void *pdest,
assert(pca->pgetNative);
status = fConvert(pca->pgetNative, pdest, 0);
} else {
unsigned long ntoget = *nelements;
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->usedelements)
ntoget = pca->usedelements;
if (ntoget > pca->nelements)
ntoget = pca->nelements;
*nelements = ntoget;
memset((void *)&dbAddr, 0, sizeof(dbAddr));
@@ -459,7 +409,6 @@ 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;
*/
@@ -469,7 +418,6 @@ 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);
@@ -482,7 +430,10 @@ long dbCaPutLinkCallback(struct link *plink,short dbrType,
if(nRequest>pca->nelements)
nRequest = pca->nelements;
status = aConvert(&dbAddr, pbuffer, nRequest, pca->nelements, 0);
pca->putnelements = nRequest;
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);
}
}
link_action |= CA_WRITE_NATIVE;
pca->gotOutNative = TRUE;
@@ -502,13 +453,7 @@ long dbCaPutLinkCallback(struct link *plink,short dbrType,
return status;
}
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)
int dbCaIsLinkConnected(const struct link *plink)
{
caLink *pca;
@@ -518,7 +463,7 @@ static int isConnected(const struct link *plink)
return pca->isConnected;
}
static void scanForward(struct link *plink) {
void dbCaScanFwdLink(struct link *plink) {
short fwdLinkValue = 1;
if (plink->value.pv_link.pvlMask & pvlOptFWD)
@@ -537,7 +482,7 @@ static void scanForward(struct link *plink) {
return -1; \
}
static long getElements(const struct link *plink, long *nelements)
long dbCaGetNelements(const struct link *plink, long *nelements)
{
caLink *pca;
@@ -547,7 +492,7 @@ static long getElements(const struct link *plink, long *nelements)
return 0;
}
static long getAlarm(const struct link *plink,
long dbCaGetAlarm(const struct link *plink,
epicsEnum16 *pstat, epicsEnum16 *psevr)
{
caLink *pca;
@@ -559,7 +504,7 @@ static long getAlarm(const struct link *plink,
return 0;
}
static long getTimeStamp(const struct link *plink,
long dbCaGetTimeStamp(const struct link *plink,
epicsTimeStamp *pstamp)
{
caLink *pca;
@@ -570,7 +515,7 @@ static long getTimeStamp(const struct link *plink,
return 0;
}
static int getDBFtype(const struct link *plink)
int dbCaGetLinkDBFtype(const struct link *plink)
{
caLink *pca;
int type;
@@ -601,7 +546,7 @@ long dbCaGetAttributes(const struct link *plink,
return 0;
}
static long getControlLimits(const struct link *plink,
long dbCaGetControlLimits(const struct link *plink,
double *low, double *high)
{
caLink *pca;
@@ -617,7 +562,7 @@ static long getControlLimits(const struct link *plink,
return gotAttributes ? 0 : -1;
}
static long getGraphicLimits(const struct link *plink,
long dbCaGetGraphicLimits(const struct link *plink,
double *low, double *high)
{
caLink *pca;
@@ -633,7 +578,7 @@ static long getGraphicLimits(const struct link *plink,
return gotAttributes ? 0 : -1;
}
static long getAlarmLimits(const struct link *plink,
long dbCaGetAlarmLimits(const struct link *plink,
double *lolo, double *low, double *high, double *hihi)
{
caLink *pca;
@@ -651,7 +596,7 @@ static long getAlarmLimits(const struct link *plink,
return gotAttributes ? 0 : -1;
}
static long getPrecision(const struct link *plink, short *precision)
long dbCaGetPrecision(const struct link *plink, short *precision)
{
caLink *pca;
int gotAttributes;
@@ -663,7 +608,7 @@ static long getPrecision(const struct link *plink, short *precision)
return gotAttributes ? 0 : -1;
}
static long getUnits(const struct link *plink,
long dbCaGetUnits(const struct link *plink,
char *units, int unitsSize)
{
caLink *pca;
@@ -678,52 +623,6 @@ static long getUnits(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;
@@ -738,13 +637,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 = plink->precord;
dbCommon *precord = ppv_link->precord;
pca->nDisconnect++;
if (precord &&
((ppv_link->pvlMask & pvlOptCP) ||
((ppv_link->pvlMask & pvlOptCPP) && precord->scan == 0)))
scanLinkOnce(precord, pca);
scanOnce(precord);
goto done;
}
pca->hasReadAccess = ca_read_access(arg.chid);
@@ -774,7 +673,6 @@ 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;
@@ -813,7 +711,7 @@ static void eventCallback(struct event_handler_args arg)
if (!plink) goto done;
monitor = pca->monitor;
userPvt = pca->userPvt;
precord = plink->precord;
precord = plink->value.pv_link.precord;
if (arg.status != ECA_NORMAL) {
if (precord) {
if (arg.status != ECA_NORDACCESS &&
@@ -827,7 +725,6 @@ 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) {
@@ -844,12 +741,10 @@ 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:
errlogPrintf("dbCa: eventCallback Logic Error. dbr=%ld dbf=%d\n",
arg.type, ca_field_type(pca->chid));
errMessage(-1, "dbCa: eventCallback Logic Error\n");
break;
}
pdbr_time_double = (struct dbr_time_double *)arg.dbr;
@@ -861,7 +756,7 @@ static void eventCallback(struct event_handler_args arg)
if ((ppv_link->pvlMask & pvlOptCP) ||
((ppv_link->pvlMask & pvlOptCPP) && precord->scan == 0))
scanLinkOnce(precord, pca);
scanOnce(precord);
}
done:
epicsMutexUnlock(pca->lock);
@@ -930,11 +825,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 = plink->precord;
precord = ppv_link->precord;
if (precord &&
((ppv_link->pvlMask & pvlOptCP) ||
((ppv_link->pvlMask & pvlOptCPP) && precord->scan == 0)))
scanLinkOnce(precord, pca);
scanOnce(precord);
done:
epicsMutexUnlock(pca->lock);
}
@@ -961,7 +856,7 @@ static void getAttribEventCallback(struct event_handler_args arg)
getAttributes = pca->getAttributes;
getAttributesPvt = pca->getAttributesPvt;
if (arg.status != ECA_NORMAL) {
dbCommon *precord = plink->precord;
dbCommon *precord = plink->value.pv_link.precord;
if (precord) {
errlogPrintf("dbCa: getAttribEventCallback record %s error %s\n",
precord->name, ca_message(arg.status));
@@ -1017,15 +912,11 @@ 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 */
caLinkDec(pca);
dbCaLinkFree(pca);
/* No alarm is raised. Since link is changing so what? */
continue; /* No other link_action makes sense */
}
@@ -1054,11 +945,11 @@ static void dbCaTask(void *arg)
assert(pca->pputNative);
if (pca->putType == CA_PUT) {
status = ca_array_put(
pca->dbrType, pca->putnelements,
pca->dbrType, pca->nelements,
pca->chid, pca->pputNative);
} else if (pca->putType==CA_PUT_CALLBACK) {
status = ca_array_put_callback(
pca->dbrType, pca->putnelements,
pca->dbrType, pca->nelements,
pca->chid, pca->pputNative,
putComplete, pca);
} else {
@@ -1108,15 +999,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->elementSize = dbr_value_size[ca_field_type(pca->chid)];
pca->pgetNative = dbCalloc(pca->nelements, pca->elementSize);
pca->pgetNative = dbCalloc(pca->nelements, element_size);
epicsMutexUnlock(pca->lock);
status = ca_add_array_event(
dbf_type_to_DBR_TIME(ca_field_type(pca->chid)),
0, /* dynamic size */
ca_field_type(pca->chid)+DBR_TIME_STRING,
ca_element_count(pca->chid),
pca->chid, eventCallback, pca, 0.0, 0.0, 0.0, 0);
if (status != ECA_NORMAL) {
errlogPrintf("dbCaTask ca_add_array_event %s\n",

View File

@@ -1,17 +1,20 @@
/*************************************************************************\
* Copyright (c) 2015 The University of Chicago, as Operator of Argonne
* 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 is distributed subject to a Software License Agreement found
* in file LICENSE that is included with this distribution.
* 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.
\*************************************************************************/
/* dbCa.h */
/* dbCa.h */
#ifndef INCdbCah
#define INCdbCah
#include "dbLink.h"
#include "shareLib.h"
#include "epicsTime.h"
#include "link.h"
#ifdef __cplusplus
extern "C" {
@@ -20,70 +23,54 @@ 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);
epicsShareFunc long dbCaAddLink(struct dbLocker *locker, struct link *plink, short dbfType);
epicsShareFunc void dbCaRemoveLink(struct dbLocker *locker, struct link *plink);
#define dbCaAddLink(plink) dbCaAddLinkCallback((plink), 0, 0, 0)
epicsShareFunc void dbCaRemoveLink(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);
epicsShareFunc long dbCaPutLink(struct link *plink,short dbrType,
const void *pbuffer,long nRequest);
#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);
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

View File

@@ -6,11 +6,14 @@
* EPICS BASE is distributed subject to a Software License Agreement found
* in file LICENSE that is included with this distribution.
\*************************************************************************/
/* dbCaPvt.h
*
* Original Authors: Bob Dalesio, Marty Kraimer
*
*/
/* dbCaPvt.h */
/****************************************************************
*
* Current Author: Bob Dalesio
* Contributing Author: Marty Kraimer
* Date: 08APR96
*
****************************************************************/
#ifndef INC_dbCaPvt_H
#define INC_dbCaPvt_H
@@ -29,67 +32,61 @@
#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;
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*/
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*/
}caLink;
#endif /* INC_dbCaPvt_H */

View File

@@ -20,7 +20,6 @@
#include <string.h>
#include <errno.h>
#define EPICS_DBCA_PRIVATE_API
#include "dbDefs.h"
#include "epicsEvent.h"
#include "epicsPrint.h"
@@ -53,61 +52,7 @@
#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;
@@ -123,7 +68,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, "*")) {
@@ -142,32 +87,56 @@ 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++;
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;
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);
}
}
}
dbLockSetGblUnlock();
}
dbScanUnlock(precord);
if (precordname) goto done;
}
status = dbNextRecord(pdbentry);
@@ -220,8 +189,10 @@ 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 (dbIsLinkConnected(plink)) {
if (pca && pca->chid && ca_state(pca->chid) == cs_conn) {
nconnected++;
}
}

View File

@@ -19,6 +19,7 @@
#include "cantProceed.h"
#include "epicsAssert.h"
#include "epicsExit.h"
#include "epicsString.h"
#include "errlog.h"
#include "freeList.h"

View File

@@ -1,118 +0,0 @@
#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 &notify)
: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 &notify, 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

View File

@@ -4,7 +4,7 @@
# Copyright (c) 2002 The Regents of the University of California, as
# Operator of Los Alamos National Laboratory.
# EPICS BASE is distributed subject to a Software License Agreement found
# in file LICENSE that is included with this distribution.
# in file LICENSE that is included with this distribution.
#*************************************************************************
%#include "epicsTypes.h"
%#include "link.h"
@@ -15,59 +15,59 @@
}
field(DESC,DBF_STRING) {
prompt("Descriptor")
promptgroup(GUI_COMMON)
promptgroup("10 - Common")
size(41)
}
field(ASG,DBF_STRING) {
prompt("Access Security Group")
promptgroup(GUI_COMMON)
promptgroup("10 - Common")
special(SPC_AS)
size(29)
}
field(SCAN,DBF_MENU) {
prompt("Scan Mechanism")
promptgroup(GUI_SCAN)
promptgroup("20 - Scan")
special(SPC_SCAN)
interest(1)
menu(menuScan)
}
field(PINI,DBF_MENU) {
prompt("Process at iocInit")
promptgroup(GUI_SCAN)
promptgroup("20 - Scan")
interest(1)
menu(menuPini)
}
field(PHAS,DBF_SHORT) {
prompt("Scan Phase")
promptgroup(GUI_SCAN)
promptgroup("20 - Scan")
special(SPC_SCAN)
interest(1)
}
field(EVNT,DBF_STRING) {
prompt("Event Name")
promptgroup(GUI_SCAN)
promptgroup("20 - Scan")
special(SPC_SCAN)
size(40)
interest(1)
}
field(TSE,DBF_SHORT) {
prompt("Time Stamp Event")
promptgroup(GUI_SCAN)
promptgroup("20 - Scan")
interest(1)
}
field(TSEL,DBF_INLINK) {
prompt("Time Stamp Link")
promptgroup(GUI_SCAN)
promptgroup("20 - Scan")
interest(1)
}
field(DTYP,DBF_DEVICE) {
prompt("Device Type")
promptgroup(GUI_LINKS)
promptgroup("10 - Common")
interest(1)
}
field(DISV,DBF_SHORT) {
prompt("Disable Value")
promptgroup(GUI_SCAN)
promptgroup("20 - Scan")
initial("1")
}
field(DISA,DBF_SHORT) {
@@ -75,7 +75,7 @@
}
field(SDIS,DBF_INLINK) {
prompt("Scanning Disable")
promptgroup(GUI_SCAN)
promptgroup("20 - Scan")
interest(1)
}
%#include "epicsMutex.h"
@@ -92,12 +92,6 @@
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")
}
@@ -137,7 +131,7 @@
}
field(ACKT,DBF_MENU) {
prompt("Alarm Ack Transient")
promptgroup(GUI_ALARMS)
promptgroup("70 - Alarm")
special(SPC_NOMOD)
interest(2)
menu(menuYesNo)
@@ -145,7 +139,7 @@
}
field(DISS,DBF_MENU) {
prompt("Disable Alarm Sevrty")
promptgroup(GUI_SCAN)
promptgroup("70 - Alarm")
interest(1)
menu(menuAlarmSevr)
}
@@ -225,7 +219,7 @@
}
field(PRIO,DBF_MENU) {
prompt("Scheduling Priority")
promptgroup(GUI_SCAN)
promptgroup("20 - Scan")
special(SPC_SCAN)
interest(1)
menu(menuPriority)
@@ -241,14 +235,14 @@
}
field(UDF,DBF_UCHAR) {
prompt("Undefined")
promptgroup(GUI_COMMON)
promptgroup("10 - Common")
pp(TRUE)
interest(1)
initial("1")
}
field(UDFS,DBF_MENU) {
prompt("Undefined Alarm Sevrty")
promptgroup(GUI_COMMON)
promptgroup("70 - Alarm")
interest(1)
menu(menuAlarmSevr)
initial("INVALID")
@@ -262,6 +256,6 @@
}
field(FLNK,DBF_FWDLINK) {
prompt("Forward Process Link")
promptgroup(GUI_LINKS)
promptgroup("20 - Scan")
interest(1)
}

View File

@@ -39,7 +39,6 @@
#include "dbCAC.h"
#include "dbChannel.h"
#include "dbChannelIO.h"
#include "dbChannelNOOP.h"
#include "dbPutNotifyBlocker.h"
class dbService : public cacService {
@@ -62,16 +61,9 @@ cacContext & dbService::contextCreate (
mutualExclusion, notify );
}
extern "C" int dbServiceIsolate;
int dbServiceIsolate = 0;
extern "C" void dbServiceIOInit ()
{
static int init=0;
if(!init) {
caInstallDefaultService ( dbs );
init=1;
}
caInstallDefaultService ( dbs );
}
dbBaseIO::dbBaseIO () {}
@@ -80,8 +72,7 @@ dbContext::dbContext ( epicsMutex & cbMutexIn,
epicsMutex & mutexIn, cacContextNotify & notifyIn ) :
readNotifyCache ( mutexIn ), ctx ( 0 ),
stateNotifyCacheSize ( 0 ), mutex ( mutexIn ), cbMutex ( cbMutexIn ),
notify ( notifyIn ), pNetContext ( 0 ), pStateNotifyCache ( 0 ),
isolated(dbServiceIsolate)
notify ( notifyIn ), pNetContext ( 0 ), pStateNotifyCache ( 0 )
{
}
@@ -101,10 +92,7 @@ cacChannel & dbContext::createChannel (
dbChannel *dbch = dbChannel_create ( pName );
if ( ! dbch ) {
if ( isolated ) {
return *new dbChannelNOOP(pName, notifyIn);
} else if ( ! this->pNetContext.get() ) {
if ( ! this->pNetContext.get() ) {
this->pNetContext.reset (
& this->notify.createNetworkContext (
this->mutex, this->cbMutex ) );
@@ -154,8 +142,7 @@ void dbContext::callStateNotify ( struct dbChannel * dbch,
const struct db_field_log * pfl,
cacStateNotify & notifyIn )
{
long realcount = (count==0)?dbChannelElements(dbch):count;
unsigned long size = dbr_size_n ( type, realcount );
unsigned long size = dbr_size_n ( type, count );
if ( type > INT_MAX ) {
epicsGuard < epicsMutex > guard ( this->mutex );
@@ -182,13 +169,8 @@ void dbContext::callStateNotify ( struct dbChannel * dbch,
this->stateNotifyCacheSize = size;
}
void *pvfl = (void *) pfl;
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 );
int status = dbChannel_get ( dbch, static_cast <int> ( type ),
this->pStateNotifyCache, static_cast <int> ( count ), pvfl );
if ( status ) {
epicsGuard < epicsMutex > guard ( this->mutex );
notifyIn.exception ( guard, ECA_GETFAIL,
@@ -196,7 +178,7 @@ void dbContext::callStateNotify ( struct dbChannel * dbch,
}
else {
epicsGuard < epicsMutex > guard ( this->mutex );
notifyIn.current ( guard, type, realcount, this->pStateNotifyCache );
notifyIn.current ( guard, type, count, this->pStateNotifyCache );
}
}

View File

@@ -61,32 +61,27 @@ void dbContextReadNotifyCache::callReadNotify (
return;
}
const long maxcount = dbChannelElements(dbch);
if ( maxcount < 0 ) {
if ( dbChannelElements(dbch) < 0 ) {
notify.exception ( guard, ECA_BADCOUNT,
"database has negetive element count",
type, count);
return;
}
} else if ( count > (unsigned long)maxcount ) {
if ( count > static_cast < unsigned long > ( dbChannelElements(dbch) ) ) {
notify.exception ( guard, ECA_BADCOUNT,
"element count out of range (high side)",
type, count);
return;
}
long realcount = (count==0)?maxcount:count;
unsigned long size = dbr_size_n ( type, realcount );
unsigned long size = dbr_size_n ( type, count );
privateAutoDestroyPtr ptr ( _allocator, size );
int status;
{
epicsGuardRelease < epicsMutex > unguard ( guard );
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 );
status = dbChannel_get ( dbch, static_cast <int> ( type ),
ptr.get (), static_cast <long> ( count ), 0 );
}
if ( status ) {
notify.exception ( guard, ECA_GETFAIL,
@@ -95,7 +90,7 @@ void dbContextReadNotifyCache::callReadNotify (
}
else {
notify.completion (
guard, type, realcount, ptr.get () );
guard, type, count, ptr.get () );
}
}

View File

@@ -1,13 +1,14 @@
/*************************************************************************\
* 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
*
* 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$ */
/*
* Original Authors: Bob Dalesio, Marty Kraimer
* Current Author: Andrew Johnson
*/
@@ -18,7 +19,6 @@
#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 "dbLockPvt.h"
#include "dbLock.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->precord;
const struct dbCommon *precord = plink->value.pv_link.precord;
const dbRecordType *pdbRecordType = precord->rdes;
dbFldDes * const *papFldDes = pdbRecordType->papFldDes;
const short *link_ind = pdbRecordType->link_ind;
@@ -97,28 +97,13 @@ 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]
@@ -131,7 +116,7 @@ static long dbConstGetNelements(const struct link *plink, long *nelements)
return 0;
}
static long dbConstGetValue(struct link *plink, short dbrType, void *pbuffer,
static long dbConstGetLink(struct link *plink, short dbrType, void *pbuffer,
epicsEnum16 *pstat, epicsEnum16 *psevr, long *pnRequest)
{
if (pnRequest)
@@ -139,25 +124,8 @@ static long dbConstGetValue(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;
@@ -168,49 +136,45 @@ 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;
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);
dbLockSetMerge(plink->value.pv_link.precord, pdbAddr->precord);
return 0;
}
static void dbDbAddLink(dbLocker *locker, struct link *plink, short dbfType, DBADDR *ptarget)
static long dbDbAddLink(struct link *plink, short dbfType)
{
plink->lset = &dbDb_lset;
plink->type = DB_LINK;
plink->value.pv_link.pvt = ptarget;
plink->value.pv_link.backend = "db";
ellAdd(&ptarget->precord->bklnk, &plink->value.pv_link.backlinknode);
DBADDR dbaddr;
long status;
DBADDR *pdbAddr;
/* target record is already locked in dbPutFieldLink() */
dbLockSetMerge(locker, plink->precord, ptarget->precord);
status = dbNameToAddr(plink->value.pv_link.pvname, &dbaddr);
if (status)
return status;
plink->type = DB_LINK;
pdbAddr = dbCalloc(1, sizeof(struct dbAddr));
*pdbAddr = dbaddr; /* structure copy */
plink->value.pv_link.pvt = pdbAddr;
dbLockSetRecordLock(pdbAddr->precord);
dbLockSetMerge(plink->value.pv_link.precord, pdbAddr->precord);
return 0;
}
static void dbDbRemoveLink(dbLocker *locker, struct link *plink)
static void dbDbRemoveLink(struct link *plink)
{
DBADDR *pdbAddr = (DBADDR *) plink->value.pv_link.pvt;
free(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;
ellDelete(&pdbAddr->precord->bklnk, &plink->value.pv_link.backlinknode);
dbLockSetSplit(locker, plink->precord, pdbAddr->precord);
free(pdbAddr);
dbLockSetSplit(plink->value.pv_link.precord);
}
static int dbDbIsConnected(const struct link *plink)
static int dbDbIsLinkConnected(const struct link *plink)
{
return TRUE;
}
@@ -235,7 +199,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->precord;
dbCommon *precord = plink->value.pv_link.precord;
long status;
/* scan passive records if link is process passive */
@@ -260,6 +224,7 @@ static long dbDbGetValue(struct link *plink, short dbrType, void *pbuffer,
return S_db_badDbrtype;
if (paddr->no_elements == 1 && (!pnRequest || *pnRequest == 1)
&& paddr->special != SPC_DBADDR
&& paddr->special != SPC_ATTRIBUTE) {
ppv_link->getCvt = dbFastGetConvertRoutine[dbfType][dbrType];
status = ppv_link->getCvt(paddr->pfield, pbuffer, paddr);
@@ -399,7 +364,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 = plink->precord;
struct dbCommon *psrce = ppv_link->precord;
DBADDR *paddr = (DBADDR *) ppv_link->pvt;
dbCommon *pdest = paddr->precord;
long status = dbPut(paddr, dbrType, pbuffer, nRequest);
@@ -430,118 +395,23 @@ static long dbDbPutValue(struct link *plink, short dbrType,
static void dbDbScanFwdLink(struct link *plink)
{
dbCommon *precord = plink->precord;
dbCommon *precord = plink->value.pv_link.precord;
dbAddr *paddr = (dbAddr *) plink->value.pv_link.pvt;
dbScanPassive(precord, paddr->precord);
}
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
};
lset dbDb_lset = { dbDbInitLink, dbDbAddLink, NULL, dbDbRemoveLink,
dbDbIsLinkConnected, dbDbGetDBFtype, dbDbGetElements, dbDbGetValue,
dbDbGetControlLimits, dbDbGetGraphicLimits, dbDbGetAlarmLimits,
dbDbGetPrecision, dbDbGetUnits, dbDbGetAlarm, dbDbGetTimeStamp,
dbDbPutValue, dbDbScanFwdLink };
/***************************** Generic Link API *****************************/
void dbSetBrokenLink(struct link *plink, short dbfType)
void dbInitLink(struct dbCommon *precord, struct link *plink, short dbfType)
{
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;
plink->value.pv_link.precord = precord;
if (plink == &precord->tsel)
recGblTSELwasModified(plink);
@@ -556,7 +426,7 @@ void dbInitLink(struct link *plink, short dbfType)
if (dbfType == DBF_INLINK)
plink->value.pv_link.pvlMask |= pvlOptInpNative;
customlset(plink, dbfType);
dbCaAddLink(plink);
if (dbfType == DBF_FWDLINK) {
char *pperiod = strrchr(plink->value.pv_link.pvname, '.');
@@ -573,29 +443,24 @@ void dbInitLink(struct link *plink, short dbfType)
}
}
void dbAddLink(dbLocker *locker, struct link *plink, short dbfType, DBADDR *ptarget)
void dbAddLink(struct dbCommon *precord, struct link *plink, short dbfType)
{
struct dbCommon *precord = plink->precord;
if (plink->type == CONSTANT) {
dbConstAddLink(plink);
return;
}
plink->value.pv_link.precord = precord;
if (plink == &precord->tsel)
recGblTSELwasModified(plink);
if (ptarget) {
/* It's a DB link */
dbDbAddLink(locker, plink, dbfType, ptarget);
return;
if (!(plink->value.pv_link.pvlMask & (pvlOptCA | pvlOptCP | pvlOptCPP))) {
/* Can we make it a DB link? */
if (!dbDbAddLink(plink, dbfType))
return;
}
/* Make it a CA link */
if (dbfType == DBF_INLINK)
plink->value.pv_link.pvlMask |= pvlOptInpNative;
customlset(plink, dbfType);
dbCaAddLink(plink);
if (dbfType == DBF_FWDLINK) {
char *pperiod = strrchr(plink->value.pv_link.pvname, '.');
@@ -606,69 +471,69 @@ void dbAddLink(dbLocker *locker, struct link *plink, short dbfType, DBADDR *ptar
long dbLoadLink(struct link *plink, short dbrType, void *pbuffer)
{
if (plink->type == CONSTANT)
switch (plink->type) {
case CONSTANT:
return dbConstLoadLink(plink, dbrType, pbuffer);
/* Could pass a type hint to the other link types here */
}
return S_db_notFound;
}
void dbRemoveLink(dbLocker *locker, struct link *plink)
void dbRemoveLink(struct link *plink)
{
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);
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);
}
plink->type = PV_LINK;
plink->value.pv_link.pvlMask = 0;
}
int dbIsLinkConnected(const struct link *plink)
{
lset *plset = plink->lset;
if (!plset || !plset->isConnected)
return FALSE;
return plset->isConnected(plink);
switch (plink->type) {
case DB_LINK:
return dbDbIsLinkConnected(plink);
case CA_LINK:
return dbCaIsLinkConnected(plink);
}
return FALSE;
}
int dbGetLinkDBFtype(const struct link *plink)
{
lset *plset = plink->lset;
if (!plset || !plset->getDBFtype)
return -1;
return plset->getDBFtype(plink);
switch (plink->type) {
case DB_LINK:
return dbDbGetDBFtype(plink);
case CA_LINK:
return dbCaGetLinkDBFtype(plink);
}
return -1;
}
long dbGetNelements(const struct link *plink, long *nelements)
{
lset *plset = plink->lset;
if (!plset || !plset->getElements)
return S_db_badField;
return plset->getElements(plink, 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;
}
long dbGetLink(struct link *plink, short dbrType, void *pbuffer,
long *poptions, long *pnRequest)
{
struct dbCommon *precord = plink->precord;
struct dbCommon *precord = plink->value.pv_link.precord;
epicsEnum16 sevr = 0, stat = 0;
lset *plset = plink->lset;
long status;
if (poptions && *poptions) {
@@ -676,10 +541,21 @@ long dbGetLink(struct link *plink, short dbrType, void *pbuffer,
*poptions = 0;
}
if (!plset || !plset->getValue)
return -1;
status = plset->getValue(plink, dbrType, pbuffer, &stat, &sevr, pnRequest);
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 (status) {
recGblSetSevr(precord, LINK_ALARM, INVALID_ALARM);
} else {
@@ -690,88 +566,104 @@ long dbGetLink(struct link *plink, short dbrType, void *pbuffer,
long dbGetControlLimits(const struct link *plink, double *low, double *high)
{
lset *plset = plink->lset;
if (!plset || !plset->getControlLimits)
return S_db_notFound;
return plset->getControlLimits(plink, low, high);
switch (plink->type) {
case DB_LINK:
return dbDbGetControlLimits(plink, low, high);
case CA_LINK:
return dbCaGetControlLimits(plink, low, high);
}
return S_db_notFound;
}
long dbGetGraphicLimits(const struct link *plink, double *low, double *high)
{
lset *plset = plink->lset;
if (!plset || !plset->getGraphicLimits)
return S_db_notFound;
return plset->getGraphicLimits(plink, low, high);
switch (plink->type) {
case DB_LINK:
return dbDbGetGraphicLimits(plink, low, high);
case CA_LINK:
return dbCaGetGraphicLimits(plink, low, high);
}
return S_db_notFound;
}
long dbGetAlarmLimits(const struct link *plink, double *lolo, double *low,
double *high, double *hihi)
{
lset *plset = plink->lset;
if (!plset || !plset->getAlarmLimits)
return S_db_notFound;
return plset->getAlarmLimits(plink, lolo, low, high, 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;
}
long dbGetPrecision(const struct link *plink, short *precision)
{
lset *plset = plink->lset;
if (!plset || !plset->getPrecision)
return S_db_notFound;
return plset->getPrecision(plink, precision);
switch (plink->type) {
case DB_LINK:
return dbDbGetPrecision(plink, precision);
case CA_LINK:
return dbCaGetPrecision(plink, precision);
}
return S_db_notFound;
}
long dbGetUnits(const struct link *plink, char *units, int unitsSize)
{
lset *plset = plink->lset;
if (!plset || !plset->getUnits)
return S_db_notFound;
return plset->getUnits(plink, units, unitsSize);
switch (plink->type) {
case DB_LINK:
return dbDbGetUnits(plink, units, unitsSize);
case CA_LINK:
return dbCaGetUnits(plink, units, unitsSize);
}
return S_db_notFound;
}
long dbGetAlarm(const struct link *plink, epicsEnum16 *status,
epicsEnum16 *severity)
{
lset *plset = plink->lset;
if (!plset || !plset->getAlarm)
return S_db_notFound;
return plset->getAlarm(plink, status, severity);
switch (plink->type) {
case DB_LINK:
return dbDbGetAlarm(plink, status, severity);
case CA_LINK:
return dbCaGetAlarm(plink, status, severity);
}
return S_db_notFound;
}
long dbGetTimeStamp(const struct link *plink, epicsTimeStamp *pstamp)
{
lset *plset = plink->lset;
if (!plset || !plset->getTimeStamp)
return S_db_notFound;
return plset->getTimeStamp(plink, pstamp);
switch (plink->type) {
case DB_LINK:
return dbDbGetTimeStamp(plink, pstamp);
case CA_LINK:
return dbCaGetTimeStamp(plink, pstamp);
}
return S_db_notFound;
}
long dbPutLink(struct link *plink, short dbrType, const void *pbuffer,
long nRequest)
{
lset *plset = plink->lset;
long status;
if (!plset || !plset->putValue)
return S_db_notFound;
status = plset->putValue(plink, dbrType, pbuffer, nRequest);
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 (status) {
struct dbCommon *precord = plink->precord;
struct dbCommon *precord = plink->value.pv_link.precord;
recGblSetSevr(precord, LINK_ALARM, INVALID_ALARM);
}
@@ -780,10 +672,14 @@ long dbPutLink(struct link *plink, short dbrType, const void *pbuffer,
void dbScanFwdLink(struct link *plink)
{
lset *plset = plink->lset;
if (plset && plset->scanForward)
plset->scanForward(plink);
switch (plink->type) {
case DB_LINK:
dbDbScanFwdLink(plink);
break;
case CA_LINK:
dbCaScanFwdLink(plink);
break;
}
}
/* Helper functions for long string support */

View File

@@ -6,7 +6,8 @@
* 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
@@ -19,50 +20,21 @@
#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 {
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);
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);
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,
@@ -74,21 +46,19 @@ typedef struct lset {
long (*getTimeStamp)(const struct link *plink, epicsTimeStamp *pstamp);
long (*putValue)(struct link *plink, short dbrType,
const void *pbuffer, long nRequest);
void (*scanForward)(struct link *plink);
void (*scanFwdLink)(struct link *plink);
} lset;
#define dbGetSevr(link, sevr) \
dbGetAlarm(link, NULL, sevr)
#define dbGetSevr(PLINK, PSEVERITY) \
dbGetAlarm((PLINK), NULL, (PSEVERITY))
epicsShareFunc void dbInitLink(struct link *plink, short dbfType);
epicsShareFunc void dbAddLink(struct dbLocker *locker, struct link *plink, short dbfType,
DBADDR *ptarget);
epicsShareFunc void dbInitLink(struct dbCommon *precord, struct link *plink,
short dbfType);
epicsShareFunc void dbAddLink(struct dbCommon *precord, struct link *plink,
short dbfType);
epicsShareFunc long dbLoadLink(struct link *plink, short dbrType,
void *pbuffer);
epicsShareFunc void dbRemoveLink(struct dbLocker *locker, struct link *plink);
epicsShareFunc void dbReportLink(const struct link *plink, dbLinkReportInfo *pinfo);
epicsShareFunc void dbRemoveLink(struct link *plink);
epicsShareFunc long dbGetNelements(const struct link *plink, long *nelements);
epicsShareFunc int dbIsLinkConnected(const struct link *plink);
epicsShareFunc int dbGetLinkDBFtype(const struct link *plink);
@@ -107,7 +77,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 *plink, short dbrType,
epicsShareFunc long dbPutLink(struct link *, short dbrType,
const void *pbuffer, long nRequest);
epicsShareFunc void dbScanFwdLink(struct link *plink);

File diff suppressed because it is too large Load Diff

View File

@@ -13,7 +13,6 @@
#ifndef INCdbLockh
#define INCdbLockh
#include "ellLib.h"
#include "shareLib.h"
#ifdef __cplusplus
@@ -22,26 +21,21 @@ 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);
@@ -53,10 +47,6 @@ 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

View File

@@ -1,110 +0,0 @@
/*************************************************************************\
* 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 */

View File

@@ -25,6 +25,7 @@
#include "ellLib.h"
#include "epicsAssert.h"
#include "epicsEvent.h"
#include "epicsExit.h"
#include "epicsMutex.h"
#include "epicsString.h"
#include "epicsThread.h"
@@ -108,15 +109,6 @@ 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;
@@ -309,7 +301,7 @@ static void notifyCallback(CALLBACK *pcallback)
void dbProcessNotifyExit(void)
{
ellFree2(&pnotifyGlobal->freeList, &notifyFree);
assert(ellCount(&pnotifyGlobal->freeList)==0);
epicsMutexDestroy(pnotifyGlobal->lock);
free(pnotifyGlobal);
pnotifyGlobal = NULL;

View File

@@ -25,9 +25,11 @@
#include "dbDefs.h"
#include "ellLib.h"
#include "epicsEvent.h"
#include "epicsExit.h"
#include "epicsInterrupt.h"
#include "epicsMutex.h"
#include "epicsPrint.h"
#include "epicsRingBytes.h"
#include "epicsRingPointer.h"
#include "epicsStdio.h"
#include "epicsStdlib.h"
#include "epicsString.h"
@@ -62,7 +64,7 @@ static volatile enum ctl scanCtl;
static int onceQueueSize = 1000;
static epicsEventId onceSem;
static epicsRingBytesId onceQ;
static epicsRingPointerId onceQ;
static epicsThreadId onceTaskId;
static void *exitOnce;
@@ -174,7 +176,7 @@ void scanCleanup(void)
deletePeriodic();
ioscanDestroy();
epicsRingBytesDelete(onceQ);
epicsRingPointerDelete(onceQ);
free(periodicTaskId);
papPeriodic = NULL;
@@ -455,9 +457,7 @@ event_list *eventNameToHandle(const char *eventname)
if (strcmp(pel->event_name, eventname) == 0) break;
}
if (pel == NULL) {
pel = calloc(1, sizeof(event_list));
if (!pel)
goto done;
pel = dbCalloc(1, sizeof(event_list));
strcpy(pel->event_name, eventname);
for (prio = 0; prio < NUM_CALLBACK_PRIORITIES; prio++) {
callbackSetUser(&pel->scan_list[prio], &pel->callback[prio]);
@@ -475,7 +475,6 @@ event_list *eventNameToHandle(const char *eventname)
pevent_list[e] = pel;
}
}
done:
epicsMutexUnlock(event_lock);
return pel;
}
@@ -582,28 +581,6 @@ 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)
{
@@ -611,27 +588,15 @@ void scanIoSetComplete(IOSCANPVT piosh, io_scan_complete cb, void *arg)
piosh->arg = arg;
}
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)
void scanOnce(struct dbCommon *precord)
{
static int newOverflow = TRUE;
onceEntry ent;
int lockKey;
int pushOK;
ent.prec = precord;
ent.cb = cb;
ent.usr = usr;
pushOK = epicsRingBytesPut(onceQ, (void*)&ent, sizeof(ent));
lockKey = epicsInterruptLock();
pushOK = epicsRingPointerPush(onceQ, precord);
epicsInterruptUnlock(lockKey);
if (!pushOK) {
if (newOverflow) errlogPrintf("scanOnce: Ring buffer overflow\n");
@@ -640,8 +605,6 @@ int scanOnceCallback(struct dbCommon *precord, once_complete cb, void *usr)
newOverflow = TRUE;
}
epicsEventSignal(onceSem);
return !pushOK;
}
static void onceTask(void *arg)
@@ -650,24 +613,14 @@ static void onceTask(void *arg)
epicsEventSignal(startStopEvent);
while (TRUE) {
void *precord;
epicsEventMustWait(onceSem);
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);
while ((precord = epicsRingPointerPop(onceQ))) {
if (precord == &exitOnce) goto shutdown;
dbScanLock(precord);
dbProcess(precord);
dbScanUnlock(precord);
}
}
@@ -684,7 +637,7 @@ int scanOnceSetQueueSize(int size)
static void initOnce(void)
{
if ((onceQ = epicsRingBytesLockedCreate(sizeof(onceEntry)*onceQueueSize)) == NULL) {
if ((onceQ = epicsRingPointerCreate(onceQueueSize)) == NULL) {
cantProceed("initOnce: Ring buffer create failed\n");
}
if(!onceSem)
@@ -862,11 +815,9 @@ static void spawnPeriodic(int ind)
static void ioscanCallback(CALLBACK *pcallback)
{
ioscan_head *piosh;
int prio;
ioscan_head *piosh = (ioscan_head *) pcallback->user;
int prio = pcallback->priority;
callbackGetUser(piosh, pcallback);
callbackGetPriority(prio, pcallback);
scanList(&piosh->iosl[prio].scan_list);
if (piosh->cb)
piosh->cb(piosh->arg, piosh, prio);

View File

@@ -39,10 +39,9 @@ struct ioscan_head;
typedef struct ioscan_head *IOSCANPVT;
typedef struct event_list *EVENTPVT;
struct dbCommon;
typedef void (*io_scan_complete)(void *usr, IOSCANPVT, int prio);
typedef void (*once_complete)(void *usr, struct dbCommon*);
struct dbCommon;
epicsShareFunc long scanInit(void);
epicsShareFunc void scanRun(void);
@@ -56,8 +55,7 @@ epicsShareFunc void post_event(int event) EPICS_DEPRECATED;
epicsShareFunc void scanAdd(struct dbCommon *);
epicsShareFunc void scanDelete(struct dbCommon *);
epicsShareFunc double scanPeriod(int scan);
epicsShareFunc int scanOnce(struct dbCommon *);
epicsShareFunc int scanOnceCallback(struct dbCommon *, once_complete cb, void *usr);
epicsShareFunc void scanOnce(struct dbCommon *);
epicsShareFunc int scanOnceSetQueueSize(int size);
/*print periodic lists*/
@@ -71,7 +69,6 @@ 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

View File

@@ -15,35 +15,19 @@
#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)
{
if(!testEvtLock)
testEvtLock = epicsMutexMustCreate();
/* No-op at the moment */
}
void testdbReadDatabase(const char* file,
@@ -53,35 +37,19 @@ 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)) {
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);
}
if(dbReadDatabase(&pdbbase, file, path, substitutions))
testAbort("Failed to load test database\ndbReadDatabase(%s,%s,%s)",
file, path, substitutions);
}
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");
}
@@ -231,90 +199,3 @@ 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;
}

View File

@@ -57,26 +57,6 @@ 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

View File

@@ -39,6 +39,7 @@
#include "dbChannel.h"
#include "dbCommon.h"
#include "dbEvent.h"
#include "dbLock.h"
#include "dbNotify.h"
#include "dbStaticLib.h"
#include "recSup.h"
@@ -153,28 +154,30 @@ int dbChannel_get_count(
* in the dbAccess.c dbGet() and getOptions() routines.
*/
dbScanLock(dbChannelRecord(chan));
switch(buffer_type) {
case(oldDBR_STRING):
status = dbChannelGetField(chan, DBR_STRING, pbuffer, &zero, nRequest, pfl);
status = dbChannelGet(chan, DBR_STRING, pbuffer, &zero, nRequest, pfl);
break;
/* case(oldDBR_INT): */
case(oldDBR_SHORT):
status = dbChannelGetField(chan, DBR_SHORT, pbuffer, &zero, nRequest, pfl);
status = dbChannelGet(chan, DBR_SHORT, pbuffer, &zero, nRequest, pfl);
break;
case(oldDBR_FLOAT):
status = dbChannelGetField(chan, DBR_FLOAT, pbuffer, &zero, nRequest, pfl);
status = dbChannelGet(chan, DBR_FLOAT, pbuffer, &zero, nRequest, pfl);
break;
case(oldDBR_ENUM):
status = dbChannelGetField(chan, DBR_ENUM, pbuffer, &zero, nRequest, pfl);
status = dbChannelGet(chan, DBR_ENUM, pbuffer, &zero, nRequest, pfl);
break;
case(oldDBR_CHAR):
status = dbChannelGetField(chan, DBR_CHAR, pbuffer, &zero, nRequest, pfl);
status = dbChannelGet(chan, DBR_CHAR, pbuffer, &zero, nRequest, pfl);
break;
case(oldDBR_LONG):
status = dbChannelGetField(chan, DBR_LONG, pbuffer, &zero, nRequest, pfl);
status = dbChannelGet(chan, DBR_LONG, pbuffer, &zero, nRequest, pfl);
break;
case(oldDBR_DOUBLE):
status = dbChannelGetField(chan, DBR_DOUBLE, pbuffer, &zero, nRequest, pfl);
status = dbChannelGet(chan, DBR_DOUBLE, pbuffer, &zero, nRequest, pfl);
break;
case(oldDBR_STS_STRING):
@@ -187,10 +190,10 @@ int dbChannel_get_count(
} newSt;
options = DBR_STATUS;
status = dbChannelGetField(chan, DBR_STRING, &newSt, &options, &zero, pfl);
status = dbChannelGet(chan, DBR_STRING, &newSt, &options, &zero, pfl);
pold->status = newSt.status;
pold->severity = newSt.severity;
status = dbChannelGetField(chan, DBR_STRING, pold->value, &zero,
status = dbChannelGet(chan, DBR_STRING, pold->value, &zero,
nRequest, pfl);
}
break;
@@ -203,10 +206,10 @@ int dbChannel_get_count(
} newSt;
options = DBR_STATUS;
status = dbChannelGetField(chan, DBR_SHORT, &newSt, &options, &zero, pfl);
status = dbChannelGet(chan, DBR_SHORT, &newSt, &options, &zero, pfl);
pold->status = newSt.status;
pold->severity = newSt.severity;
status = dbChannelGetField(chan, DBR_SHORT, &pold->value, &zero,
status = dbChannelGet(chan, DBR_SHORT, &pold->value, &zero,
nRequest, pfl);
}
break;
@@ -218,10 +221,10 @@ int dbChannel_get_count(
} newSt;
options = DBR_STATUS;
status = dbChannelGetField(chan, DBR_FLOAT, &newSt, &options, &zero, pfl);
status = dbChannelGet(chan, DBR_FLOAT, &newSt, &options, &zero, pfl);
pold->status = newSt.status;
pold->severity = newSt.severity;
status = dbChannelGetField(chan, DBR_FLOAT, &pold->value, &zero,
status = dbChannelGet(chan, DBR_FLOAT, &pold->value, &zero,
nRequest, pfl);
}
break;
@@ -233,10 +236,10 @@ int dbChannel_get_count(
} newSt;
options = DBR_STATUS;
status = dbChannelGetField(chan, DBR_ENUM, &newSt, &options, &zero, pfl);
status = dbChannelGet(chan, DBR_ENUM, &newSt, &options, &zero, pfl);
pold->status = newSt.status;
pold->severity = newSt.severity;
status = dbChannelGetField(chan, DBR_ENUM, &pold->value, &zero,
status = dbChannelGet(chan, DBR_ENUM, &pold->value, &zero,
nRequest, pfl);
}
break;
@@ -248,10 +251,10 @@ int dbChannel_get_count(
} newSt;
options = DBR_STATUS;
status = dbChannelGetField(chan, DBR_UCHAR, &newSt, &options, &zero, pfl);
status = dbChannelGet(chan, DBR_UCHAR, &newSt, &options, &zero, pfl);
pold->status = newSt.status;
pold->severity = newSt.severity;
status = dbChannelGetField(chan, DBR_UCHAR, &pold->value, &zero,
status = dbChannelGet(chan, DBR_UCHAR, &pold->value, &zero,
nRequest, pfl);
}
break;
@@ -263,10 +266,10 @@ int dbChannel_get_count(
} newSt;
options = DBR_STATUS;
status = dbChannelGetField(chan, DBR_LONG, &newSt, &options, &zero, pfl);
status = dbChannelGet(chan, DBR_LONG, &newSt, &options, &zero, pfl);
pold->status = newSt.status;
pold->severity = newSt.severity;
status = dbChannelGetField(chan, DBR_LONG, &pold->value, &zero,
status = dbChannelGet(chan, DBR_LONG, &pold->value, &zero,
nRequest, pfl);
}
break;
@@ -278,11 +281,11 @@ int dbChannel_get_count(
} newSt;
options = DBR_STATUS;
status = dbChannelGetField(chan, DBR_DOUBLE, &newSt, &options, &zero, pfl);
status = dbChannelGet(chan, DBR_DOUBLE, &newSt, &options, &zero, pfl);
pold->status = newSt.status;
pold->severity = newSt.severity;
options = 0;
status = dbChannelGetField(chan, DBR_DOUBLE, &pold->value, &options,
status = dbChannelGet(chan, DBR_DOUBLE, &pold->value, &options,
nRequest, pfl);
}
break;
@@ -296,12 +299,12 @@ int dbChannel_get_count(
} newSt;
options = DBR_STATUS | DBR_TIME;
status = dbChannelGetField(chan, DBR_STRING, &newSt, &options, &zero, pfl);
status = dbChannelGet(chan, DBR_STRING, &newSt, &options, &zero, pfl);
pold->status = newSt.status;
pold->severity = newSt.severity;
pold->stamp = newSt.time; /* structure copy */
options = 0;
status = dbChannelGetField(chan, DBR_STRING, pold->value, &options,
status = dbChannelGet(chan, DBR_STRING, pold->value, &options,
nRequest, pfl);
}
break;
@@ -315,12 +318,12 @@ int dbChannel_get_count(
} newSt;
options = DBR_STATUS | DBR_TIME;
status = dbChannelGetField(chan, DBR_SHORT, &newSt, &options, &zero, pfl);
status = dbChannelGet(chan, DBR_SHORT, &newSt, &options, &zero, pfl);
pold->status = newSt.status;
pold->severity = newSt.severity;
pold->stamp = newSt.time; /* structure copy */
options = 0;
status = dbChannelGetField(chan, DBR_SHORT, &pold->value, &options,
status = dbChannelGet(chan, DBR_SHORT, &pold->value, &options,
nRequest, pfl);
}
break;
@@ -333,12 +336,12 @@ int dbChannel_get_count(
} newSt;
options = DBR_STATUS | DBR_TIME;
status = dbChannelGetField(chan, DBR_FLOAT, &newSt, &options, &zero, pfl);
status = dbChannelGet(chan, DBR_FLOAT, &newSt, &options, &zero, pfl);
pold->status = newSt.status;
pold->severity = newSt.severity;
pold->stamp = newSt.time; /* structure copy */
options = 0;
status = dbChannelGetField(chan, DBR_FLOAT, &pold->value, &options,
status = dbChannelGet(chan, DBR_FLOAT, &pold->value, &options,
nRequest, pfl);
}
break;
@@ -351,12 +354,12 @@ int dbChannel_get_count(
} newSt;
options = DBR_STATUS | DBR_TIME;
status = dbChannelGetField(chan, DBR_ENUM, &newSt, &options, &zero, pfl);
status = dbChannelGet(chan, DBR_ENUM, &newSt, &options, &zero, pfl);
pold->status = newSt.status;
pold->severity = newSt.severity;
pold->stamp = newSt.time; /* structure copy */
options = 0;
status = dbChannelGetField(chan, DBR_ENUM, &pold->value, &options,
status = dbChannelGet(chan, DBR_ENUM, &pold->value, &options,
nRequest, pfl);
}
break;
@@ -369,12 +372,12 @@ int dbChannel_get_count(
} newSt;
options = DBR_STATUS | DBR_TIME;
status = dbChannelGetField(chan, DBR_CHAR, &newSt, &options, &zero, pfl);
status = dbChannelGet(chan, DBR_CHAR, &newSt, &options, &zero, pfl);
pold->status = newSt.status;
pold->severity = newSt.severity;
pold->stamp = newSt.time; /* structure copy */
options = 0;
status = dbChannelGetField(chan, DBR_CHAR, &pold->value, &options,
status = dbChannelGet(chan, DBR_CHAR, &pold->value, &options,
nRequest, pfl);
}
break;
@@ -387,12 +390,12 @@ int dbChannel_get_count(
} newSt;
options = DBR_STATUS | DBR_TIME;
status = dbChannelGetField(chan, DBR_LONG, &newSt, &options, &zero, pfl);
status = dbChannelGet(chan, DBR_LONG, &newSt, &options, &zero, pfl);
pold->status = newSt.status;
pold->severity = newSt.severity;
pold->stamp = newSt.time; /* structure copy */
options = 0;
status = dbChannelGetField(chan, DBR_LONG, &pold->value, &options,
status = dbChannelGet(chan, DBR_LONG, &pold->value, &options,
nRequest, pfl);
}
break;
@@ -405,12 +408,12 @@ int dbChannel_get_count(
} newSt;
options = DBR_STATUS | DBR_TIME;
status = dbChannelGetField(chan, DBR_DOUBLE, &newSt, &options, &zero, pfl);
status = dbChannelGet(chan, DBR_DOUBLE, &newSt, &options, &zero, pfl);
pold->status = newSt.status;
pold->severity = newSt.severity;
pold->stamp = newSt.time; /* structure copy */
options = 0;
status = dbChannelGetField(chan, DBR_DOUBLE, &pold->value, &options,
status = dbChannelGet(chan, DBR_DOUBLE, &pold->value, &options,
nRequest, pfl);
}
break;
@@ -428,7 +431,7 @@ int dbChannel_get_count(
} newSt;
options = DBR_STATUS | DBR_UNITS | DBR_GR_LONG | DBR_AL_LONG;
status = dbChannelGetField(chan, DBR_SHORT, &newSt, &options, &zero, pfl);
status = dbChannelGet(chan, DBR_SHORT, &newSt, &options, &zero, pfl);
pold->status = newSt.status;
pold->severity = newSt.severity;
strncpy(pold->units, newSt.units, MAX_UNITS_SIZE);
@@ -440,7 +443,7 @@ int dbChannel_get_count(
pold->lower_warning_limit = newSt.lower_warning_limit;
pold->lower_alarm_limit = newSt.lower_alarm_limit;
options = 0;
status = dbChannelGetField(chan, DBR_SHORT, &pold->value, &options,
status = dbChannelGet(chan, DBR_SHORT, &pold->value, &options,
nRequest, pfl);
}
break;
@@ -457,7 +460,7 @@ int dbChannel_get_count(
options = DBR_STATUS | DBR_UNITS | DBR_PRECISION | DBR_GR_DOUBLE |
DBR_AL_DOUBLE;
status = dbChannelGetField(chan, DBR_FLOAT, &newSt, &options, &zero, pfl);
status = dbChannelGet(chan, DBR_FLOAT, &newSt, &options, &zero, pfl);
pold->status = newSt.status;
pold->severity = newSt.severity;
pold->precision = (dbr_short_t) newSt.precision.dp;
@@ -470,7 +473,7 @@ int dbChannel_get_count(
pold->upper_warning_limit = epicsConvertDoubleToFloat(newSt.upper_warning_limit);
pold->lower_warning_limit = epicsConvertDoubleToFloat(newSt.lower_warning_limit);
options = 0;
status = dbChannelGetField(chan, DBR_FLOAT, &pold->value, &options,
status = dbChannelGet(chan, DBR_FLOAT, &pold->value, &options,
nRequest, pfl);
}
break;
@@ -486,7 +489,7 @@ int dbChannel_get_count(
} newSt;
options = DBR_STATUS | DBR_UNITS | DBR_GR_LONG | DBR_AL_LONG;
status = dbChannelGetField(chan, DBR_UCHAR, &newSt, &options, &zero, pfl);
status = dbChannelGet(chan, DBR_UCHAR, &newSt, &options, &zero, pfl);
pold->status = newSt.status;
pold->severity = newSt.severity;
strncpy(pold->units, newSt.units, MAX_UNITS_SIZE);
@@ -498,7 +501,7 @@ int dbChannel_get_count(
pold->lower_warning_limit = newSt.lower_warning_limit;
pold->lower_alarm_limit = newSt.lower_alarm_limit;
options = 0;
status = dbChannelGetField(chan, DBR_UCHAR, &pold->value, &options,
status = dbChannelGet(chan, DBR_UCHAR, &pold->value, &options,
nRequest, pfl);
}
break;
@@ -513,7 +516,7 @@ int dbChannel_get_count(
} newSt;
options = DBR_STATUS | DBR_UNITS | DBR_GR_LONG | DBR_AL_LONG;
status = dbChannelGetField(chan, DBR_LONG, &newSt, &options, &zero, pfl);
status = dbChannelGet(chan, DBR_LONG, &newSt, &options, &zero, pfl);
pold->status = newSt.status;
pold->severity = newSt.severity;
strncpy(pold->units, newSt.units, MAX_UNITS_SIZE);
@@ -525,7 +528,7 @@ int dbChannel_get_count(
pold->lower_warning_limit = newSt.lower_warning_limit;
pold->lower_alarm_limit = newSt.lower_alarm_limit;
options = 0;
status = dbChannelGetField(chan, DBR_LONG, &pold->value, &options,
status = dbChannelGet(chan, DBR_LONG, &pold->value, &options,
nRequest, pfl);
}
break;
@@ -542,7 +545,7 @@ int dbChannel_get_count(
options = DBR_STATUS | DBR_UNITS | DBR_PRECISION | DBR_GR_DOUBLE |
DBR_AL_DOUBLE;
status = dbChannelGetField(chan, DBR_DOUBLE, &newSt, &options, &zero, pfl);
status = dbChannelGet(chan, DBR_DOUBLE, &newSt, &options, &zero, pfl);
pold->status = newSt.status;
pold->severity = newSt.severity;
pold->precision = (dbr_short_t) newSt.precision.dp;
@@ -555,7 +558,7 @@ int dbChannel_get_count(
pold->lower_warning_limit = newSt.lower_warning_limit;
pold->lower_alarm_limit = newSt.lower_alarm_limit;
options = 0;
status = dbChannelGetField(chan, DBR_DOUBLE, &pold->value, &options,
status = dbChannelGet(chan, DBR_DOUBLE, &pold->value, &options,
nRequest, pfl);
}
break;
@@ -574,7 +577,7 @@ int dbChannel_get_count(
options = DBR_STATUS | DBR_UNITS | DBR_GR_LONG | DBR_CTRL_LONG |
DBR_AL_LONG;
status = dbChannelGetField(chan, DBR_SHORT, &newSt, &options, &zero, pfl);
status = dbChannelGet(chan, DBR_SHORT, &newSt, &options, &zero, pfl);
pold->status = newSt.status;
pold->severity = newSt.severity;
strncpy(pold->units, newSt.units, MAX_UNITS_SIZE);
@@ -588,7 +591,7 @@ int dbChannel_get_count(
pold->upper_ctrl_limit = newSt.upper_ctrl_limit;
pold->lower_ctrl_limit = newSt.lower_ctrl_limit;
options = 0;
status = dbChannelGetField(chan, DBR_SHORT, &pold->value, &options,
status = dbChannelGet(chan, DBR_SHORT, &pold->value, &options,
nRequest, pfl);
}
break;
@@ -606,7 +609,7 @@ int dbChannel_get_count(
options = DBR_STATUS | DBR_UNITS | DBR_PRECISION | DBR_GR_DOUBLE |
DBR_CTRL_DOUBLE | DBR_AL_DOUBLE;
status = dbChannelGetField(chan, DBR_FLOAT, &newSt, &options, &zero, pfl);
status = dbChannelGet(chan, DBR_FLOAT, &newSt, &options, &zero, pfl);
pold->status = newSt.status;
pold->severity = newSt.severity;
pold->precision = (dbr_short_t) newSt.precision.dp;
@@ -621,7 +624,7 @@ int dbChannel_get_count(
pold->upper_ctrl_limit = epicsConvertDoubleToFloat(newSt.upper_ctrl_limit);
pold->lower_ctrl_limit = epicsConvertDoubleToFloat(newSt.lower_ctrl_limit);
options = 0;
status = dbChannelGetField(chan, DBR_FLOAT, &pold->value, &options,
status = dbChannelGet(chan, DBR_FLOAT, &pold->value, &options,
nRequest, pfl);
}
break;
@@ -638,7 +641,7 @@ int dbChannel_get_count(
memset(pold, '\0', sizeof(struct dbr_ctrl_enum));
/* first get status and severity */
options = DBR_STATUS | DBR_ENUM_STRS;
status = dbChannelGetField(chan, DBR_ENUM, &newSt, &options, &zero, pfl);
status = dbChannelGet(chan, DBR_ENUM, &newSt, &options, &zero, pfl);
pold->status = newSt.status;
pold->severity = newSt.severity;
no_str = newSt.no_str;
@@ -648,7 +651,7 @@ int dbChannel_get_count(
strncpy(pold->strs[i], newSt.strs[i], sizeof(pold->strs[i]));
/*now get values*/
options = 0;
status = dbChannelGetField(chan, DBR_ENUM, &pold->value, &options,
status = dbChannelGet(chan, DBR_ENUM, &pold->value, &options,
nRequest, pfl);
}
break;
@@ -665,7 +668,7 @@ int dbChannel_get_count(
options = DBR_STATUS | DBR_UNITS | DBR_GR_LONG | DBR_CTRL_LONG |
DBR_AL_LONG;
status = dbChannelGetField(chan, DBR_UCHAR, &newSt, &options, &zero, pfl);
status = dbChannelGet(chan, DBR_UCHAR, &newSt, &options, &zero, pfl);
pold->status = newSt.status;
pold->severity = newSt.severity;
strncpy(pold->units, newSt.units, MAX_UNITS_SIZE);
@@ -679,7 +682,7 @@ int dbChannel_get_count(
pold->upper_ctrl_limit = newSt.upper_ctrl_limit;
pold->lower_ctrl_limit = newSt.lower_ctrl_limit;
options = 0;
status = dbChannelGetField(chan, DBR_UCHAR, &pold->value, &options,
status = dbChannelGet(chan, DBR_UCHAR, &pold->value, &options,
nRequest, pfl);
}
break;
@@ -696,7 +699,7 @@ int dbChannel_get_count(
options = DBR_STATUS | DBR_UNITS | DBR_GR_LONG | DBR_CTRL_LONG |
DBR_AL_LONG;
status = dbChannelGetField(chan, DBR_LONG, &newSt, &options, &zero, pfl);
status = dbChannelGet(chan, DBR_LONG, &newSt, &options, &zero, pfl);
pold->status = newSt.status;
pold->severity = newSt.severity;
strncpy(pold->units, newSt.units, MAX_UNITS_SIZE);
@@ -710,7 +713,7 @@ int dbChannel_get_count(
pold->upper_ctrl_limit = newSt.upper_ctrl_limit;
pold->lower_ctrl_limit = newSt.lower_ctrl_limit;
options = 0;
status = dbChannelGetField(chan, DBR_LONG, &pold->value, &options,
status = dbChannelGet(chan, DBR_LONG, &pold->value, &options,
nRequest, pfl);
}
break;
@@ -728,7 +731,7 @@ int dbChannel_get_count(
options = DBR_STATUS | DBR_UNITS | DBR_PRECISION | DBR_GR_DOUBLE |
DBR_CTRL_DOUBLE | DBR_AL_DOUBLE;
status = dbChannelGetField(chan, DBR_DOUBLE, &newSt, &options, &zero, pfl);
status = dbChannelGet(chan, DBR_DOUBLE, &newSt, &options, &zero, pfl);
pold->status = newSt.status;
pold->severity = newSt.severity;
pold->precision = (dbr_short_t) newSt.precision.dp;
@@ -743,7 +746,7 @@ int dbChannel_get_count(
pold->upper_ctrl_limit = newSt.upper_ctrl_limit;
pold->lower_ctrl_limit = newSt.lower_ctrl_limit;
options = 0;
status = dbChannelGetField(chan, DBR_DOUBLE, &pold->value, &options,
status = dbChannelGet(chan, DBR_DOUBLE, &pold->value, &options,
nRequest, pfl);
}
break;
@@ -756,13 +759,13 @@ int dbChannel_get_count(
} newSt;
options = DBR_STATUS;
status = dbChannelGetField(chan, DBR_STRING, &newSt, &options, &zero, pfl);
status = dbChannelGet(chan, DBR_STRING, &newSt, &options, &zero, pfl);
pold->status = newSt.status;
pold->severity = newSt.severity;
pold->ackt = newSt.ackt;
pold->acks = newSt.acks;
options = 0;
status = dbChannelGetField(chan, DBR_STRING, pold->value,
status = dbChannelGet(chan, DBR_STRING, pold->value,
&options, nRequest, pfl);
}
break;
@@ -791,8 +794,12 @@ int dbChannel_get_count(
}
break;
default:
return -1;
status = -1;
break;
}
dbScanUnlock(dbChannelRecord(chan));
if (status) return -1;
return 0;
}

View File

@@ -19,7 +19,8 @@
#ifndef INCLdb_field_logh
#define INCLdb_field_logh
#include "epicsTime.h"
#include <epicsTime.h>
#include <epicsTypes.h>
#ifdef __cplusplus
extern "C" {
@@ -32,18 +33,20 @@ extern "C" {
* priority task pending on the event queue wakes up). Strings would slow down
* events for more reasonable size values. DB fields of native type string
* will most likely change infrequently.
*
*
* Strings can be added to the set of types for which updates will be queued
* by defining the macro DB_EVENT_LOG_STRINGS. The code in db_add_event()
* will adjust automatically, it just compares field sizes.
*/
union native_value {
short dbf_int;
short dbf_short;
float dbf_float;
short dbf_enum;
char dbf_char;
long dbf_long;
double dbf_double;
epicsInt8 dbf_char;
epicsInt16 dbf_short;
epicsEnum16 dbf_enum;
epicsInt32 dbf_long;
epicsFloat32 dbf_float;
epicsFloat64 dbf_double;
#ifdef DB_EVENT_LOG_STRINGS
char dbf_string[MAX_STRING_SIZE];
char dbf_string[MAX_STRING_SIZE];
#endif
};

View File

@@ -48,14 +48,6 @@ 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.

View File

@@ -161,9 +161,14 @@ 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)
{
return !dbLoadLink(plink, dbftype, pdest);
long status = dbLoadLink(plink, dbftype, pdest);
if (status)
return FALSE;
return TRUE;
}
unsigned short recGblResetAlarms(void *precord)

View File

@@ -10,25 +10,20 @@ 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
DBDDEPENDS_FILES += dbTestIoc.dbd$(DEP)
dbTestIoc_DBD += menuGlobal.dbd
dbTestIoc_DBD += menuConvert.dbd
dbTestIoc_DBD += menuScan.dbd
#dbTestIoc_DBD += arrRecord.dbd
dbTestIoc_DBD += xRecord.dbd
dbTestIoc_DBD += arrRecord.dbd
dbTestIoc_DBD += devx.dbd
dbTestIoc_DBD += dbLinkdset.dbd
TESTFILES += $(COMMON_DIR)/dbTestIoc.dbd ../xRecord.db
@@ -36,12 +31,6 @@ 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
@@ -53,7 +42,7 @@ dbPutLinkTest_SRCS += dbPutLinkTest.c
dbPutLinkTest_SRCS += dbTestIoc_registerRecordDeviceDriver.cpp
testHarness_SRCS += dbPutLinkTest.c
TESTS += dbPutLinkTest
TESTFILES += ../dbPutLinkTest.db ../dbBadLink.db
TESTFILES += ../dbPutLinkTest.db
TESTPROD_HOST += dbLockTest
dbLockTest_SRCS += dbLockTest.c
@@ -62,14 +51,6 @@ 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
@@ -95,23 +76,21 @@ dbCaStatsTest_SRCS += dbCaStatsTest.c
dbCaStatsTest_SRCS += dbTestIoc_registerRecordDeviceDriver.cpp
testHarness_SRCS += dbCaStatsTest.c
TESTS += dbCaStatsTest
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
TESTFILES += ../dbCaStatsTest.db
TARGETS += $(COMMON_DIR)/scanIoTest.dbd
DBDDEPENDS_FILES += scanIoTest.dbd$(DEP)
scanIoTest_DBD += menuGlobal.dbd
scanIoTest_DBD += menuConvert.dbd
scanIoTest_DBD += menuScan.dbd
scanIoTest_DBD += yRecord.dbd
TESTPROD_HOST += scanIoTest
scanIoTest_SRCS += scanIoTest.c
scanIoTest_SRCS += dbTestIoc_registerRecordDeviceDriver.cpp
scanIoTest_REGRDDFLAGS = -l
scanIoTest_SRCS += scanIoTest_registerRecordDeviceDriver.cpp
testHarness_SRCS += scanIoTest.c
TESTFILES += ../scanIoTest.db
testHarness_SRCS += scanIoTest_registerRecordDeviceDriver.cpp
TESTFILES += $(COMMON_DIR)/scanIoTest.dbd ../scanIoTest.db
TESTS += scanIoTest
TESTPROD_HOST += dbChannelTest
@@ -121,6 +100,7 @@ testHarness_SRCS += dbChannelTest.c
TESTS += dbChannelTest
TARGETS += $(COMMON_DIR)/dbChArrTest.dbd
DBDDEPENDS_FILES += dbChArrTest.dbd$(DEP)
dbChArrTest_DBD += arrRecord.dbd
TESTPROD_HOST += dbChArrTest
dbChArrTest_SRCS += dbChArrTest.cpp
@@ -168,10 +148,6 @@ TESTSCRIPTS_HOST += $(TESTS:%=%.t)
include $(TOP)/configure/RULES
arrRecord$(DEP): $(COMMON_DIR)/arrRecord.h
dbCaLinkTest$(DEP): $(COMMON_DIR)/xRecord.h $(COMMON_DIR)/arrRecord.h
dbPutLinkTest$(DEP): $(COMMON_DIR)/xRecord.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
dbPutLinkTest$(DEP): $(COMMON_DIR)/xRecord.h
scanIoTest$(DEP): $(COMMON_DIR)/yRecord.h

View File

@@ -97,12 +97,6 @@ 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;
}

View File

@@ -31,12 +31,4 @@ 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*)")
}
}

View File

@@ -22,9 +22,7 @@
#include "dbStaticLib.h"
#include "dbAccessDefs.h"
#include "registry.h"
#include "errlog.h"
#include "epicsExit.h"
#include "dbUnitTest.h"
#include "epicsUnitTest.h"
#include "testMain.h"
#include "osiFileName.h"
@@ -87,20 +85,22 @@ MAIN(arrShorthandTest)
testPlan(26);
testdbPrepare();
db_init_events();
dbChannelInit();
testdbReadDatabase("dbTestIoc.dbd", NULL, NULL);
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");
dbTestIoc_registerRecordDeviceDriver(pdbbase);
testdbReadDatabase("xRecord.db", NULL, NULL);
if (dbReadDatabase(&pdbbase, "xRecord.db",
"." OSI_PATH_LIST_SEPARATOR "..", NULL))
testAbort("Test database 'xRecord.db' not found");
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,8 +129,9 @@ MAIN(arrShorthandTest)
TESTGOOD("range [s::e]", "[2::4]", 2, 1, 4);
TESTGOOD("range with incr [s:i:e]", "[2:3:4]", 2, 3, 4);
testIocShutdownOk();
testdbCleanup();
dbFreeBase(pdbbase);
registryFree();
pdbbase = NULL;
return testDone();
}

View File

@@ -99,7 +99,7 @@ MAIN(callbackParallelTest)
myPvt *pcbt[NCALLBACKS];
epicsTimeStamp start;
int noCpus = epicsThreadGetCPUs();
int i, j;
int i, j, slowups, faults;
/* Statistics: min/max/sum/sum^2/n for each priority */
double setupError[NUM_CALLBACK_PRIORITIES][5];
double timeError[NUM_CALLBACK_PRIORITIES][5];
@@ -109,7 +109,7 @@ MAIN(callbackParallelTest)
for (j = 0; j < 5; j++)
setupError[i][j] = timeError[i][j] = defaultError[j];
testPlan(NCALLBACKS * 2 + 1);
testPlan(4);
testDiag("Starting %d parallel callback threads", noCpus);
@@ -138,7 +138,7 @@ MAIN(callbackParallelTest)
pcbt[NCALLBACKS-1]->delay = TEST_DELAY(NCALLBACKS) + 1.0;
pcbt[NCALLBACKS-1]->pass = 0;
testOk1(epicsTimeGetCurrent(&start)==epicsTimeOK);
testOk(epicsTimeGetCurrent(&start)==epicsTimeOK, "Time-of-day clock Ok");
for (i = 0; i < NCALLBACKS ; i++) {
callbackRequest(&pcbt[i]->cb1);
@@ -147,28 +147,43 @@ MAIN(callbackParallelTest)
testDiag("Waiting %.02f sec", pcbt[NCALLBACKS-1]->delay);
epicsEventWait(finished);
slowups = 0;
faults = 0;
for (i = 0; i < NCALLBACKS ; i++) {
if(pcbt[i]->resultFail || pcbt[i]->pass!=2)
testFail("pass = %d for delay = %f", pcbt[i]->pass, pcbt[i]->delay);
testDiag("callback setup fault #%d: pass = %d for delay = %.02f",
++faults, pcbt[i]->pass, pcbt[i]->delay);
else {
double delta = epicsTimeDiffInSeconds(&pcbt[i]->pass1Time, &start);
testOk(fabs(delta) < 0.05, "callback %.02f setup time |%f| < 0.05",
if (fabs(delta) >= 0.05) {
slowups++;
testDiag("callback %.02f setup time |%f| >= 0.05 seconds",
pcbt[i]->delay, delta);
}
updateStats(setupError[i%NUM_CALLBACK_PRIORITIES], delta);
}
}
testOk(faults == 0, "%d faults during callback setup", faults);
testOk(slowups <= 1, "%d slowups during callback setup", slowups);
slowups = 0;
for (i = 0; i < NCALLBACKS ; i++) {
double delta, error;
if(pcbt[i]->resultFail || pcbt[i]->pass!=2)
continue;
delta = epicsTimeDiffInSeconds(&pcbt[i]->pass2Time, &pcbt[i]->pass1Time);
error = delta - pcbt[i]->delay;
testOk(fabs(error) < 0.05, "delay %.02f seconds, callback time error |%.04f| < 0.05",
if (fabs(error) >= 0.05) {
slowups++;
testDiag("delay %.02f seconds, delay error |%.04f| >= 0.05",
pcbt[i]->delay, error);
}
updateStats(timeError[i%NUM_CALLBACK_PRIORITIES], error);
}
testOk(slowups < 5, "%d slowups during callbacks", slowups);
testDiag("Setup time statistics");
printStats(setupError[0], "LOW");

View File

@@ -99,7 +99,7 @@ MAIN(callbackTest)
{
myPvt *pcbt[NCALLBACKS];
epicsTimeStamp start;
int i, j;
int i, j, slowups, faults;
/* Statistics: min/max/sum/sum^2/n for each priority */
double setupError[NUM_CALLBACK_PRIORITIES][5];
double timeError[NUM_CALLBACK_PRIORITIES][5];
@@ -109,7 +109,7 @@ MAIN(callbackTest)
for (j = 0; j < 5; j++)
setupError[i][j] = timeError[i][j] = defaultError[j];
testPlan(NCALLBACKS * 2 + 1);
testPlan(4);
callbackInit();
epicsThreadSleep(1.0);
@@ -135,7 +135,7 @@ MAIN(callbackTest)
pcbt[NCALLBACKS-1]->delay = TEST_DELAY(NCALLBACKS) + 1.0;
pcbt[NCALLBACKS-1]->pass = 0;
testOk1(epicsTimeGetCurrent(&start)==epicsTimeOK);
testOk(epicsTimeGetCurrent(&start)==epicsTimeOK, "Time-of-day clock Ok");
for (i = 0; i < NCALLBACKS ; i++) {
callbackRequest(&pcbt[i]->cb1);
@@ -144,28 +144,43 @@ MAIN(callbackTest)
testDiag("Waiting %.02f sec", pcbt[NCALLBACKS-1]->delay);
epicsEventWait(finished);
slowups = 0;
faults = 0;
for (i = 0; i < NCALLBACKS ; i++) {
if(pcbt[i]->resultFail || pcbt[i]->pass!=2)
testFail("pass = %d for delay = %f", pcbt[i]->pass, pcbt[i]->delay);
testDiag("callback setup fault #%d: pass = %d for delay = %.02f",
++faults, pcbt[i]->pass, pcbt[i]->delay);
else {
double delta = epicsTimeDiffInSeconds(&pcbt[i]->pass1Time, &start);
testOk(fabs(delta) < 0.05, "callback %.02f setup time |%f| < 0.05",
if (fabs(delta) >= 0.05) {
slowups++;
testDiag("callback %.02f setup time |%f| >= 0.05 seconds",
pcbt[i]->delay, delta);
}
updateStats(setupError[i%NUM_CALLBACK_PRIORITIES], delta);
}
}
testOk(faults == 0, "%d faults during callback setup", faults);
testOk(slowups <= 1, "%d slowups during callback setup", slowups);
slowups = 0;
for (i = 0; i < NCALLBACKS ; i++) {
double delta, error;
if(pcbt[i]->resultFail || pcbt[i]->pass!=2)
continue;
delta = epicsTimeDiffInSeconds(&pcbt[i]->pass2Time, &pcbt[i]->pass1Time);
error = delta - pcbt[i]->delay;
testOk(fabs(error) < 0.05, "delay %.02f seconds, callback time error |%.04f| < 0.05",
if (fabs(error) >= 0.05) {
slowups++;
testDiag("delay %.02f seconds, delay error |%.04f| >= 0.05",
pcbt[i]->delay, error);
}
updateStats(timeError[i%NUM_CALLBACK_PRIORITIES], error);
}
testOk(slowups < 5, "%d slowups during callbacks", slowups);
testDiag("Setup time statistics");
printStats(setupError[0], "LOW");

View File

@@ -1,84 +0,0 @@
/*************************************************************************\
* 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());
}
}

View File

@@ -1,613 +0,0 @@
/*************************************************************************\
* 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();
}

View File

@@ -1,5 +0,0 @@
record(x, "target") {}
record(x, "source") {
field(LNK, "$(TARGET)")
}

View File

@@ -1,10 +0,0 @@
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=))")
}

View File

@@ -1,14 +0,0 @@
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=))")
}

View File

@@ -18,7 +18,7 @@
#include "dbAccessDefs.h"
#include "registry.h"
#include "recSup.h"
#include "dbUnitTest.h"
#include "epicsUnitTest.h"
#include "testMain.h"
#include "osiFileName.h"
#include "errlog.h"
@@ -158,16 +158,17 @@ MAIN(testDbChannel) /* dbChannelTest is an API routine... */
testPlan(76);
testdbPrepare();
testdbReadDatabase("dbTestIoc.dbd", NULL, NULL);
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");
dbTestIoc_registerRecordDeviceDriver(pdbbase);
testdbReadDatabase("xRecord.db", NULL, NULL);
if (dbReadDatabase(&pdbbase, "xRecord.db",
"." OSI_PATH_LIST_SEPARATOR "..", NULL))
testAbort("Test database 'xRecord.db' not found");
eltc(0);
testIocInitOk();
eltc(1);
dbChannelInit();
r = e = 0;
/* dbChannelTest() checks record and field names */
@@ -266,8 +267,9 @@ MAIN(testDbChannel) /* dbChannelTest is an API routine... */
e = e_start | e_start_map | e_abort;
testOk1(!dbChannelCreate("x.{\"scalar\":{}}"));
testIocShutdownOk();
testdbCleanup();
dbFreeBase(pdbbase);
registryFree();
pdbbase = NULL;
return testDone();
}

View File

@@ -12,10 +12,9 @@ long link_test_extend(struct dbCommon *junk)
static dsxt xrecextend = {&link_test_extend, &link_test_extend};
static
long link_test_init(int pass)
long link_test_init(int junk)
{
if (pass == 0)
devExtend(&xrecextend);
devExtend(&xrecextend);
return 0;
}
@@ -29,6 +28,7 @@ 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)

View File

@@ -1,3 +1,5 @@
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")

View File

@@ -9,15 +9,7 @@
* Author: Michael Davidsaver <mdavidsaver@bnl.gov>
*/
#include <stdlib.h>
#include "epicsSpin.h"
#include "epicsMutex.h"
#include "dbCommon.h"
#include "epicsThread.h"
#include "dbLockPvt.h"
#include "dbStaticLib.h"
#include "dbLock.h"
#include "dbUnitTest.h"
#include "testMain.h"
@@ -36,19 +28,15 @@ void compareSets(int match, const char *A, const char *B)
rA = testdbRecordPtr(A);
rB = testdbRecordPtr(B);
actual = rA->lset->plockSet==rB->lset->plockSet;
actual = dbLockGetLockId(rA)==dbLockGetLockId(rB);
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) {
DBENTRY entry;
long status;
testDiag("Check initial creation of DB links");
testdbPrepare();
@@ -60,27 +48,6 @@ 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");
@@ -104,310 +71,6 @@ 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();
@@ -415,17 +78,7 @@ static void testLinkNOP(void)
MAIN(dbLockTest)
{
#ifdef LOCKSET_DEBUG
testPlan(100);
#else
testPlan(88);
#endif
testPlan(15);
testSets();
testSingleLock();
testMultiLock();
testLinkBreak();
testLinkMake();
testLinkChange();
testLinkNOP();
return testDone();
}

View File

@@ -19,6 +19,3 @@ record(x, "rece") {
record(x, "recf") {
}
record(x, "recg") {
}

View File

@@ -20,7 +20,6 @@
#include "dbAccess.h"
#include "registry.h"
#include "dbStaticLib.h"
#include "dbStaticPvt.h"
#include "osiFileName.h"
#include "dbmf.h"
#include "errlog.h"
@@ -29,124 +28,8 @@
#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;
@@ -156,7 +39,6 @@ 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"},
@@ -195,17 +77,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)
@@ -445,7 +327,7 @@ static void testLinkInitFail(void)
testOk1(plink->type==VME_IO);
testOk1(plink->value.vmeio.parm!=NULL);
testdbGetFieldEqual("eVME_IO2.INP", DBR_STRING, "#C0 S0 @");
testdbGetFieldEqual("eVME_IO2.INP", DBR_STRING, "#C200 S0 @");
prec = (xRecord*)testdbRecordPtr("eVME_IO2");
plink = &prec->inp;
@@ -482,8 +364,9 @@ static void testLinkFail(void)
/* INST_IO doesn't accept empty string */
testdbPutFieldFail(S_dbLib_badField, "rINST_IO.INP", DBR_STRING, "");
/* INST_IO doesn't accept empty string */
testdbPutFieldFail(S_dbLib_badField, "rINST_IO.INP", DBR_STRING, "abc");
/* INST_IO accepts invalid input as empty string */
testdbPutFieldOk("rINST_IO.INP", DBR_STRING, "abc");
testdbGetFieldEqual("rINST_IO.INP", DBR_STRING, "@");
/* syntax errors */
testdbPutFieldFail(S_dbLib_badField, "rVME_IO.INP", DBR_STRING, "#S201 C200 @another VME_IO");
@@ -504,9 +387,7 @@ static void testLinkFail(void)
MAIN(dbPutLinkTest)
{
testPlan(251);
testLinkParse();
testLinkFailParse();
testPlan(200);
testCADBSet();
testHWInitSet();
testHWMod();

View File

@@ -1,73 +0,0 @@
/*************************************************************************\
* 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();
}

View File

@@ -1,358 +0,0 @@
/*************************************************************************\
* 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();
}

View File

@@ -1,40 +0,0 @@
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") {}

View File

@@ -1,163 +0,0 @@
/*************************************************************************\
* 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);

View File

@@ -1,2 +0,0 @@
device(x, CONSTANT, devxSoft, "Soft Channel")
device(x, INST_IO , devxScanIO, "Scan I/O")

View File

@@ -1,52 +0,0 @@
/*************************************************************************\
* 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 */

View File

@@ -22,11 +22,9 @@ 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);
@@ -42,11 +40,9 @@ 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);

View File

@@ -12,8 +12,6 @@
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "epicsEvent.h"
#include "epicsMessageQueue.h"
@@ -28,6 +26,9 @@
#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"
@@ -37,272 +38,482 @@
#include "testMain.h"
#include "osiFileName.h"
#define GEN_SIZE_OFFSET
#include "yRecord.h"
#include "epicsExport.h"
#include "devx.h"
#include "xRecord.h"
#define ONE_THREAD_LOOPS 101
#define PAR_THREAD_LOOPS 53
#define CB_THREAD_LOOPS 13
STATIC_ASSERT(NUM_CALLBACK_PRIORITIES==3);
#define NO_OF_THREADS 7
#define NO_OF_MEMBERS 5
#define NO_OF_GROUPS 11
void dbTestIoc_registerRecordDeviceDriver(struct dbBase *);
#define NO_OF_MID_THREADS 3
static void loadRecord(int group, int member, const char *prio)
{
char buf[40];
sprintf(buf, "GROUP=%d,MEMBER=%d,PRIO=%s",
group, member, prio);
testdbReadDatabase("scanIoTest.db", NULL, buf);
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;
}
typedef struct {
int hasprocd[NUM_CALLBACK_PRIORITIES];
int getcomplete[NUM_CALLBACK_PRIORITIES];
epicsEventId wait[NUM_CALLBACK_PRIORITIES];
epicsEventId wake[NUM_CALLBACK_PRIORITIES];
} testsingle;
/*************************************************************************\
* yRecord: minimal record needed to test I/O Intr scanning
\*************************************************************************/
static void testcb(xpriv *priv, void *raw)
static long get_ioint_info(int cmd, yRecord *prec, IOSCANPVT *ppvt)
{
testsingle *td = raw;
int prio = priv->prec->prio;
struct pvtY *pvt = (struct pvtY *)(prec->dpvt);
testOk1(td->hasprocd[prio]==0);
td->hasprocd[prio] = 1;
if (testNo == 2)
*ppvt = ioscanpvt[groupTable[pvt->group].ioscan];
else
*ppvt = ioscanpvt[pvt->group];
return 0;
}
static void testcomp(void *raw, IOSCANPVT scan, int prio)
{
testsingle *td = raw;
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);
testOk1(td->hasprocd[prio]==1);
testOk1(td->getcomplete[prio]==0);
td->getcomplete[prio] = 1;
epicsEventMustTrigger(td->wait[prio]);
epicsEventMustWait(td->wake[prio]);
static long init_record(yRecord *prec, int pass)
{
struct pvtY *pvt;
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;
}
static void testSingleThreading(void)
static long process(yRecord *prec)
{
int i;
testsingle data[2];
xdrv *drvs[2];
struct pvtY *pvt = (struct pvtY *)(prec->dpvt);
memset(data, 0, sizeof(data));
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;
}
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);
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'");
}
}
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]);
static void stopMockIoc(void) {
int 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();
static void checkProcessed(void *user, IOSCANPVT ioscan, int prio) {
struct pvtY *pvt;
int group = -1;
int i;
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]);
for (i = 0; i < noOfGroups; i++) {
if (ioscanpvt[groupTable[i].ioscan] == ioscan
&& groupTable[i].prio == prio) {
group = i;
break;
}
}
}
if (group == -1)
testAbort("invalid ioscanpvt in scanio callback");
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];
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);
}
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);
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;
}
}
/*************************************************************************\
* scanIoTest: Test I/O Intr scanning
* including parallel callback threads and scanio callbacks
\*************************************************************************/
MAIN(scanIoTest)
{
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();
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();
return testDone();
}

View File

@@ -1,6 +1,4 @@
record(x, "g$(GROUP)m$(MEMBER)") {
field(DTYP, "Scan I/O")
field(INP , "@$(GROUP) $(MEMBER)")
record(y, g$(GROUP)m$(MEMBER)) {
field(SCAN, "I/O Intr")
field(PRIO, "$(PRIO)")
}

View File

@@ -16,47 +16,22 @@
#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 ret;
return 0;
}
static rset xRSET = {
RSETNUMBER, NULL, NULL, init_record, process
RSETNUMBER, NULL, NULL, NULL, process
};
epicsExportAddress(rset,xRSET);

View File

@@ -11,9 +11,4 @@ recordtype(x) {
field(INP, DBF_INLINK) {
prompt("Input Link")
}
field(CLBK, DBF_NOACCESS) {
prompt("Processing callback")
special(SPC_NOMOD)
extra("void (*clbk)(struct xRecord*)")
}
}

View File

@@ -0,0 +1,10 @@
# 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")

View File

@@ -76,9 +76,8 @@ 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 isDevLink:1; /* true for INP/OUT fields */
unsigned int process_passive:1;/*should dbPutField process passive */
unsigned int prop:1;/*field is a metadata, post DBE_PROPERTY on change*/
ctType base; /*base for integer to string conversions*/
short promptgroup; /*prompt, i.e. gui group */
short interest; /*interest level */
@@ -166,8 +165,9 @@ typedef struct dbBase {
ELLLIST functionList;
ELLLIST variableList;
ELLLIST bptList;
ELLLIST filterList;
void *pathPvt;
ELLLIST filterList;
ELLLIST guiGroupList;
void *pathPvt;
struct dbPvd *ppvd;
struct gphPvt *pgpHash;
short ignoreMissingMenus;

View File

@@ -34,7 +34,6 @@
#include "dbStaticLib.h"
#include "dbStaticPvt.h"
#include "epicsExport.h"
#include "guigroup.h"
#include "link.h"
#include "special.h"
@@ -71,6 +70,7 @@ static void dbRecordtypeEmpty(void);
static void dbRecordtypeBody(void);
static void dbRecordtypeFieldHead(char *name,char *type);
static void dbRecordtypeFieldItem(char *name,char *value);
static short findOrAddGuiGroup(const char *name);
static void dbDevice(char *recordtype,char *linktype,
char *dsetname,char *choicestring);
@@ -204,10 +204,6 @@ 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) {
@@ -266,10 +262,9 @@ static long dbReadCOM(DBBASE **ppdbbase,const char *filename, FILE *fp,
ellAdd(&inputFileList,&pinputFile->node);
status = pvt_yy_parse();
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 */
if (yyAbort)
while (ellCount(&tempList))
popFirstTemp();
dbFreePath(pdbbase);
if(!status) { /*add RTYP and VERS as an attribute */
@@ -495,14 +490,28 @@ 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");
pdbFldDes->field_type = i;
}
static short findOrAddGuiGroup(const char *name)
{
dbGuiGroup *pdbGuiGroup;
GPHENTRY *pgphentry;
pgphentry = gphFind(pdbbase->pgpHash, name, &pdbbase->guiGroupList);
if (!pgphentry) {
pdbGuiGroup = dbCalloc(1,sizeof(dbGuiGroup));
pdbGuiGroup->name = epicsStrDup(name);
ellAdd(&pdbbase->guiGroupList, &pdbGuiGroup->node);
pdbGuiGroup->key = ellCount(&pdbbase->guiGroupList);
pgphentry = gphAdd(pdbbase->pgpHash, pdbGuiGroup->name, &pdbbase->guiGroupList);
pgphentry->userPvt = pdbGuiGroup;
}
return ((dbGuiGroup *)pgphentry->userPvt)->key;
}
static void dbRecordtypeFieldItem(char *name,char *value)
{
dbFldDes *pdbFldDes;
@@ -524,14 +533,7 @@ static void dbRecordtypeFieldItem(char *name,char *value)
return;
}
if(strcmp(name,"promptgroup")==0) {
int i;
for(i=0; i<GUI_NTYPES; i++) {
if(strcmp(value,pamapguiGroup[i].strvalue)==0) {
pdbFldDes->promptgroup = pamapguiGroup[i].value;
return;
}
}
yyerror("Illegal promptgroup. See guigroup.h for legal values");
pdbFldDes->promptgroup = findOrAddGuiGroup(value);
return;
}
if(strcmp(name,"prompt")==0) {

File diff suppressed because it is too large Load Diff

View File

@@ -47,7 +47,7 @@ extern "C" {
typedef dbBase DBBASE;
typedef struct DBENTRY {
typedef struct{
DBBASE *pdbbase;
dbRecordType *precordType;
dbFldDes *pflddes;
@@ -81,6 +81,10 @@ epicsShareFunc long dbReadDatabaseFP(DBBASE **ppdbbase,
FILE *fp, const char *path, const char *substitutions);
epicsShareFunc long dbPath(DBBASE *pdbbase, const char *path);
epicsShareFunc long dbAddPath(DBBASE *pdbbase, const char *path);
epicsShareFunc char * dbGetPromptGroupNameFromKey(DBBASE *pdbbase,
const short key);
epicsShareFunc short dbGetPromptGroupKeyFromName(DBBASE *pdbbase,
const char *name);
epicsShareFunc long dbWriteRecord(DBBASE *ppdbbase,
const char *filename, const char *precordTypename, int level);
epicsShareFunc long dbWriteRecordFP(DBBASE *ppdbbase,

View File

@@ -36,45 +36,19 @@ 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;
char *directory;
} dbPathNode;
/* Element of the global gui group list */
typedef struct dbGuiGroup {
ELLNODE node;
short key;
char *name;
} dbGuiGroup;
/*The following are in dbPvdLib.c*/
/*directory*/
typedef struct{

View File

@@ -25,12 +25,141 @@
#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 */
@@ -69,7 +198,7 @@ long dbAllocRecord(DBENTRY *pdbentry,const char *precordName)
dbRecordNode *precnode = pdbentry->precnode;
dbFldDes *pflddes;
int i;
dbCommon *precord;
char *precord;
char *pfield;
if(!pdbRecordType) return(S_dbLib_recordTypeNotFound);
@@ -80,25 +209,24 @@ long dbAllocRecord(DBENTRY *pdbentry,const char *precordName)
precordName, pdbRecordType->name);
return(S_dbLib_noRecSup);
}
precord = dbCalloc(1, pdbRecordType->rec_size);
precnode->precord = precord;
precnode->precord = dbCalloc(1,pdbRecordType->rec_size);
precord = (char *)precnode->precord;
pflddes = pdbRecordType->papFldDes[0];
if(!pflddes) {
epicsPrintf("dbAllocRecord pflddes for NAME not found\n");
return(S_dbLib_flddesNotFound);
}
assert(pflddes->offset == 0);
assert(pflddes->size == sizeof(precord->name));
if(strlen(precordName) >= sizeof(precord->name)) {
if(strlen(precordName)>=pflddes->size) {
epicsPrintf("dbAllocRecord: NAME(%s) too long\n",precordName);
return(S_dbLib_nameLength);
}
strcpy(precord->name, precordName);
pfield = precord + pflddes->offset;
strcpy(pfield,precordName);
for(i=1; i<pdbRecordType->no_fields; i++) {
pflddes = pdbRecordType->papFldDes[i];
if(!pflddes) continue;
pfield = (char*)precord + pflddes->offset;
pfield = precord + pflddes->offset;
pdbentry->pfield = (void *)pfield;
pdbentry->pflddes = pflddes;
pdbentry->indfield = i;
@@ -142,9 +270,9 @@ long dbAllocRecord(DBENTRY *pdbentry,const char *precordName)
plink->type = CONSTANT;
if(pflddes->initial) {
plink->text =
plink->value.constantStr =
dbCalloc(strlen(pflddes->initial)+1,sizeof(char));
strcpy(plink->text,pflddes->initial);
strcpy(plink->value.constantStr,pflddes->initial);
}
}
break;
@@ -350,7 +478,95 @@ 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;

View File

@@ -118,7 +118,7 @@ recordtype_field: tokenFIELD recordtype_field_head recordtype_field_body
| tokenCDEFS
{
if(dbStaticDebug>2) printf("recordtype_cdef %s", $1);
dbRecordtypeCdef($1); dbmfFree($1);
dbRecordtypeCdef($1);
}
| include ;

Some files were not shown because too many files have changed in this diff Show More