diff --git a/.ci/travis-build.sh b/.ci/travis-build.sh index e7c2531b3..39eb0ea5f 100755 --- a/.ci/travis-build.sh +++ b/.ci/travis-build.sh @@ -6,18 +6,9 @@ die() { exit 1 } -ticker() { - while true - do - sleep 60 - date -R - [ -r "$1" ] && tail -n10 "$1" - done -} - CACHEKEY=1 -EPICS_HOST_ARCH=`sh startup/EpicsHostArch` +export EPICS_HOST_ARCH=`perl src/tools/EpicsHostArch.pl` [ -e configure/os/CONFIG_SITE.Common.linux-x86 ] || die "Wrong location: $PWD" @@ -57,28 +48,24 @@ EOF fi # set RTEMS to eg. "4.9" or "4.10" -# requires qemu, bison, flex, texinfo, install-info if [ -n "$RTEMS" ] then echo "Cross RTEMS${RTEMS} for pc386" - install -d /home/travis/.cache - curl -L "https://github.com/mdavidsaver/rsb/releases/download/travis-20160306-2/rtems${RTEMS}-i386-trusty-20190306-2.tar.gz" \ - | tar -C /home/travis/.cache -xj + curl -L "https://github.com/mdavidsaver/rsb/releases/download/20171203-${RTEMS}/i386-rtems${RTEMS}-trusty-20171203-${RTEMS}.tar.bz2" \ + | tar -C / -xmj sed -i -e '/^RTEMS_VERSION/d' -e '/^RTEMS_BASE/d' configure/os/CONFIG_SITE.Common.RTEMS cat << EOF >> configure/os/CONFIG_SITE.Common.RTEMS RTEMS_VERSION=$RTEMS -RTEMS_BASE=/home/travis/.cache/rtems${RTEMS}-i386 +RTEMS_BASE=$HOME/.rtems EOF cat << EOF >> configure/CONFIG_SITE -CROSS_COMPILER_TARGET_ARCHS+=RTEMS-pc386 +CROSS_COMPILER_TARGET_ARCHS += RTEMS-pc386-qemu EOF # find local qemu-system-i386 - export PATH="$HOME/.cache/qemu/usr/bin:$PATH" echo -n "Using QEMU: " type qemu-system-i386 || echo "Missing qemu" - EXTRA=RTEMS_QEMU_FIXUPS=YES fi make -j2 $EXTRA diff --git a/.ci/travis-prepare.sh b/.ci/travis-prepare.sh deleted file mode 100755 index 0107c2c0c..000000000 --- a/.ci/travis-prepare.sh +++ /dev/null @@ -1,46 +0,0 @@ -#!/bin/sh -set -e -x - -die() { - echo "$1" >&2 - exit 1 -} - -CURDIR="$PWD" - -QDIR="$HOME/.cache/qemu" - -if [ -n "$RTEMS" -a "$TEST" = "YES" ] -then - git clone --quiet --branch vme --depth 10 https://github.com/mdavidsaver/qemu.git "$HOME/.build/qemu" - cd "$HOME/.build/qemu" - - HEAD=`git log -n1 --pretty=format:%H` - echo "HEAD revision $HEAD" - - [ -e "$HOME/.cache/qemu/built" ] && BUILT=`cat "$HOME/.cache/qemu/built"` - echo "Cached revision $BUILT" - - if [ "$HEAD" != "$BUILT" ] - then - echo "Building QEMU" - git submodule --quiet update --init - - install -d "$HOME/.build/qemu/build" - cd "$HOME/.build/qemu/build" - - "$HOME/.build/qemu/configure" --prefix="$HOME/.cache/qemu/usr" --target-list=i386-softmmu --disable-werror - make -j2 - make install - - echo "$HEAD" > "$HOME/.cache/qemu/built" - fi -fi - -cd "$CURDIR" - -cat <> configure/CONFIG_SITE -USR_CPPFLAGS += $USR_CPPFLAGS -USR_CFLAGS += $USR_CFLAGS -USR_CXXFLAGS += $USR_CXXFLAGS -EOF diff --git a/.travis.yml b/.travis.yml index ba866bb9f..d6130c753 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,23 +11,17 @@ addons: - perl - clang - g++-mingw-w64-i686 - - bison - - flex - - texinfo - - install-info -cache: - directories: - - $HOME/.cache -install: - - ./.ci/travis-prepare.sh + - qemu-system-x86 script: - - ./.ci/travis-build.sh + - .ci/travis-build.sh env: - - BRCORE=master BRLIBCOM=master BRCA=master - - CMPLR=clang - - USR_CXXFLAGS=-std=c++11 - - CMPLR=clang USR_CXXFLAGS=-std=c++11 - - WINE=32 TEST=NO STATIC=YES - - WINE=32 TEST=NO STATIC=NO - - RTEMS=4.10 TEST=NO - - RTEMS=4.9 TEST=NO + - CMPLR=gcc + - CMPLR=clang + - CMPLR=gcc STATIC=YES + - CMPLR=clang STATIC=YES + - CMPLR=gcc EXTRA=CMD_CXXFLAGS=-std=c++11 + - CMPLR=clang EXTRA=CMD_CXXFLAGS=-std=c++11 + - WINE=32 TEST=NO STATIC=YES + - WINE=32 TEST=NO STATIC=NO + - RTEMS=4.10 TEST=YES + - RTEMS=4.9 TEST=YES diff --git a/configure/CONFIG b/configure/CONFIG index 435ff9e77..e40d3f5f7 100644 --- a/configure/CONFIG +++ b/configure/CONFIG @@ -20,11 +20,14 @@ else endif # Provide a default if the user hasn't set EPICS_HOST_ARCH -ifeq ($(strip $(EPICS_HOST_ARCH)),) - # NB: We must set the environment variable for submodules to include - # the correct modules/RELEASE..local file to set EPICS_BASE, - # they can't do this for themselves since CONFIG is relative to it: - export EPICS_HOST_ARCH := $(shell $(CONFIG)/../startup/EpicsHostArch.pl) +# +ifeq ($(origin EPICS_HOST_ARCH), undefined) + # Bootstrapping ... + EHA := $(firstword $(wildcard $(EPICS_BASE)/lib/perl/EpicsHostArch.pl \ + $(TOP)/src/tools/EpicsHostArch.pl)) + # NB: We use a simply expanded variable here for performance: + export EPICS_HOST_ARCH := $(shell perl $(EHA)) + EHA := endif -include $(CONFIG)/RELEASE diff --git a/configure/CONFIG.gnuCommon b/configure/CONFIG.gnuCommon index 025391548..c4fd8cedd 100644 --- a/configure/CONFIG.gnuCommon +++ b/configure/CONFIG.gnuCommon @@ -24,9 +24,14 @@ LD = $(GNU_BIN)/$(CMPLR_PREFIX)ld$(CMPLR_SUFFIX) -r CPP = $(CC) -x c -E RANLIB = $(GNU_BIN)/$(CMPLR_PREFIX)ranlib$(CMPLR_SUFFIX) +# Requires at least GCC 4.8 or LLVM (clang) 3.1 +ASAN_FLAGS_YES = -fsanitize=address +ASAN_LDFLAGS_YES = $(ASAN_FLAGS_YES) + PROF_CFLAGS_YES = -p GPROF_CFLAGS_YES = -pg CODE_CFLAGS = $(PROF_CFLAGS_$(PROFILE)) $(GPROF_CFLAGS_$(GPROF)) +CODE_CFLAGS += $(ASAN_FLAGS_$(ENABLE_ASAN)) WARN_CFLAGS_YES = -Wall WARN_CFLAGS_NO = -w OPT_CFLAGS_YES = -O3 @@ -35,12 +40,14 @@ OPT_CFLAGS_NO = -g PROF_CXXFLAGS_YES = -p GPROF_CXXFLAGS_YES = -pg CODE_CXXFLAGS = $(PROF_CXXFLAGS_$(PROFILE)) $(GPROF_CXXFLAGS_$(GPROF)) +CODE_CXXFLAGS += $(ASAN_FLAGS_$(ENABLE_ASAN)) WARN_CXXFLAGS_YES = -Wall WARN_CXXFLAGS_NO = -w OPT_CXXFLAGS_YES = -O3 OPT_CXXFLAGS_NO = -g CODE_LDFLAGS = $(PROF_CXXFLAGS_$(PROFILE)) $(GPROF_CXXFLAGS_$(GPROF)) +CODE_LDFLAGS += $(ASAN_LDFLAGS_$(ENABLE_ASAN)) PIPE_CFLAGS_YES_YES = -pipe PIPE_CFLAGS = $(PIPE_CFLAGS_$(GCC_PIPE)_$(GNU)) diff --git a/configure/CONFIG_BASE b/configure/CONFIG_BASE index 6d0416369..137ce0098 100644 --- a/configure/CONFIG_BASE +++ b/configure/CONFIG_BASE @@ -42,7 +42,7 @@ PODTOHTML = $(PERL) $(TOOLS)/podToHtml.pl CONVERTRELEASE = $(PERL) $(call FIND_TOOL,convertRelease.pl) FULLPATHNAME = $(PERL) $(TOOLS)/fullPathName.pl TAPTOJUNIT = $(PERL) $(TOOLS)/tap-to-junit-xml.pl -GENVERSIONHEADER = $(PERL) $(TOOLS)/genVersionHeader.pl $(QUIET_FLAG) +GENVERSIONHEADER = $(PERL) $(TOOLS)/genVersionHeader.pl $(QUIET_FLAG) $(QUESTION_FLAG) #--------------------------------------------------------------- # tools for installing libraries and products diff --git a/configure/CONFIG_BASE_VERSION b/configure/CONFIG_BASE_VERSION index 677fb5acf..715fc168c 100644 --- a/configure/CONFIG_BASE_VERSION +++ b/configure/CONFIG_BASE_VERSION @@ -15,17 +15,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 +# We define convenience macros for our release series to be NO or YES, so +# Makefiles can detect 'Series X or later', or 'Series X only' like this: +# # ifdef BASE_3_14 # true for 3.14 or later # ifdef BASE_3_15 # true for 3.15 or later +# ifdef BASE_3_16 +# true for 3.16 or later +# ifdef BASE_7_0 +# true for 7.0 or later +# # ifeq ($(BASE_3_14),YES) # true for 3.14.x only # ifeq ($(BASE_3_15),YES) -# true for 3.15 only +# true for 3.15.x only # ifeq ($(BASE_3_16),YES) -# true for 3.16 only. +# true for 3.16.x only. +# ifeq ($(BASE_7_0),YES) +# true for 7.0.x only. BASE_3_14 = NO BASE_3_15 = NO @@ -39,11 +48,11 @@ EPICS_VERSION = 7 EPICS_REVISION = 0 # EPICS_MODIFICATION must be a number >=0 and <256 -EPICS_MODIFICATION = 1 +EPICS_MODIFICATION = 2 # EPICS_PATCH_LEVEL must be a number (win32 resource file requirement) # Not included if zero -EPICS_PATCH_LEVEL = 2 +EPICS_PATCH_LEVEL = 0 # This will end in -DEV between official releases EPICS_DEV_SNAPSHOT=-DEV diff --git a/configure/CONFIG_COMMON b/configure/CONFIG_COMMON index 67cf33e1d..d3a66968f 100644 --- a/configure/CONFIG_COMMON +++ b/configure/CONFIG_COMMON @@ -76,10 +76,14 @@ COMMON_DIR = ../O.Common IOCS_APPL_TOP = $(shell $(FULLPATHNAME) $(INSTALL_LOCATION)) #------------------------------------------------------- -# Make echo output - suppress echoing if make's '-s' flag is set +# Silencing the build - suppress messages during 'make -s' NOP = : -ECHO = @$(if $(findstring s,$(patsubst T_A=%,,$(MAKEFLAGS))),$(NOP),echo) -QUIET_FLAG := $(if $(findstring s,$(MAKEFLAGS)),-q,) +ECHO = @$(if $(findstring s,$(MFLAGS)),$(NOP),echo) +QUIET_FLAG := $(if $(findstring s,$(MFLAGS)),-q,) + +#------------------------------------------------------- +# Convert 'make -q' flag into '-i' for genVersionHeader.pl +QUESTION_FLAG := $(if $(findstring q,$(MFLAGS)),-i,) #------------------------------------------------------- ifdef T_A @@ -89,7 +93,7 @@ INSTALL_SHRLIB = $(INSTALL_LOCATION_LIB)/$(T_A) INSTALL_TCLLIB = $(INSTALL_LOCATION_LIB)/$(T_A) INSTALL_BIN = $(INSTALL_LOCATION_BIN)/$(T_A) -#Directories for libraries +# Directories for libraries SHRLIB_SEARCH_DIRS = $(INSTALL_LIB) #------------------------------------------------------- diff --git a/configure/RULES b/configure/RULES index bdc895a4d..e4ae22497 100644 --- a/configure/RULES +++ b/configure/RULES @@ -1,11 +1,10 @@ #************************************************************************* -# Copyright (c) 2002 The University of Chicago, as Operator of Argonne +# Copyright (c) 2006 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 Versions 3.13.7 -# and higher are distributed subject to a Software License Agreement found -# in file LICENSE that is included with this distribution. +# EPICS BASE is distributed subject to a Software License Agreement found +# in the file LICENSE that is included with this distribution. #************************************************************************* ifndef T_A diff --git a/configure/RULES.Db b/configure/RULES.Db index 65212ec29..89f98b6a7 100644 --- a/configure/RULES.Db +++ b/configure/RULES.Db @@ -4,9 +4,10 @@ # 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 the file LICENSE that is included with this distribution. #************************************************************************* -#RULES.Db + +# RULES.Db # Set db substitutions and template file suffixes SUBST_SUFFIX ?= .substitutions diff --git a/configure/RULES.ioc b/configure/RULES.ioc index fa0482ec0..385e9e6de 100644 --- a/configure/RULES.ioc +++ b/configure/RULES.ioc @@ -4,9 +4,10 @@ # 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 the file LICENSE that is included with this distribution. #************************************************************************* -#RULES.ioc + +# RULES.ioc include $(CONFIG)/RULES_DIRS diff --git a/configure/RULES_ARCHS b/configure/RULES_ARCHS index dc3fa04fe..4aaa75870 100644 --- a/configure/RULES_ARCHS +++ b/configure/RULES_ARCHS @@ -90,6 +90,4 @@ realclean: .PHONY : $(BUILD_ARCHS) rebuild archsCommonClean .PHONY : $(ACTIONS) clean realclean archclean host all -# User specific rules -# --include $(HOME)/configure/RULES_USER +include $(CONFIG)/RULES_COMMON diff --git a/configure/RULES_BUILD b/configure/RULES_BUILD index daaae86f6..2b6386041 100644 --- a/configure/RULES_BUILD +++ b/configure/RULES_BUILD @@ -6,11 +6,12 @@ # EPICS BASE is distributed subject to a Software License Agreement found # in the file LICENSE that is included with this distribution. #************************************************************************* + +# RULES_BUILD + +# Rules for making things specified in a Makefile # -# Rules for making things specified in Makefile -# -# we are in O.$(T_A), but most sources are elsewhere -# +# CWD is O.$(T_A), but most sources are elsewhere ifndef BASE_RULES_BUILD BASE_RULES_BUILD=1 @@ -42,6 +43,7 @@ LOADABLE_LIBRARY += $(LOADABLE_LIBRARY_HOST) OBJS += $(OBJS_HOST) PROD += $(PROD_HOST) SCRIPTS += $(SCRIPTS_HOST) +TARGETS += $(TARGETS_HOST) TESTLIBRARY += $(TESTLIBRARY_HOST) TESTSCRIPTS += $(TESTSCRIPTS_HOST) TESTPROD += $(TESTPROD_HOST) @@ -53,6 +55,7 @@ LOADABLE_LIBRARY += $(LOADABLE_LIBRARY_IOC) OBJS += $(OBJS_IOC) PROD += $(PROD_IOC) SCRIPTS += $(SCRIPTS_IOC) +TARGETS += $(TARGETS_IOC) TESTLIBRARY += $(TESTLIBRARY_IOC) TESTSCRIPTS += $(TESTSCRIPTS_IOC) TESTPROD += $(TESTPROD_IOC) @@ -79,9 +82,9 @@ else host: endif --include $(CONFIG)/RULES_FILE_TYPE +include $(CONFIG)/RULES_FILE_TYPE --include $(CONFIG)/RULES.Db +include $(CONFIG)/RULES.Db #--------------------------------------------------------------- # Do not build anything if current path matches SKIP_BUILD @@ -202,7 +205,7 @@ endif # RELEASE file consistency checking checkRelease: - $(CONVERTRELEASE) checkRelease + +$(CONVERTRELEASE) checkRelease warnRelease: -$(CONVERTRELEASE) checkRelease noCheckRelease: @@ -216,9 +219,9 @@ endif $(TESTPRODNAME) $(PRODNAME): $(PRODUCT_OBJS) $(PROD_RESS) $(PROD_DEPLIBS) -$(TESTPRODNAME) $(PRODNAME): %$(EXE): +$(TESTPRODNAME) $(PRODNAME): %$(EXE): | $(INSTALL_LIB) @$(RM) $@ - $(DEBUGCMD) $(LINK.cpp) + $(LINK.cpp) $(MT_EXE_COMMAND) %_ctdt$(OBJ) : %_ctdt.c @@ -273,15 +276,13 @@ YACCOPT ?= $($*_YACCOPT) $(MV) $*.tab.c $*.c $(if $(findstring -d, $(YACCOPT)),$(MV) $*.tab.h $*.h,) -# must be a seperate rule since when not using '-d' the +# must be a separate rule since when not using '-d' the # prefix for .h will be different then .c %.h : %.c %.y %.c: %.l - @$(RM) $*.yy.c - $(LEX) $(LEXOPT) -t $< > $*.yy.c @$(RM) $@ - $(MV) $*.yy.c $@ + $(LEX) $(LEXOPT) -o$@ $< #--------------------------------------------------------------- # Libraries, shared/DLL and stubs @@ -321,6 +322,10 @@ $(LOADABLE_SHRLIBNAME): $(LOADABLE_SHRLIB_PREFIX)%$(LOADABLE_SHRLIB_SUFFIX): $(LINK.shrlib) $(MT_DLL_COMMAND) +$(LIBNAME) $(SHRLIBNAME) $(LOADABLE_SHRLIBNAME): | $(INSTALL_LIB) +$(INSTALL_LIB): + @$(MKDIR) $@ + #--------------------------------------------------------------- # C++ munching for VxWorks @@ -378,14 +383,17 @@ ifneq ($(TAPFILES),) ifdef RUNTESTS_ENABLED prove --failures --ext .tap --exec "$(CAT)" --color $(TAPFILES) endif + +CURRENT_TAPFILES := $(wildcard $(TAPFILES)) +CURRENT_JUNITFILES := $(wildcard $(JUNITFILES)) endif clean-tests: -ifneq ($(TAPFILES),) - $(RM) $(TAPFILES) +ifneq ($(CURRENT_TAPFILES),) + $(RM) $(CURRENT_TAPFILES) endif -ifneq ($(JUNITFILES),) - $(RM) $(JUNITFILES) +ifneq ($(CURRENT_JUNITFILES),) + $(RM) $(CURRENT_JUNITFILES) endif tapfiles: $(TESTSCRIPTS) $(TAPFILES) @@ -409,7 +417,7 @@ endif # Generate a perl program to exec the real test binary. %.t: %$(EXE) $(TOOLS)/makeTestfile.pl @$(RM) $@ - $(PERL) $(TOOLS)/makeTestfile.pl $@ $< + $(PERL) $(TOOLS)/makeTestfile.pl $(T_A) $(EPICS_HOST_ARCH) $@ $< #--------------------------------------------------------------- # Generate header with version number from VCS @@ -480,9 +488,11 @@ $(INSTALL_TCLLIB)/%: ../% @$(INSTALL) -d -m $(BIN_PERMISSIONS) $< $(INSTALL_TCLLIB) endif +ifneq ($(TCLINDEX),) $(INSTALL_TCLLIB)/$(TCLINDEX): $(INSTALL_TCLLIBS) $(ECHO) "Updating $@" $(ECHO) eval auto_mkindex $(INSTALL_TCLLIB) "$(TCLLIBNAME)" | tclsh +endif $(INSTALL_LOADABLE_SHRLIBS): $(INSTALL_SHRLIB)/%: % $(ECHO) "Installing loadable shared library $@" @@ -548,7 +558,7 @@ $(INSTALL_TEMPLATES_SUBDIR)/%: % $(ECHO) "Installing $@" @$(INSTALL) -d -m $(INSTALL_PERMISSIONS) $< $(@D) --include $(CONFIG)/RULES_EXPAND +include $(CONFIG)/RULES_EXPAND .PRECIOUS: %.i %.o %.c %.nm %.cpp %.cc .PRECIOUS: $(COMMON_INC) @@ -557,5 +567,9 @@ $(INSTALL_TEMPLATES_SUBDIR)/%: % .PHONY: runtests tapfiles clean-tests test-results junitfiles .PHONY: checkRelease warnRelease noCheckRelease FORCE +include $(CONFIG)/RULES_COMMON + +else + $(warning RULES_BUILD included more than once. \ + Use 'make show-makefiles' to work out why.) endif # BASE_RULES_BUILD -# EOF RULES_BUILD diff --git a/configure/RULES_COMMON b/configure/RULES_COMMON new file mode 100644 index 000000000..fb8f211e4 --- /dev/null +++ b/configure/RULES_COMMON @@ -0,0 +1,35 @@ +#************************************************************************* +# Copyright (c) 2018 UChicago Argonne LLC, as Operator of Argonne +# National Laboratory. +# EPICS BASE is distributed subject to a Software License Agreement found +# in the file LICENSE that is included with this distribution. +#************************************************************************* + +# These rules show the set of Makefiles, config files and +# rules files loaded by GNUmake. + +# Protect against filenames containing colons (Windows) +SAFE_MAKEFILES = $(subst :,__colon__,$(MAKEFILE_LIST)) +SHOW_MAKEFILES = $(SAFE_MAKEFILES:%=show-makefile.%) +show-makefiles: $(SHOW_MAKEFILES) + +# The sort prevents warnings about duplicate targets: +$(sort $(SHOW_MAKEFILES)): show-makefile.%: + @echo " $(subst __colon__,:,$(@:show-makefile.%=%))" + +.PHONY: show-makefiles show-makefile.% + +# These rules support printing a Makefile variable values. +# Many variables are only set inside an O. build directory. +# make PRINT.T_A + +PRINT_Var = $(@:PRINT.%=%) +PRINT.%: + @echo $(PRINT_Var) = '$($(PRINT_Var))' + +.PHONY: PRINT PRINT.% + + +# User specific rules +# +-include $(HOME)/configure/RULES_USER diff --git a/configure/RULES_DIRS b/configure/RULES_DIRS index 37cea5e9a..ec156745d 100644 --- a/configure/RULES_DIRS +++ b/configure/RULES_DIRS @@ -92,7 +92,4 @@ $(ARCHS) $(ACTIONS) $(actionArchTargets) :%: \ .PHONY : $(dirActionArchTargets) .PHONY : $(actionArchTargets) - -# User specific rules -# --include $(HOME)/configure/RULES_USER +include $(CONFIG)/RULES_COMMON diff --git a/configure/RULES_EXPAND b/configure/RULES_EXPAND index 51657ebd8..f315a7e9e 100644 --- a/configure/RULES_EXPAND +++ b/configure/RULES_EXPAND @@ -1,4 +1,13 @@ -# /configure/RULES_EXPAND +#************************************************************************* +# Copyright (c) 2011 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 the file LICENSE that is included with this distribution. +#************************************************************************* + +# RULES_EXPAND vpath %@ $(USR_VPATH) $(ALL_SRC_DIRS) diff --git a/configure/RULES_FILE_TYPE b/configure/RULES_FILE_TYPE index 28cc74bd4..5936fd5e0 100644 --- a/configure/RULES_FILE_TYPE +++ b/configure/RULES_FILE_TYPE @@ -7,9 +7,11 @@ # in the file LICENSE that is included with this distribution. #************************************************************************* -# Include /configure/RULES_BUILD from tops defined in RELEASE* files +# Include /configure/RULES_BUILD from tops defined in RELEASE* files, +# excluding EPICS_BASE # -RELEASE_RULES_BUILDS = $(foreach top, $(RELEASE_TOPS), \ +RELEASE_RULES_BUILDS = $(foreach top, \ + $(filter-out EPICS_BASE, $(RELEASE_TOPS)), \ $(wildcard $($(top))/configure/RULES_BUILD)) ifneq ($(RELEASE_RULES_BUILDS),) include $(RELEASE_RULES_BUILDS) @@ -23,7 +25,7 @@ ifneq ($(RELEASE_CFG_RULES),) include $(RELEASE_CFG_RULES) endif -# If this is not BASE then include /configure/RULES_BUILD +# If this is not BASE then include /configure/RULES_BUILD # ifeq ($(wildcard $(TOP)/configure/CONFIG_BASE_VERSION),) TOP_RULES_BUILDS = $(wildcard $(TOP)/configure/RULES_BUILD) @@ -67,7 +69,3 @@ file_type_clean: @$(RM) $(foreach type, $(FILE_TYPE), $($(type))) .PHONY : file_type_clean - -# User specific rules -# --include $(HOME)/configure/RULES_USER diff --git a/configure/RULES_OCTAVE b/configure/RULES_OCTAVE index a06fb075a..1ec4cc7b2 100644 --- a/configure/RULES_OCTAVE +++ b/configure/RULES_OCTAVE @@ -1,11 +1,10 @@ #************************************************************************* -# Copyright (c) 2002 The University of Chicago, as Operator of Argonne +# Copyright (c) 2006 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 Versions 3.13.7 -# and higher are distributed subject to a Software License Agreement found -# in file LICENSE that is included with this distribution. +# EPICS BASE is distributed subject to a Software License Agreement found +# in the file LICENSE that is included with this distribution. #************************************************************************* # Octave definitions and rules diff --git a/configure/RULES_TARGET b/configure/RULES_TARGET index 7a5dcba81..e96232876 100644 --- a/configure/RULES_TARGET +++ b/configure/RULES_TARGET @@ -1,18 +1,13 @@ #************************************************************************* -# Copyright (c) 2002 The University of Chicago, as Operator of Argonne +# Copyright (c) 2011 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 Versions 3.13.7 -# and higher are distributed subject to a Software License Agreement found -# in file LICENSE that is included with this distribution. +# EPICS BASE is distributed subject to a Software License Agreement found +# in the file LICENSE that is included with this distribution. #************************************************************************* -# -# RULES_TARGET -# -# This file is to be maintained by the community. -# -#----------------------------------------------------------------------- + +# RULES_TARGET define TARGET_template $(1)_$(2) += $$(if $$(strip $$($(1)_$(2)_$$(OS_CLASS))), \ diff --git a/configure/RULES_TOP b/configure/RULES_TOP index 838994bd0..b18fbf026 100644 --- a/configure/RULES_TOP +++ b/configure/RULES_TOP @@ -15,6 +15,10 @@ CVSCLEAN = $(call FIND_TOOL,cvsclean.pl) cvsclean: $(PERL) $(CVSCLEAN) +DEPCLEAN = $(call FIND_TOOL,depclean.pl) +depclean: + $(PERL) $(DEPCLEAN) + realuninstall: uninstallDirs $(RMDIR) $(INSTALL_LOCATION_BIN) $(RMDIR) $(INSTALL_LOCATION_LIB) @@ -73,12 +77,14 @@ help: @echo " uninstall - Remove install directories created by this hostarch." @echo " realuninstall - Removes ALL install dirs" @echo " distclean - Same as realclean cvsclean realuninstall." + @echo " depclean - Removes all .d files from O. directories." @echo " cvsclean - Removes cvs .#* files in all dirs of directory tree" @echo " help - Prints this list of valid make targets " @echo "Indiv. object targets are supported by O. level Makefile .e.g" @echo " xxxRecord.o" -.PHONY: cleandirs distclean cvsclean realuninstall archuninstall uninstallDirs +.PHONY: cleandirs distclean cvsclean depclean +.PHONY: realuninstall archuninstall uninstallDirs .PHONY: uninstall help # Include /cfg/TOP_RULES* files from tops defined in RELEASE* files diff --git a/configure/os/CONFIG.Common.RTEMS b/configure/os/CONFIG.Common.RTEMS index c22e37a66..8425e6ef9 100644 --- a/configure/os/CONFIG.Common.RTEMS +++ b/configure/os/CONFIG.Common.RTEMS @@ -27,9 +27,13 @@ ifneq ($(CONFIG),$(TOP)/configure) -include $(TOP)/configure/CONFIG_SITE.Common.RTEMS endif +#-------------------------------------------------- +# Set RTEMS_BSP from T_A if not already done +RTEMS_BSP ?= $(subst RTEMS-,,$(T_A)) + #------------------------------------------------------- # Pick up the RTEMS tool/path definitions from the RTEMS BSP directory. -include $(RTEMS_BASE)/$(RTEMS_TARGET_CPU)-rtems$(RTEMS_VERSION)/$(subst RTEMS-,,$(T_A))/Makefile.inc +include $(RTEMS_BASE)/$(RTEMS_TARGET_CPU)-rtems$(RTEMS_VERSION)/$(RTEMS_BSP)/Makefile.inc include $(RTEMS_CUSTOM) include $(CONFIG.CC) @@ -72,7 +76,7 @@ CPPFLAGS += $($(BUILD_CLASS)_CPPFLAGS) $(POSIX_CPPFLAGS) $(OPT_CPPFLAGS)\ $(USR_CPPFLAGS) $(CMD_CPPFLAGS) $(ARCH_DEP_CPPFLAGS) $(OP_SYS_CPPFLAGS)\ $(OP_SYS_INCLUDE_CPPFLAGS) $(CODE_CPPFLAGS) -ECHO = @$(if $(findstring s,$(patsubst T_A=%,,$(MAKEFLAGS))),$(NOP),echo) +ECHO = @$(if $(findstring s,$(MFLAGS)),$(NOP),echo) #-------------------------------------------------- # Although RTEMS uses gcc, it wants to use gcc its own way diff --git a/configure/os/CONFIG.Common.RTEMS-at91rm9200ek b/configure/os/CONFIG.Common.RTEMS-at91rm9200ek index 6f5f97732..735c07bb5 100644 --- a/configure/os/CONFIG.Common.RTEMS-at91rm9200ek +++ b/configure/os/CONFIG.Common.RTEMS-at91rm9200ek @@ -9,5 +9,6 @@ # # All RTEMS targets use the same Makefile fragment # -RTEMS_TARGET_CPU=arm +RTEMS_BSP = at91rm9200ek +RTEMS_TARGET_CPU = arm include $(CONFIG)/os/CONFIG.Common.RTEMS diff --git a/configure/os/CONFIG.Common.RTEMS-beatnik b/configure/os/CONFIG.Common.RTEMS-beatnik index aaf611638..aaa45e4c9 100644 --- a/configure/os/CONFIG.Common.RTEMS-beatnik +++ b/configure/os/CONFIG.Common.RTEMS-beatnik @@ -5,6 +5,7 @@ # All RTEMS targets use the same Makefile fragment # EXE = .elf +RTEMS_BSP = beatnik RTEMS_TARGET_CPU = powerpc GNU_TARGET = powerpc-rtems ARCH_DEP_CFLAGS += -DMY_DO_BOOTP=NULL diff --git a/configure/os/CONFIG.Common.RTEMS-gen68360 b/configure/os/CONFIG.Common.RTEMS-gen68360 index a6663afc2..0a8a6e85e 100644 --- a/configure/os/CONFIG.Common.RTEMS-gen68360 +++ b/configure/os/CONFIG.Common.RTEMS-gen68360 @@ -5,5 +5,6 @@ # # All RTEMS targets use the same Makefile fragment # -RTEMS_TARGET_CPU=m68k +RTEMS_BSP = gen68360 +RTEMS_TARGET_CPU = m68k include $(CONFIG)/os/CONFIG.Common.RTEMS diff --git a/configure/os/CONFIG.Common.RTEMS-mcp750 b/configure/os/CONFIG.Common.RTEMS-mcp750 index d834ad9ec..035e6eb36 100644 --- a/configure/os/CONFIG.Common.RTEMS-mcp750 +++ b/configure/os/CONFIG.Common.RTEMS-mcp750 @@ -5,5 +5,6 @@ # # All RTEMS targets use the same Makefile fragment # -RTEMS_TARGET_CPU=ppc +RTEMS_BSP = mcp750 +RTEMS_TARGET_CPU = ppc include $(CONFIG)/os/CONFIG.Common.RTEMS diff --git a/configure/os/CONFIG.Common.RTEMS-mvme167 b/configure/os/CONFIG.Common.RTEMS-mvme167 index a6663afc2..3651f966f 100644 --- a/configure/os/CONFIG.Common.RTEMS-mvme167 +++ b/configure/os/CONFIG.Common.RTEMS-mvme167 @@ -5,5 +5,6 @@ # # All RTEMS targets use the same Makefile fragment # -RTEMS_TARGET_CPU=m68k +RTEMS_BSP = mvme167 +RTEMS_TARGET_CPU = m68k include $(CONFIG)/os/CONFIG.Common.RTEMS diff --git a/configure/os/CONFIG.Common.RTEMS-mvme2100 b/configure/os/CONFIG.Common.RTEMS-mvme2100 index 0ae64c791..a8e8bf76e 100644 --- a/configure/os/CONFIG.Common.RTEMS-mvme2100 +++ b/configure/os/CONFIG.Common.RTEMS-mvme2100 @@ -5,6 +5,7 @@ # All RTEMS targets use the same Makefile fragment # EXE = .elf +RTEMS_BSP = mvme2100 RTEMS_TARGET_CPU = powerpc GNU_TARGET = powerpc-rtems ARCH_DEP_CFLAGS += -DMY_DO_BOOTP=NULL diff --git a/configure/os/CONFIG.Common.RTEMS-mvme2700 b/configure/os/CONFIG.Common.RTEMS-mvme2700 index 899fab17f..a5ad7fbf5 100644 --- a/configure/os/CONFIG.Common.RTEMS-mvme2700 +++ b/configure/os/CONFIG.Common.RTEMS-mvme2700 @@ -1,6 +1,7 @@ # # Author: Matt Rippa # +RTEMS_BSP = mvme2700 RTEMS_TARGET_CPU = powerpc ARCH_DEP_CFLAGS += -DMY_DO_BOOTP=NULL ARCH_DEP_CFLAGS += -DHAVE_PPCBUG diff --git a/configure/os/CONFIG.Common.RTEMS-mvme3100 b/configure/os/CONFIG.Common.RTEMS-mvme3100 index e94d46211..283e7d680 100644 --- a/configure/os/CONFIG.Common.RTEMS-mvme3100 +++ b/configure/os/CONFIG.Common.RTEMS-mvme3100 @@ -5,6 +5,7 @@ # All RTEMS targets use the same Makefile fragment # EXE = .elf +RTEMS_BSP = mvme3100 RTEMS_TARGET_CPU = powerpc GNU_TARGET = powerpc-rtems ARCH_DEP_CFLAGS += -DMY_DO_BOOTP=NULL diff --git a/configure/os/CONFIG.Common.RTEMS-mvme5500 b/configure/os/CONFIG.Common.RTEMS-mvme5500 index 44ef7ea3e..0329185bb 100644 --- a/configure/os/CONFIG.Common.RTEMS-mvme5500 +++ b/configure/os/CONFIG.Common.RTEMS-mvme5500 @@ -5,6 +5,7 @@ # All RTEMS targets use the same Makefile fragment # EXE = .elf +RTEMS_BSP = mvme5500 RTEMS_TARGET_CPU = powerpc GNU_TARGET = powerpc-rtems ARCH_DEP_CFLAGS += -DMY_DO_BOOTP=NULL diff --git a/configure/os/CONFIG.Common.RTEMS-pc386 b/configure/os/CONFIG.Common.RTEMS-pc386 index 92ef4ac22..8dfa7d2ba 100644 --- a/configure/os/CONFIG.Common.RTEMS-pc386 +++ b/configure/os/CONFIG.Common.RTEMS-pc386 @@ -5,14 +5,15 @@ # # All RTEMS targets use the same Makefile fragment # -RTEMS_TARGET_CPU=i386 +RTEMS_BSP = pc386 +RTEMS_TARGET_CPU = i386 MUNCH_SUFFIX = .boot define MUNCH_CMD - $(RTEMS_TOOLS)/bin/$(OBJCOPY_FOR_TARGET) -O binary -R .comment -S $< temp.bin + $(RM) $*.bin + $(RTEMS_TOOLS)/bin/$(OBJCOPY_FOR_TARGET) -O binary -R .comment -S $< $*.bin $(BIN2BOOT) $@ 0x00097E00 \ - $(PROJECT_RELEASE)/lib/start16.bin 0x00097C00 0 temp.bin 0x00100000 0 - rm -f temp.bin + $(PROJECT_RELEASE)/lib/start16.bin 0x00097C00 0 $*.bin 0x00100000 0 endef include $(CONFIG)/os/CONFIG.Common.RTEMS diff --git a/configure/os/CONFIG.Common.RTEMS-pc386-qemu b/configure/os/CONFIG.Common.RTEMS-pc386-qemu new file mode 100644 index 000000000..684f01a19 --- /dev/null +++ b/configure/os/CONFIG.Common.RTEMS-pc386-qemu @@ -0,0 +1,11 @@ +# CONFIG.Common.RTEMS-pc386-qemu +# +# Definitions for the RTEMS-pc386-qemu target +# Site-specific overrides go in CONFIG_SITE.Common.RTEMS-pc386-qemu +# +#------------------------------------------------------- + +# Include definitions from RTEMS-pc386 +include $(CONFIG)/os/CONFIG.Common.RTEMS-pc386 + +RTEMS_QEMU_FIXUPS = YES diff --git a/configure/os/CONFIG.Common.RTEMS-psim b/configure/os/CONFIG.Common.RTEMS-psim index 230d72e1f..58ad72852 100644 --- a/configure/os/CONFIG.Common.RTEMS-psim +++ b/configure/os/CONFIG.Common.RTEMS-psim @@ -5,5 +5,6 @@ # # All RTEMS targets use the same Makefile fragment # -RTEMS_TARGET_CPU=ppc +RTEMS_BSP = psim +RTEMS_TARGET_CPU = ppc include $(CONFIG)/os/CONFIG.Common.RTEMS diff --git a/configure/os/CONFIG.Common.RTEMS-uC5282 b/configure/os/CONFIG.Common.RTEMS-uC5282 index 6b0903e07..2cb215aca 100644 --- a/configure/os/CONFIG.Common.RTEMS-uC5282 +++ b/configure/os/CONFIG.Common.RTEMS-uC5282 @@ -5,6 +5,7 @@ # # All RTEMS targets use the same Makefile fragment # +RTEMS_BSP = uC5282 RTEMS_TARGET_CPU = m68k ARCH_DEP_CFLAGS += -DMY_DO_BOOTP=NULL diff --git a/configure/os/CONFIG.Common.vxWorksCommon b/configure/os/CONFIG.Common.vxWorksCommon index 7fe56b874..66d0c2902 100644 --- a/configure/os/CONFIG.Common.vxWorksCommon +++ b/configure/os/CONFIG.Common.vxWorksCommon @@ -173,8 +173,10 @@ SHRLIB_CFLAGS = SHRLIB_LDFLAGS = #-------------------------------------------------- -# Earlier versions of gcc don't understand -MF -HDEPENDS_COMPFLAGS = -MM > $@ +# Don't use gcc 2.x for dependency generation + +HDEPENDS_METHOD_2 = MKMF +HDEPENDS_METHOD = $(firstword $(HDEPENDS_METHOD_$(VX_GNU_MAJOR_VERSION)) COMP) #-------------------------------------------------- # osithead use default stack, YES or NO override diff --git a/configure/os/CONFIG.darwinCommon.darwinCommon b/configure/os/CONFIG.darwinCommon.darwinCommon index 56b539545..4a8f3ef8e 100644 --- a/configure/os/CONFIG.darwinCommon.darwinCommon +++ b/configure/os/CONFIG.darwinCommon.darwinCommon @@ -65,14 +65,14 @@ GNU = NO # # Darwin shared libraries # -SHRLIB_LDFLAGS = -dynamiclib -flat_namespace -undefined suppress \ +SHRLIB_LDFLAGS = -dynamiclib -undefined dynamic_lookup \ -install_name $(shell $(FULLPATHNAME) $(INSTALL_LIB))/$@ \ $(addprefix -compatibility_version , $(SHRLIB_VERSION)) \ $(addprefix -current_version , $(SHRLIB_VERSION)) SHRLIB_SUFFIX_BASE = .dylib SHRLIB_SUFFIX = $(addprefix ., $(SHRLIB_VERSION))$(SHRLIB_SUFFIX_BASE) -LOADABLE_SHRLIB_LDFLAGS = -bundle -flat_namespace -undefined suppress +LOADABLE_SHRLIB_LDFLAGS = -bundle -undefined dynamic_lookup # # Position-independent code is the default on Darwin. diff --git a/configure/os/CONFIG.win32-x86.Common b/configure/os/CONFIG.win32-x86.Common index be328cc19..43cfc342f 100644 --- a/configure/os/CONFIG.win32-x86.Common +++ b/configure/os/CONFIG.win32-x86.Common @@ -26,3 +26,5 @@ endif # Needed to find dlls for base installed build tools (antelope,eflex,...) PATH := $(EPICS_BASE_BIN):$(PATH) +# Silence the tr1 namespace deprecation warnings +USR_CXXFLAGS_WIN32 += -D_SILENCE_TR1_NAMESPACE_DEPRECATION_WARNING diff --git a/configure/os/CONFIG_SITE.Common.RTEMS-pc386 b/configure/os/CONFIG_SITE.Common.RTEMS-pc386 deleted file mode 100644 index c772c44fc..000000000 --- a/configure/os/CONFIG_SITE.Common.RTEMS-pc386 +++ /dev/null @@ -1,3 +0,0 @@ -# -# Site-specific overrides for RTEMS-pc386 target -# diff --git a/configure/os/CONFIG_SITE.Common.RTEMS-pc386-qemu b/configure/os/CONFIG_SITE.Common.RTEMS-pc386-qemu new file mode 100644 index 000000000..027dcf4ab --- /dev/null +++ b/configure/os/CONFIG_SITE.Common.RTEMS-pc386-qemu @@ -0,0 +1,9 @@ +# CONFIG_SITE.Common.RTEMS-pc386-qemu +# +# Site-specific overrides for the RTEMS-pc386-qemu target +# + +# If you're building this architecture you _probably_ want to +# run the tests for it under QEMU, but if not you can turn +# them off here by commenting out this line: +CROSS_COMPILER_RUNTEST_ARCHS += RTEMS-pc386-qemu diff --git a/configure/os/CONFIG_SITE.Common.linux-x86 b/configure/os/CONFIG_SITE.Common.linux-x86 index 07182820d..e4e7dd0fd 100644 --- a/configure/os/CONFIG_SITE.Common.linux-x86 +++ b/configure/os/CONFIG_SITE.Common.linux-x86 @@ -54,3 +54,7 @@ OPT_CXXFLAGS_YES += -g # Tune GNU compiler output for a specific 32-bit cpu-type # (e.g. generic, native, i386, i686, pentium2/3/4, prescott, k6, athlon etc.) GNU_TUNE_CFLAGS = -mtune=generic + + +# Developers using a suitable compiler may enable its address sanitizer: +#ENABLE_ASAN = YES diff --git a/configure/os/CONFIG_SITE.Common.linux-x86_64 b/configure/os/CONFIG_SITE.Common.linux-x86_64 index 18fb2974d..6f7a2dc7b 100644 --- a/configure/os/CONFIG_SITE.Common.linux-x86_64 +++ b/configure/os/CONFIG_SITE.Common.linux-x86_64 @@ -50,3 +50,7 @@ OPT_CXXFLAGS_YES += -g # Tune GNU compiler output for a specific 64-bit cpu-type # (e.g. generic, native, core2, nocona, k8, opteron, athlon64, barcelona etc.) GNU_TUNE_CFLAGS = -mtune=generic + + +# Developers using a suitable compiler may enable its address sanitizer: +#ENABLE_ASAN = YES diff --git a/documentation/KnownProblems.html b/documentation/KnownProblems.html index 6ef523808..d38ad630c 100644 --- a/documentation/KnownProblems.html +++ b/documentation/KnownProblems.html @@ -4,17 +4,17 @@ - Known Problems in EPICS 7.0.1 + Known Problems in EPICS 7.0.2 -

EPICS 7.0.1: Known Problems

+

EPICS 7.0.2: Known Problems

Any patch files linked below should be applied at the root of the -base-7.0.1 tree. Download them, then use the GNU Patch program as +base-7.0.2 tree. Download them, then use the GNU Patch program as follows:

-
% cd /path/to/base-7.0.1
+
% cd /path/to/base-7.0.2
 % patch -p1 < /path/to/file.patch

The following problems were known by the developers at the time of this diff --git a/documentation/README.1st b/documentation/README.1st index 042cede60..f0c560fb0 100644 --- a/documentation/README.1st +++ b/documentation/README.1st @@ -1,6 +1,6 @@ Installation Instructions - EPICS Base Release 7.0.1.1 + EPICS Base Release 7.0.2 -------------------------------------------------------------------------- @@ -164,12 +164,11 @@ base/startup directory - contains scripts to set environment and path - EpicsHostArch C shell script to set EPICS_HOST_ARCH env variable - EpicsHostArch.pl Perl script to set EPICS_HOST_ARCH env variable - Site.profile bourne shell script to set path and env variables - Site.cshrc c shell script to set path and env variables - cygwin.bat WIN32 bat file to set cygwin path and env variables - win32.bat WIN32 bat file to set path and env variables + EpicsHostArch Shell script to set EPICS_HOST_ARCH env variable + unix.csh C shell script to set path and env variables + unix.sh Bourne shell script to set path and env variables + win32.bat Bat file example to configure win32-x86 target + windows.bat Bat file example to configure windows-x64 target base/configure directory - contains build definitions and rules diff --git a/documentation/README.darwin.html b/documentation/README.darwin.html index cbc290178..dd11ce857 100644 --- a/documentation/README.darwin.html +++ b/documentation/README.darwin.html @@ -21,7 +21,7 @@ of my Bash login script (~/.bash_login): # EPICS_BASE="${HOME}/src/EPICS/base" EPICS_EXTENSIONS="${HOME}/src/EPICS/extensions" -. "${EPICS_BASE}"/startup/Site.profile +. "${EPICS_BASE}"/startup/unix.sh

  • diff --git a/documentation/README.html b/documentation/README.html index 4ab314d05..ab9a1f7a4 100644 --- a/documentation/README.html +++ b/documentation/README.html @@ -9,7 +9,7 @@

    Installation Instructions

    -

    EPICS Base Release 7.0.1.1


    +

    EPICS Base Release 7.0.2



    Table of Contents

    @@ -176,12 +176,11 @@

    base/startup directory - contains scripts to set environment and path

    -        EpicsHostArch       C shell script to set EPICS_HOST_ARCH env variable
    -        EpicsHostArch.pl    Perl script to set EPICS_HOST_ARCH env variable
    -        Site.profile        bourne shell script to set path and env variables
    -        Site.cshrc          c shell script to set path and env variables
    -        cygwin.bat          WIN32 bat file to set cygwin path and env variables
    -        win32.bat           WIN32 bat file to set path and env variables
    +        EpicsHostArch       Shell script to set EPICS_HOST_ARCH env variable
    +        unix.csh            C shell script to set path and env variables
    +        unix.sh             Bourne shell script to set path and env variables
    +        win32.bat           Bat file example to configure win32-x86 target
    +        windows.bat         Bat file example to configure windows-x64 target
     

    base/configure directory - contains build definitions and rules

    diff --git a/documentation/RELEASE_NOTES.html b/documentation/RELEASE_NOTES.html index 7ec1fa64e..5cc286495 100644 --- a/documentation/RELEASE_NOTES.html +++ b/documentation/RELEASE_NOTES.html @@ -8,7 +8,20 @@ -

    EPICS Release 7.0.2.x

    +

    These release notes describe changes that have been made since the previous +release of this series of EPICS Base. Note that changes which were merged up +from commits to new releases in an older Base series are not described at the +top of this file but have entries that appear lower down, under the series to +which they were originally committed. Thus it is important to read more than +just the first section to understand everything that has changed in each +release.

    + +

    The external PVA submodules each have their own separate set of release notes +which should also be read to understand what has changed since an earlier +release.

    + + +

    EPICS Release 7.0.3

    +

    The list of tracked bugs fixed in this release can be found on the +Launchpad Milestone +page for EPICS Base 3.16.2.

    + +

    Status reporting for the callback and scanOnce task queues

    + +

    Two new iocsh commands and some associated underlying APIs have been added to +show the state of the queues that feed the three callback tasks and the scanOnce +task, including a high-water mark which can optionally be reset. The new iocsh +commands are callbackQueueShow and scanOnceQueueShow; both +take an optional integer argument which must be non-zero to reset the +high-water mark.

    + +

    Support for event codes greater than or equal to NUM_TIME_EVENTS

    + +

    Event numbers greater than or equal to NUM_TIME_EVENTS are now allowed if +supported by the registered event time provider, which must provide its own +advancing timestamp validation for such events.

    + +

    Time events numbered 0 through (NUM_TIME_EVENTS-1) are still validated by +code in epicsGeneralTime.c that checks for advancing timestamps and enforces +that restriction.

    + +

    Type-safe Device and Driver Support Tables

    + +

    Type-safe versions of the device and driver support structures dset +and drvet have been added to the devSup.h and drvSup.h headers +respectively. The original structure definitions have not been changed so +existing support modules will still build normally, but older modules can be +modified and new code written to be compatible with both.

    + +

    The old structure definitions will be replaced by the new ones if the macros +USE_TYPED_DSET and/or USE_TYPED_DRVET are defined when the +appropriate header is included. The best place to define these is in the +Makefile, as with the USE_TYPED_RSET macro that was introduced in +Base-3.16.1 and described below. See the comments in devSup.h for a brief usage +example, or look at +this commit to the ipac module to see a module conversion.

    + +

    A helper function DBLINK* dbGetDevLink(dbCommon *prec) has also been +added to devSup.h which fetches a pointer to the INP or OUT field of the +record.

    + +

    RTEMS build configuration update, running tests under QEMU

    + +

    This release includes the ability to run the EPICS unit tests built for a +special version of the RTEMS-pc386 target architecture on systems that have an +appropriate QEMU emulator installed (qemu-system-i386). It is also now +possible to create sub-architectures of RTEMS targets, whereas previously the +EPICS target architecture name had to be RTEMS-$(RTEMS_BSP).

    + +

    The new target RTEMS-pc386-qemu builds binaries that can be run in +the qemu-system-i386 PC System emulator. This target is a derivative of +the original RTEMS-pc386 target but with additional software to build +an in-memory file-system, and some minor modifications to allow the unit tests +to work properly under QEMU. When this target is enabled, building any of the +make targets that cause the built-in self-tests to be run (such as +make runtests) will also run the tests for RTEMS using QEMU.

    + +

    To allow the new 3-component RTEMS target name, the EPICS build system for +RTEMS was modified to allow a configure/os/CONFIG.Common.<arch> +file to set the RTEMS_BSP variable to inform the build what RTEMS BSP +to use. Previously this was inferred from the value of the T_A make +variable, but that prevents having multiple EPICS targets that build against the +same BSP. All the included RTEMS target configuration files have been updated; +build configuration files for out-of-tree RTEMS targets will continue to work as +the original rules are used to set RTEMS_BSP if it hasn't been set when +needed.

    + +

    Link type enhancements

    + +

    This release adds three new link types: "state", "debug" and "trace". The +"state" link type gets and puts boolean values from/to the dbState library that +was added in the 3.15.1 release. The "debug" link type sets the +jlink::debug flag in its child link, while the "trace" link type +also causes the arguments and return values for all calls to the child link's +jlif and lset routines to be printed on stdout. The debug flag can no longer be +set using an info tag. The addition of the "trace" link type has allowed over +200 lines of conditional diagnostic printf() calls to be removed from the other +link types.

    + +

    The "calc" link type can now be used for output links as well as input links. +This allows modification of the output value and even combining it with values +from other input links. See the separate JSON Link types document for +details.

    + +

    A new start_child() method was added to the end of the jlif +interface table.

    + +

    The lset methods have now been properly documented in the +dbLink.h header file using Doxygen annotations, although we do not run Doxygen +on the source tree yet to generate API documentation.

    + +

    Link types that utilize child links must now indicate whether the child will +be used for input, output or forward linking by the return value from its +parse_start_map() method. The jlif_key_result enum now +contains 3 values jlif_key_child_inlink, +jlif_key_child_outlink and jlif_key_child_fwdlink +instead of the single jlif_key_child_link that was previously used +for this.

    + +

    GNUmake targets for debugging

    + +

    Some additional build rules have been added to help debug configuration +problems with the build system. Run make show-makefiles to get a sorted +list of all the files that the build system includes when building in the +current directory.

    + +

    A new pattern rule for PRINT.% can be used to show the value of any +GNUmake variable for the current build directory (make sure you are in the right +directory though, many variables are only set when inside the +O.arch build directory). For example make PRINT.T_A +will display the build target architecture name from inside a +O.arch directory but the variable will be empty from an +application top or src directory. make PRINT.EPICS_BASE will show the +path to Base from any EPICS application directory though.

    + +

    Propagate PUTF across Asynchronous record processing

    + +

    The IOC contains a mechanism involving the PUTF and RPRO fields of each +record to ensure that if a record is busy when it receives a put to one of its +fields, the record will be processed again to ensure that the new field value +has been correctly acted on. Until now that mechanism only worked if the put was +to the asynchronous record itself, so puts that were chained from some other +record via a DB link did not cause reprocessing.

    + +

    In this release the mechanism has been extended to propagate the PUTF state +across DB links until all downstream records have been reprocessed. Some +additional information about the record state can be shown by setting the TPRO +field of an upstream record, and even more trace data is displayed if the +debugging variable dbAccessDebugPUTF is set in addition to TPRO.

    Finding info fields

    @@ -122,15 +306,18 @@ release also includes additional protection against buffer overflows while printing long links in dbpr, and corrects the output of long strings from the dbgf command.

    -

    Record types mbbiDirect and mbboDirect extended to 32 bit

    +

    Record types mbbiDirect and mbboDirect upgraded to 32 bit

    -

    The VAL fields of mbbiDirect and mbboDirect records have -been extended from DBF_USHORT (16 bit) to DBF_LONG (32 bit). -New bit fields B10...B1F have been added.

    +

    The VAL fields and related fields of these records are now DBF_LONG. +(Not DBF_ULONG in order to prevent Channel Access from promoting them +to DBF_DOUBLE.) Additional bit fields B10...B1F have +been added.

    -

    Device support which accesses the bit fields can test if the macro -mbbiDirectRecord1BF or mbboDirectRecord1BF is -defined. Device support which only accesses RVAL needs no modification.

    +

    Device support that accesses VAL or the bit fields directly (most +don't) and aims for compatibility with old and new versions of these records +should use at least 32 bit integer types to avoid bit loss. The number of bit +fields can be calculated using 8 * sizeof(prec->val) +which is correct in both versions.

    Restore use of ledlib for VxWorks command editing

    @@ -772,11 +959,76 @@ the stdout stream, making it hard to parse.

    callback.h header and removed the need for dbScan.c to reach into the internals of its CALLBACK objects.

    - -

    Changes from the 3.15 branch since 3.15.5

    +

    Changes from the 3.15 branch since 3.15.6

    +

    Routine epicsTempName() removed from libCom

    + +

    This routine was a simple wrapper around the C89 function tmpnam() +which is now seen as unsafe and causes warning messages to be generated by +most modern compilers. The two internal uses of this function have been +modified to call epicsTempFile() instead. We were unable to find any +published code that used this function, so it was removed immediately instead +of being deprecated.

    + +

    DBD Parsing of Record Types

    + +

    The Perl DBD file parser has been made slightly more liberal; the order in +which DBD files must be parsed is now more flexible, so that a record type +definition can now be parsed after a device support that referred to that +record type. A warning message will be displayed when the device support is +seen, but the subsequent loading of the record type will be accepted without +triggering an error. See +Launchpad bug +#1801145.

    + +

    menuScan and several record types documented with POD

    + +

    The EPICS Wiki pages describing a number of standard record types has been +converted into the Perl POD documentation format and added to the DBD files, +so at build-time an HTML version of these documents is generated and installed +into the htmls directory. Thanks to Tony Pietryla.

    + +

    CA client tools learned -V option

    + +

    This displays the version numbers of EPICS Base and the CA protocol.

    + + +

    Changes made between 3.15.5 and 3.15.6

    + +

    Unsetting environment variables

    + +

    The new command epicsEnvUnset varname can be used to +unset an environment variable.

    + +

    Warning indicators in msi (and macLib) output

    + +

    The libCom macro expansion library has been modified so that when the +SUPPRESS_WARNINGS flag is set it will no longer include any ,undefined +or ,recursive indicators in its output when undefined or recursive +macros are encountered. These indicators were harmless when the output was fed +into an IOC along with a definition for the macro, but when the msi +tool was used to generate other kinds of files they caused problems. If the +msi -V flag is used the markers will still be present in the output +whenever the appropriate condition is seen.

    + +

    Improvements to msi

    + +

    In addition to fixing its response to discovering parsing errors in its +substitution input file (reported as Launchpad +bug #1503661) +so it now deletes the incomplete output file, the msi program has been cleaned +up a little bit internally.

    + +

    All array records now post monitors on their array-length fields

    + +

    The waveform record has been posting monitors on its NORD field since Base +3.15.0.1; we finally got around to doing the equivalent in all the other +built-in record types, which even required modifying device support in some +cases. This fixes +Launchpad bug #1730727.

    +

    HOWTO: Converting Wiki Record Reference to POD

    Some documentation has been added to the dbdToHtml.pl script @@ -808,15 +1060,84 @@ of having go modify or replace the original. A new .gitignore pattern tells git to ignore all configure/*.local files.

    -

    Changes from the 3.14 branch since 3.15.5

    +

    Changes from the 3.14 branch between 3.15.5 and 3.15.6

    - +

    Fix broken EPICS_IOC_LOG_FILE_LIMIT=0 setting

    + +

    The Application Developers' Guide says this is allowed and disables the +limit on the log-file, but it hasn't actually worked for some time (if ever). +Note that the iocLogServer will be removed from newer Base release sometime +soon as its functionality can be implemented by other dedicated log servers +such as logstash or syslog-ng.

    + +

    Fixes lp:1786858 +and part of lp:1786966. +

    + +

    Cleanup of startup directory

    + +

    The files in the startup directory have not been maintained in recent years +and have grown crufty (technical term). This release includes the following +updates to these files:

    + +
      + +
    • The Perl EpicsHostArch.pl script has been rewritten, and support +for a few previously missing host architectures has been added to it.
    • + +
    • The EpicsHostArch.pl script has also been moved into the standard +src/tools directory, from where it will be installed into +lib/perl. In this new location it is no longer executable, so it must +be run by the perl executable.
    • + +
    • The build system has been adjusted to look for EpicsHostArch.pl in +both places if the EPICS_HOST_ARCH environment variable has not been +set at build-time.
    • + +
    • Sites that used the original Perl script to set EPICS_HOST_ARCH as +part of their standard environment will need to adjust their scripts when they +upgrade to this release.
    • + +
    • The EpicsHostArch shell script has been replaced with a wrapper +routine that calls the Perl EpicsHostArch.pl script. Sites that rely on +this script to set EPICS_HOST_ARCH should consider switching to the +Perl script instead.
    • + +
    • The Site.cshrc and Site.profile files have been renamed to +unix.csh and unix.sh, respectively.
    • + +
    • The existing win32.bat file has been cleaned up and a new +windows.bat file added for 64-bit targets. The contents of these files +should be seen as examples, don't uncomment or install parts for software that +you don't explicitly know that you need.
    • + +
    + +

    Recent Apple XCode Build Issues

    + +

    The latest version of XCode will not compile calls to system() or +clock_settime() for iOS targets. There were several places in Base +where these were being compiled, although there were probably never called. The +code has now been modified to permit iOS builds to complete again.

    + +

    Prevent illegal alarm severities

    + +

    A check has been added to recGblResetAlarms() that prevents records +from getting an alarm severity higher than INVALID_ALARM. It is still possible +for a field like HSV to get set to a value that is not a legal alarm severity, +but the core IOC code should never copy such a value into a record's SEVR or +ACKS fields. With this fix the record's alarm severity will be limited to +INVALID_ALARM.

    Fixes for Launchpad bugs

    The following launchpad bugs have fixes included:

    - cd base-7.1
    - git tag -m 'ANJ: Tagged for 7.1.1-rc1' R7.1.1-rc1 + cd base-7.0
    + git tag -m 'ANJ: Tagged for 7.0.2-rc1' R7.0.2-rc1
    Note that submodules must not be tagged with the version used for the top-level, they each have their own separate version numbers @@ -159,12 +159,12 @@ made.

    script generates a gzipped tarfile directly from the tag, excluding the files and directories that are only used for continuous integration:
    - cd base-7.1
    - .ci/make-tar.sh R7.1.1-rc1 base-7.1.1-rc1.tar.gz base-7.1.1-rc1/ + cd base-7.0
    + ./.tools/make-tar.sh R7.0.2-rc1 base-7.0.2-rc1.tar.gz base-7.0.2-rc1/
    Create a GPG signature file of the tarfile as follows:
    - gpg --armor --sign --detach-sig base-7.1.1-rc1.tar.gz + gpg --armor --sign --detach-sig base-7.0.2-rc1.tar.gz
    @@ -247,6 +247,7 @@ made.

    + Release Approval @@ -263,18 +264,36 @@ made.

      Release Manager - Edit and commit changes to the EPICS version number file - configure/CONFIG_BASE_VERSION. + +

    For each external submodule, check if the module's release version + number (and SHRLIB_VERSION setting) has been updated if appropriate, + and that its Release Notes have been updated to cover any changes. + Commit any fixes and tag the module if that hasn't already been + done.

    +

    Update all external submodules on the Base-7.0 branch and + commit.

      Release Manager - Tag the module in Git: + Edit and commit changes to the EPICS Base version number file and + the embedded module version files: +
      +
    • configure/CONFIG_BASE_VERSION
    • +
    • modules/libcom/configure/CONFIG_LIBCOM_VERSION
    • +
    • modules/ca/configure/CONFIG_CA_VERSION
    • +
    • modules/database/configure/CONFIG_DATABASE_VERSION
    • +
    + + +   + Release Manager + Tag the epics-base module in Git:
    - cd base-7.1
    - git tag -m 'ANJ: Tagged for 7.1.1' R7.1.1 + cd base-7.0
    + git tag -m 'ANJ: Tagged for 7.0.2' R7.0.2
    - + Don't push the new tag to the Launchpad repository yet.   @@ -283,12 +302,12 @@ made.

    script generates a gzipped tarfile directly from the tag, excluding the files and directories that are only used for continuous integration:
    - cd base-7.1
    - .ci/make-tar.sh R7.1.1 base-7.1.1.tar.gz base-7.1.1/ + cd base-7.0
    + ./.tools/make-tar.sh R7.0.2 base-7.0.2.tar.gz base-7.0.2/
    Create a GPG signature file of the tarfile as follows:
    - gpg --armor --sign --detach-sig base-7.1.1.tar.gz + gpg --armor --sign --detach-sig base-7.0.2.tar.gz
    @@ -296,7 +315,12 @@ made.

      Release Manager Test the tar file by extracting its contents and building it on at - least one supported platform + least one supported platform. When this succeeds the new git tag can be + pushed to the Launchpad repository: +
    + git push --follow-tags upstream 7.0 +
    +   @@ -316,8 +340,8 @@ made.

      Website Manager - Copy the tar file and its .asc signature file to the Base - download area of the website. + Upload the tar file and its .asc signature file to the + Launchpad milestone for this release version.   diff --git a/modules/Makefile b/modules/Makefile index cd782fe80..214e914f1 100644 --- a/modules/Makefile +++ b/modules/Makefile @@ -55,4 +55,4 @@ $(RELEASE_LOCAL): $(ECHO) Creating $@, EPICS_BASE = $(INSTALL_LOCATION_ABS) @echo EPICS_BASE = $(INSTALL_LOCATION_ABS)> $@ realclean: - $(RM) $(RELEASE_LOCAL) + $(RM) $(wildcard RELEASE.*.local) diff --git a/modules/ca/configure/CONFIG_CA_VERSION b/modules/ca/configure/CONFIG_CA_VERSION index c50549018..fb80868c0 100644 --- a/modules/ca/configure/CONFIG_CA_VERSION +++ b/modules/ca/configure/CONFIG_CA_VERSION @@ -1,4 +1,4 @@ EPICS_CA_MAJOR_VERSION = 4 EPICS_CA_MINOR_VERSION = 13 -EPICS_CA_MAINTENANCE_VERSION = 2 -EPICS_CA_DEVELOPMENT_FLAG = 1 +EPICS_CA_MAINTENANCE_VERSION = 3 +EPICS_CA_DEVELOPMENT_FLAG = 0 diff --git a/modules/ca/src/client/Makefile b/modules/ca/src/client/Makefile index 8686a8bb0..d707d5395 100644 --- a/modules/ca/src/client/Makefile +++ b/modules/ca/src/client/Makefile @@ -126,7 +126,7 @@ EXPANDVARS += EPICS_CA_DEVELOPMENT_FLAG EXPANDFLAGS += $(foreach var,$(EXPANDVARS),-D$(var)="$(strip $($(var)))") # shared library ABI version. -SHRLIB_VERSION = 4.13.1 +SHRLIB_VERSION = $(EPICS_CA_MAJOR_VERSION).$(EPICS_CA_MINOR_VERSION).$(EPICS_CA_MAINTENANCE_VERSION) include $(TOP)/configure/RULES diff --git a/modules/ca/src/client/udpiiu.cpp b/modules/ca/src/client/udpiiu.cpp index 73d4ee4cb..1bcb03716 100644 --- a/modules/ca/src/client/udpiiu.cpp +++ b/modules/ca/src/client/udpiiu.cpp @@ -193,7 +193,7 @@ udpiiu::udpiiu ( #ifdef IP_MULTICAST_TTL { - int ttl; + osiSockOptMcastTTL_t ttl; long val; if(envGetLongConfigParam(&EPICS_CA_MCAST_TTL, &val)) val =1; @@ -961,7 +961,7 @@ bool udpiiu::pushDatagramMsg ( epicsGuard < epicsMutex > & guard, udpiiu :: SearchDestUDP :: SearchDestUDP ( const osiSockAddr & destAddr, udpiiu & udpiiuIn ) : - _destAddr ( destAddr ), _udpiiu ( udpiiuIn ) + _lastError (0u), _destAddr ( destAddr ), _udpiiu ( udpiiuIn ) { } @@ -976,6 +976,13 @@ void udpiiu :: SearchDestUDP :: searchRequest ( int status = sendto ( _udpiiu.sock, const_cast(pBuf), bufSizeAsInt, 0, & _destAddr.sa, sizeof ( _destAddr.sa ) ); if ( status == bufSizeAsInt ) { + if ( _lastError ) { + char buf[64]; + sockAddrToDottedIP ( &_destAddr.sa, buf, sizeof ( buf ) ); + errlogPrintf ( + "CAC: ok sending UDP msg to %s\n", buf); + } + _lastError = 0; break; } if ( status >= 0 ) { @@ -1002,7 +1009,9 @@ void udpiiu :: SearchDestUDP :: searchRequest ( else if ( localErrno == SOCK_EBADF ) { break; } - else { + else if ( localErrno == _lastError) { + break; + } else { char sockErrBuf[64]; epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) ); @@ -1011,6 +1020,8 @@ void udpiiu :: SearchDestUDP :: searchRequest ( errlogPrintf ( "CAC: error = \"%s\" sending UDP msg to %s\n", sockErrBuf, buf); + + _lastError = localErrno; break; } } diff --git a/modules/ca/src/client/udpiiu.h b/modules/ca/src/client/udpiiu.h index 6b41e38ed..fdf348296 100644 --- a/modules/ca/src/client/udpiiu.h +++ b/modules/ca/src/client/udpiiu.h @@ -123,6 +123,7 @@ private: void show ( epicsGuard < epicsMutex > &, unsigned level ) const; private: + int _lastError; osiSockAddr _destAddr; udpiiu & _udpiiu; }; diff --git a/modules/ca/src/template/top/caPerlApp/caget.pl b/modules/ca/src/template/top/caPerlApp/caget.pl index 5e7be9f79..0d9af37a1 100644 --- a/modules/ca/src/template/top/caPerlApp/caget.pl +++ b/modules/ca/src/template/top/caPerlApp/caget.pl @@ -143,6 +143,10 @@ sub display { printf " Lo ctrl limit: %g\n", $data->{lower_ctrl_limit}; printf " Hi ctrl limit: %g\n", $data->{upper_ctrl_limit}; } + if (exists $data->{ackt}) { + printf " Ack transients: %s\n", $data->{ackt} ? 'YES' : 'NO'; + printf " Ack severity: %s\n", $data->{acks}; + } } else { my $value = format_number($data, $type); if ($opt_t) { diff --git a/modules/ca/src/tools/caget.c b/modules/ca/src/tools/caget.c index 00056709e..75225d07f 100644 --- a/modules/ca/src/tools/caget.c +++ b/modules/ca/src/tools/caget.c @@ -34,6 +34,7 @@ #include #include #include +#include "epicsVersion.h" #include "tool_lib.h" @@ -55,6 +56,7 @@ static void usage (void) { fprintf (stderr, "\nUsage: caget [options] ...\n\n" " -h: Help: Print this message\n" + " -V: Version: Show EPICS and CA versions\n" "Channel Access options:\n" " -w : Wait time, specifies CA timeout, default is %f second(s)\n" " -c: Asynchronous get (use ca_get_callback and wait for completion)\n" @@ -389,11 +391,14 @@ int main (int argc, char *argv[]) LINE_BUFFER(stdout); /* Configure stdout buffering */ - while ((opt = getopt(argc, argv, ":taicnhsSe:f:g:l:#:d:0:w:p:F:")) != -1) { + while ((opt = getopt(argc, argv, ":taicnhsSVe:f:g:l:#:d:0:w:p:F:")) != -1) { switch (opt) { case 'h': /* Print usage */ usage(); return 0; + case 'V': + printf( "\nEPICS Version %s, CA Protocol version %s\n", EPICS_VERSION_STRING, ca_version() ); + return 0; case 't': /* Terse output mode */ complainIfNotPlainAndSet(&format, terse); break; diff --git a/modules/ca/src/tools/cainfo.c b/modules/ca/src/tools/cainfo.c index ad580f473..fc18ccd3f 100644 --- a/modules/ca/src/tools/cainfo.c +++ b/modules/ca/src/tools/cainfo.c @@ -23,6 +23,7 @@ #include #include +#include "epicsVersion.h" #include #include @@ -36,12 +37,14 @@ void usage (void) { fprintf (stderr, "\nUsage: cainfo [options] ...\n\n" " -h: Help: Print this message\n" + " -V: Version: Show EPICS and CA versions\n" "Channel Access options:\n" " -w : Wait time, specifies CA timeout, default is %f second(s)\n" " -s : Call ca_client_status with the specified interest level\n" " -p : CA priority (0-%u, default 0=lowest)\n" "\nExample: cainfo my_channel another_channel\n\n" , DEFAULT_TIMEOUT, CA_PRIORITY_MAX); + fprintf (stderr, "\nEPICS Version %s, CA Protocol version %s\n", EPICS_VERSION_STRING, ca_version() ); } @@ -137,11 +140,14 @@ int main (int argc, char *argv[]) LINE_BUFFER(stdout); /* Configure stdout buffering */ - while ((opt = getopt(argc, argv, ":nhw:s:p:")) != -1) { + while ((opt = getopt(argc, argv, ":nhVw:s:p:")) != -1) { switch (opt) { case 'h': /* Print usage */ usage(); return 0; + case 'V': + printf( "\nEPICS Version %s, CA Protocol version %s\n", EPICS_VERSION_STRING, ca_version() ); + return 0; case 'w': /* Set CA timeout value */ if(epicsScanDouble(optarg, &caTimeout) != 1) { diff --git a/modules/ca/src/tools/camonitor.c b/modules/ca/src/tools/camonitor.c index 307dad8d6..a3fdecd55 100644 --- a/modules/ca/src/tools/camonitor.c +++ b/modules/ca/src/tools/camonitor.c @@ -26,6 +26,7 @@ #include #include #include +#include "epicsVersion.h" #include #include @@ -44,7 +45,8 @@ void usage (void) { fprintf (stderr, "\nUsage: camonitor [options] ...\n" "\n" - " -h: Help; Print this message\n" + " -h: Help: Print this message\n" + " -V: Version: Show EPICS and CA versions\n" "Channel Access options:\n" " -w : Wait time, specifies CA timeout, default is %f second(s)\n" " -m : Specify CA event mask to use. is any combination of\n" @@ -209,11 +211,14 @@ int main (int argc, char *argv[]) LINE_BUFFER(stdout); /* Configure stdout buffering */ - while ((opt = getopt(argc, argv, ":nhm:sSe:f:g:l:#:0:w:t:p:F:")) != -1) { + while ((opt = getopt(argc, argv, ":nhVm:sSe:f:g:l:#:0:w:t:p:F:")) != -1) { switch (opt) { case 'h': /* Print usage */ usage(); return 0; + case 'V': + printf( "\nEPICS Version %s, CA Protocol version %s\n", EPICS_VERSION_STRING, ca_version() ); + return 0; case 'n': /* Print ENUM as index numbers */ enumAsNr=1; break; diff --git a/modules/ca/src/tools/caput.c b/modules/ca/src/tools/caput.c index 5e4d10e23..79ffef8c3 100644 --- a/modules/ca/src/tools/caput.c +++ b/modules/ca/src/tools/caput.c @@ -37,6 +37,7 @@ #include #include #include +#include "epicsVersion.h" #include "tool_lib.h" @@ -59,6 +60,7 @@ void usage (void) fprintf (stderr, "\nUsage: caput [options] ...\n" " caput -a [options] ...\n\n" " -h: Help: Print this message\n" + " -V: Version: Show EPICS and CA versions\n" "Channel Access options:\n" " -w : Wait time, specifies CA timeout, default is %f second(s)\n" " -c: Asynchronous put (use ca_put_callback and wait for completion)\n" @@ -281,11 +283,14 @@ int main (int argc, char *argv[]) LINE_BUFFER(stdout); /* Configure stdout buffering */ putenv("POSIXLY_CORRECT="); /* Behave correct on GNU getopt systems */ - while ((opt = getopt(argc, argv, ":cnlhatsS#:w:p:F:")) != -1) { + while ((opt = getopt(argc, argv, ":cnlhatsVS#:w:p:F:")) != -1) { switch (opt) { case 'h': /* Print usage */ usage(); return 0; + case 'V': + printf( "\nEPICS Version %s, CA Protocol version %s\n", EPICS_VERSION_STRING, ca_version() ); + return 0; case 'n': /* Force interpret ENUM as index number */ enumAsNr = 1; enumAsString = 0; diff --git a/modules/database/configure/CONFIG b/modules/database/configure/CONFIG index 9ebdbeb20..c471407c4 100644 --- a/modules/database/configure/CONFIG +++ b/modules/database/configure/CONFIG @@ -25,7 +25,7 @@ INSTALL_LOCATION = $(TOP) BASE_CPPFLAGS += -DUSE_TYPED_RSET # Shared library ABI version. -SHRLIB_VERSION = 3.17.0 +SHRLIB_VERSION = $(EPICS_DATABASE_MAJOR_VERSION).$(EPICS_DATABASE_MINOR_VERSION).$(EPICS_DATABASE_MAINTENANCE_VERSION) # CONFIG_SITE files contain other build configuration settings include $(TOP)/configure/CONFIG_SITE diff --git a/modules/database/configure/CONFIG_DATABASE_VERSION b/modules/database/configure/CONFIG_DATABASE_VERSION index a42eab01e..63ad9cd4c 100644 --- a/modules/database/configure/CONFIG_DATABASE_VERSION +++ b/modules/database/configure/CONFIG_DATABASE_VERSION @@ -1,4 +1,4 @@ EPICS_DATABASE_MAJOR_VERSION = 3 EPICS_DATABASE_MINOR_VERSION = 17 -EPICS_DATABASE_MAINTENANCE_VERSION = 1 -EPICS_DATABASE_DEVELOPMENT_FLAG = 1 +EPICS_DATABASE_MAINTENANCE_VERSION = 2 +EPICS_DATABASE_DEVELOPMENT_FLAG = 0 diff --git a/modules/database/src/ioc/Makefile b/modules/database/src/ioc/Makefile index 00851867e..325872826 100644 --- a/modules/database/src/ioc/Makefile +++ b/modules/database/src/ioc/Makefile @@ -25,11 +25,6 @@ INC += databaseVersionNum.h PROD_LIBS = Com -EPICS_DATABASE_MAJOR_VERSION = 3 -EPICS_DATABASE_MINOR_VERSION = 17 -EPICS_DATABASE_MAINTENANCE_VERSION = 0 -EPICS_DATABASE_DEVELOPMENT_FLAG = 1 - include $(IOCDIR)/as/Makefile include $(IOCDIR)/bpt/Makefile include $(IOCDIR)/db/Makefile diff --git a/modules/database/src/ioc/db/Makefile b/modules/database/src/ioc/db/Makefile index 1f4bfd181..7e9d56704 100644 --- a/modules/database/src/ioc/db/Makefile +++ b/modules/database/src/ioc/db/Makefile @@ -35,7 +35,6 @@ INC += dbTest.h INC += dbCaTest.h INC += db_test.h INC += db_field_log.h -INC += initHooks.h INC += recGbl.h INC += dbIocRegister.h INC += chfPlugin.h @@ -63,7 +62,7 @@ DBDINC += menuScan DBDINC += dbCommon dbMenusPod = $(notdir $(wildcard ../db/menu*.dbd.pod)) -HTMLS += $(patsubst %.dbd.pod,%.html,$(menusPod)) +HTMLS += $(patsubst %.dbd.pod,%.html,$(dbMenusPod)) dbCore_SRCS += dbLock.c dbCore_SRCS += dbAccess.c @@ -87,7 +86,6 @@ dbCore_SRCS += recGbl.c dbCore_SRCS += callback.c dbCore_SRCS += dbCa.c dbCore_SRCS += dbCaTest.c -dbCore_SRCS += initHooks.c dbCore_SRCS += cvtBpt.c dbCore_SRCS += dbContext.cpp dbCore_SRCS += dbChannelIO.cpp diff --git a/modules/database/src/ioc/db/callback.c b/modules/database/src/ioc/db/callback.c index ae074141c..fe6db815b 100644 --- a/modules/database/src/ioc/db/callback.c +++ b/modules/database/src/ioc/db/callback.c @@ -54,6 +54,7 @@ typedef struct cbQueueSet { epicsEventId semWakeUp; epicsRingPointerId queue; int queueOverflow; + int queueOverflows; int shutdown; int threadsConfigured; int threadsRunning; @@ -103,6 +104,51 @@ int callbackSetQueueSize(int size) return 0; } +int callbackQueueStatus(const int reset, callbackQueueStats *result) +{ + int ret; + if (!callbackIsInit) return -1; + if (result) { + int prio; + result->size = callbackQueueSize; + for(prio = 0; prio < NUM_CALLBACK_PRIORITIES; prio++) { + epicsRingPointerId qId = callbackQueue[prio].queue; + result->numUsed[prio] = epicsRingPointerGetUsed(qId); + result->maxUsed[prio] = epicsRingPointerGetHighWaterMark(qId); + result->numOverflow[prio] = epicsAtomicGetIntT(&callbackQueue[prio].queueOverflows); + } + ret = 0; + } else { + ret = -2; + } + if (reset) { + int prio; + for(prio = 0; prio < NUM_CALLBACK_PRIORITIES; prio++) { + epicsRingPointerResetHighWaterMark(callbackQueue[prio].queue); + } + } + return ret; +} + +void callbackQueueShow(const int reset) +{ + callbackQueueStats stats; + if (callbackQueueStatus(reset, &stats) == -1) { + fprintf(stderr, "Callback system not initialized, yet. Please run " + "iocInit before using this command.\n"); + } else { + int prio; + printf("PRIORITY HIGH-WATER MARK ITEMS IN Q Q SIZE %% USED Q OVERFLOWS\n"); + for (prio = 0; prio < NUM_CALLBACK_PRIORITIES; prio++) { + double qusage = 100.0 * stats.numUsed[prio] / stats.size; + printf("%8s %15d %10d %6d %6.1f %11d\n", + threadNamePrefix[prio], stats.maxUsed[prio], + stats.numUsed[prio], stats.size, qusage, + stats.numOverflow[prio]); + } + } +} + int callbackParallelThreads(int count, const char *prio) { if (callbackIsInit) { @@ -290,6 +336,7 @@ int callbackRequest(CALLBACK *pcallback) if (!pushOK) { epicsInterruptContextMessage(fullMessage[priority]); mySet->queueOverflow = TRUE; + epicsAtomicIncrIntT(&mySet->queueOverflows); return S_db_bufFull; } epicsEventSignal(mySet->semWakeUp); diff --git a/modules/database/src/ioc/db/callback.h b/modules/database/src/ioc/db/callback.h index fa626d1d0..720cf337d 100644 --- a/modules/database/src/ioc/db/callback.h +++ b/modules/database/src/ioc/db/callback.h @@ -48,6 +48,13 @@ typedef epicsCallback CALLBACK; typedef void (*CALLBACKFUNC)(struct callbackPvt*); +typedef struct callbackQueueStats { + int size; + int numUsed[NUM_CALLBACK_PRIORITIES]; + int maxUsed[NUM_CALLBACK_PRIORITIES]; + int numOverflow[NUM_CALLBACK_PRIORITIES]; +} callbackQueueStats; + #define callbackSetCallback(PFUN, PCALLBACK) \ ( (PCALLBACK)->callback = (PFUN) ) #define callbackSetPriority(PRIORITY, PCALLBACK) \ @@ -73,6 +80,8 @@ epicsShareFunc void callbackCancelDelayed(CALLBACK *pcallback); epicsShareFunc void callbackRequestProcessCallbackDelayed( CALLBACK *pCallback, int Priority, void *pRec, double seconds); epicsShareFunc int callbackSetQueueSize(int size); +epicsShareFunc int callbackQueueStatus(const int reset, callbackQueueStats *result); +epicsShareFunc void callbackQueueShow(const int reset); epicsShareFunc int callbackParallelThreads(int count, const char *prio); #ifdef __cplusplus diff --git a/modules/database/src/ioc/db/dbAccess.c b/modules/database/src/ioc/db/dbAccess.c index f52b8881f..af396c74e 100644 --- a/modules/database/src/ioc/db/dbAccess.c +++ b/modules/database/src/ioc/db/dbAccess.c @@ -34,7 +34,7 @@ #include "errlog.h" #include "errMdef.h" -#define epicsExportSharedSymbols +#include "epicsExport.h" /* #define epicsExportSharedSymbols */ #include "caeventmask.h" #include "callback.h" #include "dbAccessDefs.h" @@ -65,6 +65,9 @@ epicsShareDef struct dbBase *pdbbase = 0; epicsShareDef volatile int interruptAccept=FALSE; +epicsShareDef int dbAccessDebugPUTF = 0; +epicsExportAddress(int, dbAccessDebugPUTF); + /* Hook Routines */ epicsShareDef DB_LOAD_RECORDS_HOOK_ROUTINE dbLoadRecordsHook = NULL; @@ -446,22 +449,6 @@ int dbGetFieldIndex(const struct dbAddr *paddr) return paddr->pfldDes->indRecordType; } -/* - * Process a record if its scan field is passive. - * Will notify if processing is complete by callback. - * (only if you are interested in completion) - */ -long dbScanPassive(dbCommon *pfrom, dbCommon *pto) -{ - /* if not passive just return success */ - if (pto->scan != 0) - return 0; - - if (pfrom && pfrom->ppn) - dbNotifyAdd(pfrom,pto); - return dbProcess(pto); -} - /* * Process the record. * 1. Check for breakpoints. @@ -527,7 +514,8 @@ long dbProcess(dbCommon *precord) unsigned short monitor_mask; if (*ptrace) - printf("%s: Active %s\n", context, precord->name); + printf("%s: dbProcess of Active '%s' with RPRO=%d\n", + context, precord->name, precord->rpro); /* raise scan alarm after MAX_LOCK times */ if ((precord->stat == SCAN_ALARM) || @@ -556,7 +544,8 @@ long dbProcess(dbCommon *precord) /* if disabled check disable alarm severity and return success */ if (precord->disa == precord->disv) { if (*ptrace) - printf("%s: Disabled %s\n", context, precord->name); + printf("%s: dbProcess of Disabled '%s'\n", + context, precord->name); /*take care of caching and notifyCompletion*/ precord->rpro = FALSE; @@ -593,7 +582,7 @@ long dbProcess(dbCommon *precord) } if (*ptrace) - printf("%s: Process %s\n", context, precord->name); + printf("%s: dbProcess of '%s'\n", context, precord->name); /* process record */ status = prset->process(precord); @@ -612,6 +601,31 @@ all_done: return status; } +long dbEntryToAddr(const DBENTRY *pdbentry, DBADDR *paddr) +{ + dbFldDes *pflddes = pdbentry->pflddes; + short dbfType = pflddes->field_type; + + paddr->precord = pdbentry->precnode->precord; + paddr->pfield = pdbentry->pfield; + paddr->pfldDes = pflddes; + paddr->no_elements = 1; + paddr->field_type = dbfType; + paddr->field_size = pflddes->size; + paddr->special = pflddes->special; + paddr->dbr_field_type = mapDBFToDBR[dbfType]; + + if (paddr->special == SPC_DBADDR) { + const rset *prset = dbGetRset(paddr); + + /* Let record type modify paddr */ + if (prset && prset->cvt_dbaddr) { + return prset->cvt_dbaddr(paddr); + } + } + return 0; +} + /* * Fill out a database structure (*paddr) for * a record given by the name "pname." @@ -622,9 +636,7 @@ all_done: long dbNameToAddr(const char *pname, DBADDR *paddr) { DBENTRY dbEntry; - dbFldDes *pflddes; long status = 0; - short dbfType; if (!pname || !*pname || !pdbbase) return S_db_notFound; @@ -639,46 +651,28 @@ long dbNameToAddr(const char *pname, DBADDR *paddr) status = dbGetAttributePart(&dbEntry, &pname); if (status) goto finish; - pflddes = dbEntry.pflddes; - dbfType = pflddes->field_type; - - paddr->precord = dbEntry.precnode->precord; - paddr->pfield = dbEntry.pfield; - paddr->pfldDes = pflddes; - paddr->no_elements = 1; - paddr->field_type = dbfType; - paddr->field_size = pflddes->size; - paddr->special = pflddes->special; - paddr->dbr_field_type = mapDBFToDBR[dbfType]; - - if (paddr->special == SPC_DBADDR) { - rset *prset = dbGetRset(paddr); - - /* Let record type modify paddr */ - if (prset && prset->cvt_dbaddr) { - status = prset->cvt_dbaddr(paddr); - if (status) - goto finish; - dbfType = paddr->field_type; - } - } + status = dbEntryToAddr(&dbEntry, paddr); + if (status) goto finish; /* Handle field modifiers */ if (*pname++ == '$') { + short dbfType = paddr->field_type; + /* Some field types can be accessed as char arrays */ if (dbfType == DBF_STRING) { paddr->no_elements = paddr->field_size; paddr->field_type = DBF_CHAR; paddr->field_size = 1; paddr->dbr_field_type = DBR_CHAR; - } else if (dbfType >= DBF_INLINK && dbfType <= DBF_FWDLINK) { + } + else if (dbfType >= DBF_INLINK && dbfType <= DBF_FWDLINK) { /* Clients see a char array, but keep original dbfType */ paddr->no_elements = PVLINK_STRINGSZ; paddr->field_size = 1; paddr->dbr_field_type = DBR_CHAR; - } else { + } + else { status = S_dbLib_fieldNotFound; - goto finish; } } @@ -713,6 +707,18 @@ void dbInitEntryFromRecord(struct dbCommon *prec, DBENTRY *pdbentry) pdbentry->precnode = ppvt->recnode; } +struct link* dbGetDevLink(struct dbCommon* prec) +{ + DBLINK *plink = 0; + DBENTRY entry; + dbInitEntryFromRecord(prec, &entry); + if(dbFindField(&entry, "INP")==0 || dbFindField(&entry, "OUT")==0) { + plink = (DBLINK*)entry.pfield; + } + dbFinishEntry(&entry); + return plink; +} + long dbValueSize(short dbr_type) { /* sizes for value associated with each DBR request type */ @@ -1049,7 +1055,7 @@ static long dbPutFieldLink(DBADDR *paddr, return S_db_badDbrtype; } - status = dbParseLink(pstring, pfldDes->field_type, &link_info, 0); + status = dbParseLink(pstring, pfldDes->field_type, &link_info); if (status) return status; @@ -1222,8 +1228,8 @@ long dbPutField(DBADDR *paddr, short dbrType, precord->scan == 0 && dbrType < DBR_PUT_ACKT)) { if (precord->pact) { - if (precord->tpro) - printf("%s: Active %s\n", + if (dbAccessDebugPUTF && precord->tpro) + printf("%s: dbPutField to Active '%s', setting RPRO=1\n", epicsThreadGetNameSelf(), precord->name); precord->rpro = TRUE; } else { @@ -1356,4 +1362,3 @@ done: paddr->pfield = pfieldsave; return status; } - diff --git a/modules/database/src/ioc/db/dbAccessDefs.h b/modules/database/src/ioc/db/dbAccessDefs.h index cc45b17fe..52ac4016d 100644 --- a/modules/database/src/ioc/db/dbAccessDefs.h +++ b/modules/database/src/ioc/db/dbAccessDefs.h @@ -34,6 +34,7 @@ extern "C" { epicsShareExtern struct dbBase *pdbbase; epicsShareExtern volatile int interruptAccept; +epicsShareExtern int dbAccessDebugPUTF; /* The database field and request types are defined in dbFldTypes.h*/ /* Data Base Request Options */ @@ -203,6 +204,8 @@ struct dbr_alDouble {DBRalDouble}; #define S_db_notInit (M_dbAccess|67) /*Not initialized*/ #define S_db_bufFull (M_dbAccess|68) /*Buffer full*/ +struct dbEntry; + epicsShareFunc long dbPutSpecial(struct dbAddr *paddr,int pass); epicsShareFunc rset * dbGetRset(const struct dbAddr *paddr); epicsShareFunc long dbPutAttribute( @@ -212,8 +215,29 @@ epicsShareFunc int dbGetFieldIndex(const struct dbAddr *paddr); epicsShareFunc long dbScanPassive( struct dbCommon *pfrom,struct dbCommon *pto); epicsShareFunc long dbProcess(struct dbCommon *precord); -epicsShareFunc long dbNameToAddr( - const char *pname,struct dbAddr *); +epicsShareFunc long dbNameToAddr(const char *pname, struct dbAddr *paddr); + +/** Initialize DBADDR from a dbEntry + * Also handles SPC_DBADDR processing. This is really an internal + * routine for use by dbNameToAddr() and dbChannelCreate(). + */ +epicsShareFunc long dbEntryToAddr(const struct dbEntry *pdbentry, + struct dbAddr *paddr); + +/** Initialize DBENTRY from a valid dbAddr* + * Constant time equivalent of dbInitEntry() then dbFindRecord(), + * and finally dbFollowAlias(). + */ +epicsShareFunc void dbInitEntryFromAddr(struct dbAddr *paddr, + struct dbEntry *pdbentry); + +/** Initialize DBENTRY from a valid record (dbCommon*) + * Constant time equivalent of dbInitEntry() then dbFindRecord(), + * and finally dbFollowAlias() when no field is specified. + */ +epicsShareFunc void dbInitEntryFromRecord(struct dbCommon *prec, + struct dbEntry *pdbentry); + epicsShareFunc devSup* dbDTYPtoDevSup(dbRecordType *prdes, int dtyp); epicsShareFunc devSup* dbDSETtoDevSup(dbRecordType *prdes, struct dset *pdset); epicsShareFunc long dbGetField( diff --git a/modules/database/src/ioc/db/dbCa.c b/modules/database/src/ioc/db/dbCa.c index e844ad826..843fbfc0c 100644 --- a/modules/database/src/ioc/db/dbCa.c +++ b/modules/database/src/ioc/db/dbCa.c @@ -703,7 +703,12 @@ static long doLocked(struct link *plink, dbLinkUserCallback rtn, void *priv) caLink *pca; long status; - pcaGetCheck + assert(plink); + if (plink->type != CA_LINK) return -1; + pca = (caLink *)plink->value.pv_link.pvt; + assert(pca); + epicsMutexMustLock(pca->lock); + assert(pca->plink); status = rtn(plink, priv); epicsMutexUnlock(pca->lock); return status; @@ -842,6 +847,7 @@ static void eventCallback(struct event_handler_args arg) struct dbr_time_double *pdbr_time_double; dbCaCallback monitor = 0; void *userPvt = 0; + int doScan = 1; assert(pca); epicsMutexMustLock(pca->lock); @@ -872,10 +878,13 @@ static void eventCallback(struct event_handler_args arg) memcpy(pca->pgetString, dbr_value_ptr(arg.dbr, arg.type), size); pca->gotInString = TRUE; } else switch (arg.type){ + case DBR_TIME_ENUM: + /* Disable the record scan if we also have a string monitor */ + doScan = !(plink->value.pv_link.pvlMask & pvlOptInpString); + /* fall through */ case DBR_TIME_STRING: case DBR_TIME_SHORT: case DBR_TIME_FLOAT: - case DBR_TIME_ENUM: case DBR_TIME_CHAR: case DBR_TIME_LONG: case DBR_TIME_DOUBLE: @@ -893,7 +902,7 @@ static void eventCallback(struct event_handler_args arg) pca->sevr = pdbr_time_double->severity; pca->stat = pdbr_time_double->status; memcpy(&pca->timeStamp, &pdbr_time_double->stamp, sizeof(epicsTimeStamp)); - if (precord) { + if (doScan && precord) { struct pv_link *ppv_link = &plink->value.pv_link; if ((ppv_link->pvlMask & pvlOptCP) || diff --git a/modules/database/src/ioc/db/dbChannel.c b/modules/database/src/ioc/db/dbChannel.c index 92be08c77..6db2982d8 100644 --- a/modules/database/src/ioc/db/dbChannel.c +++ b/modules/database/src/ioc/db/dbChannel.c @@ -20,6 +20,7 @@ #include "cantProceed.h" #include "epicsAssert.h" #include "epicsString.h" +#include "epicsStdio.h" #include "errlog.h" #include "freeList.h" #include "gpHash.h" @@ -472,9 +473,7 @@ dbChannel * dbChannelCreate(const char *name) dbChannel *chan = NULL; char *cname; dbAddr *paddr; - dbFldDes *pflddes; long status; - short dbfType; if (!name || !*name || !pdbbase) return NULL; @@ -497,32 +496,14 @@ dbChannel * dbChannelCreate(const char *name) ellInit(&chan->post_chain); paddr = &chan->addr; - pflddes = dbEntry.pflddes; - dbfType = pflddes->field_type; - - paddr->precord = dbEntry.precnode->precord; - paddr->pfield = dbEntry.pfield; - paddr->pfldDes = pflddes; - paddr->no_elements = 1; - paddr->field_type = dbfType; - paddr->field_size = pflddes->size; - paddr->special = pflddes->special; - paddr->dbr_field_type = mapDBFToDBR[dbfType]; - - if (paddr->special == SPC_DBADDR) { - rset *prset = dbGetRset(paddr); - - /* Let record type modify paddr */ - if (prset && prset->cvt_dbaddr) { - status = prset->cvt_dbaddr(paddr); - if (status) - goto finish; - dbfType = paddr->field_type; - } - } + status = dbEntryToAddr(&dbEntry, paddr); + if (status) + goto finish; /* Handle field modifiers */ if (*pname) { + short dbfType = paddr->field_type; + if (*pname == '$') { /* Some field types can be accessed as char arrays */ if (dbfType == DBF_STRING) { @@ -530,12 +511,14 @@ dbChannel * dbChannelCreate(const char *name) paddr->field_type = DBF_CHAR; paddr->field_size = 1; paddr->dbr_field_type = DBR_CHAR; - } else if (dbfType >= DBF_INLINK && dbfType <= DBF_FWDLINK) { + } + else if (dbfType >= DBF_INLINK && dbfType <= DBF_FWDLINK) { /* Clients see a char array, but keep original dbfType */ paddr->no_elements = PVLINK_STRINGSZ; paddr->field_size = 1; paddr->dbr_field_type = DBR_CHAR; - } else { + } + else { status = S_dbLib_fieldNotFound; goto finish; } diff --git a/modules/database/src/ioc/db/dbDbLink.c b/modules/database/src/ioc/db/dbDbLink.c index 105799876..ce0110a64 100644 --- a/modules/database/src/ioc/db/dbDbLink.c +++ b/modules/database/src/ioc/db/dbDbLink.c @@ -12,6 +12,29 @@ * Current Author: Andrew Johnson */ +/* The PUTF and RPRO fields in dbCommon are flags that indicate when a record + * is being processed as a result of an external put (i.e. some server process + * calling dbPutField()), ensuring that the record and its successors will + * eventually get processed even if they happen to be busy at the time of the + * put. From Base-3.16.2 and 7.0.2 the code ensures that all records downstream + * from the original are processed even if a busy asynchronous device appears + * in the processing chain (this breaks the chain in older versions). + * + * PUTF - This field is set in dbPutField() prior to it calling dbProcess(). + * It is normally cleared at the end of processing in recGblFwdLink(). + * It may also be cleared in dbProcess() if DISA==DISV (scan disabled), + * or by the processTarget() function below. + * + * If PUTF is TRUE before a call to dbProcess(prec), then after it returns + * either PACT is TRUE, or PUTF will be FALSE. + * + * RPRO - This field is set by dbPutField() or by the processTarget() function + * below when a record to be processed is found to be busy (PACT==1). + * It is normally cleared in recGblFwdLink() when the record is queued + * for re-processing, or in dbProcess() if DISA==DISV (scan disabled). + */ + + #include #include #include @@ -43,17 +66,22 @@ #include "dbNotify.h" #include "dbScan.h" #include "dbStaticLib.h" +#include "dbServer.h" #include "devSup.h" #include "link.h" #include "recGbl.h" #include "recSup.h" #include "special.h" +#include "dbDbLink.h" + /***************************** Database Links *****************************/ -/* Forward definition */ +/* Forward definitions */ static lset dbDb_lset; +static long processTarget(dbCommon *psrc, dbCommon *pdst); + long dbDbInitLink(struct link *plink, short dbfType) { DBADDR dbaddr; @@ -138,11 +166,7 @@ static long dbDbGetValue(struct link *plink, short dbrType, void *pbuffer, /* scan passive records if link is process passive */ if (ppv_link->pvlMask & pvlOptPP) { - unsigned char pact = precord->pact; - - precord->pact = TRUE; status = dbScanPassive(precord, paddr->precord); - precord->pact = pact; if (status) return status; } @@ -311,22 +335,10 @@ static long dbDbPutValue(struct link *plink, short dbrType, return status; if (paddr->pfield == (void *) &pdest->proc || - (ppv_link->pvlMask & pvlOptPP && pdest->scan == 0)) { - /* if dbPutField caused asyn record to process */ - /* ask for reprocessing*/ - if (pdest->putf) { - pdest->rpro = TRUE; - } else { /* process dest record with source's PACT true */ - unsigned char pact; - - if (psrce && psrce->ppn) - dbNotifyAdd(psrce, pdest); - pact = psrce->pact; - psrce->pact = TRUE; - status = dbProcess(pdest); - psrce->pact = pact; - } + (ppv_link->pvlMask & pvlOptPP && pdest->scan == 0)) { + status = processTarget(psrce, pdest); } + return status; } @@ -356,3 +368,75 @@ static lset dbDb_lset = { dbDbPutValue, NULL, dbDbScanFwdLink, doLocked }; + + +/* + * Process a record if its scan field is passive. + */ +long dbScanPassive(dbCommon *pfrom, dbCommon *pto) +{ + /* if not passive we're done */ + if (pto->scan != 0) + return 0; + + return processTarget(pfrom, pto); +} + +static long processTarget(dbCommon *psrc, dbCommon *pdst) +{ + char context[40] = ""; + int trace = dbAccessDebugPUTF && *dbLockSetAddrTrace(psrc); + long status; + epicsUInt8 pact = psrc->pact; + + psrc->pact = TRUE; + + if (psrc && psrc->ppn) + dbNotifyAdd(psrc, pdst); + + if (trace && dbServerClient(context, sizeof(context))) { + /* No client, use thread name */ + strncpy(context, epicsThreadGetNameSelf(), sizeof(context)); + context[sizeof(context) - 1] = 0; + } + + if (!pdst->pact) { + /* Normal propagation of PUTF from src to dst */ + if (trace) + printf("%s: '%s' -> '%s' with PUTF=%u\n", + context, psrc->name, pdst->name, psrc->putf); + + if (pdst->putf) + errlogPrintf("Warning: '%s.PUTF' found true with PACT false\n", + pdst->name); + + pdst->putf = psrc->putf; + } + else if (psrc->putf) { + /* The dst record is busy (awaiting async reprocessing) and + * we were originally triggered by a call to dbPutField(), + * so we mark the dst record for reprocessing once the async + * completion is over. + */ + if (trace) + printf("%s: '%s' -> Active '%s', setting RPRO=1\n", + context, psrc->name, pdst->name); + + pdst->putf = FALSE; + pdst->rpro = TRUE; + } + else { + /* The dst record is busy, but we weren't triggered by a call + * to dbPutField(). Do nothing. + */ + if (trace) + printf("%s: '%s' -> Active '%s', done\n", + context, psrc->name, pdst->name); + } + + status = dbProcess(pdst); + + psrc->pact = pact; + + return status; +} diff --git a/modules/database/src/ioc/db/dbIocRegister.c b/modules/database/src/ioc/db/dbIocRegister.c index 4d0b88cd9..c40af92c1 100644 --- a/modules/database/src/ioc/db/dbIocRegister.c +++ b/modules/database/src/ioc/db/dbIocRegister.c @@ -296,6 +296,17 @@ static void scanOnceSetQueueSizeCallFunc(const iocshArgBuf *args) scanOnceSetQueueSize(args[0].ival); } +/* scanOnceQueueShow */ +static const iocshArg scanOnceQueueShowArg0 = { "reset",iocshArgInt}; +static const iocshArg * const scanOnceQueueShowArgs[1] = + {&scanOnceQueueShowArg0}; +static const iocshFuncDef scanOnceQueueShowFuncDef = + {"scanOnceQueueShow",1,scanOnceQueueShowArgs}; +static void scanOnceQueueShowCallFunc(const iocshArgBuf *args) +{ + scanOnceQueueShow(args[0].ival); +} + /* scanppl */ static const iocshArg scanpplArg0 = { "rate",iocshArgDouble}; static const iocshArg * const scanpplArgs[1] = {&scanpplArg0}; @@ -335,6 +346,17 @@ static void callbackSetQueueSizeCallFunc(const iocshArgBuf *args) callbackSetQueueSize(args[0].ival); } +/* callbackQueueShow */ +static const iocshArg callbackQueueShowArg0 = { "reset", iocshArgInt}; +static const iocshArg * const callbackQueueShowArgs[1] = + {&callbackQueueShowArg0}; +static const iocshFuncDef callbackQueueShowFuncDef = + {"callbackQueueShow",1,callbackQueueShowArgs}; +static void callbackQueueShowCallFunc(const iocshArgBuf *args) +{ + callbackQueueShow(args[0].ival); +} + /* callbackParallelThreads */ static const iocshArg callbackParallelThreadsArg0 = { "no of threads", iocshArgInt}; static const iocshArg callbackParallelThreadsArg1 = { "priority", iocshArgString}; @@ -441,12 +463,14 @@ void dbIocRegister(void) iocshRegister(&dbLockShowLockedFuncDef,dbLockShowLockedCallFunc); iocshRegister(&scanOnceSetQueueSizeFuncDef,scanOnceSetQueueSizeCallFunc); + iocshRegister(&scanOnceQueueShowFuncDef,scanOnceQueueShowCallFunc); iocshRegister(&scanpplFuncDef,scanpplCallFunc); iocshRegister(&scanpelFuncDef,scanpelCallFunc); iocshRegister(&postEventFuncDef,postEventCallFunc); iocshRegister(&scanpiolFuncDef,scanpiolCallFunc); iocshRegister(&callbackSetQueueSizeFuncDef,callbackSetQueueSizeCallFunc); + iocshRegister(&callbackQueueShowFuncDef,callbackQueueShowCallFunc); iocshRegister(&callbackParallelThreadsFuncDef,callbackParallelThreadsCallFunc); /* Needed before callback system is initialized */ diff --git a/modules/database/src/ioc/db/dbJLink.c b/modules/database/src/ioc/db/dbJLink.c index ea054eee9..3acf75660 100644 --- a/modules/database/src/ioc/db/dbJLink.c +++ b/modules/database/src/ioc/db/dbJLink.c @@ -2,7 +2,7 @@ * Copyright (c) 2016 UChicago Argonne LLC, as Operator of Argonne * 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. \*************************************************************************/ /* dbJLink.c */ @@ -25,19 +25,33 @@ #include "dbLock.h" #include "dbStaticLib.h" #include "link.h" +#include "epicsExport.h" -#define IFDEBUG(n) if(parser->parse_debug) +epicsShareDef int dbJLinkDebug = 0; +epicsExportAddress(int, dbJLinkDebug); + +#define IFDEBUG(n) if (dbJLinkDebug >= (n)) typedef struct parseContext { jlink *pjlink; jlink *product; short dbfType; short jsonDepth; - unsigned key_is_link:1; - unsigned parse_debug:1; - unsigned lset_debug:1; } parseContext; +epicsShareDef const char *jlif_result_name[2] = { + "jlif_stop", + "jlif_continue", +}; + +epicsShareDef const char *jlif_key_result_name[5] = { + "jlif_key_stop", + "jlif_key_continue", + "jlif_key_child_inlink", + "jlif_key_child_outlink", + "jlif_key_child_fwdlink" +}; + #define CALL_OR_STOP(routine) !(routine) ? jlif_stop : (routine) static int dbjl_return(parseContext *parser, jlif_result result) { @@ -45,8 +59,8 @@ static int dbjl_return(parseContext *parser, jlif_result result) { IFDEBUG(10) { printf("dbjl_return(%s@%p, %d)\t", pjlink ? pjlink->pif->name : "", pjlink, result); - printf(" jsonDepth=%d, parseDepth=%d, key_is_link=%d\n", - parser->jsonDepth, pjlink ? pjlink->parseDepth : 0, parser->key_is_link); + printf(" jsonDepth=%d, parseDepth=%d, dbfType=%d\n", + parser->jsonDepth, pjlink ? pjlink->parseDepth : 0, parser->dbfType); } if (result == jlif_stop && pjlink) { @@ -59,6 +73,9 @@ static int dbjl_return(parseContext *parser, jlif_result result) { pjlink->pif->free_jlink(pjlink); } + IFDEBUG(10) + printf(" returning %d %s\n", result, + result == jlif_stop ? "*** STOP ***" : "Continue"); return result; } @@ -68,8 +85,8 @@ static int dbjl_value(parseContext *parser, jlif_result result) { IFDEBUG(10) { printf("dbjl_value(%s@%p, %d)\t", pjlink ? pjlink->pif->name : "", pjlink, result); - printf(" jsonDepth=%d, parseDepth=%d, key_is_link=%d\n", - parser->jsonDepth, pjlink ? pjlink->parseDepth : 0, parser->key_is_link); + printf(" jsonDepth=%d, parseDepth=%d, dbfType=%d\n", + parser->jsonDepth, pjlink ? pjlink->parseDepth : 0, parser->dbfType); } if (result == jlif_stop || pjlink->parseDepth > 0) @@ -81,7 +98,6 @@ static int dbjl_value(parseContext *parser, jlif_result result) { } else if (parent->pif->end_child) { parent->pif->end_child(parent, pjlink); } - pjlink->debug = 0; parser->pjlink = parent; @@ -159,29 +175,46 @@ static int dbjl_start_map(void *ctx) { if (!pjlink) { IFDEBUG(10) { printf("dbjl_start_map(NULL)\t"); - printf(" jsonDepth=%d, parseDepth=00, key_is_link=%d\n", - parser->jsonDepth, parser->key_is_link); + printf(" jsonDepth=%d, parseDepth=00, dbfType=%d\n", + parser->jsonDepth, parser->dbfType); } assert(parser->jsonDepth == 0); parser->jsonDepth++; - parser->key_is_link = 1; return jlif_continue; /* Opening '{' */ } IFDEBUG(10) { printf("dbjl_start_map(%s@%p)\t", pjlink ? pjlink->pif->name : "", pjlink); - printf(" jsonDepth=%d, parseDepth=%d, key_is_link=%d\n", - parser->jsonDepth, pjlink ? pjlink->parseDepth : 0, parser->key_is_link); + printf(" jsonDepth=%d, parseDepth=%d, dbfType=%d\n", + parser->jsonDepth, pjlink ? pjlink->parseDepth : 0, parser->dbfType); } pjlink->parseDepth++; parser->jsonDepth++; result = CALL_OR_STOP(pjlink->pif->parse_start_map)(pjlink); - if (result == jlif_key_child_link) { - parser->key_is_link = 1; + switch (result) { + case jlif_key_child_inlink: + parser->dbfType = DBF_INLINK; result = jlif_continue; + break; + case jlif_key_child_outlink: + parser->dbfType = DBF_OUTLINK; + result = jlif_continue; + break; + case jlif_key_child_fwdlink: + parser->dbfType = DBF_FWDLINK; + result = jlif_continue; + break; + case jlif_key_stop: + case jlif_key_continue: + break; + default: + errlogPrintf("dbJLinkInit: Bad return %d from '%s'::parse_start_map()\n", + result, pjlink->pif->name); + result = jlif_stop; + break; } IFDEBUG(10) @@ -196,8 +229,9 @@ static int dbjl_map_key(void *ctx, const unsigned char *key, size_t len) { char *link_name; linkSup *linkSup; jlif *pjlif; + jlink *child; - if (!parser->key_is_link) { + if (parser->dbfType == 0) { if (!pjlink) { errlogPrintf("dbJLinkInit: Illegal second link key '%.*s'\n", (int) len, key); @@ -207,8 +241,8 @@ static int dbjl_map_key(void *ctx, const unsigned char *key, size_t len) { IFDEBUG(10) { printf("dbjl_map_key(%s@%p, \"%.*s\")\t", pjlink->pif->name, pjlink, (int) len, key); - printf(" jsonDepth=%d, parseDepth=%d, key_is_link=%d\n", - parser->jsonDepth, pjlink ? pjlink->parseDepth : 0, parser->key_is_link); + printf(" jsonDepth=%d, parseDepth=%d, dbfType=%d\n", + parser->jsonDepth, pjlink ? pjlink->parseDepth : 0, parser->dbfType); } assert(pjlink->parseDepth > 0); @@ -219,8 +253,8 @@ static int dbjl_map_key(void *ctx, const unsigned char *key, size_t len) { IFDEBUG(10) { printf("dbjl_map_key(NULL, \"%.*s\")\t", (int) len, key); - printf(" jsonDepth=%d, parseDepth=00, key_is_link=%d\n", - parser->jsonDepth, parser->key_is_link); + printf(" jsonDepth=%d, parseDepth=00, dbfType=%d\n", + parser->jsonDepth, parser->dbfType); } link_name = dbmfStrndup((const char *) key, len); @@ -241,27 +275,35 @@ static int dbjl_map_key(void *ctx, const unsigned char *key, size_t len) { return dbjl_return(parser, jlif_stop); } - dbmfFree(link_name); - - pjlink = pjlif->alloc_jlink(parser->dbfType); - if (!pjlink) { - errlogPrintf("dbJLinkInit: Out of memory\n"); + child = pjlif->alloc_jlink(parser->dbfType); + if (!child) { + errlogPrintf("dbJLinkInit: Link type '%s' allocation failed. \n", + link_name); + dbmfFree(link_name); return dbjl_return(parser, jlif_stop); } - pjlink->pif = pjlif; - pjlink->parent = NULL; - pjlink->parseDepth = 0; - pjlink->debug = !!parser->lset_debug; + + child->pif = pjlif; + child->parseDepth = 0; + child->debug = 0; if (parser->pjlink) { /* We're starting a child link, save its parent */ - pjlink->parent = parser->pjlink; + child->parent = pjlink; + + if (pjlink->pif->start_child) + pjlink->pif->start_child(pjlink, child); } - parser->pjlink = pjlink; - parser->key_is_link = 0; + else + child->parent = NULL; + + parser->pjlink = child; + parser->dbfType = 0; + + dbmfFree(link_name); IFDEBUG(8) - printf("dbjl_map_key: New %s@%p\n", pjlink ? pjlink->pif->name : "", pjlink); + printf("dbjl_map_key: New %s@%p\n", child ? child->pif->name : "", child); return jlif_continue; } @@ -274,9 +316,9 @@ static int dbjl_end_map(void *ctx) { IFDEBUG(10) { printf("dbjl_end_map(%s@%p)\t", pjlink ? pjlink->pif->name : "NULL", pjlink); - printf(" jsonDepth=%d, parseDepth=%d, key_is_link=%d\n", + printf(" jsonDepth=%d, parseDepth=%d, dbfType=%d\n", parser->jsonDepth, pjlink ? pjlink->parseDepth : 0, - parser->key_is_link); + parser->dbfType); } parser->jsonDepth--; @@ -298,8 +340,8 @@ static int dbjl_start_array(void *ctx) { IFDEBUG(10) { printf("dbjl_start_array(%s@%p)\t", pjlink ? pjlink->pif->name : "", pjlink); - printf(" jsonDepth=%d, parseDepth=%d, key_is_link=%d\n", - parser->jsonDepth, pjlink ? pjlink->parseDepth : 0, parser->key_is_link); + printf(" jsonDepth=%d, parseDepth=%d, dbfType=%d\n", + parser->jsonDepth, pjlink ? pjlink->parseDepth : 0, parser->dbfType); } assert(pjlink); @@ -316,8 +358,8 @@ static int dbjl_end_array(void *ctx) { IFDEBUG(10) { printf("dbjl_end_array(%s@%p)\t", pjlink ? pjlink->pif->name : "", pjlink); - printf(" jsonDepth=%d, parseDepth=%d, key_is_link=%d\n", - parser->jsonDepth, pjlink ? pjlink->parseDepth : 0, parser->key_is_link); + printf(" jsonDepth=%d, parseDepth=%d, dbfType=%d\n", + parser->jsonDepth, pjlink ? pjlink->parseDepth : 0, parser->dbfType); } assert(pjlink); @@ -335,7 +377,7 @@ static yajl_callbacks dbjl_callbacks = { }; long dbJLinkParse(const char *json, size_t jlen, short dbfType, - jlink **ppjlink, unsigned opts) + jlink **ppjlink) { parseContext context, *parser = &context; yajl_alloc_funcs dbjl_allocs; @@ -347,17 +389,14 @@ long dbJLinkParse(const char *json, size_t jlen, short dbfType, parser->product = NULL; parser->dbfType = dbfType; parser->jsonDepth = 0; - parser->key_is_link = 0; - parser->parse_debug = !!(opts&LINK_DEBUG_JPARSE); - parser->lset_debug = !!(opts&LINK_DEBUG_LSET); IFDEBUG(10) printf("dbJLinkInit(\"%.*s\", %d, %p)\n", (int) jlen, json, dbfType, ppjlink); IFDEBUG(10) - printf("dbJLinkInit: jsonDepth=%d, key_is_link=%d\n", - parser->jsonDepth, parser->key_is_link); + printf("dbJLinkInit: jsonDepth=%d, dbfType=%d\n", + parser->jsonDepth, parser->dbfType); yajl_set_default_alloc_funcs(&dbjl_allocs); yh = yajl_alloc(&dbjl_callbacks, &dbjl_allocs, parser); @@ -365,8 +404,14 @@ long dbJLinkParse(const char *json, size_t jlen, short dbfType, return S_db_noMemory; ys = yajl_parse(yh, (const unsigned char *) json, jlen); - if (ys == yajl_status_ok) + IFDEBUG(10) + printf("dbJLinkInit: yajl_parse() returned %d\n", ys); + + if (ys == yajl_status_ok) { ys = yajl_complete_parse(yh); + IFDEBUG(10) + printf("dbJLinkInit: yajl_complete_parse() returned %d\n", ys); + } switch (ys) { unsigned char *err; @@ -378,6 +423,9 @@ long dbJLinkParse(const char *json, size_t jlen, short dbfType, break; case yajl_status_error: + IFDEBUG(10) + printf(" jsonDepth=%d, product=%p, pjlink=%p\n", + parser->jsonDepth, parser->product, parser->pjlink); err = yajl_get_error(yh, 1, (const unsigned char *) json, jlen); errlogPrintf("dbJLinkInit: %s\n", err); yajl_free_error(yh, err); @@ -389,18 +437,24 @@ long dbJLinkParse(const char *json, size_t jlen, short dbfType, } yajl_free(yh); + + IFDEBUG(10) + printf("dbJLinkInit: returning status=0x%lx\n\n", + status); + return status; } long dbJLinkInit(struct link *plink) { - jlink *pjlink; - assert(plink); - pjlink = plink->value.json.jlink; - if (pjlink) - plink->lset = pjlink->pif->get_lset(pjlink); + if (plink->type == JSON_LINK) { + jlink *pjlink = plink->value.json.jlink; + + if (pjlink) + plink->lset = pjlink->pif->get_lset(pjlink); + } dbLinkOpen(plink); return 0; diff --git a/modules/database/src/ioc/db/dbJLink.h b/modules/database/src/ioc/db/dbJLink.h index 61b59670b..bd1a6c8a2 100644 --- a/modules/database/src/ioc/db/dbJLink.h +++ b/modules/database/src/ioc/db/dbJLink.h @@ -2,7 +2,7 @@ * Copyright (c) 2016 UChicago Argonne LLC, as Operator of Argonne * 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. \*************************************************************************/ /* dbJLink.h */ @@ -21,12 +21,16 @@ typedef enum { jlif_continue = 1 } jlif_result; +epicsShareExtern const char *jlif_result_name[2]; + typedef enum { jlif_key_stop = jlif_stop, jlif_key_continue = jlif_continue, - jlif_key_child_link + jlif_key_child_inlink, jlif_key_child_outlink, jlif_key_child_fwdlink } jlif_key_result; +epicsShareExtern const char *jlif_key_result_name[5]; + struct link; struct lset; struct jlif; @@ -35,7 +39,7 @@ typedef struct jlink { struct jlif *pif; /* Link methods */ struct jlink *parent; /* NULL for top-level links */ int parseDepth; /* Used by parser, unused afterwards */ - unsigned debug:1; /* set by caller of jlif operations to request debug output to console */ + unsigned debug:1; /* Set to request debug output to console */ /* Link types extend or embed this structure for private storage */ } jlink; @@ -72,8 +76,9 @@ typedef struct jlif { /* Optional, parser saw a string value */ jlif_key_result (*parse_start_map)(jlink *); - /* Optional, parser saw an open-brace '{'. Return jlif_key_child_link - * to expect a child link next (extra key/value pairs may follow). + /* Optional, parser saw an open-brace '{'. Return jlif_key_child_inlink, + * jlif_key_child_outlink, or jlif_key_child_fwdlink to expect a child + * link next (extra key/value pairs may follow) */ jlif_result (*parse_map_key)(jlink *, const char *key, size_t len); @@ -90,7 +95,8 @@ typedef struct jlif { void (*end_child)(jlink *parent, jlink *child); /* Optional, called with pointer to the new child link after - * parse_start_map() returned jlif_key_child_link */ + * the child link has finished parsing successfully + */ struct lset* (*get_lset)(const jlink *); /* Required, return lset for this link instance */ @@ -98,7 +104,7 @@ typedef struct jlif { void (*report)(const jlink *, int level, int indent); /* Optional, print status information about this link instance, then * if (level > 0) print a link identifier (at indent+2) and call - * dbJLinkReport(child, level-1, indent+4) + * dbJLinkReport(child, level-1, indent+4) * for each child. */ @@ -107,13 +113,19 @@ typedef struct jlif { * Stop immediately and return status if non-zero. */ + void (*start_child)(jlink *parent, jlink *child); + /* Optional, called with pointer to the new child link after + * parse_start_map() returned a jlif_key_child_link value and + * the child link has been allocated (but not parsed yet) + */ + /* Link types must NOT extend this table with their own routines, * this space is reserved for extensions to the jlink interface. */ } jlif; epicsShareFunc long dbJLinkParse(const char *json, size_t len, short dbfType, - jlink **ppjlink, unsigned opts); + jlink **ppjlink); epicsShareFunc long dbJLinkInit(struct link *plink); epicsShareFunc void dbJLinkFree(jlink *); @@ -130,4 +142,3 @@ epicsShareFunc long dbJLinkMapAll(char *recname, jlink_map_fn rtn, void *ctx); #endif #endif /* INC_dbJLink_H */ - diff --git a/modules/database/src/ioc/db/dbLink.c b/modules/database/src/ioc/db/dbLink.c index bcdbec473..7c37058b0 100644 --- a/modules/database/src/ioc/db/dbLink.c +++ b/modules/database/src/ioc/db/dbLink.c @@ -265,8 +265,19 @@ int dbIsLinkConnected(const struct link *plink) { lset *plset = plink->lset; - if (!plset || !plset->isConnected) + if (!plset) return FALSE; + if (!plset->isVolatile) + return TRUE; + + if (!plset->isConnected) { + struct dbCommon *precord = plink->precord; + + errlogPrintf("dbLink: Link type for '%s.%s' is volatile but has no" + " lset::isConnected() method\n", + precord->name, dbLinkFieldName(plink)); + return FALSE; + } return plset->isConnected(plink); } diff --git a/modules/database/src/ioc/db/dbLink.h b/modules/database/src/ioc/db/dbLink.h index 16066e0d5..94e1f32a3 100644 --- a/modules/database/src/ioc/db/dbLink.h +++ b/modules/database/src/ioc/db/dbLink.h @@ -27,54 +27,337 @@ extern "C" { struct dbLocker; +/** @file dbLink.h + * @brief Link Support API + * + * Link support run-time API, all link types provide an lset which is used by + * the IOC database to control and operate the link. This file also declares the + * dbLink routines that IOC, record and device code can call to perform link + * operations. + */ + +/** @brief callback routine for locked link operations + * + * Called by the lset::doLocked method to permit multiple link operations + * while the link instance is locked. + * + * @param plink the link + * @param priv context for the callback routine + */ typedef long (*dbLinkUserCallback)(struct link *plink, void *priv); +/** @brief Link Support Entry Table + * + * This structure provides information about and methods for an individual link + * type. A pointer to this structure is included in every link's lset field, and + * is used to perform operations on the link. For JSON links the pointer is + * obtained by calling pjlink->pif->get_lset() at link initialization time, + * immediately before calling dbLinkOpen() to activate the link. + */ typedef struct lset { /* Characteristics of the link type */ + + /** @brief link constancy + * + * 1 means this is a constant link type whose value doesn't change. + * The link's value will be obtained using one of the methods loadScalar, + * loadLS or loadArray. + */ const unsigned isConstant:1; + + /** @brief link volatility + * + * 0 means the link is always connected. + */ const unsigned isVolatile:1; - /* Activation */ + /** @brief activate link + * + * Optional, called whenever a JSON link is initialized or added at runtime. + * + * @param plink the link + */ void (*openLink)(struct link *plink); - /* Destructor */ + /** @brief deactivate link + * + * Optional, called whenever a link address is changed at runtime, or the + * IOC is shutting down. + * + * @param locker + * @param plink the link + */ void (*removeLink)(struct dbLocker *locker, struct link *plink); - /* Const init, data type hinting */ + /* Constant link initialization and data type hinting */ + + /** @brief load constant scalar from link type + * + * Usually called during IOC initialization, constant link types must copy a + * scalar value of the indicated data type to the buffer provided and return + * 0. A non-constant link type can use this method call as an early hint + * that subsequent calls to dbGetLink() will request scalar data of the + * indicated type, although the type might change. + * + * @param plink the link + * @param dbrType data type code + * @param pbuffer where to put the value + * @returns 0 if a value was loaded, non-zero otherwise + */ long (*loadScalar)(struct link *plink, short dbrType, void *pbuffer); + + /** @brief load constant long string from link type + * + * Usually called during IOC initialization, constant link types must copy a + * nil-terminated string up to size characters long to the buffer provided, + * and write the length of that string to the plen location. A non-constant + * link type can use this as an early hint that subsequent calls to + * dbGetLink() will request long string data, although this might change. + * + * @param plink the link + * @param pbuffer where to put the string + * @param size length of pbuffer in chars + * @param plen set to number of chars written + * @returns status value + */ long (*loadLS)(struct link *plink, char *pbuffer, epicsUInt32 size, epicsUInt32 *plen); + + /** @brief load constant array from link type + * + * Usually called during IOC initialization, constant link types must copy + * an array value of the indicated data type to the buffer provided, update + * the pnRequest location to indicate how many elements were loaded, and + * return 0. A non-constant link type can use this method call as an early + * hint that subsequent calls to dbGetLink() will request array data of the + * indicated type and max size, although the request might change. + * + * @param plink the link + * @param dbrType data type code + * @param pbuffer where to put the value + * @param pnRequest Max elements on entry, actual on exit + * @returns 0 if elements were loaded, non-zero otherwise + */ long (*loadArray)(struct link *plink, short dbrType, void *pbuffer, long *pnRequest); /* Metadata */ + + /** @brief return link connection status + * + * Return an indication whether this link is connected or not. This routine + * is polled by the calcout and some external record types. Not required for + * non-volatile link types, which are by definition always connected. + * + * @param plink the link + * @returns 1 if connected, 0 if disconnected + */ int (*isConnected)(const struct link *plink); + + /** @brief get data type of link destination + * + * Called on both input and output links by long string support code to + * decide whether to use DBR_CHAR/DBR_UCHAR or DBR_STRING for a subsequent + * dbPutLink() or dbGetLink() call. Optional, but if not provided long + * strings cannot be transported over this link type, and no warning or + * error will appear to explain why. Not required for constant link types. + * + * @param plink the link + * @returns DBF_* type code, or -1 on error/disconnected link + */ int (*getDBFtype)(const struct link *plink); - long (*getElements)(const struct link *plink, long *nelements); /* Get data */ + + /** @brief get array size of an input link + * + * Called on input links by the compress record type for memory allocation + * purposes, before using the dbGetLink() routine to fetch the actual + * array data. + * + * @param plink the link + * @param pnElements where to put the answer + * @returns status value + */ + long (*getElements)(const struct link *plink, long *pnElements); + + /** @brief get value from an input link + * + * Called to fetch data from the link, which must be converted into the + * given data type and placed in the buffer indicated. The actual number of + * elements retrieved should be updated in the pnRequest location. If this + * method returns an error status value, the link's record will be placed + * into an Invalid severity / Link Alarm state by the dbGetLink() routine + * that calls this method. + * + * @param plink the link + * @param dbrType data type code + * @param pbuffer where to put the value + * @param pnRequest max elements on entry, actual on exit + * @returns status value + */ long (*getValue)(struct link *plink, short dbrType, void *pbuffer, long *pnRequest); + + /** @brief get the control range for an output link + * + * Called to fetch the control range for the link target, as a pair of + * double values for the lowest and highest values that the target will + * accept. This method is not used at all by the IOC or built-in record + * types, although external record types may require it. + * + * @param plink the link + * @param lo lowest accepted value + * @param hi highest accepted value + * @returns status value + */ long (*getControlLimits)(const struct link *plink, double *lo, double *hi); + + /** @brief get the display range from an input link + * + * Called to fetch the display range for an input link target, as a pair of + * double values for the lowest and highest values that the PV expects to + * return. This method is used by several built-in record types to obtain + * the display range for their generic input links. + * + * @param plink the link + * @param lo lowest accepted value + * @param hi highest accepted value + * @returns status value + */ long (*getGraphicLimits)(const struct link *plink, double *lo, double *hi); + + /** @brief get the alarm limits from an input link + * + * Called to fetch the alarm limits for an input link target, as four + * double values for the warning and alarm levels that the PV checks its + * value against. This method is used by several built-in record types to + * obtain the alarm limits for their generic input links. + * + * @param plink the link + * @param lolo low alarm value + * @param lo low warning value + * @param hi high warning value + * @param hihi high alarm value + * @returns status value + */ long (*getAlarmLimits)(const struct link *plink, double *lolo, double *lo, double *hi, double *hihi); + + /** @brief get the precision from an input link + * + * Called to fetch the precision for an input link target. This method is + * used by several built-in record types to obtain the precision for their + * generic input links. + * + * @param plink the link + * @param precision where to put the answer + * @returns status value + */ long (*getPrecision)(const struct link *plink, short *precision); + + /** @brief get the units string from an input link + * + * Called to fetch the units string for an input link target. This method is + * used by several built-in record types to obtain the units string for + * their generic input links. + * + * @param plink the link + * @param units where to put the answer + * @param unitsSize buffer size for the answer + * @returns status value + */ long (*getUnits)(const struct link *plink, char *units, int unitsSize); + + /** @brief get the alarm condition from an input link + * + * Called to fetch the alarm status and severity for an input link target. + * Either status or severity pointers may be NULL when that value is not + * needed by the calling code. This method is used by several built-in + * record types to obtain the alarm condition for their generic input links. + * + * @param plink the link + * @param status where to put the alarm status (or NULL) + * @param severity where to put the severity (or NULL) + * @returns status value + */ long (*getAlarm)(const struct link *plink, epicsEnum16 *status, epicsEnum16 *severity); + + /** @brief get the time-stamp from an input link + * + * Called to fetch the time-stamp for an input link target. This method is + * used by many built-in device supports to obtain the precision for their + * generic input links. + * + * @param plink the link + * @param pstamp where to put the answer + * @returns status value + */ long (*getTimeStamp)(const struct link *plink, epicsTimeStamp *pstamp); /* Put data */ + + /** @brief put a value to an output link + * + * Called to send nRequest elements of type dbrType found at pbuffer to an + * output link target. + * + * @param plink the link + * @param dbrType data type code + * @param pbuffer where to put the value + * @param nRequest number of elements to send + * @returns status value + */ long (*putValue)(struct link *plink, short dbrType, const void *pbuffer, long nRequest); + + /** @brief put a value to an output link with asynchronous completion + * + * Called to send nRequest elements of type dbrType found at pbuffer to an + * output link target. If the return status is zero, the link type will + * later indicate the put has completed by calling dbLinkAsyncComplete() + * from a background thread, which will be used to continue the record + * process operation from where it left off. + * + * @param plink the link + * @param dbrType data type code + * @param pbuffer where to put the value + * @param nRequest number of elements to send + * @returns status value + */ long (*putAsync)(struct link *plink, short dbrType, const void *pbuffer, long nRequest); /* Process */ + + /** @brief trigger processing of a forward link + * + * Called to trigger processing of the record pointed to by a forward link. + * This routine is optional, but if not provided no warning message will be + * shown when called by dbScanFwdLink(). JSON link types that do not support + * this operation should return NULL from their jlif::alloc_jlink() method + * if it gets called with a dbfType of DBF_FWDLINK. + * + * @param plink the link + */ void (*scanForward)(struct link *plink); /* Atomicity */ + + /** @brief execute a callback routine with link locked + * + * Called on an input link when multiple link attributes need to be fetched + * in an atomic fashion. The link type must call the callback routine and + * prevent any background I/O from updating any cached link data until that + * routine returns. This method is used by most input device support to + * fetch the timestamp along with the value when the record's TSE field is + * set to epicsTimeEventDeviceTime. + * + * @param plink the link + * @param rtn routine to execute + * @returns status value + */ long (*doLocked)(struct link *plink, dbLinkUserCallback rtn, void *priv); } lset; @@ -99,7 +382,7 @@ epicsShareFunc long dbLoadLink(struct link *plink, short dbrType, epicsShareFunc long dbLoadLinkArray(struct link *, short dbrType, void *pbuffer, long *pnRequest); -epicsShareFunc long dbGetNelements(const struct link *plink, long *nelements); +epicsShareFunc long dbGetNelements(const struct link *plink, long *pnElements); epicsShareFunc int dbIsLinkConnected(const struct link *plink); /* 0 or 1 */ epicsShareFunc int dbGetLinkDBFtype(const struct link *plink); epicsShareFunc long dbTryGetLink(struct link *, short dbrType, void *pbuffer, diff --git a/modules/database/src/ioc/db/dbLock.h b/modules/database/src/ioc/db/dbLock.h index 1d6388ed3..e15ddd0e9 100644 --- a/modules/database/src/ioc/db/dbLock.h +++ b/modules/database/src/ioc/db/dbLock.h @@ -13,6 +13,8 @@ #ifndef INCdbLockh #define INCdbLockh +#include + #include "ellLib.h" #include "shareLib.h" diff --git a/modules/database/src/ioc/db/dbScan.c b/modules/database/src/ioc/db/dbScan.c index e5c78fea1..9224ed504 100644 --- a/modules/database/src/ioc/db/dbScan.c +++ b/modules/database/src/ioc/db/dbScan.c @@ -24,6 +24,7 @@ #include "cantProceed.h" #include "dbDefs.h" #include "ellLib.h" +#include "epicsAtomic.h" #include "epicsEvent.h" #include "epicsMutex.h" #include "epicsPrint.h" @@ -63,6 +64,7 @@ static volatile enum ctl scanCtl; static int onceQueueSize = 1000; static epicsEventId onceSem; static epicsRingBytesId onceQ; +static int onceQOverruns = 0; static epicsThreadId onceTaskId; static void *exitOnce; @@ -676,6 +678,7 @@ int scanOnceCallback(struct dbCommon *precord, once_complete cb, void *usr) if (!pushOK) { if (newOverflow) errlogPrintf("scanOnce: Ring buffer overflow\n"); newOverflow = FALSE; + epicsAtomicIncrIntT(&onceQOverruns); } else { newOverflow = TRUE; } @@ -722,6 +725,40 @@ int scanOnceSetQueueSize(int size) return 0; } +int scanOnceQueueStatus(const int reset, scanOnceQueueStats *result) +{ + int ret; + if (!onceQ) return -1; + if (result) { + result->size = epicsRingBytesSize(onceQ) / sizeof(onceEntry); + result->numUsed = epicsRingBytesUsedBytes(onceQ) / sizeof(onceEntry); + result->maxUsed = epicsRingBytesHighWaterMark(onceQ) / sizeof(onceEntry); + result->numOverflow = epicsAtomicGetIntT(&onceQOverruns); + ret = 0; + } else { + ret = -2; + } + if (reset) { + epicsRingBytesResetHighWaterMark(onceQ); + } + return ret; +} + +void scanOnceQueueShow(const int reset) +{ + scanOnceQueueStats stats; + if (scanOnceQueueStatus(reset, &stats) == -1) { + fprintf(stderr, "scanOnce system not initialized, yet. Please run " + "iocInit before using this command.\n"); + } else { + double qusage = 100.0 * stats.numUsed / stats.size; + printf("PRIORITY HIGH-WATER MARK ITEMS IN Q Q SIZE %% USED Q OVERFLOWS\n"); + printf("%8s %15d %10d %6d %6.1f %11d\n", "scanOnce", stats.maxUsed, + stats.numUsed, stats.size, qusage, + epicsAtomicGetIntT(&onceQOverruns)); + } +} + static void initOnce(void) { if ((onceQ = epicsRingBytesLockedCreate(sizeof(onceEntry)*onceQueueSize)) == NULL) { diff --git a/modules/database/src/ioc/db/dbScan.h b/modules/database/src/ioc/db/dbScan.h index 028d09ec8..622103c3b 100644 --- a/modules/database/src/ioc/db/dbScan.h +++ b/modules/database/src/ioc/db/dbScan.h @@ -19,6 +19,7 @@ #include "menuScan.h" #include "shareLib.h" #include "compilerDependencies.h" +#include "devSup.h" #ifdef __cplusplus extern "C" { @@ -33,9 +34,7 @@ extern "C" { #define MIN_PHASE SHRT_MIN /*definitions for I/O Interrupt Scanning */ -struct ioscan_head; - -typedef struct ioscan_head *IOSCANPVT; +/* IOSCANPVT now defined in devSup.h */ typedef struct event_list *EVENTPVT; struct dbCommon; @@ -43,6 +42,13 @@ struct dbCommon; typedef void (*io_scan_complete)(void *usr, IOSCANPVT, int prio); typedef void (*once_complete)(void *usr, struct dbCommon*); +typedef struct scanOnceQueueStats { + int size; + int numUsed; + int maxUsed; + int numOverflow; +} scanOnceQueueStats; + epicsShareFunc long scanInit(void); epicsShareFunc void scanRun(void); epicsShareFunc void scanPause(void); @@ -58,6 +64,8 @@ epicsShareFunc double scanPeriod(int scan); epicsShareFunc int scanOnce(struct dbCommon *); epicsShareFunc int scanOnceCallback(struct dbCommon *, once_complete cb, void *usr); epicsShareFunc int scanOnceSetQueueSize(int size); +epicsShareFunc int scanOnceQueueStatus(const int reset, scanOnceQueueStats *result); +epicsShareFunc void scanOnceQueueShow(const int reset); /*print periodic lists*/ epicsShareFunc int scanppl(double rate); diff --git a/modules/database/src/ioc/db/dbServer.c b/modules/database/src/ioc/db/dbServer.c index 5374f02ad..ec89f4fec 100644 --- a/modules/database/src/ioc/db/dbServer.c +++ b/modules/database/src/ioc/db/dbServer.c @@ -12,6 +12,7 @@ #include #include +#include "dbDefs.h" #include "ellLib.h" #include "envDefs.h" #include "epicsStdio.h" @@ -29,6 +30,7 @@ static char *stateNames[] = { int dbRegisterServer(dbServer *psrv) { const char * ignore = envGetConfigParamPtr(&EPICS_IOC_IGNORE_SERVERS); + ELLNODE *node; if (!psrv || !psrv->name || state != registering) return -1; @@ -55,10 +57,14 @@ int dbRegisterServer(dbServer *psrv) } } - if (ellNext(&psrv->node) || ellLast(&serverList) == &psrv->node) { - fprintf(stderr, "dbRegisterServer: '%s' registered twice?\n", - psrv->name); - return -1; + for(node=ellFirst(&serverList); node; node = ellNext(node)) { + dbServer *cur = CONTAINER(node, dbServer, node); + if(cur==psrv) { + return 0; /* already registered identically. */ + } else if(strcmp(cur->name, psrv->name)==0) { + fprintf(stderr, "dbRegisterServer: Can't redefine '%s'.\n", cur->name); + return -1; + } } ellAdd(&serverList, &psrv->node); diff --git a/modules/database/src/ioc/db/dbState.h b/modules/database/src/ioc/db/dbState.h index abd23259e..c7cd81c52 100644 --- a/modules/database/src/ioc/db/dbState.h +++ b/modules/database/src/ioc/db/dbState.h @@ -15,6 +15,10 @@ #include "shareLib.h" +#ifdef __cplusplus +extern "C" { +#endif + /** @file dbState.h * @brief Generic IOC state facility * @@ -89,4 +93,9 @@ epicsShareFunc void dbStateShow(dbStateId id, unsigned int level); */ epicsShareFunc void dbStateShowAll(unsigned int level); + +#ifdef __cplusplus +} +#endif + #endif // INCdbStateH diff --git a/modules/database/src/ioc/db/menuScan.dbd b/modules/database/src/ioc/db/menuScan.dbd.pod similarity index 50% rename from modules/database/src/ioc/db/menuScan.dbd rename to modules/database/src/ioc/db/menuScan.dbd.pod index 17fedd59b..e716e79da 100644 --- a/modules/database/src/ioc/db/menuScan.dbd +++ b/modules/database/src/ioc/db/menuScan.dbd.pod @@ -1,11 +1,48 @@ #************************************************************************* -# Copyright (c) 2008 UChicago Argonne LLC, as Operator of Argonne +# Copyright (c) 2018 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. #************************************************************************* + +=head1 Menu menuScan + +This menu is used for the C field of all record types. + +The set of periodic scan rates may be modified for an individual IOC by +copying the F file from Base into the IOC's source +directory and changing it to contain the desired scan rates. + +The scan periods are extracted from the choice strings at runtime, which +must be expressed as a number with any of the following units appended: + +=over 4 + + second + seconds + minute + minutes + hour + hours + Hertz + Hz + +=back + +At IOC start-up a separate scan thread will be created for each period, +with thread priority increasing further down the list, so faster periods +should appear after slower ones. + +Scan rates that cannot be achieved will generate a warning message from +the C command. + + +=menu menuScan + +=cut + menu(menuScan) { choice(menuScanPassive,"Passive") choice(menuScanEvent,"Event") diff --git a/modules/database/src/ioc/db/recGbl.c b/modules/database/src/ioc/db/recGbl.c index 12c0a0e0f..5ff4730fe 100644 --- a/modules/database/src/ioc/db/recGbl.c +++ b/modules/database/src/ioc/db/recGbl.c @@ -19,6 +19,7 @@ #include "alarm.h" #include "dbDefs.h" +#include "alarm.h" #include "epicsMath.h" #include "epicsPrint.h" #include "epicsStdlib.h" @@ -180,6 +181,9 @@ unsigned short recGblResetAlarms(void *precord) epicsEnum16 val_mask = 0; epicsEnum16 stat_mask = 0; + if (new_sevr > INVALID_ALARM) + new_sevr = INVALID_ALARM; + pdbc->stat = new_stat; pdbc->sevr = new_sevr; pdbc->nsta = 0; diff --git a/modules/database/src/ioc/dbStatic/dbStaticLib.c b/modules/database/src/ioc/dbStatic/dbStaticLib.c index 1a3d9883f..7f5f1f885 100644 --- a/modules/database/src/ioc/dbStatic/dbStaticLib.c +++ b/modules/database/src/ioc/dbStatic/dbStaticLib.c @@ -79,16 +79,6 @@ epicsShareDef maplinkType pamaplinkType[LINK_NTYPES] = { {"VXI_IO",VXI_IO} }; -static int mapDBFtoDCT[DBF_NOACCESS+1] = { - DCT_STRING, - DCT_INTEGER,DCT_INTEGER,DCT_INTEGER,DCT_INTEGER,DCT_INTEGER,DCT_INTEGER, - DCT_REAL,DCT_REAL, - DCT_INTEGER, - DCT_MENU, - DCT_MENUFORM, - DCT_INLINK,DCT_OUTLINK,DCT_FWDLINK, - DCT_NOACCESS}; - /*forward references for private routines*/ static void dbMsgPrint(DBENTRY *pdbentry, const char *fmt, ...) EPICS_PRINTF_STYLE(2,3); @@ -166,38 +156,6 @@ void dbFreePath(DBBASE *pdbbase) } -static void entryErrMessage(DBENTRY *pdbentry,long status,char *mess) -{ - char message[200]; - char *pmessage=&message[0]; - dbRecordNode *precnode = pdbentry->precnode; - dbFldDes *pflddes = pdbentry->pflddes; - char *pname = NULL; - - *pmessage=0; - if(pdbentry->precordType) pname = pdbentry->precordType->name; - if(pname) { - strcat(pmessage,"RecordType:"); - strcat(pmessage,pname); - } - if(precnode){ - if (dbIsAlias(pdbentry)) - strcat(pmessage," Record Alias:"); - else - strcat(pmessage," Record:"); - strcat(pmessage,(char *)precnode->precord); - } - if(pflddes) { - char *pstr=pflddes->name; - - strcat(pmessage," Field:"); - strcat(pmessage,pstr); - } - strcat(pmessage,"\n"); - strcat(pmessage,mess); - errMessage(status,pmessage); -} - static void zeroDbentry(DBENTRY *pdbentry) { /*NOTE that pdbbase and message MUST NOT be set to NULL*/ @@ -1398,19 +1356,6 @@ long dbNextField(DBENTRY *pdbentry,int dctonly) } } -int dbGetFieldType(DBENTRY *pdbentry) -{ - dbFldDes *pflddes = pdbentry->pflddes; - long status; - - if(!pflddes){ - status = S_dbLib_flddesNotFound; - entryErrMessage(pdbentry,status,"dbGetFieldType"); - return(status); - } - return(mapDBFtoDCT[pflddes->field_type]); -} - int dbGetNFields(DBENTRY *pdbentry,int dctonly) { dbRecordType *precordType = pdbentry->precordType; @@ -2241,7 +2186,7 @@ long dbInitRecordLinks(dbRecordType *rtyp, struct dbCommon *prec) if(!plink->text) continue; - if(dbParseLink(plink->text, pflddes->field_type, &link_info, 0)!=0) { + if(dbParseLink(plink->text, pflddes->field_type, &link_info)!=0) { /* This was already parsed once when ->text was set. * Any syntax error messages were printed at that time. */ @@ -2270,7 +2215,7 @@ void dbFreeLinkInfo(dbLinkInfo *pinfo) pinfo->target = NULL; } -long dbParseLink(const char *str, short ftype, dbLinkInfo *pinfo, unsigned opts) +long dbParseLink(const char *str, short ftype, dbLinkInfo *pinfo) { char *pstr; size_t len; @@ -2306,7 +2251,7 @@ long dbParseLink(const char *str, short ftype, dbLinkInfo *pinfo, unsigned opts) /* Check for braces => JSON */ if (*str == '{' && str[len-1] == '}') { - if (dbJLinkParse(str, len, ftype, &pinfo->jlink, opts)) + if (dbJLinkParse(str, len, ftype, &pinfo->jlink)) goto fail; pinfo->ltype = JSON_LINK; @@ -2353,9 +2298,13 @@ long dbParseLink(const char *str, short ftype, dbLinkInfo *pinfo, unsigned opts) else if (strcmp(pinfo->hwid, "VS")==0) pinfo->ltype = VXI_IO; else goto fail; - if (parm && pinfo->ltype != RF_IO) { - /* move parm string to beginning of buffer */ - memmove(pinfo->target, parm, len + 1); + if (pinfo->ltype != RF_IO) { + if (!parm) { + pinfo->target[0] = '\0'; + } else { + /* move parm string to beginning of buffer */ + memmove(pinfo->target, parm, len + 1); + } } else if (!parm && pinfo->ltype == RF_IO) { /* RF_IO, the string isn't needed at all */ free(pinfo->target); @@ -2641,21 +2590,8 @@ long dbPutString(DBENTRY *pdbentry,const char *pstring) case DBF_FWDLINK: { dbLinkInfo link_info; DBLINK *plink = (DBLINK *)pfield; - DBENTRY infoentry; - unsigned opts = 0; - if(pdbentry->precnode && ellCount(&pdbentry->precnode->infoList)) { - dbCopyEntryContents(pdbentry, &infoentry); - - if(dbFindInfo(&infoentry, "base:lsetDebug")==0 && epicsStrCaseCmp(dbGetInfoString(&infoentry), "YES")==0) - opts |= LINK_DEBUG_LSET; - if(dbFindInfo(&infoentry, "base:jlinkDebug")==0 && epicsStrCaseCmp(dbGetInfoString(&infoentry), "YES")==0) - opts |= LINK_DEBUG_JPARSE; - - dbFinishEntry(&infoentry); - } - - status = dbParseLink(pstring, pflddes->field_type, &link_info, opts); + status = dbParseLink(pstring, pflddes->field_type, &link_info); if (status) break; if (plink->type==CONSTANT && plink->value.constantStr==NULL) { @@ -3225,48 +3161,21 @@ int dbGetNLinks(DBENTRY *pdbentry) return((int)precordType->no_links); } -long dbGetLinkField(DBENTRY *pdbentry,int index) +long dbGetLinkField(DBENTRY *pdbentry, int index) { - dbRecordType *precordType = pdbentry->precordType; - dbFldDes *pflddes; + dbRecordType *precordType = pdbentry->precordType; + dbFldDes *pflddes; + + if (!precordType) + return S_dbLib_recordTypeNotFound; + + if (index < 0 || index >= precordType->no_links) + return S_dbLib_badLink; - if(!precordType) return(S_dbLib_recordTypeNotFound); - if(index<0 || index>=precordType->no_links) return(S_dbLib_badLink); pdbentry->indfield = precordType->link_ind[index]; pdbentry->pflddes = pflddes = precordType->papFldDes[pdbentry->indfield]; dbGetFieldAddress(pdbentry); - return(0); -} - -int dbGetLinkType(DBENTRY *pdbentry) -{ - dbFldDes *pflddes; - DBLINK *plink; - int field_type; - - dbGetFieldAddress(pdbentry); - pflddes = pdbentry->pflddes; - if(!pflddes) return(-1); - plink = (DBLINK *)pdbentry->pfield; - if(!plink) return(-1); - field_type = pflddes->field_type; - switch (field_type) { - case DBF_INLINK: - case DBF_OUTLINK: - case DBF_FWDLINK: - switch(plink->type) { - case CONSTANT: - return(DCT_LINK_CONSTANT); - case PV_LINK: - case PN_LINK: - case DB_LINK: - case CA_LINK: - return(DCT_LINK_PV); - default: - return(DCT_LINK_FORM); - } - } - return(-1); + return 0; } void dbDumpPath(DBBASE *pdbbase) @@ -3565,68 +3474,110 @@ void dbDumpBreaktable(DBBASE *pdbbase,const char *name) return; } -static char *bus[VXI_IO+1] = {"","","VME","CAMAC","AB", - "GPIB","BITBUS","","","","","","INST","BBGPIB","VXI"}; -void dbReportDeviceConfig(dbBase *pdbbase,FILE *report) +static char *bus[LINK_NTYPES] = { + "", /* CONSTANT */ + NULL, /* PV_LINK */ + "VME", + "CAMAC", + "AB", + "GPIB", + "BITBUS", + NULL, /* MACRO_LINK */ + NULL, /* JSON_LINK */ + NULL, /* PN_LINK */ + NULL, /* DB_LINK */ + NULL, /* CA_LINK */ + "INST", + "BBGPIB", + "VXI" +}; +void dbReportDeviceConfig(dbBase *pdbbase, FILE *report) { - DBENTRY dbentry; - DBENTRY *pdbentry=&dbentry; - long status; - char linkValue[messagesize]; - char dtypValue[50]; - char cvtValue[40]; - int ilink,nlinks; - struct link *plink; - int linkType; - FILE *stream = (report==0) ? stdout : report; + DBENTRY dbentry, *pdbentry = &dbentry; + long status; + FILE *stream = report ? report : stdout; - if(!pdbbase) { - fprintf(stderr,"pdbbase not specified\n"); - return; + if (!pdbbase) { + fprintf(stderr, "dbReportDeviceConfig: pdbbase not specified\n"); + return; } + dbInitEntry(pdbbase,pdbentry); status = dbFirstRecordType(pdbentry); - while(!status) { - status = dbFirstRecord(pdbentry); - while(!status) { - nlinks = dbGetNLinks(pdbentry); - for(ilink=0; ilinkpfield; - linkType = plink->type; - if(bus[linkType][0]==0) continue; - strncpy(linkValue, dbGetString(pdbentry), NELEMENTS(linkValue)-1); - linkValue[NELEMENTS(linkValue)-1] = '\0'; - status = dbFindField(pdbentry,"DTYP"); - if(status) break; - strcpy(dtypValue,dbGetString(pdbentry)); - status = dbFindField(pdbentry,"LINR"); - if(status) { - cvtValue[0] = 0; - } else { - if(strcmp(dbGetString(pdbentry),"LINEAR")!=0) { - cvtValue[0] = 0; - } else { - strcpy(cvtValue,"cvt("); - status = dbFindField(pdbentry,"EGUL"); - if(!status) strcat(cvtValue,dbGetString(pdbentry)); - status = dbFindField(pdbentry,"EGUF"); - if(!status) { - strcat(cvtValue,","); - strcat(cvtValue,dbGetString(pdbentry)); - } - strcat(cvtValue,")"); - } - } - fprintf(stream,"%-8s %-20s %-20s %-20s %-s\n", - bus[linkType],linkValue,dtypValue, - dbGetRecordName(pdbentry),cvtValue); - break; - } - status = dbNextRecord(pdbentry); - } - status = dbNextRecordType(pdbentry); + while (!status) { + const int nlinks = dbGetNLinks(pdbentry); + + status = dbFirstRecord(pdbentry); + while (!status) { + int ilink; + + for (ilink=0; ilinkpfield; + linkType = plink->type; + if (plink->text) { /* Not yet parsed */ + dbLinkInfo linfo; + + if (dbParseLink(plink->text, pdbentry->pflddes->field_type, &linfo)) + continue; + + linkType = linfo.ltype; + if (linkType && bus[linkType]) + strncpy(linkValue, plink->text, messagesize-1); + + dbFreeLinkInfo(&linfo); + } + else { + strncpy(linkValue, dbGetString(pdbentry), messagesize-1); + } + + if (!linkType || !bus[linkType]) + continue; + linkValue[messagesize-1] = '\0'; + + status = dbFindField(pdbentry, "DTYP"); + if (status) + break; /* Next record type */ + + strcpy(dtypValue, dbGetString(pdbentry)); + status = dbFindField(pdbentry, "LINR"); + if (status) { + cvtValue[0] = 0; + } + else { + if (strcmp(dbGetString(pdbentry), "LINEAR") != 0) { + cvtValue[0] = 0; + } + else { + strcpy(cvtValue,"cvt("); + status = dbFindField(pdbentry, "EGUL"); + if (!status) + strcat(cvtValue, dbGetString(pdbentry)); + status = dbFindField(pdbentry, "EGUF"); + if (!status) { + strcat(cvtValue, ","); + strcat(cvtValue, dbGetString(pdbentry)); + } + strcat(cvtValue, ")"); + } + } + fprintf(stream,"%-8s %-20s %-20s %-20s %-s\n", + bus[linkType], linkValue, dtypValue, + dbGetRecordName(pdbentry), cvtValue); + break; + } + status = dbNextRecord(pdbentry); + } + status = dbNextRecordType(pdbentry); } dbFinishEntry(pdbentry); finishOutstream(stream); diff --git a/modules/database/src/ioc/dbStatic/dbStaticLib.h b/modules/database/src/ioc/dbStatic/dbStaticLib.h index 4082600be..e0a2ddc6b 100644 --- a/modules/database/src/ioc/dbStatic/dbStaticLib.h +++ b/modules/database/src/ioc/dbStatic/dbStaticLib.h @@ -28,25 +28,9 @@ extern "C" { #endif -/*Field types as seen by static database access clients*/ -#define DCT_STRING 0 -#define DCT_INTEGER 1 -#define DCT_REAL 2 -#define DCT_MENU 3 -#define DCT_MENUFORM 4 -#define DCT_INLINK 5 -#define DCT_OUTLINK 6 -#define DCT_FWDLINK 7 -#define DCT_NOACCESS 8 - -/*Link types as seen by static database access clients*/ -#define DCT_LINK_CONSTANT 0 -#define DCT_LINK_FORM 1 -#define DCT_LINK_PV 2 - typedef dbBase DBBASE; -typedef struct{ +typedef struct dbEntry { DBBASE *pdbbase; dbRecordType *precordType; dbFldDes *pflddes; @@ -57,9 +41,6 @@ typedef struct{ short indfield; } DBENTRY; -struct dbAddr; -struct dbCommon; - /* Static database access routines*/ epicsShareFunc DBBASE * dbAllocBase(void); epicsShareFunc void dbFreeBase(DBBASE *pdbbase); @@ -68,18 +49,6 @@ epicsShareFunc void dbFreeEntry(DBENTRY *pdbentry); epicsShareFunc void dbInitEntry(DBBASE *pdbbase, DBENTRY *pdbentry); -/** Initialize DBENTRY from a valid dbAddr*. - * Constant time equivalent of dbInitEntry() then dbFindRecord(), - * and finally dbFollowAlias() - */ -epicsShareFunc void dbInitEntryFromAddr(struct dbAddr *paddr, DBENTRY *pdbentry); - -/** Initialize DBENTRY from a valid record (dbCommon*). - * Constant time equivalent of dbInitEntry() then dbFindRecord(), - * and finally dbFollowAlias() when no field is specified. - */ -epicsShareFunc void dbInitEntryFromRecord(struct dbCommon *prec, DBENTRY *pdbentry); - epicsShareFunc void dbFinishEntry(DBENTRY *pdbentry); epicsShareFunc DBENTRY * dbCopyEntry(DBENTRY *pdbentry); epicsShareFunc void dbCopyEntryContents(DBENTRY *pfrom, @@ -139,7 +108,6 @@ epicsShareFunc long dbGetAttributePart(DBENTRY *pdbentry, epicsShareFunc long dbFirstField(DBENTRY *pdbentry, int dctonly); epicsShareFunc long dbNextField(DBENTRY *pdbentry, int dctonly); -epicsShareFunc int dbGetFieldType(DBENTRY *pdbentry); epicsShareFunc int dbGetNFields(DBENTRY *pdbentry, int dctonly); epicsShareFunc char * dbGetFieldName(DBENTRY *pdbentry); epicsShareFunc char * dbGetDefault(DBENTRY *pdbentry); @@ -231,7 +199,6 @@ epicsShareFunc linkSup * dbFindLinkSup(dbBase *pdbbase, epicsShareFunc int dbGetNLinks(DBENTRY *pdbentry); epicsShareFunc long dbGetLinkField(DBENTRY *pdbentry, int index); -epicsShareFunc int dbGetLinkType(DBENTRY *pdbentry); /* Dump routines */ epicsShareFunc void dbDumpPath(DBBASE *pdbbase); diff --git a/modules/database/src/ioc/dbStatic/dbStaticPvt.h b/modules/database/src/ioc/dbStatic/dbStaticPvt.h index 58d32c28c..85fc02217 100644 --- a/modules/database/src/ioc/dbStatic/dbStaticPvt.h +++ b/modules/database/src/ioc/dbStatic/dbStaticPvt.h @@ -5,7 +5,7 @@ * Operator of Los Alamos National Laboratory. * EPICS BASE Versions 3.13.7 * and higher are distributed subject to a Software License Agreement found -* in file LICENSE that is included with this distribution. +* in file LICENSE that is included with this distribution. \*************************************************************************/ /* dbStaticPvt.h */ /* @@ -59,13 +59,10 @@ typedef struct dbLinkInfo { long dbInitRecordLinks(dbRecordType *rtyp, struct dbCommon *prec); -#define LINK_DEBUG_LSET 1 -#define LINK_DEBUG_JPARSE 2 - /* Parse link string. no record locks needed. * on success caller must free pinfo->target */ -epicsShareFunc long dbParseLink(const char *str, short ftype, dbLinkInfo *pinfo, unsigned opts); +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. diff --git a/modules/database/src/ioc/dbStatic/devSup.h b/modules/database/src/ioc/dbStatic/devSup.h index bd900cae4..73dba198c 100644 --- a/modules/database/src/ioc/dbStatic/devSup.h +++ b/modules/database/src/ioc/dbStatic/devSup.h @@ -6,7 +6,11 @@ * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ -/* devSup.h Device Support */ +/** @file devSup.h + * + * @brief Device support routines + */ + /* * Author: Marty Kraimer * Date: 6-1-90 @@ -21,6 +25,111 @@ /* structures defined elsewhere */ struct dbCommon; struct devSup; +typedef struct ioscan_head *IOSCANPVT; +struct link; /* aka DBLINK */ + +/** Type safe version of 'struct dset' + * + * Recommended usage: + * + * In Makefile: + @code + USR_CFLAGS += -DUSE_TYPED_RSET -DUSE_TYPED_DSET + @endcode + * + * In C source file: + @code + #include + #include // For IOCSCANPVT + ... + #include // defines epicsExportSharedSymbols + ... + static long init_record(dbCommon *prec); + static long get_iointr_info(int detach, dbCommon *prec, IOCSCANPVT* pscan); + static long longin_read(longinRecord *prec); + + const struct { + dset common; + long (*read)(longinRecord *prec); + } devLiDevName = { + { + 5, // 4 from dset + 1 from longinRecord + NULL, + NULL, + &init_record, + &get_iointr_info + }, + &longin_read + }; + epicsExportAddress(dset, devLiDevName); + @endcode + */ +typedef struct typed_dset { + /** Number of function pointers which follow. + * The value depends on the recordtype, but must be >=4 */ + long number; + /** Called from dbior() */ + long (*report)(int lvl); + /** Called twice during iocInit(). + * First with @a after = 0 before init_record() or array field allocation. + * Again with @a after = 1 after init_record() has finished. + */ + long (*init)(int after); + /** Called once per record instance */ + long (*init_record)(struct dbCommon *prec); + /** Called when SCAN="I/O Intr" on startup, or after SCAN is changed. + * + * Caller must assign the third arguement (IOCSCANPVT*). eg. + @code + struct mpvt { + IOSCANPVT drvlist; + }; + ... + // init_record() routine calls + scanIoInit(&pvt->drvlist); + ... + static long get_ioint_info(int detach, struct dbCommon *prec, IOCSCANPVT* pscan) { + if(prec->dpvt) + *pscan = &((mypvt*)prec->dpvt)->drvlist; + @endcode + * + * When a particular record instance can/will only used a single scan list, + * the @a detach argument can be ignored. + * + * If this is not the case, then the following should be noted. + * + get_ioint_info() is called with @a detach = 0 to fetch the scan list to + * which this record will be added. + * + get_ioint_info() is called later with @a detach = 1 to fetch the scan + * list from which this record should be removed. + * + Calls will be balanced, so a call with @a detach = 0 will be followed + * by one with @a detach = 1. + * + * @note get_ioint_info() will be called during IOC shutdown if the + * dsxt::del_record() extended callback is defined. (from 3.15.0.1) + */ + long (*get_ioint_info)(int detach, struct dbCommon *prec, IOSCANPVT* pscan); + /* Any further functions are specified by the record type. */ +} typed_dset; + +/** Device support extension table. + * + * Optional routines to allow run-time address modifications to be communicated + * to device support, which must register a struct dsxt by calling devExtend() + * from its init() routine. + */ +typedef struct dsxt { + /** Optional, called to offer device support a new record to control. + * + * Routine may return a non-zero error code to refuse record. + */ + long (*add_record)(struct dbCommon *precord); + /** Optional, called to remove record from device support control. + * + * Routine return a non-zero error code to refuse record removal. + */ + long (*del_record)(struct dbCommon *precord); + /* Only future Base releases may extend this table. */ +} dsxt; #ifdef __cplusplus extern "C" { @@ -29,6 +138,8 @@ extern "C" { typedef long (*DEVSUPFUN)(); /* ptr to device support function*/ #endif +#ifndef USE_TYPED_DSET + typedef struct dset { /* device support entry table */ long number; /*number of support routines*/ DEVSUPFUN report; /*print report*/ @@ -38,11 +149,15 @@ typedef struct dset { /* device support entry table */ /*other functions are record dependent*/ } dset; -typedef struct dsxt { /* device support extension table */ - long (*add_record)(struct dbCommon *precord); - long (*del_record)(struct dbCommon *precord); - /* Recordtypes are *not* allowed to extend this table */ -} dsxt; +#else +typedef typed_dset dset; +#endif /* USE_TYPED_DSET */ + +/** Fetch INP or OUT link (or NULL if record type has neither). + * + * Recommended for use in device support init_record() + */ +epicsShareFunc struct link* dbGetDevLink(struct dbCommon* prec); epicsShareExtern dsxt devSoft_DSXT; /* Allow anything table */ diff --git a/modules/database/src/ioc/dbStatic/drvSup.h b/modules/database/src/ioc/dbStatic/drvSup.h index 5778038e7..193d57482 100644 --- a/modules/database/src/ioc/dbStatic/drvSup.h +++ b/modules/database/src/ioc/dbStatic/drvSup.h @@ -6,7 +6,10 @@ * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ -/* drvSup.h Driver Support */ +/** @file drvSup.h + * + * @brief Driver support routines. + */ /* * Author: Marty Kraimer @@ -18,16 +21,38 @@ #include "errMdef.h" -typedef long (*DRVSUPFUN) (); /* ptr to driver support function*/ +/** Driver entry table */ +typedef struct typed_drvet { + /** Number of function pointers which follow. Must be >=2 */ + long number; + /** Called from dbior() */ + long (*report)(int lvl); + /** Called during iocInit() */ + long (*init)(void); + /* Any further functions are driver-specific */ +} typed_drvet; + +#ifdef USE_TYPED_DRVET + +typedef typed_drvet drvet; + +#else + +/* These interfaces may eventually get deprecated */ + +typedef long (*DRVSUPFUN) (); /* ptr to driver support function */ + +typedef struct drvet { /* driver entry table */ + long number; /* number of support routines */ + DRVSUPFUN report; /* print report */ + DRVSUPFUN init; /* init support */ + /* Any further functions are driver-specific */ +} drvet; -typedef struct drvet { /* driver entry table */ - long number; /*number of support routines*/ - DRVSUPFUN report; /*print report*/ - DRVSUPFUN init; /*init support*/ - /*other functions are device dependent*/ -}drvet; #define DRVETNUMBER ( (sizeof(struct drvet) -sizeof(long))/sizeof(DRVSUPFUN) ) +#endif /* USE_TYPED_DRVET */ + #define S_drv_noDrvSup (M_drvSup| 1) /*SDR_DRVSUP: Driver support missing*/ #define S_drv_noDrvet (M_drvSup| 3) /*Missing driver support entry table*/ diff --git a/modules/database/src/ioc/dbtemplate/msi.c b/modules/database/src/ioc/dbtemplate/msi.c index 5a5023163..69680e17e 100644 --- a/modules/database/src/ioc/dbtemplate/msi.c +++ b/modules/database/src/ioc/dbtemplate/msi.c @@ -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 the file LICENSE that is included with this distribution. +* in the file LICENSE that is included with this distribution. \*************************************************************************/ /* msi - macro substitutions and include */ @@ -22,10 +22,36 @@ #include #include #include +#include #define MAX_BUFFER_SIZE 4096 #define MAX_DEPS 1024 +#if 0 +/* Debug Tracing */ +int din = 0; +#define ENTER fprintf(stderr, "%*sEntering %s\n", 2*din++, "", __FUNCTION__) + +#define STEP(s) fprintf(stderr, "%*s%s: %s\n", 2*din, "", __FUNCTION__, s) +#define STEPS(s, v) fprintf(stderr, "%*s%s: %s '%s'\n", 2*din, "", __FUNCTION__, s, v) +#define STEPD(s, v) fprintf(stderr, "%*s%s: %s %d\n", 2*din, "", __FUNCTION__, s, v) + +#define EXIT fprintf(stderr, "%*s%s: Returning\n", 2*din--, "", __FUNCTION__) +#define EXITD(r) fprintf(stderr, "%*s%s: Returning %d\n", 2*din--, "", __FUNCTION__, r) +#define EXITS(r) fprintf(stderr, "%*s%s: Returning '%s'\n", 2*din--, "", __FUNCTION__, r) +#else +#define ENTER + +#define STEP(s) +#define STEPS(s, v) +#define STEPD(s, v) + +#define EXIT +#define EXITD(r) +#define EXITS(r) +#endif + + /* Module to read the template files */ typedef struct inputData inputData; @@ -49,6 +75,7 @@ static char *substituteGetGlobalReplacements(subInfo *pvt); /* Forward references to local routines */ static void usageExit(int status); +static void abortExit(int status); static void addMacroReplacements(MAC_HANDLE *macPvt, char *pval); static void makeSubstitutions(inputData *inputPvt, MAC_HANDLE *macPvt, char *templateName); @@ -66,48 +93,62 @@ int main(int argc,char **argv) MAC_HANDLE *macPvt; char *pval; int narg; - char *substitutionName=0; - char *templateName=0; + char *substitutionName = 0; + char *templateName = 0; int i; int localScope = 1; inputConstruct(&inputPvt); - macCreateHandle(&macPvt,0); - while((argc>1) && (argv[1][0] == '-')) { - narg = (strlen(argv[1])==2) ? 2 : 1; - pval = (narg==1) ? (argv[1]+2) : argv[2]; - if(strncmp(argv[1],"-I",2)==0) { - inputAddPath(inputPvt,pval); - } else if (strcmp(argv[1], "-D") == 0) { + macCreateHandle(&macPvt, 0); + while ((argc > 1) && (argv[1][0] == '-')) { + narg = (strlen(argv[1]) == 2) ? 2 : 1; + pval = (narg == 1) ? (argv[1] + 2) : argv[2]; + + if (strncmp(argv[1], "-I", 2) == 0) { + inputAddPath(inputPvt, pval); + } + else if (strcmp(argv[1], "-D") == 0) { opt_D = 1; narg = 1; /* no argument for this option */ - } else if(strncmp(argv[1],"-o",2)==0) { + } + else if(strncmp(argv[1], "-o", 2) == 0) { outFile = epicsStrDup(pval); - } else if(strncmp(argv[1],"-M",2)==0) { - addMacroReplacements(macPvt,pval); - } else if(strncmp(argv[1],"-S",2)==0) { + } + else if(strncmp(argv[1], "-M", 2) == 0) { + addMacroReplacements(macPvt, pval); + } + else if(strncmp(argv[1], "-S", 2) == 0) { substitutionName = epicsStrDup(pval); - } else if (strcmp(argv[1], "-V") == 0) { + } + else if (strcmp(argv[1], "-V") == 0) { opt_V = 1; narg = 1; /* no argument for this option */ - } else if (strcmp(argv[1], "-g") == 0) { + } + else if (strcmp(argv[1], "-g") == 0) { localScope = 0; narg = 1; /* no argument for this option */ - } else if (strcmp(argv[1], "-h") == 0) { + } + else if (strcmp(argv[1], "-h") == 0) { usageExit(0); - } else { + } + else { fprintf(stderr, "msi: Bad argument \"%s\"\n", argv[1]); usageExit(1); } + argc -= narg; - for(i=1; i2) { - fprintf(stderr,"msi: Too many arguments\n"); + macSuppressWarning(macPvt, 1); + + if (argc > 2) { + fprintf(stderr, "msi: Too many arguments\n"); usageExit(1); } + if (opt_D) { if (!outFile) { fprintf(stderr, "msi: Option -D requires -o for Makefile target\n"); @@ -120,34 +161,47 @@ int main(int argc,char **argv) outFile, strerror(errno)); exit(1); } - if(argc==2) { + + if (argc == 2) templateName = epicsStrDup(argv[1]); + + if (!substitutionName) { + STEP("Single template+substitutions file"); + makeSubstitutions(inputPvt, macPvt, templateName); } - if(!substitutionName) { - makeSubstitutions(inputPvt,macPvt,templateName); - } else { + else { subInfo *substitutePvt; char *filename = 0; int isGlobal, isFile; - substituteOpen(&substitutePvt,substitutionName); + STEPS("Substitutions from file", substitutionName); + substituteOpen(&substitutePvt, substitutionName); do { - if ((isGlobal = substituteGetGlobalSet(substitutePvt))) { + isGlobal = substituteGetGlobalSet(substitutePvt); + if (isGlobal) { + STEP("Handling global macros"); pval = substituteGetGlobalReplacements(substitutePvt); - if(pval) { - addMacroReplacements(macPvt,pval); - } - } else if ((isFile = substituteGetNextSet(substitutePvt,&filename))) { - if(templateName) filename = templateName; - if(!filename) { - fprintf(stderr,"msi: No template file\n"); + if (pval) + addMacroReplacements(macPvt, pval); + } + else if ((isFile = substituteGetNextSet(substitutePvt, &filename))) { + if (templateName) + filename = templateName; + if (!filename) { + fprintf(stderr, "msi: No template file\n"); usageExit(1); } - while((pval = substituteGetReplacements(substitutePvt))){ - if (localScope) macPushScope(macPvt); - addMacroReplacements(macPvt,pval); - makeSubstitutions(inputPvt,macPvt,filename); - if (localScope) macPopScope(macPvt); + + STEPS("Handling template file", filename); + while ((pval = substituteGetReplacements(substitutePvt))) { + if (localScope) + macPushScope(macPvt); + + addMacroReplacements(macPvt, pval); + makeSubstitutions(inputPvt, macPvt, filename); + + if (localScope) + macPopScope(macPvt); } } } while (isGlobal || isFile); @@ -182,20 +236,29 @@ void usageExit(int status) exit(status); } -static void addMacroReplacements(MAC_HANDLE *macPvt,char *pval) +void abortExit(int status) +{ + if (outFile) { + fclose(stdout); + unlink(outFile); + } + exit(status); +} + +static void addMacroReplacements(MAC_HANDLE *macPvt, char *pval) { char **pairs; long status; - status = macParseDefns(macPvt,pval,&pairs); - if(status==-1) { - fprintf(stderr,"msi: Error from macParseDefns\n"); + status = macParseDefns(macPvt, pval, &pairs); + if (status == -1) { + fprintf(stderr, "msi: Error from macParseDefns\n"); usageExit(1); } - if(status) { - status = macInstallMacros(macPvt,pairs); - if(!status) { - fprintf(stderr,"Error from macInstallMacros\n"); + if (status) { + status = macInstallMacros(macPvt, pairs); + if (!status) { + fprintf(stderr, "Error from macInstallMacros\n"); usageExit(1); } free(pairs); @@ -211,86 +274,98 @@ static void makeSubstitutions(inputData *inputPvt, MAC_HANDLE *macPvt, char *tem static char buffer[MAX_BUFFER_SIZE]; int n; - inputBegin(inputPvt,templateName); - while((input = inputNextLine(inputPvt))) { + ENTER; + inputBegin(inputPvt, templateName); + while ((input = inputNextLine(inputPvt))) { int expand=1; char *p; char *command = 0; - p = input; + p = input; /*skip whitespace at beginning of line*/ - while(*p && (isspace((int) *p))) ++p; + while (*p && (isspace((int) *p))) ++p; + /*Look for i or s */ - if(*p && (*p=='i' || *p=='s')) command = p; - if(command) { + if (*p && (*p=='i' || *p=='s')) + command = p; + + if (command) { char *pstart; char *pend; char *copy; int cmdind=-1; int i; - - for(i=0; i< NELEMENTS(cmdNames); i++) { - if(strstr(command,cmdNames[i])) { + + for (i = 0; i < NELEMENTS(cmdNames); i++) { + if (strstr(command, cmdNames[i])) { cmdind = i; } } - if(cmdind<0) goto endif; + if (cmdind < 0) goto endcmd; p = command + strlen(cmdNames[cmdind]); /*skip whitespace after command*/ - while(*p && (isspace((int) *p))) ++p; + while (*p && (isspace((int) *p))) ++p; /*Next character must be quote*/ - if((*p==0) || (*p!='"')) goto endif; + if ((*p == 0) || (*p != '"')) goto endcmd; pstart = ++p; /*Look for end quote*/ - while(*p && (*p!='"')) { - /*allow escape for imbeded quote*/ - if((*p=='\\') && *(p+1)=='"') { - p += 2; continue; - } else { - if(*p=='"') break; + while (*p && (*p != '"')) { + /*allow escape for embeded quote*/ + if ((p[0] == '\\') && p[1] == '"') { + p += 2; + continue; + } + else { + if (*p == '"') break; } ++p; } pend = p; - if(*p==0) goto endif; + if (*p == 0) goto endcmd; /*skip quote and any trailing blanks*/ - while(*++p==' ') ; - if(*p != '\n' && *p !=0) goto endif; - copy = calloc(pend-pstart+1,sizeof(char)); - strncpy(copy,pstart,pend-pstart); + while (*++p == ' ') ; + if (*p != '\n' && *p != 0) goto endcmd; + copy = calloc(pend-pstart + 1, sizeof(char)); + strncpy(copy, pstart, pend-pstart); + switch(cmdind) { case cmdInclude: inputNewIncludeFile(inputPvt,copy); break; + case cmdSubstitute: addMacroReplacements(macPvt,copy); break; + default: - fprintf(stderr,"msi: Logic error in makeSubstitutions\n"); + fprintf(stderr, "msi: Logic error in makeSubstitutions\n"); inputErrPrint(inputPvt); - exit(1); + abortExit(1); } free(copy); expand = 0; } -endif: + +endcmd: if (expand && !opt_D) { - n = macExpandString(macPvt,input,buffer,MAX_BUFFER_SIZE-1); - fputs(buffer,stdout); + STEP("Expanding to output stream"); + n = macExpandString(macPvt, input, buffer, MAX_BUFFER_SIZE - 1); + fputs(buffer, stdout); if (opt_V == 1 && n < 0) { - fprintf(stderr,"msi: Error - undefined macros present\n"); + fprintf(stderr, "msi: Error - undefined macros present\n"); opt_V++; } } } + EXIT; } -typedef struct inputFile{ +typedef struct inputFile { ELLNODE node; char *filename; FILE *fp; int lineNum; -}inputFile; +} inputFile; typedef struct pathNode { ELLNODE node; @@ -303,15 +378,15 @@ struct inputData { char inputBuffer[MAX_BUFFER_SIZE]; }; -static void inputOpenFile(inputData *pinputData,char *filename); +static void inputOpenFile(inputData *pinputData, char *filename); static void inputCloseFile(inputData *pinputData); static void inputCloseAllFiles(inputData *pinputData); static void inputConstruct(inputData **ppvt) { - inputData *pinputData; + inputData *pinputData; - pinputData = calloc(1,sizeof(inputData)); + pinputData = calloc(1, sizeof(inputData)); ellInit(&pinputData->inputFileList); ellInit(&pinputData->pathList); *ppvt = pinputData; @@ -319,11 +394,11 @@ static void inputConstruct(inputData **ppvt) static void inputDestruct(inputData *pinputData) { - pathNode *ppathNode; + pathNode *ppathNode; inputCloseAllFiles(pinputData); - while((ppathNode = (pathNode *)ellFirst(&pinputData->pathList))) { - ellDelete(&pinputData->pathList,&ppathNode->node); + while ((ppathNode = (pathNode *) ellFirst(&pinputData->pathList))) { + ellDelete(&pinputData->pathList, &ppathNode->node); free(ppathNode->directory); free(ppathNode); } @@ -340,38 +415,45 @@ static void inputAddPath(inputData *pinputData, char *path) int emptyName; const char sep = *OSI_PATH_LIST_SEPARATOR; + ENTER; pdir = path; /*an empty name at beginning, middle, or end means current directory*/ - while(pdir && *pdir) { + while (pdir && *pdir) { emptyName = ((*pdir == sep) ? 1 : 0); - if(emptyName) ++pdir; - ppathNode = (pathNode *)calloc(1,sizeof(pathNode)); - ellAdd(ppathList,&ppathNode->node); - if(!emptyName) { - pcolon = strchr(pdir,sep); + if (emptyName) ++pdir; + + ppathNode = (pathNode *) calloc(1, sizeof(pathNode)); + ellAdd(ppathList, &ppathNode->node); + + if (!emptyName) { + pcolon = strchr(pdir, sep); len = (pcolon ? (pcolon - pdir) : strlen(pdir)); - if(len>0) { - ppathNode->directory = (char *)calloc(len+1,sizeof(char)); - strncpy(ppathNode->directory,pdir,len); + if (len > 0) { + ppathNode->directory = (char *) calloc(len + 1, sizeof(char)); + strncpy(ppathNode->directory, pdir, len); pdir = pcolon; /*unless at end skip past first colon*/ - if(pdir && *(pdir+1)!=0) ++pdir; - } else { /*must have been trailing : */ - emptyName=1; + if (pdir && *(pdir + 1) != 0) ++pdir; + } + else { /*must have been trailing : */ + emptyName = 1; } } - if(emptyName) { - ppathNode->directory = (char *)calloc(2,sizeof(char)); - strcpy(ppathNode->directory,"."); + + if (emptyName) { + ppathNode->directory = (char *) calloc(2, sizeof(char)); + strcpy(ppathNode->directory, "."); } } - return; + EXIT; } static void inputBegin(inputData *pinputData, char *fileName) { + ENTER; inputCloseAllFiles(pinputData); - inputOpenFile(pinputData,fileName); + inputOpenFile(pinputData, fileName); + EXIT; } static char *inputNextLine(inputData *pinputData) @@ -379,43 +461,54 @@ static char *inputNextLine(inputData *pinputData) inputFile *pinputFile; char *pline; - while((pinputFile = (inputFile *)ellFirst(&pinputData->inputFileList))) { - pline = fgets(pinputData->inputBuffer,MAX_BUFFER_SIZE,pinputFile->fp); - if(pline) { + ENTER; + while ((pinputFile = (inputFile *) ellFirst(&pinputData->inputFileList))) { + pline = fgets(pinputData->inputBuffer, MAX_BUFFER_SIZE, pinputFile->fp); + if (pline) { ++pinputFile->lineNum; - return(pline); + EXITS(pline); + return pline; } inputCloseFile(pinputData); } - return(0); + EXITD(0); + return 0; } static void inputNewIncludeFile(inputData *pinputData, char *name) { + ENTER; inputOpenFile(pinputData,name); + EXIT; } static void inputErrPrint(inputData *pinputData) { inputFile *pinputFile; - fprintf(stderr,"input: '%s' at ",pinputData->inputBuffer); - pinputFile = (inputFile *)ellFirst(&pinputData->inputFileList); - while(pinputFile) { - fprintf(stderr,"line %d of ",pinputFile->lineNum); - if(pinputFile->filename) { - fprintf(stderr," file %s\n",pinputFile->filename); - } else { - fprintf(stderr,"stdin:\n"); + ENTER; + fprintf(stderr, "input: '%s' at ", pinputData->inputBuffer); + pinputFile = (inputFile *) ellFirst(&pinputData->inputFileList); + while (pinputFile) { + fprintf(stderr, "line %d of ", pinputFile->lineNum); + + if (pinputFile->filename) { + fprintf(stderr, " file %s\n", pinputFile->filename); } - pinputFile = (inputFile *)ellNext(&pinputFile->node); - if(pinputFile) { - fprintf(stderr," included from "); - } else { + else { + fprintf(stderr, "stdin:\n"); + } + + pinputFile = (inputFile *) ellNext(&pinputFile->node); + if (pinputFile) { + fprintf(stderr, " included from "); + } + else { fprintf(stderr,"\n"); } } fprintf(stderr,"\n"); + EXIT; } static void inputOpenFile(inputData *pinputData,char *filename) @@ -426,35 +519,48 @@ static void inputOpenFile(inputData *pinputData,char *filename) char *fullname = 0; FILE *fp = 0; - if(!filename) { + ENTER; + if (!filename) { + STEP("Using stdin"); fp = stdin; - } else if((ellCount(ppathList)==0) || strchr(filename,'/')){ - fp = fopen(filename,"r"); - } else { - ppathNode = (pathNode *)ellFirst(ppathList); - while(ppathNode) { - fullname = calloc(strlen(filename)+strlen(ppathNode->directory) +2, + } + else if ((ellCount(ppathList) == 0) || strchr(filename, '/')){ + STEPS("Opening ", filename); + fp = fopen(filename, "r"); + } + else { + ppathNode = (pathNode *) ellFirst(ppathList); + while (ppathNode) { + fullname = calloc(strlen(filename) + strlen(ppathNode->directory) + 2, sizeof(char)); - strcpy(fullname,ppathNode->directory); - strcat(fullname,"/"); - strcat(fullname,filename); - fp = fopen(fullname,"r"); - if(fp) break; + strcpy(fullname, ppathNode->directory); + strcat(fullname, "/"); + strcat(fullname, filename); + STEPS("Trying", filename); + fp = fopen(fullname, "r"); + if (fp) + break; free(fullname); - ppathNode = (pathNode *)ellNext(&ppathNode->node); + ppathNode = (pathNode *) ellNext(&ppathNode->node); } } - if(!fp) { - fprintf(stderr,"msi: Can't open file '%s'\n",filename); + + if (!fp) { + fprintf(stderr, "msi: Can't open file '%s'\n", filename); inputErrPrint(pinputData); - exit(1); + abortExit(1); } - pinputFile = calloc(1,sizeof(inputFile)); - if(ppathNode) { + + STEP("File opened"); + pinputFile = calloc(1, sizeof(inputFile)); + + if (ppathNode) { pinputFile->filename = fullname; - } else if(filename) { + } + else if (filename) { pinputFile->filename = epicsStrDup(filename); - } else { + } + else { pinputFile->filename = epicsStrDup("stdin"); } @@ -484,35 +590,41 @@ static void inputOpenFile(inputData *pinputData,char *filename) } pinputFile->fp = fp; - ellInsert(&pinputData->inputFileList,0,&pinputFile->node); + ellInsert(&pinputData->inputFileList, 0, &pinputFile->node); + EXIT; } static void inputCloseFile(inputData *pinputData) { inputFile *pinputFile; - pinputFile = (inputFile *)ellFirst(&pinputData->inputFileList); - if(!pinputFile) return; - ellDelete(&pinputData->inputFileList,&pinputFile->node); - if(fclose(pinputFile->fp)) - fprintf(stderr,"msi: Can't close input file '%s'\n",pinputFile->filename); - free(pinputFile->filename); - free(pinputFile); + ENTER; + pinputFile = (inputFile *) ellFirst(&pinputData->inputFileList); + if (pinputFile) { + ellDelete(&pinputData->inputFileList, &pinputFile->node); + if (fclose(pinputFile->fp)) + fprintf(stderr, "msi: Can't close input file '%s'\n", pinputFile->filename); + free(pinputFile->filename); + free(pinputFile); + } + EXIT; } static void inputCloseAllFiles(inputData *pinputData) { inputFile *pinputFile; - while((pinputFile=(inputFile *)ellFirst(&pinputData->inputFileList))){ + ENTER; + while ((pinputFile = (inputFile *) ellFirst(&pinputData->inputFileList))) { inputCloseFile(pinputData); } + EXIT; } /*start of code that handles substitution file*/ typedef enum { - tokenLBrace,tokenRBrace,tokenSeparater,tokenString,tokenEOF -}tokenType; + tokenLBrace, tokenRBrace, tokenSeparator, tokenString, tokenEOF +} tokenType; typedef struct subFile { char *substitutionName; @@ -549,70 +661,87 @@ static void catMacroReplacements(subInfo *psubInfo,const char *value); void freeSubFile(subInfo *psubInfo) { - subFile *psubFile = psubInfo->psubFile; - if(psubFile->fp) { - if(fclose(psubFile->fp)) - fprintf(stderr,"msi: Can't close substitution file\n"); + subFile *psubFile = psubInfo->psubFile; + + ENTER; + if (psubFile->fp) { + if (fclose(psubFile->fp)) + fprintf(stderr, "msi: Can't close substitution file\n"); } free(psubFile); free(psubInfo->filename); psubInfo->psubFile = 0; + EXIT; } void freePattern(subInfo *psubInfo) { patternNode *ppatternNode; - while((ppatternNode = (patternNode *)ellFirst(&psubInfo->patternList))) { - ellDelete(&psubInfo->patternList,&ppatternNode->node); + + ENTER; + while ((ppatternNode = (patternNode *) ellFirst(&psubInfo->patternList))) { + ellDelete(&psubInfo->patternList, &ppatternNode->node); free(ppatternNode->var); free(ppatternNode); } psubInfo->isPattern = 0; + EXIT; } static void substituteDestruct(subInfo *psubInfo) { + ENTER; freeSubFile(psubInfo); freePattern(psubInfo); free(psubInfo); - return; + EXIT; } -static void substituteOpen(subInfo **ppvt,char *substitutionName) +static void substituteOpen(subInfo **ppvt, char *substitutionName) { subInfo *psubInfo; subFile *psubFile; FILE *fp; - psubInfo = calloc(1,sizeof(subInfo)); + ENTER; + psubInfo = calloc(1, sizeof(subInfo)); *ppvt = psubInfo; - psubFile = calloc(1,sizeof(subFile)); + psubFile = calloc(1, sizeof(subFile)); psubInfo->psubFile = psubFile; ellInit(&psubInfo->patternList); - fp = fopen(substitutionName,"r"); - if(!fp) { - fprintf(stderr,"msi: Can't open file '%s'\n",substitutionName); - exit(1); + + fp = fopen(substitutionName, "r"); + if (!fp) { + fprintf(stderr, "msi: Can't open file '%s'\n", substitutionName); + abortExit(1); } + psubFile->substitutionName = substitutionName; psubFile->fp = fp; psubFile->lineNum = 1; psubFile->inputBuffer[0] = 0; psubFile->pnextChar = &psubFile->inputBuffer[0]; subGetNextToken(psubFile); - return; + EXIT; } static int substituteGetGlobalSet(subInfo *psubInfo) { - subFile *psubFile = psubInfo->psubFile; + subFile *psubFile = psubInfo->psubFile; - while(psubFile->token==tokenSeparater) subGetNextToken(psubFile); - if(psubFile->token==tokenString && strcmp(psubFile->string,"global")==0) { + ENTER; + while (psubFile->token == tokenSeparator) subGetNextToken(psubFile); - return(1); + + if (psubFile->token == tokenString && + strcmp(psubFile->string, "global") == 0) { + subGetNextToken(psubFile); + EXITD(1); + return 1; } - return(0); + + EXITD(0); + return 0; } static int substituteGetNextSet(subInfo *psubInfo,char **filename) @@ -620,96 +749,155 @@ static int substituteGetNextSet(subInfo *psubInfo,char **filename) subFile *psubFile = psubInfo->psubFile; patternNode *ppatternNode; + ENTER; *filename = 0; - while(psubFile->token==tokenSeparater) subGetNextToken(psubFile); - if(psubFile->token==tokenEOF) return(0); - if(psubFile->token==tokenString && strcmp(psubFile->string,"file")==0) { + while (psubFile->token == tokenSeparator) + subGetNextToken(psubFile); + + if (psubFile->token == tokenEOF) { + EXITD(0); + return 0; + } + + if (psubFile->token == tokenString && + strcmp(psubFile->string, "file") == 0) { + size_t len; + + STEP("Parsed 'file'"); psubInfo->isFile = 1; - if(subGetNextToken(psubFile)!=tokenString) { - subFileErrPrint(psubFile,"Parse error, expecting filename"); - exit(1); + if (subGetNextToken(psubFile) != tokenString) { + subFileErrPrint(psubFile, "Parse error, expecting a filename"); + abortExit(1); } + freePattern(psubInfo); free(psubInfo->filename); - if(psubFile->string[0]=='"'&&psubFile->string[strlen(psubFile->string)-1]=='"') { - psubFile->string[strlen(psubFile->string)-1]='\0'; - psubInfo->filename = macEnvExpand(psubFile->string+1); + + len = strlen(psubFile->string); + if (psubFile->string[0] == '"' && + psubFile->string[len - 1] == '"') { + psubFile->string[len - 1] = '\0'; + psubInfo->filename = macEnvExpand(psubFile->string + 1); } - else { + else psubInfo->filename = macEnvExpand(psubFile->string); + STEPS("Parsed filename", psubInfo->filename); + + while (subGetNextToken(psubFile) == tokenSeparator); + + if (psubFile->token != tokenLBrace) { + subFileErrPrint(psubFile, "Parse error, expecting '{'"); + abortExit(1); } - while(subGetNextToken(psubFile)==tokenSeparater); - if(psubFile->token!=tokenLBrace) { - subFileErrPrint(psubFile,"Parse error, expecting {"); - exit(1); - } + STEP("Parsed '{'"); subGetNextToken(psubFile); } *filename = psubInfo->filename; - while(psubFile->token==tokenSeparater) subGetNextToken(psubFile); - if(psubFile->token==tokenLBrace) return(1); - if(psubFile->token==tokenRBrace) return(1); - if(psubFile->token!=tokenString - || strcmp(psubFile->string,"pattern")!=0) { - subFileErrPrint(psubFile,"Parse error, expecting pattern"); - exit(1); + + while (psubFile->token == tokenSeparator) + subGetNextToken(psubFile); + + if (psubFile->token == tokenLBrace) { + EXITD(1); + return 1; } + + if (psubFile->token == tokenRBrace) { + subFileErrPrint(psubFile, "Parse error, unexpected '}'"); + abortExit(1); + } + + if (psubFile->token != tokenString || + strcmp(psubFile->string, "pattern") != 0) { + subFileErrPrint(psubFile, "Parse error, expecting 'pattern'"); + abortExit(1); + } + + STEP("Parsed 'pattern'"); freePattern(psubInfo); psubInfo->isPattern = 1; - while(subGetNextToken(psubFile)==tokenSeparater); - if(psubFile->token!=tokenLBrace) { - subFileErrPrint(psubFile,"Parse error, expecting {"); - exit(1); + + while (subGetNextToken(psubFile) == tokenSeparator); + + if (psubFile->token != tokenLBrace) { + subFileErrPrint(psubFile, "Parse error, expecting '{'"); + abortExit(1); } - while(1) { - while(subGetNextToken(psubFile)==tokenSeparater); - if(psubFile->token!=tokenString) break; - ppatternNode = calloc(1,sizeof(patternNode)); - ellAdd(&psubInfo->patternList,&ppatternNode->node); + STEP("Parsed '{'"); + + while (1) { + while (subGetNextToken(psubFile) == tokenSeparator); + + if (psubFile->token != tokenString) + break; + + ppatternNode = calloc(1, sizeof(patternNode)); + ellAdd(&psubInfo->patternList, &ppatternNode->node); ppatternNode->var = epicsStrDup(psubFile->string); } - if(psubFile->token!=tokenRBrace) { - subFileErrPrint(psubFile,"Parse error, expecting }"); - exit(1); + + if (psubFile->token != tokenRBrace) { + subFileErrPrint(psubFile, "Parse error, expecting '}'"); + abortExit(1); } + subGetNextToken(psubFile); - return(1); + EXITD(1); + return 1; } static char *substituteGetGlobalReplacements(subInfo *psubInfo) { subFile *psubFile = psubInfo->psubFile; - if(psubInfo->macroReplacements) psubInfo->macroReplacements[0] = 0; + ENTER; + if (psubInfo->macroReplacements) + psubInfo->macroReplacements[0] = 0; psubInfo->curLength = 0; - while(psubFile->token==tokenSeparater) subGetNextToken(psubFile); - if(psubFile->token==tokenRBrace && psubInfo->isFile) { + + while (psubFile->token == tokenSeparator) + subGetNextToken(psubFile); + + if (psubFile->token == tokenRBrace && psubInfo->isFile) { psubInfo->isFile = 0; free(psubInfo->filename); psubInfo->filename = 0; freePattern(psubInfo); subGetNextToken(psubFile); - return(0); + EXITD(0); + return 0; } - if(psubFile->token==tokenEOF) return(0); - if(psubFile->token!=tokenLBrace) return(0); - while(1) { + + if (psubFile->token == tokenEOF) { + EXITD(0); + return 0; + } + if (psubFile->token != tokenLBrace) { + EXITD(0); + return 0; + } + + while (1) { switch(subGetNextToken(psubFile)) { case tokenRBrace: subGetNextToken(psubFile); - if (!psubInfo->macroReplacements) { - catMacroReplacements(psubInfo,""); - } - return(psubInfo->macroReplacements); - case tokenSeparater: - catMacroReplacements(psubInfo,","); + EXITS(psubInfo->macroReplacements); + return psubInfo->macroReplacements; + + case tokenSeparator: + catMacroReplacements(psubInfo, ","); break; + case tokenString: - catMacroReplacements(psubInfo,psubFile->string); + catMacroReplacements(psubInfo, psubFile->string); break; - default: - subFileErrPrint(psubFile,"Parse error, illegal token"); - exit(1); + + case tokenLBrace: + subFileErrPrint(psubFile, "Parse error, unexpected '{'"); + abortExit(1); + case tokenEOF: + subFileErrPrint(psubFile, "Parse error, incomplete file?"); + abortExit(1); } } } @@ -719,62 +907,89 @@ static char *substituteGetReplacements(subInfo *psubInfo) subFile *psubFile = psubInfo->psubFile; patternNode *ppatternNode; - if(psubInfo->macroReplacements) psubInfo->macroReplacements[0] = 0; + ENTER; + if (psubInfo->macroReplacements) + psubInfo->macroReplacements[0] = 0; psubInfo->curLength = 0; - while(psubFile->token==tokenSeparater) subGetNextToken(psubFile); - if(psubFile->token==tokenRBrace && psubInfo->isFile) { + + while (psubFile->token == tokenSeparator) + subGetNextToken(psubFile); + + if (psubFile->token==tokenRBrace && psubInfo->isFile) { psubInfo->isFile = 0; free(psubInfo->filename); psubInfo->filename = 0; freePattern(psubInfo); subGetNextToken(psubFile); - return(0); + EXITD(0); + return 0; } - if(psubFile->token==tokenEOF) return(0); - if(psubFile->token!=tokenLBrace) return(0); - if(psubInfo->isPattern) { + + if (psubFile->token == tokenEOF) { + EXITD(0); + return 0; + } + + if (psubFile->token != tokenLBrace) { + EXITD(0); + return 0; + } + + if (psubInfo->isPattern) { int gotFirstPattern = 0; - while(subGetNextToken(psubFile)==tokenSeparater); - ppatternNode = (patternNode *)ellFirst(&psubInfo->patternList); - while(1) { - if(psubFile->token==tokenRBrace) { + while (subGetNextToken(psubFile) == tokenSeparator); + ppatternNode = (patternNode *) ellFirst(&psubInfo->patternList); + while (1) { + if (psubFile->token == tokenRBrace) { subGetNextToken(psubFile); - return(psubInfo->macroReplacements); + EXITS(psubInfo->macroReplacements); + return psubInfo->macroReplacements; } - if(psubFile->token!=tokenString) { - subFileErrPrint(psubFile,"Parse error, illegal token"); - exit(-1); + + if (psubFile->token != tokenString) { + subFileErrPrint(psubFile,"Parse error, expecting macro value"); + abortExit(1); } - if(gotFirstPattern) catMacroReplacements(psubInfo,","); + + if (gotFirstPattern) + catMacroReplacements(psubInfo, ","); gotFirstPattern = 1; - if(ppatternNode) { - catMacroReplacements(psubInfo,ppatternNode->var); - catMacroReplacements(psubInfo,"="); - catMacroReplacements(psubInfo,psubFile->string); - ppatternNode = (patternNode *)ellNext(&ppatternNode->node); - } else { - subFileErrPrint(psubFile,"Warning, too many values given"); + + if (ppatternNode) { + catMacroReplacements(psubInfo, ppatternNode->var); + catMacroReplacements(psubInfo, "="); + catMacroReplacements(psubInfo, psubFile->string); + ppatternNode = (patternNode *) ellNext(&ppatternNode->node); } - while(subGetNextToken(psubFile)==tokenSeparater); + else { + subFileErrPrint(psubFile, "Warning, too many values given"); + } + + while (subGetNextToken(psubFile) == tokenSeparator); } - } else while(1) { + } + else while(1) { switch(subGetNextToken(psubFile)) { case tokenRBrace: subGetNextToken(psubFile); - if (!psubInfo->macroReplacements) { - catMacroReplacements(psubInfo,""); - } - return(psubInfo->macroReplacements); - case tokenSeparater: - catMacroReplacements(psubInfo,","); + EXITS(psubInfo->macroReplacements); + return psubInfo->macroReplacements; + + case tokenSeparator: + catMacroReplacements(psubInfo, ","); break; + case tokenString: - catMacroReplacements(psubInfo,psubFile->string); + catMacroReplacements(psubInfo, psubFile->string); break; - default: - subFileErrPrint(psubFile,"Parse error, illegal token"); - exit(1); + + case tokenLBrace: + subFileErrPrint(psubFile, "Parse error, unexpected '{'"); + abortExit(1); + case tokenEOF: + subFileErrPrint(psubFile, "Parse error, incomplete file?"); + abortExit(1); } } } @@ -783,69 +998,84 @@ static char *subGetNextLine(subFile *psubFile) { char *pline; + ENTER; do { - pline = fgets(psubFile->inputBuffer,MAX_BUFFER_SIZE,psubFile->fp); + pline = fgets(psubFile->inputBuffer, MAX_BUFFER_SIZE, psubFile->fp); ++psubFile->lineNum; - } while(pline && psubFile->inputBuffer[0]=='#'); - if(!pline) { + } while (pline && psubFile->inputBuffer[0] == '#'); + + if (!pline) { psubFile->token = tokenEOF; psubFile->inputBuffer[0] = 0; psubFile->pnextChar = 0; - return(0); + EXITD(0); + return 0; } + psubFile->pnextChar = &psubFile->inputBuffer[0]; - return(&psubFile->inputBuffer[0]); + EXITS(&psubFile->inputBuffer[0]); + return &psubFile->inputBuffer[0]; } static void subFileErrPrint(subFile *psubFile,char * message) { - fprintf(stderr,"msi: %s\n",message); - fprintf(stderr," in substitution file '%s' at line %d:\n %s", - psubFile->substitutionName, - psubFile->lineNum,psubFile->inputBuffer); + fprintf(stderr, "msi: %s\n",message); + fprintf(stderr, " in substitution file '%s' at line %d:\n %s", + psubFile->substitutionName, psubFile->lineNum, psubFile->inputBuffer); } static tokenType subGetNextToken(subFile *psubFile) { - char *p; - char *pto; + char *p, *pto; + ENTER; p = psubFile->pnextChar; - if(!p) { psubFile->token = tokenEOF; return(tokenEOF);} - if(*p==0 || *p=='\n' || *p=='#') { - p = subGetNextLine(psubFile); - if(!p) { psubFile->token = tokenEOF; return(tokenEOF);} - else { psubFile->token = tokenSeparater; return(tokenSeparater);} + if (!p) { + STEP("Got EOF"); + psubFile->token = tokenEOF; + goto done; } - while(isspace((int) *p)) p++; - if(*p=='{') { + + if (*p == 0 || *p == '\n' || *p == '#') { + STEP("Got newline/comment"); + p = subGetNextLine(psubFile); + psubFile->token = p ? tokenSeparator : tokenEOF; + goto done; + } + + while (isspace((int) *p)) p++; + if (*p == '{') { + STEP("Got '{'"); psubFile->token = tokenLBrace; psubFile->pnextChar = ++p; - return(tokenLBrace); + goto done; } - if(*p=='}') { + if (*p == '}') { + STEP("Got '}'"); psubFile->token = tokenRBrace; psubFile->pnextChar = ++p; - return(tokenRBrace); + goto done; } - if(*p==0 || isspace((int) *p) || *p==',') { - while (isspace((int) *p) || *p==',') p++; - psubFile->token = tokenSeparater; + if (*p == 0 || isspace((int) *p) || *p == ',') { + STEP("Got space/comma"); + while (isspace((int) *p) || *p == ',') p++; + psubFile->token = tokenSeparator; psubFile->pnextChar = p; - return(tokenSeparater); + goto done; } /*now handle quoted strings*/ - if(*p=='"') { + if (*p == '"') { + STEP("Got '\"'"); pto = &psubFile->string[0]; *pto++ = *p++; - while(*p!='"') { - if(*p==0 || *p=='\n') { - subFileErrPrint(psubFile,"Strings must be on single line\n"); - exit(1); + while (*p != '"') { + if (*p == 0 || *p == '\n') { + subFileErrPrint(psubFile, "Strings must be on single line\n"); + abortExit(1); } - /*allow escape for imbeded quote*/ - if((*p=='\\') && *(p+1)=='"') { + /*allow escape for embeded quote*/ + if ((p[0] == '\\') && p[1] == '"') { *pto++ = *p++; *pto++ = *p++; continue; @@ -856,40 +1086,53 @@ static tokenType subGetNextToken(subFile *psubFile) psubFile->pnextChar = p; *pto = 0; psubFile->token = tokenString; - return(tokenString); + goto done; } + /*Now take anything up to next non String token and not space*/ pto = &psubFile->string[0]; - while(!isspace((int) *p) && (strspn(p,"\",{}")==0)) *pto++ = *p++; + + while (!isspace((int) *p) && (strspn(p, "\",{}") == 0)) + *pto++ = *p++; *pto = 0; + STEPS("Got bareword", psubFile->string); + psubFile->pnextChar = p; psubFile->token = tokenString; - return(tokenString); + +done: + EXITD(psubFile->token); + return psubFile->token; } -static void catMacroReplacements(subInfo *psubInfo,const char *value) +static void catMacroReplacements(subInfo *psubInfo, const char *value) { - size_t len = strlen(value); + size_t len = strlen(value); - if(psubInfo->size <= (psubInfo->curLength + len)) { + ENTER; + if (psubInfo->size <= (psubInfo->curLength + len)) { size_t newsize = psubInfo->size + MAX_BUFFER_SIZE; char *newbuf; - if(newsize <= psubInfo->curLength + len) + STEP("Enlarging buffer"); + if (newsize <= psubInfo->curLength + len) newsize = psubInfo->curLength + len + 1; - newbuf = calloc(1,newsize); - if(!newbuf) { - fprintf(stderr,"calloc failed for size %lu\n", - (unsigned long) newsize); - exit(1); + newbuf = calloc(1, newsize); + if (!newbuf) { + fprintf(stderr, "calloc failed for size %lu\n", + (unsigned long) newsize); + abortExit(1); } - if(psubInfo->macroReplacements) { - memcpy(newbuf,psubInfo->macroReplacements,psubInfo->curLength); + if (psubInfo->macroReplacements) { + memcpy(newbuf, psubInfo->macroReplacements, psubInfo->curLength); free(psubInfo->macroReplacements); } psubInfo->size = newsize; psubInfo->macroReplacements = newbuf; } - strcat(psubInfo->macroReplacements,value); + + STEPS("Appending", value); + strcat(psubInfo->macroReplacements, value); psubInfo->curLength += len; + EXIT; } diff --git a/modules/database/src/ioc/dbtemplate/msi.html b/modules/database/src/ioc/dbtemplate/msi.html index ff4341ead..b5929fc79 100644 --- a/modules/database/src/ioc/dbtemplate/msi.html +++ b/modules/database/src/ioc/dbtemplate/msi.html @@ -34,10 +34,10 @@ be written to stdout unless the -o option is given.

    -V
    -
    Verbose warnings; if this parameter is specified then any undefined - macro discovered in the template file which does not have an associated - default value is considered an error. An error message is generated, and - when msi terminates it will do so with an exit status of 2.
    +
    Verbose warnings; if this parameter is specified then any undefined or + recursive macros discovered in the template will be considered an error and + will be marked in the output file. An error message will be shown, and when + msi terminates it will do so with an exit status of 2.
    -g
    When this flag is given all macros defined in a substitution file will diff --git a/modules/database/src/ioc/misc/Makefile b/modules/database/src/ioc/misc/Makefile index e97efe15a..ef70941c2 100644 --- a/modules/database/src/ioc/misc/Makefile +++ b/modules/database/src/ioc/misc/Makefile @@ -26,3 +26,4 @@ dbCore_SRCS += miscIocRegister.c dbCore_SRCS += dlload.c dbCore_SRCS += iocshRegisterCommon.c +miscIocRegister_CFLAGS_iOS = -DSYSTEM_UNAVAILABLE diff --git a/modules/database/src/ioc/misc/dbCore.dbd b/modules/database/src/ioc/misc/dbCore.dbd index 921b3818e..51f9c9640 100644 --- a/modules/database/src/ioc/misc/dbCore.dbd +++ b/modules/database/src/ioc/misc/dbCore.dbd @@ -12,6 +12,9 @@ variable(asCaDebug,int) # CA server debug flag (very verbose) range[0,5] variable(CASDEBUG,int) +# Link parsing debug +variable(dbJLinkDebug,int) + # Static database access variables variable(dbRecordsOnceOnly,int) variable(dbRecordsAbcSorted,int) @@ -19,8 +22,12 @@ variable(dbBptNotMonotonic,int) variable(dbQuietMacroWarnings,int) variable(dbConvertStrict,int) +# PUTF/RPRO tracing; set TPRO on records to trace +variable(dbAccessDebugPUTF,int) + # dbLoadTemplate settings variable(dbTemplateMaxVars,int) + # Default number of parallel callback threads variable(callbackParallelThreadsDefault,int) diff --git a/modules/database/src/ioc/misc/miscIocRegister.c b/modules/database/src/ioc/misc/miscIocRegister.c index 233852ed2..6c08ef0c9 100644 --- a/modules/database/src/ioc/misc/miscIocRegister.c +++ b/modules/database/src/ioc/misc/miscIocRegister.c @@ -66,10 +66,12 @@ void miscIocRegister(void) /* system -- escape to system command interpreter. * - * Disabled by default, for security reasons. To enable this command, add + * Disabled by default for security reasons, not available on all OSs. + * To enable this command, add * registrar(iocshSystemCommand) - * to an application dbd file. + * to an application dbd file, or include system.dbd */ +#ifndef SYSTEM_UNAVAILABLE static const iocshArg systemArg0 = { "command string",iocshArgString}; static const iocshArg * const systemArgs[] = {&systemArg0}; static const iocshFuncDef systemFuncDef = {"system",1,systemArgs}; @@ -77,12 +79,15 @@ static void systemCallFunc(const iocshArgBuf *args) { system(args[0].sval); } +#endif static void iocshSystemCommand(void) { +#ifndef SYSTEM_UNAVAILABLE if (system(NULL)) iocshRegister(&systemFuncDef, systemCallFunc); else +#endif errlogPrintf ("Can't register 'system' command -- no command interpreter available.\n"); } epicsExportRegistrar(iocshSystemCommand); diff --git a/modules/database/src/ioc/rsrv/caservertask.c b/modules/database/src/ioc/rsrv/caservertask.c index 0b7a068a5..f377d837f 100644 --- a/modules/database/src/ioc/rsrv/caservertask.c +++ b/modules/database/src/ioc/rsrv/caservertask.c @@ -83,20 +83,22 @@ static void req_server (void *pParm) while (TRUE) { SOCKET clientSock; - struct sockaddr sockAddr; + osiSockAddr sockAddr; osiSocklen_t addLen = sizeof(sockAddr); while (castcp_ctl == ctlPause) { epicsThreadSleep(0.1); } - clientSock = epicsSocketAccept ( IOC_sock, &sockAddr, &addLen ); - if ( clientSock == INVALID_SOCKET ) { + clientSock = epicsSocketAccept ( IOC_sock, &sockAddr.sa, &addLen ); + if ( clientSock == INVALID_SOCKET || + sockAddr.sa.sa_family != AF_INET || + addLen < sizeof(sockAddr.ia) ) { char sockErrBuf[64]; epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) ); - errlogPrintf("CAS: Client accept error: %s\n", - sockErrBuf ); + errlogPrintf("CAS: Client accept error: %s (%d)\n", + sockErrBuf, (int)addLen ); epicsThreadSleep(15.0); continue; } @@ -105,7 +107,7 @@ static void req_server (void *pParm) struct client *pClient; /* socket passed in is closed if unsuccessful here */ - pClient = create_tcp_client ( clientSock ); + pClient = create_tcp_client ( clientSock, &sockAddr ); if ( ! pClient ) { epicsThreadSleep ( 15.0 ); continue; @@ -324,7 +326,7 @@ void rsrv_build_addr_lists(void) #ifdef IP_MULTICAST_TTL { - int ttl; + osiSockOptMcastTTL_t ttl; long val; if(envGetLongConfigParam(&EPICS_CA_MCAST_TTL, &val)) val =1; @@ -1405,12 +1407,11 @@ void casExpandRecvBuffer ( struct client *pClient, ca_uint32_t size ) /* * create_tcp_client () */ -struct client *create_tcp_client ( SOCKET sock ) +struct client *create_tcp_client (SOCKET sock , const osiSockAddr *peerAddr) { int status; struct client *client; int intTrue = TRUE; - osiSocklen_t addrSize; unsigned priorityOfEvents; /* socket passed in is destroyed here if unsuccessful */ @@ -1419,6 +1420,8 @@ struct client *create_tcp_client ( SOCKET sock ) return NULL; } + client->addr = peerAddr->ia; + /* * see TCP(4P) this seems to make unsolicited single events much * faster. I take care of queue up as load increases. @@ -1470,15 +1473,6 @@ struct client *create_tcp_client ( SOCKET sock ) } #endif - addrSize = sizeof ( client->addr ); - status = getpeername ( sock, (struct sockaddr *)&client->addr, - &addrSize ); - if ( status < 0 ) { - epicsPrintf ("CAS: peer address fetch failed\n"); - destroy_tcp_client (client); - return NULL; - } - client->evuser = (struct event_user *) db_init_events (); if ( ! client->evuser ) { errlogPrintf ("CAS: unable to init the event facility\n"); diff --git a/modules/database/src/ioc/rsrv/online_notify.c b/modules/database/src/ioc/rsrv/online_notify.c index 6b6157370..4627c39de 100644 --- a/modules/database/src/ioc/rsrv/online_notify.c +++ b/modules/database/src/ioc/rsrv/online_notify.c @@ -22,6 +22,7 @@ #include #include #include +#include #include "addrList.h" #include "dbDefs.h" @@ -45,6 +46,7 @@ void rsrv_online_notify_task(void *pParm) caHdr msg; int status; ca_uint32_t beaconCounter = 0; + int *lastError; taskwdInsert (epicsThreadGetIdSelf(),NULL,NULL); @@ -70,29 +72,45 @@ void rsrv_online_notify_task(void *pParm) msg.m_count = htons (ca_server_port); msg.m_dataType = htons (CA_MINOR_PROTOCOL_REVISION); + /* beaconAddrList should not change after rsrv_init(), which then starts this thread */ + lastError = callocMustSucceed(ellCount(&beaconAddrList), sizeof(*lastError), "rsrv_online_notify_task lastError"); epicsEventSignal(beacon_startStopEvent); while (TRUE) { ELLNODE *cur; + unsigned i; /* send beacon to each interface */ - for(cur=ellFirst(&beaconAddrList); cur; cur=ellNext(cur)) + for(i=0, cur=ellFirst(&beaconAddrList); cur; i++, cur=ellNext(cur)) { osiSockAddrNode *pAddr = CONTAINER(cur, osiSockAddrNode, node); status = sendto (beaconSocket, (char *)&msg, sizeof(msg), 0, &pAddr->addr.sa, sizeof(pAddr->addr)); if (status < 0) { - char sockErrBuf[64]; - char sockDipBuf[22]; + int err = SOCKERRNO; + if(err != lastError[i]) { + char sockErrBuf[64]; + char sockDipBuf[22]; - epicsSocketConvertErrnoToString(sockErrBuf, sizeof(sockErrBuf)); - ipAddrToDottedIP(&pAddr->addr.ia, sockDipBuf, sizeof(sockDipBuf)); - errlogPrintf ( "CAS: CA beacon send to %s error: %s\n", - sockDipBuf, sockErrBuf); + epicsSocketConvertErrorToString(sockErrBuf, sizeof(sockErrBuf), err); + ipAddrToDottedIP(&pAddr->addr.ia, sockDipBuf, sizeof(sockDipBuf)); + errlogPrintf ( "CAS: CA beacon send to %s error: %s\n", + sockDipBuf, sockErrBuf); + + lastError[i] = err; + } } else { assert (status == sizeof(msg)); + if(lastError[i]) { + char sockDipBuf[22]; + + ipAddrToDottedIP(&pAddr->addr.ia, sockDipBuf, sizeof(sockDipBuf)); + errlogPrintf ( "CAS: CA beacon send to %s ok\n", + sockDipBuf); + } + lastError[i] = 0; } } @@ -111,6 +129,8 @@ void rsrv_online_notify_task(void *pParm) delay = 0.02; /* Restart beacon timing if paused */ } } + + free(lastError); } diff --git a/modules/database/src/ioc/rsrv/server.h b/modules/database/src/ioc/rsrv/server.h index ef9730ba8..4d502f77f 100644 --- a/modules/database/src/ioc/rsrv/server.h +++ b/modules/database/src/ioc/rsrv/server.h @@ -230,7 +230,7 @@ void rsrv_online_notify_task (void *); void cast_server (void *); struct client *create_client ( SOCKET sock, int proto ); void destroy_client ( struct client * ); -struct client *create_tcp_client ( SOCKET sock ); +struct client *create_tcp_client ( SOCKET sock, const osiSockAddr* peerAddr ); void destroy_tcp_client ( struct client * ); void casAttachThreadToClient ( struct client * ); int camessage ( struct client *client ); diff --git a/modules/database/src/std/dev/devAaiSoft.c b/modules/database/src/std/dev/devAaiSoft.c index d2a9875ee..cb4aa0213 100644 --- a/modules/database/src/std/dev/devAaiSoft.c +++ b/modules/database/src/std/dev/devAaiSoft.c @@ -23,6 +23,7 @@ #include "dbDefs.h" #include "dbAccess.h" #include "dbConstLink.h" +#include "dbEvent.h" #include "recGbl.h" #include "devSup.h" #include "cantProceed.h" @@ -96,11 +97,15 @@ static long readLocked(struct link *pinp, void *dummy) static long read_aai(aaiRecord *prec) { + epicsUInt32 nord = prec->nord; struct link *pinp = prec->simm == menuYesNoYES ? &prec->siol : &prec->inp; long status = dbLinkDoLocked(pinp, readLocked, NULL); if (status == S_db_noLSET) status = readLocked(pinp, NULL); + if (!status && nord != prec->nord) + db_post_events(prec, &prec->nord, DBE_VALUE | DBE_LOG); + return status; } diff --git a/modules/database/src/std/dev/devSASoft.c b/modules/database/src/std/dev/devSASoft.c index 01a076e6d..69894dd89 100644 --- a/modules/database/src/std/dev/devSASoft.c +++ b/modules/database/src/std/dev/devSASoft.c @@ -4,7 +4,7 @@ * Copyright (c) 2002 Lawrence Berkeley Laboratory,The Control Systems * Group, Systems Engineering Department * 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. \*************************************************************************/ /* @@ -19,6 +19,7 @@ #include "alarm.h" #include "dbDefs.h" #include "dbAccess.h" +#include "dbEvent.h" #include "recGbl.h" #include "devSup.h" #include "subArrayRecord.h" @@ -101,6 +102,7 @@ static long read_sa(subArrayRecord *prec) { long status; struct sart rt; + epicsUInt32 nord = prec->nord; rt.nRequest = prec->indx + prec->nelm; if (rt.nRequest > prec->malm) @@ -123,8 +125,12 @@ static long read_sa(subArrayRecord *prec) status = readLocked(&prec->inp, &rt); } - if (!status && rt.nRequest > 0) + if (!status && rt.nRequest > 0) { subset(prec, rt.nRequest); + if (nord != prec->nord) + db_post_events(prec, &prec->nord, DBE_VALUE | DBE_LOG); + } + return status; } diff --git a/modules/database/src/std/dev/devWfSoft.c b/modules/database/src/std/dev/devWfSoft.c index 8d8295696..5b521518d 100644 --- a/modules/database/src/std/dev/devWfSoft.c +++ b/modules/database/src/std/dev/devWfSoft.c @@ -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. \*************************************************************************/ /* @@ -81,6 +81,7 @@ static long read_wf(waveformRecord *prec) { long status; struct wfrt rt; + epicsUInt32 nord = prec->nord; rt.nRequest = prec->nelm; rt.ptime = (dbLinkIsConstant(&prec->tsel) && @@ -93,6 +94,8 @@ static long read_wf(waveformRecord *prec) if (!status && rt.nRequest > 0) { prec->nord = rt.nRequest; prec->udf = FALSE; + if (nord != prec->nord) + db_post_events(prec, &prec->nord, DBE_VALUE | DBE_LOG); } return status; diff --git a/modules/database/src/std/link/Makefile b/modules/database/src/std/link/Makefile index 31d14b825..9b6abd705 100644 --- a/modules/database/src/std/link/Makefile +++ b/modules/database/src/std/link/Makefile @@ -2,7 +2,7 @@ # Copyright (c) 2016 UChicago Argonne LLC, as Operator of Argonne # 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. #************************************************************************* # This is a Makefile fragment, see src/std/Makefile. @@ -13,6 +13,7 @@ DBD += links.dbd dbRecStd_SRCS += lnkConst.c dbRecStd_SRCS += lnkCalc.c +dbRecStd_SRCS += lnkState.c +dbRecStd_SRCS += lnkDebug.c HTMLS += links.html - diff --git a/modules/database/src/std/link/links.dbd.pod b/modules/database/src/std/link/links.dbd.pod index d94e9e8f0..b5ed582f2 100644 --- a/modules/database/src/std/link/links.dbd.pod +++ b/modules/database/src/std/link/links.dbd.pod @@ -13,6 +13,12 @@ The following additional link types are available in this release: =item * L +=item * L + +=item * L + +=item * L + =back =head2 Using JSON Links @@ -31,15 +37,16 @@ database file syntax. =cut + link(const, lnkConstIf) =head3 Constant Link C<"const"> -Constant links provide one or more values at link initalization time, but do not -return any data when their C routine is called. Most record types -support the use of constant links by calling C at -record initialization, which results in the constant value being loaded into the -target field at that time. +Constant links are input links that provide literal values at link initalization +time, but do not return any data when their C routine is called. +Most record types support the use of constant links on their input links by +calling C at record initialization, which results in +the constant value being loaded into the target field at that time. Note that for most record types (the C and C records are the main exceptions) it is pointless to set an input link to a constant link at @@ -70,14 +77,35 @@ converted to the desired double value at initialization, for example: =cut + link(calc, lnkCalcIf) =head3 Calculation Link C<"calc"> -Calculation links can perform simple mathematical expressions on scalar -(double-precision floating-point) values obtained from other link types and -return a single double-precision floating-point result. The expressions are -evaluated by the EPICS Calc engine, and up to 12 inputs can be provided. +A calculation link is an input link that can evaluate mathematical expressions +on scalar (double-precision floating-point) values obtained from up to 12 child +input links, and returns a double-precision floating-point result. The +expression is evaluated by the EPICS Calc engine, and the result is returned as +the value of the link. + +Two additional expressions may also be provided and are evaluated to determine +whether the record owning the link should be placed in alarm state. In both +cases the result of the main calculation is available to these expressions as +C (attempts to assign to C inside either expression will have no +lasting effect). If the C expression evaluates to a non-zero value the +record will be placed in C alarm. If not and the C expression +evaluates to non-zero the record will be placed in C alarm state. + +A calculation link can also be an output link, with the scalar output value +being converted to a double and provided to the expression as C. Up to 12 +additional input links can also be read and provided to the expression as above. +The result of the calculation is forwarded to a child output link specified in +the link's C parameter. + +For an output link the main expression is actually optional; if not provided the +converted value will be forwarded to the output link unchanged. The two alarm +expressions may still be used to put the output link into alarm state as +described above. =head4 Parameters @@ -88,6 +116,7 @@ The link address is a JSON map with the following keys: =item expr The primary expression to be evaluated, given as a string. +This is optional for output links, required for input links. =item major @@ -104,6 +133,12 @@ to the inputs C, C, C, ... C. Each input argument may be either a numeric literal or an embedded JSON link inside C<{}> braces. The same input values are provided to the two alarm expressions as to the primary expression. +=item out + +A JSON link inside C<{}> braces which specifies the destination of C +operations after any expressions have been evaluated. +This key is required for output links, not used by input links. + =item units An optional string specifying the engineering units for the result of the @@ -129,3 +164,72 @@ atomically with the value of the input argument. {calc: {expr:"A*B", args:[{db:"record.VAL"}, 1.5], prec:3}} =cut + + +link(state, lnkStateIf) + +=head3 dbState Link C<"state"> + +A dbState link is one that gets or puts a boolean value from/to a named global +flag as implemented by the dbState facility in C. The link type can +invert the sense of the dbState flag during the get or put if desired. + +The value of the named flag is read or written at the time of the link I/O +operation. When reading a flag, the value returned by the link will be zero or +one converted to the requested data type. When writing to a flag the boolean +value of the data written is determined in the originating data type. All +strings are regarded as true other than C<""> and C<"0"> which are both false. + +A link can be configured to invert the sense of the flag data by putting an +exclamation mark C before the first character of the flag's name in the link +address. + +These dbState flags can be accessed from the IOC Shell with various dbState +commands, and are also used by the C<"sync"> Channel-Access server-side filter +mechanism. + +=head4 Parameters + +The link takes a single parameter which must be a string, providing the name of +the dbState object, with an optional leading C character to indicate that the +flag's value should be inverted. The dbState object will be created when the +link is initialized if it doesn't already exist. + +=head4 Examples + + {state:"redBeam"} + {state:"!simEnable"} + +=cut + + +link(debug, lnkDebugIf) +variable(lnkDebug_debug, int) + +=head3 Debug Link C<"debug"> + +The debug link type exists to enable debugging of other link types; it provides +no functionality itself other than to turn on the debug flag for the child link +that is its only parameter and pass all link operations down to that link. + +=head4 Example + + {debug:{state:"redBeam"}} + +=cut + + +link(trace, lnkTraceIf) + +=head3 Trace Link C<"trace"> + +The trace link type is a relative of the debug link type that also traces the +operation of its child link. At creation it turns on the debug flag of its child +link, then it prints the method arguments and return values of all link +operations before / after passing control down to the child link. + +=head4 Example + + {trace:{state:"redBeam"}} + +=cut diff --git a/modules/database/src/std/link/lnkCalc.c b/modules/database/src/std/link/lnkCalc.c index e370c36d2..6f525059b 100644 --- a/modules/database/src/std/link/lnkCalc.c +++ b/modules/database/src/std/link/lnkCalc.c @@ -6,13 +6,9 @@ \*************************************************************************/ /* lnkCalc.c */ -/* Current usage - * {calc:{expr:"A", args:[{...}, ...]}} +/* Usage + * {calc:{expr:"A*B", args:[{...}, ...], units:"mm"}} * First link in 'args' is 'A', second is 'B', and so forth. - * - * TODO: - * Support setting individual input links instead of the args list. - * {calc:{expr:"K", K:{...}}} */ #include @@ -26,6 +22,7 @@ #include "epicsAssert.h" #include "epicsString.h" #include "epicsTypes.h" +#include "epicsTime.h" #include "dbAccessDefs.h" #include "dbCommon.h" #include "dbConvertFast.h" @@ -40,15 +37,14 @@ typedef long (*FASTCONVERT)(); -#define IFDEBUG(n) if(clink->jlink.debug) - typedef struct calc_link { jlink jlink; /* embedded object */ int nArgs; + short dbfType; enum { ps_init, ps_expr, ps_major, ps_minor, - ps_args, + ps_args, ps_out, ps_prec, ps_units, ps_time, @@ -66,7 +62,9 @@ typedef struct calc_link { char *units; short tinp; struct link inp[CALCPERFORM_NARGS]; + struct link out; double arg[CALCPERFORM_NARGS]; + epicsTimeStamp time; double val; } calc_link; @@ -77,19 +75,25 @@ static lset lnkCalc_lset; static jlink* lnkCalc_alloc(short dbfType) { - calc_link *clink = calloc(1, sizeof(struct calc_link)); + calc_link *clink; - IFDEBUG(10) - printf("lnkCalc_alloc()\n"); + if (dbfType == DBF_FWDLINK) { + errlogPrintf("lnkCalc: No support for forward links\n"); + return NULL; + } + + clink = calloc(1, sizeof(struct calc_link)); + if (!clink) { + errlogPrintf("lnkCalc: calloc() failed.\n"); + return NULL; + } clink->nArgs = 0; + clink->dbfType = dbfType; clink->pstate = ps_init; clink->prec = 15; /* standard value for a double */ clink->tinp = -1; - IFDEBUG(10) - printf("lnkCalc_alloc -> calc@%p\n", clink); - return &clink->jlink; } @@ -98,12 +102,11 @@ static void lnkCalc_free(jlink *pjlink) calc_link *clink = CONTAINER(pjlink, struct calc_link, jlink); int i; - IFDEBUG(10) - printf("lnkCalc_free(calc@%p)\n", clink); - for (i = 0; i < clink->nArgs; i++) dbJLinkFree(clink->inp[i].value.json.jlink); + dbJLinkFree(clink->out.value.json.jlink); + free(clink->expr); free(clink->major); free(clink->minor); @@ -118,9 +121,6 @@ static jlif_result lnkCalc_integer(jlink *pjlink, long long num) { calc_link *clink = CONTAINER(pjlink, struct calc_link, jlink); - IFDEBUG(10) - printf("lnkCalc_integer(calc@%p, %lld)\n", clink, num); - if (clink->pstate == ps_prec) { clink->prec = num; return jlif_continue; @@ -146,9 +146,6 @@ static jlif_result lnkCalc_double(jlink *pjlink, double num) { calc_link *clink = CONTAINER(pjlink, struct calc_link, jlink); - IFDEBUG(10) - printf("lnkCalc_double(calc@%p, %g)\n", clink, num); - if (clink->pstate != ps_args) { return jlif_stop; errlogPrintf("lnkCalc: Unexpected double %g\n", num); @@ -171,9 +168,6 @@ static jlif_result lnkCalc_string(jlink *pjlink, const char *val, size_t len) char *inbuf, *postbuf; short err; - IFDEBUG(10) - printf("lnkCalc_string(calc@%p, \"%.*s\")\n", clink, (int) len, val); - if (clink->pstate == ps_units) { clink->units = epicsStrnDup(val, len); return jlif_continue; @@ -181,7 +175,8 @@ static jlif_result lnkCalc_string(jlink *pjlink, const char *val, size_t len) if (clink->pstate == ps_time) { char tinp; - if (len != 1 || (tinp = toupper(val[0])) < 'A' || tinp > 'L') { + + if (len != 1 || (tinp = toupper((int) val[0])) < 'A' || tinp > 'L') { errlogPrintf("lnkCalc: Bad 'time' parameter \"%.*s\"\n", (int) len, val); return jlif_stop; } @@ -234,11 +229,10 @@ static jlif_key_result lnkCalc_start_map(jlink *pjlink) { calc_link *clink = CONTAINER(pjlink, struct calc_link, jlink); - IFDEBUG(10) - printf("lnkCalc_start_map(calc@%p)\n", clink); - if (clink->pstate == ps_args) - return jlif_key_child_link; + return jlif_key_child_inlink; + if (clink->pstate == ps_out) + return jlif_key_child_outlink; if (clink->pstate != ps_init) { errlogPrintf("lnkCalc: Unexpected map\n"); @@ -252,10 +246,21 @@ static jlif_result lnkCalc_map_key(jlink *pjlink, const char *key, size_t len) { calc_link *clink = CONTAINER(pjlink, struct calc_link, jlink); - IFDEBUG(10) - printf("lnkCalc_map_key(calc@%p, \"%.*s\")\n", pjlink, (int) len, key); + /* FIXME: These errors messages are wrong when a key is duplicated. + * The key is known, we just don't allow it more than once. + */ - if (len == 4) { + if (len == 3) { + if (!strncmp(key, "out", len) && + clink->dbfType == DBF_OUTLINK && + clink->out.type == 0) + clink->pstate = ps_out; + else { + errlogPrintf("lnkCalc: Unknown key \"%.3s\"\n", key); + return jlif_stop; + } + } + else if (len == 4) { if (!strncmp(key, "expr", len) && !clink->post_expr) clink->pstate = ps_expr; else if (!strncmp(key, "args", len) && !clink->nArgs) @@ -293,13 +298,16 @@ static jlif_result lnkCalc_end_map(jlink *pjlink) { calc_link *clink = CONTAINER(pjlink, struct calc_link, jlink); - IFDEBUG(10) - printf("lnkCalc_end_map(calc@%p)\n", clink); - if (clink->pstate == ps_error) return jlif_stop; - else if (!clink->post_expr) { - errlogPrintf("lnkCalc: no expression ('expr' key)\n"); + else if (clink->dbfType == DBF_INLINK && + !clink->post_expr) { + errlogPrintf("lnkCalc: No expression ('expr' key)\n"); + return jlif_stop; + } + else if (clink->dbfType == DBF_OUTLINK && + clink->out.type != JSON_LINK) { + errlogPrintf("lnkCalc: No output link ('out' key)\n"); return jlif_stop; } @@ -310,9 +318,6 @@ static jlif_result lnkCalc_start_array(jlink *pjlink) { calc_link *clink = CONTAINER(pjlink, struct calc_link, jlink); - IFDEBUG(10) - printf("lnkCalc_start_array(calc@%p)\n", clink); - if (clink->pstate != ps_args) { errlogPrintf("lnkCalc: Unexpected array\n"); return jlif_stop; @@ -325,9 +330,6 @@ static jlif_result lnkCalc_end_array(jlink *pjlink) { calc_link *clink = CONTAINER(pjlink, struct calc_link, jlink); - IFDEBUG(10) - printf("lnkCalc_end_array(calc@%p)\n", clink); - if (clink->pstate == ps_error) return jlif_stop; @@ -339,15 +341,27 @@ static void lnkCalc_end_child(jlink *parent, jlink *child) calc_link *clink = CONTAINER(parent, struct calc_link, jlink); struct link *plink; - if (clink->nArgs == CALCPERFORM_NARGS) { - dbJLinkFree(child); - errlogPrintf("lnkCalc: Too many input args, limit is %d\n", - CALCPERFORM_NARGS); + if (clink->pstate == ps_args) { + if (clink->nArgs == CALCPERFORM_NARGS) { + errlogPrintf("lnkCalc: Too many input args, limit is %d\n", + CALCPERFORM_NARGS); + goto errOut; + } + + plink = &clink->inp[clink->nArgs++]; + } + else if (clink->pstate == ps_out) { + plink = &clink->out; + } + else { + errlogPrintf("lnkCalc: Unexpected child link, parser state = %d\n", + clink->pstate); +errOut: clink->pstate = ps_error; + dbJLinkFree(child); return; } - plink = &clink->inp[clink->nArgs++]; plink->type = JSON_LINK; plink->value.json.string = NULL; plink->value.json.jlink = child; @@ -355,11 +369,6 @@ static void lnkCalc_end_child(jlink *parent, jlink *child) static struct lset* lnkCalc_get_lset(const jlink *pjlink) { - calc_link *clink = CONTAINER(pjlink, struct calc_link, jlink); - - IFDEBUG(10) - printf("lnkCalc_get_lset(calc@%p)\n", pjlink); - return &lnkCalc_lset; } @@ -368,9 +377,6 @@ static void lnkCalc_report(const jlink *pjlink, int level, int indent) calc_link *clink = CONTAINER(pjlink, struct calc_link, jlink); int i; - IFDEBUG(10) - printf("lnkCalc_report(calc@%p)\n", clink); - printf("%*s'calc': \"%s\" = %.*g %s\n", indent, "", clink->expr, clink->prec, clink->val, clink->units ? clink->units : ""); @@ -388,9 +394,13 @@ static void lnkCalc_report(const jlink *pjlink, int level, int indent) printf("%*s Minor expression: \"%s\"\n", indent, "", clink->minor); - if (clink->tinp >= 0 && clink->tinp < clink->nArgs) - printf("%*s Timestamp input \"%c\"\n", indent, "", - clink->tinp + 'A'); + if (clink->tinp >= 0) { + char timeStr[40]; + epicsTimeToStrftime(timeStr, 40, "%Y-%m-%d %H:%M:%S.%09f", + &clink->time); + printf("%*s Timestamp input %c: %s\n", indent, "", + clink->tinp + 'A', timeStr); + } for (i = 0; i < clink->nArgs; i++) { struct link *plink = &clink->inp[i]; @@ -403,17 +413,20 @@ static void lnkCalc_report(const jlink *pjlink, int level, int indent) if (child) dbJLinkReport(child, level - 1, indent + 4); } + + if (clink->out.type == JSON_LINK) { + printf("%*s Output:\n", indent, ""); + + dbJLinkReport(clink->out.value.json.jlink, level - 1, indent + 4); + } } } -long lnkCalc_map_children(jlink *pjlink, jlink_map_fn rtn, void *ctx) +static long lnkCalc_map_children(jlink *pjlink, jlink_map_fn rtn, void *ctx) { calc_link *clink = CONTAINER(pjlink, struct calc_link, jlink); int i; - IFDEBUG(10) - printf("lnkCalc_map_children(calc@%p)\n", clink); - for (i = 0; i < clink->nArgs; i++) { struct link *child = &clink->inp[i]; long status = dbJLinkMapChildren(child, rtn, ctx); @@ -421,6 +434,10 @@ long lnkCalc_map_children(jlink *pjlink, jlink_map_fn rtn, void *ctx) if (status) return status; } + + if (clink->out.type == JSON_LINK) { + return dbJLinkMapChildren(&clink->out, rtn, ctx); + } return 0; } @@ -432,9 +449,6 @@ static void lnkCalc_open(struct link *plink) struct calc_link, jlink); int i; - IFDEBUG(10) - printf("lnkCalc_open(calc@%p)\n", clink); - for (i = 0; i < clink->nArgs; i++) { struct link *child = &clink->inp[i]; @@ -442,6 +456,10 @@ static void lnkCalc_open(struct link *plink) dbJLinkInit(child); dbLoadLink(child, DBR_DOUBLE, &clink->arg[i]); } + + if (clink->out.type == JSON_LINK) { + dbJLinkInit(&clink->out); + } } static void lnkCalc_remove(struct dbLocker *locker, struct link *plink) @@ -450,15 +468,16 @@ static void lnkCalc_remove(struct dbLocker *locker, struct link *plink) struct calc_link, jlink); int i; - IFDEBUG(10) - printf("lnkCalc_remove(calc@%p)\n", clink); - for (i = 0; i < clink->nArgs; i++) { struct link *child = &clink->inp[i]; dbRemoveLink(locker, child); } + if (clink->out.type == JSON_LINK) { + dbRemoveLink(locker, &clink->out); + } + free(clink->expr); free(clink->major); free(clink->minor); @@ -477,9 +496,6 @@ static int lnkCalc_isConn(const struct link *plink) int connected = 1; int i; - IFDEBUG(10) - printf("lnkCalc_isConn(calc@%p)\n", clink); - for (i = 0; i < clink->nArgs; i++) { struct link *child = &clink->inp[i]; @@ -488,37 +504,24 @@ static int lnkCalc_isConn(const struct link *plink) connected = 0; } + if (clink->out.type == JSON_LINK) { + struct link *child = &clink->out; + + if (dbLinkIsVolatile(child) && + !dbIsLinkConnected(child)) + connected = 0; + } + return connected; } static int lnkCalc_getDBFtype(const struct link *plink) { - calc_link *clink = CONTAINER(plink->value.json.jlink, - struct calc_link, jlink); - - IFDEBUG(10) { - calc_link *clink = CONTAINER(plink->value.json.jlink, - struct calc_link, jlink); - - printf("lnkCalc_getDBFtype(calc@%p)\n", clink); - } - return DBF_DOUBLE; } static long lnkCalc_getElements(const struct link *plink, long *nelements) { - calc_link *clink = CONTAINER(plink->value.json.jlink, - struct calc_link, jlink); - - IFDEBUG(10) { - calc_link *clink = CONTAINER(plink->value.json.jlink, - struct calc_link, jlink); - - printf("lnkCalc_getElements(calc@%p, (%ld))\n", - clink, *nelements); - } - *nelements = 1; return 0; } @@ -551,23 +554,22 @@ static long lnkCalc_getValue(struct link *plink, short dbrType, void *pbuffer, long status; FASTCONVERT conv = dbFastPutConvertRoutine[DBR_DOUBLE][dbrType]; - IFDEBUG(10) - printf("lnkCalc_getValue(calc@%p, %d, ...)\n", - clink, dbrType); - /* Any link errors will trigger a LINK/INVALID alarm in the child link */ for (i = 0; i < clink->nArgs; i++) { struct link *child = &clink->inp[i]; long nReq = 1; - if (i == clink->tinp && - dbLinkIsConstant(&prec->tsel) && - prec->tse == epicsTimeEventDeviceTime) { - struct lcvt vt = {&clink->arg[i], &prec->time}; + if (i == clink->tinp) { + struct lcvt vt = {&clink->arg[i], &clink->time}; status = dbLinkDoLocked(child, readLocked, &vt); if (status == S_db_noLSET) status = readLocked(child, &vt); + + if (dbLinkIsConstant(&prec->tsel) && + prec->tse == epicsTimeEventDeviceTime) { + prec->time = clink->time; + } } else dbGetLink(child, DBR_DOUBLE, &clink->arg[i], NULL, &nReq); @@ -599,7 +601,7 @@ static long lnkCalc_getValue(struct link *plink, short dbrType, void *pbuffer, } } - if (!status && clink->post_minor) { + if (!status && !clink->sevr && clink->post_minor) { double alval = clink->val; status = calcPerform(clink->arg, &alval, clink->post_minor); @@ -613,14 +615,79 @@ static long lnkCalc_getValue(struct link *plink, short dbrType, void *pbuffer, return status; } +static long lnkCalc_putValue(struct link *plink, short dbrType, + const void *pbuffer, long nRequest) +{ + calc_link *clink = CONTAINER(plink->value.json.jlink, + struct calc_link, jlink); + dbCommon *prec = plink->precord; + int i; + long status; + FASTCONVERT conv = dbFastGetConvertRoutine[dbrType][DBR_DOUBLE]; + + /* Any link errors will trigger a LINK/INVALID alarm in the child link */ + for (i = 0; i < clink->nArgs; i++) { + struct link *child = &clink->inp[i]; + long nReq = 1; + + if (i == clink->tinp) { + struct lcvt vt = {&clink->arg[i], &clink->time}; + + status = dbLinkDoLocked(child, readLocked, &vt); + if (status == S_db_noLSET) + status = readLocked(child, &vt); + + if (dbLinkIsConstant(&prec->tsel) && + prec->tse == epicsTimeEventDeviceTime) { + prec->time = clink->time; + } + } + else + dbGetLink(child, DBR_DOUBLE, &clink->arg[i], NULL, &nReq); + } + clink->stat = 0; + clink->sevr = 0; + + /* Get the value being output as VAL */ + status = conv(pbuffer, &clink->val, NULL); + + if (!status && clink->post_expr) + status = calcPerform(clink->arg, &clink->val, clink->post_expr); + + if (!status && clink->post_major) { + double alval = clink->val; + + status = calcPerform(clink->arg, &alval, clink->post_major); + if (!status && alval) { + clink->stat = LINK_ALARM; + clink->sevr = MAJOR_ALARM; + recGblSetSevr(prec, clink->stat, clink->sevr); + } + } + + if (!status && !clink->sevr && clink->post_minor) { + double alval = clink->val; + + status = calcPerform(clink->arg, &alval, clink->post_minor); + if (!status && alval) { + clink->stat = LINK_ALARM; + clink->sevr = MINOR_ALARM; + recGblSetSevr(prec, clink->stat, clink->sevr); + } + } + + if (!status) { + status = dbPutLink(&clink->out, DBR_DOUBLE, &clink->val, 1); + } + + return status; +} + static long lnkCalc_getPrecision(const struct link *plink, short *precision) { calc_link *clink = CONTAINER(plink->value.json.jlink, struct calc_link, jlink); - IFDEBUG(10) - printf("lnkCalc_getPrecision(calc@%p)\n", clink); - *precision = clink->prec; return 0; } @@ -630,9 +697,6 @@ static long lnkCalc_getUnits(const struct link *plink, char *units, int len) calc_link *clink = CONTAINER(plink->value.json.jlink, struct calc_link, jlink); - IFDEBUG(10) - printf("lnkCalc_getUnits(calc@%p)\n", clink); - if (clink->units) { strncpy(units, clink->units, --len); units[len] = '\0'; @@ -648,9 +712,6 @@ static long lnkCalc_getAlarm(const struct link *plink, epicsEnum16 *status, calc_link *clink = CONTAINER(plink->value.json.jlink, struct calc_link, jlink); - IFDEBUG(10) - printf("lnkCalc_getAlarm(calc@%p)\n", clink); - if (status) *status = clink->stat; if (severity) @@ -659,6 +720,19 @@ static long lnkCalc_getAlarm(const struct link *plink, epicsEnum16 *status, return 0; } +static long lnkCalc_getTimestamp(const struct link *plink, epicsTimeStamp *pstamp) +{ + calc_link *clink = CONTAINER(plink->value.json.jlink, + struct calc_link, jlink); + + if (clink->tinp >= 0) { + *pstamp = clink->time; + return 0; + } + + return -1; +} + static long doLocked(struct link *plink, dbLinkUserCallback rtn, void *priv) { return rtn(plink, priv); @@ -675,8 +749,8 @@ static lset lnkCalc_lset = { lnkCalc_getValue, NULL, NULL, NULL, lnkCalc_getPrecision, lnkCalc_getUnits, - lnkCalc_getAlarm, NULL, - NULL, NULL, + lnkCalc_getAlarm, lnkCalc_getTimestamp, + lnkCalc_putValue, NULL, NULL, doLocked }; @@ -686,6 +760,6 @@ static jlif lnkCalcIf = { lnkCalc_start_map, lnkCalc_map_key, lnkCalc_end_map, lnkCalc_start_array, lnkCalc_end_array, lnkCalc_end_child, lnkCalc_get_lset, - lnkCalc_report, lnkCalc_map_children + lnkCalc_report, lnkCalc_map_children, NULL }; epicsExportAddress(jlif, lnkCalcIf); diff --git a/modules/database/src/std/link/lnkConst.c b/modules/database/src/std/link/lnkConst.c index c2eb032c1..cb948fcb3 100644 --- a/modules/database/src/std/link/lnkConst.c +++ b/modules/database/src/std/link/lnkConst.c @@ -2,7 +2,7 @@ * Copyright (c) 2016 UChicago Argonne LLC, as Operator of Argonne * 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. \*************************************************************************/ /* lnkConst.c */ @@ -22,8 +22,6 @@ #include "epicsExport.h" -#define IFDEBUG(n) if (clink->jlink.debug) - typedef long (*FASTCONVERT)(); typedef struct const_link { @@ -48,18 +46,23 @@ static lset lnkConst_lset; static jlink* lnkConst_alloc(short dbfType) { - const_link *clink = calloc(1, sizeof(*clink)); + const_link *clink; - IFDEBUG(10) - printf("lnkConst_alloc()\n"); + if (dbfType != DBF_INLINK) { + errlogPrintf("lnkConst: Only works with input links\n"); + return NULL; + } + + clink = calloc(1, sizeof(*clink)); + if (!clink) { + errlogPrintf("lnkConst: calloc() failed.\n"); + return NULL; + } clink->type = s0; clink->nElems = 0; clink->value.pmem = NULL; - IFDEBUG(10) - printf("lnkConst_alloc -> const@%p\n", clink); - return &clink->jlink; } @@ -67,9 +70,6 @@ static void lnkConst_free(jlink *pjlink) { const_link *clink = CONTAINER(pjlink, const_link, jlink); - IFDEBUG(10) - printf("lnkConst_free(const@%p) type=%d\n", pjlink, clink->type); - switch (clink->type) { int i; case ac40: @@ -95,16 +95,13 @@ static jlif_result lnkConst_integer(jlink *pjlink, long long num) const_link *clink = CONTAINER(pjlink, const_link, jlink); int newElems = clink->nElems + 1; - IFDEBUG(10) - printf("lnkConst_integer(const@%p, %lld)\n", pjlink, num); - switch (clink->type) { void *buf; case s0: clink->type = si64; clink->value.scalar_integer = num; - IFDEBUG(12) + if (pjlink->debug) printf(" si64 := %lld\n", num); break; @@ -118,7 +115,7 @@ static jlif_result lnkConst_integer(jlink *pjlink, long long num) clink->value.pmem = buf; clink->value.pintegers[clink->nElems] = num; - IFDEBUG(12) + if (pjlink->debug) printf(" ai64 += %lld\n", num); break; @@ -129,7 +126,7 @@ static jlif_result lnkConst_integer(jlink *pjlink, long long num) clink->value.pmem = buf; clink->value.pdoubles[clink->nElems] = num; - IFDEBUG(12) + if (pjlink->debug) printf(" af64 += %lld\n", num); break; @@ -146,10 +143,6 @@ static jlif_result lnkConst_integer(jlink *pjlink, long long num) static jlif_result lnkConst_boolean(jlink *pjlink, int val) { - const_link *clink = CONTAINER(pjlink, const_link, jlink); - IFDEBUG(10) - printf("lnkConst_boolean(const@%p, %d)\n", pjlink, val); - return lnkConst_integer(pjlink, val); } @@ -158,9 +151,6 @@ static jlif_result lnkConst_double(jlink *pjlink, double num) const_link *clink = CONTAINER(pjlink, const_link, jlink); int newElems = clink->nElems + 1; - IFDEBUG(10) - printf("lnkConst_double(const@%p, %g)\n", pjlink, num); - switch (clink->type) { epicsFloat64 *f64buf; int i; @@ -212,9 +202,6 @@ static jlif_result lnkConst_string(jlink *pjlink, const char *val, size_t len) const_link *clink = CONTAINER(pjlink, const_link, jlink); int newElems = clink->nElems + 1; - IFDEBUG(10) - printf("lnkConst_string(const@%p, \"%.*s\")\n", clink, (int) len, val); - switch (clink->type) { char **vec, *str; @@ -262,9 +249,6 @@ static jlif_result lnkConst_start_array(jlink *pjlink) { const_link *clink = CONTAINER(pjlink, const_link, jlink); - IFDEBUG(10) - printf("lnkConst_start_array(const@%p)\n", pjlink); - if (clink->type != s0) { errlogPrintf("lnkConst: Embedded array value\n"); return jlif_stop; @@ -276,21 +260,11 @@ static jlif_result lnkConst_start_array(jlink *pjlink) static jlif_result lnkConst_end_array(jlink *pjlink) { - const_link *clink = CONTAINER(pjlink, const_link, jlink); - - IFDEBUG(10) - printf("lnkConst_end_array(const@%p)\n", pjlink); - return jlif_continue; } static struct lset* lnkConst_get_lset(const jlink *pjlink) { - const_link *clink = CONTAINER(pjlink, const_link, jlink); - - IFDEBUG(10) - printf("lnkConst_get_lset(const@%p)\n", pjlink); - return &lnkConst_lset; } @@ -318,9 +292,6 @@ static void lnkConst_report(const jlink *pjlink, int level, int indent) }; const char * const dtype = type_names[clink->type & 3]; - IFDEBUG(10) - printf("lnkConst_report(const@%p)\n", clink); - if (clink->type > a0) { const char * const plural = clink->nElems > 1 ? "s" : ""; @@ -382,11 +353,6 @@ static void lnkConst_report(const jlink *pjlink, int level, int indent) static void lnkConst_remove(struct dbLocker *locker, struct link *plink) { - const_link *clink = CONTAINER(plink->value.json.jlink, const_link, jlink); - - IFDEBUG(10) - printf("lnkConst_remove(const@%p)\n", clink); - lnkConst_free(plink->value.json.jlink); } @@ -395,55 +361,51 @@ static long lnkConst_loadScalar(struct link *plink, short dbrType, void *pbuffer const_link *clink = CONTAINER(plink->value.json.jlink, const_link, jlink); long status; - IFDEBUG(10) - printf("lnkConst_loadScalar(const@%p, %d, %p)\n", - clink, dbrType, pbuffer); - switch (clink->type) { case si64: - IFDEBUG(12) + if (clink->jlink.debug) printf(" si64 %lld\n", clink->value.scalar_integer); status = dbFastPutConvertRoutine[DBF_INT64][dbrType] (&clink->value.scalar_integer, pbuffer, NULL); break; case sf64: - IFDEBUG(12) + if (clink->jlink.debug) printf(" sf64 %g\n", clink->value.scalar_double); status = dbFastPutConvertRoutine[DBF_DOUBLE][dbrType] (&clink->value.scalar_double, pbuffer, NULL); break; case sc40: - IFDEBUG(12) + if (clink->jlink.debug) printf(" sc40 '%s'\n", clink->value.scalar_string); status = dbFastPutConvertRoutine[DBF_STRING][dbrType] (clink->value.scalar_string, pbuffer, NULL); break; case ai64: - IFDEBUG(12) + if (clink->jlink.debug) printf(" ai64 [%lld, ...]\n", clink->value.pintegers[0]); status = dbFastPutConvertRoutine[DBF_INT64][dbrType] (clink->value.pintegers, pbuffer, NULL); break; case af64: - IFDEBUG(12) + if (clink->jlink.debug) printf(" af64 [%g, ...]\n", clink->value.pdoubles[0]); status = dbFastPutConvertRoutine[DBF_DOUBLE][dbrType] (clink->value.pdoubles, pbuffer, NULL); break; case ac40: - IFDEBUG(12) + if (clink->jlink.debug) printf(" ac40 ['%s', ...]\n", clink->value.pstrings[0]); status = dbFastPutConvertRoutine[DBF_STRING][dbrType] (clink->value.pstrings[0], pbuffer, NULL); break; default: - IFDEBUG(12) + if (clink->jlink.debug) printf(" Bad type %d\n", clink->type); status = S_db_badField; break; @@ -458,27 +420,23 @@ static long lnkConst_loadLS(struct link *plink, char *pbuffer, epicsUInt32 size, const_link *clink = CONTAINER(plink->value.json.jlink, const_link, jlink); const char *pstr; - IFDEBUG(10) - printf("lnkConst_loadLS(const@%p, %p, %d, %d)\n", - clink, pbuffer, size, *plen); - if(!size) return 0; switch (clink->type) { case sc40: - IFDEBUG(12) + if (clink->jlink.debug) printf(" sc40 '%s'\n", clink->value.scalar_string); pstr = clink->value.scalar_string; break; case ac40: - IFDEBUG(12) + if (clink->jlink.debug) printf(" ac40 ['%s', ...]\n", clink->value.pstrings[0]); pstr = clink->value.pstrings[0]; break; default: - IFDEBUG(12) + if (clink->jlink.debug) printf(" Bad type %d\n", clink->type); return S_db_badField; } @@ -499,10 +457,6 @@ static long lnkConst_loadArray(struct link *plink, short dbrType, void *pbuffer, FASTCONVERT conv; long status; - IFDEBUG(10) - printf("lnkConst_loadArray(const@%p, %d, %p, (%ld))\n", - clink, dbrType, pbuffer, *pnReq); - if (nElems > *pnReq) nElems = *pnReq; @@ -510,28 +464,28 @@ static long lnkConst_loadArray(struct link *plink, short dbrType, void *pbuffer, int i; case si64: - IFDEBUG(12) + if (clink->jlink.debug) printf(" si64 %lld\n", clink->value.scalar_integer); status = dbFastPutConvertRoutine[DBF_INT64][dbrType] (&clink->value.scalar_integer, pdest, NULL); break; case sf64: - IFDEBUG(12) + if (clink->jlink.debug) printf(" sf64 %g\n", clink->value.scalar_double); status = dbFastPutConvertRoutine[DBF_DOUBLE][dbrType] (&clink->value.scalar_double, pdest, NULL); break; case sc40: - IFDEBUG(12) + if (clink->jlink.debug) printf(" sc40 '%s'\n", clink->value.scalar_string); status = dbFastPutConvertRoutine[DBF_STRING][dbrType] (clink->value.scalar_string, pbuffer, NULL); break; case ai64: - IFDEBUG(12) + if (clink->jlink.debug) printf(" ai64 [%lld, ...]\n", clink->value.pintegers[0]); conv = dbFastPutConvertRoutine[DBF_INT64][dbrType]; for (i = 0; i < nElems; i++) { @@ -542,7 +496,7 @@ static long lnkConst_loadArray(struct link *plink, short dbrType, void *pbuffer, break; case af64: - IFDEBUG(12) + if (clink->jlink.debug) printf(" af64 [%g, ...]\n", clink->value.pdoubles[0]); conv = dbFastPutConvertRoutine[DBF_DOUBLE][dbrType]; for (i = 0; i < nElems; i++) { @@ -553,7 +507,7 @@ static long lnkConst_loadArray(struct link *plink, short dbrType, void *pbuffer, break; case ac40: - IFDEBUG(12) + if (clink->jlink.debug) printf(" ac40 ['%s', ...]\n", clink->value.pstrings[0]); conv = dbFastPutConvertRoutine[DBF_STRING][dbrType]; for (i = 0; i < nElems; i++) { @@ -564,7 +518,7 @@ static long lnkConst_loadArray(struct link *plink, short dbrType, void *pbuffer, break; default: - IFDEBUG(12) + if (clink->jlink.debug) printf(" Bad type %d\n", clink->type); status = S_db_badField; } @@ -574,12 +528,6 @@ static long lnkConst_loadArray(struct link *plink, short dbrType, void *pbuffer, static long lnkConst_getNelements(const struct link *plink, long *nelements) { - const_link *clink = CONTAINER(plink->value.json.jlink, const_link, jlink); - - IFDEBUG(10) - printf("lnkConst_getNelements(const@%p, (%ld))\n", - plink->value.json.jlink, *nelements); - *nelements = 0; return 0; } @@ -587,13 +535,6 @@ static long lnkConst_getNelements(const struct link *plink, long *nelements) static long lnkConst_getValue(struct link *plink, short dbrType, void *pbuffer, long *pnRequest) { - const_link *clink = CONTAINER(plink->value.json.jlink, const_link, jlink); - - IFDEBUG(10) - printf("lnkConst_getValue(const@%p, %d, %p, ... (%ld))\n", - plink->value.json.jlink, dbrType, pbuffer, - pnRequest ? *pnRequest : 0); - if (pnRequest) *pnRequest = 0; return 0; @@ -626,7 +567,6 @@ static jlif lnkConstIf = { NULL, NULL, NULL, lnkConst_start_array, lnkConst_end_array, NULL, lnkConst_get_lset, - lnkConst_report, NULL + lnkConst_report, NULL, NULL }; epicsExportAddress(jlif, lnkConstIf); - diff --git a/modules/database/src/std/link/lnkDebug.c b/modules/database/src/std/link/lnkDebug.c new file mode 100644 index 000000000..bd8f84e5a --- /dev/null +++ b/modules/database/src/std/link/lnkDebug.c @@ -0,0 +1,1045 @@ +/*************************************************************************\ +* Copyright (c) 2018 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* lnkDebug.c */ + +/* Usage + * {debug:{...:...}} + */ + +#include +#include +#include + +#include "alarm.h" +#include "dbDefs.h" +#include "dbAccessDefs.h" +#include "dbLink.h" +#include "dbJLink.h" +#include "dbStaticLib.h" +#include "errlog.h" +#include "epicsTime.h" + +#include "epicsExport.h" + +/* This is for debugging the debug link-type */ +int lnkDebug_debug; +epicsExportAddress(int, lnkDebug_debug); + +#define IFDEBUG(n) if (lnkDebug_debug >= (n)) + +typedef struct debug_link { + jlink jlink; /* embedded object */ + short dbfType; + unsigned trace:1; + const jlif *child_jlif; + const lset *child_lset; + jlif jlif; + lset lset; + struct link child_link; +} debug_link; + + +/********************* Delegating jlif Routines *********************/ + +static void delegate_free(jlink *pjlink) +{ + debug_link *dlink = CONTAINER(pjlink->parent, struct debug_link, jlink); + const jlif *pif = dlink->child_jlif; + struct link *plink = &dlink->child_link; + + if (dlink->trace) + printf("Link trace: Calling %s::free_jlink(%p)\n", + pif->name, pjlink); + + pif->free_jlink(pjlink); + plink->type = 0; + plink->value.json.jlink = NULL; + + if (dlink->trace) + printf("Link trace: %s::free_jlink(%p) returned\n", + pif->name, pjlink); +} + +static jlif_result delegate_null(jlink *pjlink) +{ + debug_link *dlink = CONTAINER(pjlink->parent, struct debug_link, jlink); + const jlif *pif = dlink->child_jlif; + jlif_result res; + + if (dlink->trace) + printf("Link trace: Calling %s::parse_null(%p)\n", + pif->name, pjlink); + + res = pif->parse_null(pjlink); + + if (dlink->trace) + printf("Link trace: %s::parse_null(%p) returned %s\n", + pif->name, pjlink, jlif_result_name[res]); + + return res; +} + +static jlif_result delegate_boolean(jlink *pjlink, int val) +{ + debug_link *dlink = CONTAINER(pjlink->parent, struct debug_link, jlink); + const jlif *pif = dlink->child_jlif; + jlif_result res; + + if (dlink->trace) + printf("Link trace: Calling %s::parse_boolean(%p, %d)\n", + pif->name, pjlink, val); + + res = pif->parse_boolean(pjlink, val); + + if (dlink->trace) + printf("Link trace: %s::parse_boolean(%p) returned %s\n", + pif->name, pjlink, jlif_result_name[res]); + + return res; +} + +static jlif_result delegate_integer(jlink *pjlink, long long num) +{ + debug_link *dlink = CONTAINER(pjlink->parent, struct debug_link, jlink); + const jlif *pif = dlink->child_jlif; + jlif_result res; + + if (dlink->trace) + printf("Link trace: Calling %s::parse_integer(%p, %lld)\n", + pif->name, pjlink, num); + + res = pif->parse_integer(pjlink, num); + + if (dlink->trace) + printf("Link trace: %s::parse_integer(%p) returned %s\n", + pif->name, pjlink, jlif_result_name[res]); + + return res; +} + +static jlif_result delegate_double(jlink *pjlink, double num) +{ + debug_link *dlink = CONTAINER(pjlink->parent, struct debug_link, jlink); + const jlif *pif = dlink->child_jlif; + jlif_result res; + + if (dlink->trace) + printf("Link trace: Calling %s::parse_double(%p, %g)\n", + pif->name, pjlink, num); + + res = pif->parse_double(pjlink, num); + + if (dlink->trace) + printf("Link trace: %s::parse_double(%p) returned %s\n", + pif->name, pjlink, jlif_result_name[res]); + + return res; +} + +static jlif_result delegate_string(jlink *pjlink, const char *val, size_t len) +{ + debug_link *dlink = CONTAINER(pjlink->parent, struct debug_link, jlink); + const jlif *pif = dlink->child_jlif; + jlif_result res; + + if (dlink->trace) + printf("Link trace: Calling %s::parse_string(%p, \"%.*s\")\n", + pif->name, pjlink, (int) len, val); + + res = pif->parse_string(pjlink, val, len); + + if (dlink->trace) + printf("Link trace: %s::parse_string(%p) returned %s\n", + pif->name, pjlink, jlif_result_name[res]); + + return res; +} + +static jlif_key_result delegate_start_map(jlink *pjlink) +{ + debug_link *dlink = CONTAINER(pjlink->parent, struct debug_link, jlink); + const jlif *pif = dlink->child_jlif; + jlif_key_result res; + + if (dlink->trace) + printf("Link trace: Calling %s::parse_start_map(%p)\n", + pif->name, pjlink); + + res = pif->parse_start_map(pjlink); + + if (dlink->trace) + printf("Link trace: %s::parse_start_map(%p) returned %s\n", + pif->name, pjlink, jlif_key_result_name[res]); + + return res; +} + +static jlif_result delegate_map_key(jlink *pjlink, const char *key, size_t len) +{ + debug_link *dlink = CONTAINER(pjlink->parent, struct debug_link, jlink); + const jlif *pif = dlink->child_jlif; + jlif_result res; + + if (dlink->trace) + printf("Link trace: Calling %s::parse_map_key(%p, \"%.*s\")\n", + pif->name, pjlink, (int) len, key); + + res = pif->parse_map_key(pjlink, key, len); + + if (dlink->trace) + printf("Link trace: %s::parse_map_key(%p) returned %s\n", + pif->name, pjlink, jlif_result_name[res]); + + return res; +} + +static jlif_result delegate_end_map(jlink *pjlink) +{ + debug_link *dlink = CONTAINER(pjlink->parent, struct debug_link, jlink); + const jlif *pif = dlink->child_jlif; + jlif_result res; + + if (dlink->trace) + printf("Link trace: Calling %s::parse_end_map(%p)\n", + pif->name, pjlink); + + res = pif->parse_end_map(pjlink); + + if (dlink->trace) + printf("Link trace: %s::parse_end_map(%p) returned %s\n", + pif->name, pjlink, jlif_result_name[res]); + + return res; +} + +static jlif_result delegate_start_array(jlink *pjlink) +{ + debug_link *dlink = CONTAINER(pjlink->parent, struct debug_link, jlink); + const jlif *pif = dlink->child_jlif; + jlif_result res; + + if (dlink->trace) + printf("Link trace: Calling %s::parse_(%p)\n", + pif->name, pjlink); + + res = pif->parse_start_array(pjlink); + + if (dlink->trace) + printf("Link trace: %s::parse_start_array(%p) returned %s\n", + pif->name, pjlink, jlif_result_name[res]); + + return res; +} + +static jlif_result delegate_end_array(jlink *pjlink) +{ + debug_link *dlink = CONTAINER(pjlink->parent, struct debug_link, jlink); + const jlif *pif = dlink->child_jlif; + jlif_result res; + + if (dlink->trace) + printf("Link trace: Calling %s::parse_end_array(%p)\n", + pif->name, pjlink); + + res = pif->parse_end_array(pjlink); + + if (dlink->trace) + printf("Link trace: %s::parse_end_array(%p) returned %s\n", + pif->name, pjlink, jlif_result_name[res]); + + return res; +} + +static void delegate_start_child(jlink *pjlink, jlink *child) +{ + debug_link *dlink = CONTAINER(pjlink->parent, struct debug_link, jlink); + const jlif *pif = dlink->child_jlif; + + if (dlink->trace) + printf("Link trace: Calling %s::start_child(%p, %p)\n", + pif->name, pjlink, child); + + pif->start_child(pjlink, child); + + if (dlink->trace) + printf("Link trace: %s::start_child(%p) returned\n", + pif->name, pjlink); +} + +static void delegate_end_child(jlink *pjlink, jlink *child) +{ + debug_link *dlink = CONTAINER(pjlink->parent, struct debug_link, jlink); + const jlif *pif = dlink->child_jlif; + + if (dlink->trace) + printf("Link trace: Calling %s::end_child(%p, %p)\n", + pif->name, pjlink, child); + + pif->end_child(pjlink, child); + + if (dlink->trace) + printf("Link trace: %s::end_child(%p) returned\n", + pif->name, pjlink); +} + +static struct lset* delegate_get_lset(const jlink *pjlink) +{ + debug_link *dlink = CONTAINER(pjlink->parent, struct debug_link, jlink); + + if (dlink->trace) + printf("Link trace: NOT calling %s::get_lset(%p)\n", + dlink->child_jlif->name, pjlink); + + return &dlink->lset; +} + +static void delegate_report(const jlink *pjlink, int level, int indent) +{ + debug_link *dlink = CONTAINER(pjlink->parent, struct debug_link, jlink); + const jlif *pif = dlink->child_jlif; + + if (dlink->trace) + printf("Link trace: Calling %s::report(%p, %d, %d)\n", + pif->name, pjlink, level, indent); + + pif->report(pjlink, level, indent); + + if (dlink->trace) + printf("Link trace: %s::report(%p) returned\n", + pif->name, pjlink); +} + +static long delegate_map_children(jlink *pjlink, jlink_map_fn rtn, void *ctx) +{ + debug_link *dlink = CONTAINER(pjlink->parent, struct debug_link, jlink); + const jlif *pif = dlink->child_jlif; + long res; + + if (dlink->trace) + printf("Link trace: Calling %s::map_children(%p, %p, %p)\n", + pif->name, pjlink, rtn, ctx); + + res = pif->map_children(pjlink, rtn, ctx); + + if (dlink->trace) + printf("Link trace: %s::map_children(%p) returned %ld\n", + pif->name, pjlink, res); + + return res; +} + + +/********************* Delegating lset Routines *********************/ + +static void delegate_openLink(struct link *plink) +{ + debug_link *dlink = CONTAINER(plink->value.json.jlink, + struct debug_link, jlink); + struct link *clink = &dlink->child_link; + const lset *clset = dlink->child_lset; + + if (dlink->trace) + printf("Link trace: Calling %s::openLink(%p = jlink %p)\n", + dlink->child_jlif->name, clink, clink->value.json.jlink); + + clink->precord = plink->precord; + clset->openLink(clink); + + if (dlink->trace) + printf("Link trace: %s::openLink(%p) returned\n", + dlink->child_jlif->name, clink); +} + +static void delegate_removeLink(struct dbLocker *locker, struct link *plink) +{ + debug_link *dlink = CONTAINER(plink->value.json.jlink, + struct debug_link, jlink); + struct link *clink = &dlink->child_link; + const lset *clset = dlink->child_lset; + + if (dlink->trace) + printf("Link trace: Calling %s::removeLink(%p, %p)\n", + dlink->child_jlif->name, locker, clink); + + clset->removeLink(locker, clink); + + if (dlink->trace) + printf("Link trace: %s::removeLink(%p) returned\n", + dlink->child_jlif->name, clink); +} + +static long delegate_loadScalar(struct link *plink, short dbrType, void *pbuffer) +{ + debug_link *dlink = CONTAINER(plink->value.json.jlink, + struct debug_link, jlink); + struct link *clink = &dlink->child_link; + const lset *clset = dlink->child_lset; + long res; + + if (dlink->trace) + printf("Link trace: Calling %s::loadScalar(%p, %s, %p)\n", + dlink->child_jlif->name, clink, + dbGetFieldTypeString(dbrType), pbuffer); + + res = clset->loadScalar(clink, dbrType, pbuffer); + + if (dlink->trace) + printf("Link trace: %s::loadScalar(%p) returned %ld (0x%lx)\n", + dlink->child_jlif->name, clink, res, res); + + return res; +} + +static long delegate_loadLS(struct link *plink, char *pbuffer, epicsUInt32 size, + epicsUInt32 *plen) +{ + debug_link *dlink = CONTAINER(plink->value.json.jlink, + struct debug_link, jlink); + struct link *clink = &dlink->child_link; + const lset *clset = dlink->child_lset; + long res; + + if (dlink->trace) + printf("Link trace: Calling %s::loadLS(%p, %p, %u)\n", + dlink->child_jlif->name, clink, pbuffer, size); + + res = clset->loadLS(clink, pbuffer, size, plen); + + if (dlink->trace) { + printf("Link trace: %s::loadLS(%p) returned %ld (0x%lx)\n", + dlink->child_jlif->name, clink, res, res); + if (res == 0) + printf(" Loaded: %u byte(s) \"%s\"\n", *plen, pbuffer); + } + + return res; +} + +static long delegate_loadArray(struct link *plink, short dbrType, void *pbuffer, + long *pnRequest) +{ + debug_link *dlink = CONTAINER(plink->value.json.jlink, + struct debug_link, jlink); + struct link *clink = &dlink->child_link; + const lset *clset = dlink->child_lset; + long res; + + if (dlink->trace) + printf("Link trace: Calling %s::loadArray(%p, %s, %p, %ld)\n", + dlink->child_jlif->name, clink, + dbGetFieldTypeString(dbrType), pbuffer, pnRequest ? *pnRequest : 0l); + + res = clset->loadArray(clink, dbrType, pbuffer, pnRequest); + + if (dlink->trace) { + printf("Link trace: %s::loadArray(%p) returned %ld (0x%lx)\n", + dlink->child_jlif->name, clink, res, res); + if (res == 0) + printf(" Loaded: %ld element(s)\n", pnRequest ? *pnRequest : 1l); + } + + return res; +} + +static int delegate_isConnected(const struct link *plink) +{ + debug_link *dlink = CONTAINER(plink->value.json.jlink, + struct debug_link, jlink); + struct link *clink = &dlink->child_link; + const lset *clset = dlink->child_lset; + int res; + + if (dlink->trace) + printf("Link trace: Calling %s::isConnected(%p)\n", + dlink->child_jlif->name, clink); + + res = clset->isConnected(clink); + + if (dlink->trace) + printf("Link trace: %s::isConnected(%p) returned %d (%s)\n", + dlink->child_jlif->name, clink, res, + res == 0 ? "No" : res == 1 ? "Yes" : "Bad value"); + + return res; +} + +static int delegate_getDBFtype(const struct link *plink) +{ + debug_link *dlink = CONTAINER(plink->value.json.jlink, + struct debug_link, jlink); + struct link *clink = &dlink->child_link; + const lset *clset = dlink->child_lset; + int res; + + if (dlink->trace) + printf("Link trace: Calling %s::getDBFtype(%p)\n", + dlink->child_jlif->name, clink); + + res = clset->getDBFtype(clink); + + if (dlink->trace) + printf("Link trace: %s::getDBFtype(%p) returned %d (%s)\n", + dlink->child_jlif->name, clink, res, + res == -1 ? "Link disconnected" : dbGetFieldTypeString(res)); + + return res; +} + +static long delegate_getElements(const struct link *plink, long *pnElements) +{ + debug_link *dlink = CONTAINER(plink->value.json.jlink, + struct debug_link, jlink); + struct link *clink = &dlink->child_link; + const lset *clset = dlink->child_lset; + long res; + + if (dlink->trace) + printf("Link trace: Calling %s::getElements(%p)\n", + dlink->child_jlif->name, clink); + + res = clset->getElements(clink, pnElements); + + if (dlink->trace) { + printf("Link trace: %s::getElements(%p) returned %ld (0x%lx)\n", + dlink->child_jlif->name, clink, res, res); + if (res == 0) + printf(" Result: %ld element(s)\n", *pnElements); + } + + return res; +} + +static long delegate_getValue(struct link *plink, short dbrType, void *pbuffer, + long *pnRequest) +{ + debug_link *dlink = CONTAINER(plink->value.json.jlink, + struct debug_link, jlink); + struct link *clink = &dlink->child_link; + const lset *clset = dlink->child_lset; + long res; + + if (dlink->trace) + printf("Link trace: Calling %s::getValue(%p, %s, %p, %ld)\n", + dlink->child_jlif->name, clink, + dbGetFieldTypeString(dbrType), pbuffer, pnRequest ? *pnRequest : 0l); + + res = clset->getValue(clink, dbrType, pbuffer, pnRequest); + + if (dlink->trace) { + printf("Link trace: %s::getValue(%p) returned %ld (0x%lx)\n", + dlink->child_jlif->name, clink, res, res); + if (res == 0) + printf(" Got: %ld element(s)\n", pnRequest ? *pnRequest : 1l); + } + + return res; +} + +static long delegate_getControlLimits(const struct link *plink, + double *lo, double *hi) +{ + debug_link *dlink = CONTAINER(plink->value.json.jlink, + struct debug_link, jlink); + struct link *clink = &dlink->child_link; + const lset *clset = dlink->child_lset; + long res; + + if (dlink->trace) + printf("Link trace: Calling %s::getControlLimits(%p)\n", + dlink->child_jlif->name, clink); + + res = clset->getControlLimits(clink, lo, hi); + + if (dlink->trace) { + printf("Link trace: %s::getControlLimits(%p) returned %ld (0x%lx)\n", + dlink->child_jlif->name, clink, res, res); + if (res == 0) + printf(" Got: Lo = %g, Hi = %g\n", *lo, *hi); + } + + return res; +} + +static long delegate_getGraphicLimits(const struct link *plink, + double *lo, double *hi) +{ + debug_link *dlink = CONTAINER(plink->value.json.jlink, + struct debug_link, jlink); + struct link *clink = &dlink->child_link; + const lset *clset = dlink->child_lset; + long res; + + if (dlink->trace) + printf("Link trace: Calling %s::getGraphicLimits(%p)\n", + dlink->child_jlif->name, clink); + + res = clset->getGraphicLimits(clink, lo, hi); + + if (dlink->trace) { + printf("Link trace: %s::getGraphicLimits(%p) returned %ld (0x%lx)\n", + dlink->child_jlif->name, clink, res, res); + if (res == 0) + printf(" Got: Lo = %g, Hi = %g\n", *lo, *hi); + } + + return res; +} + +static long delegate_getAlarmLimits(const struct link *plink, + double *lolo, double *lo, double *hi, double *hihi) +{ + debug_link *dlink = CONTAINER(plink->value.json.jlink, + struct debug_link, jlink); + struct link *clink = &dlink->child_link; + const lset *clset = dlink->child_lset; + long res; + + if (dlink->trace) + printf("Link trace: Calling %s::getAlarmLimits(%p)\n", + dlink->child_jlif->name, clink); + + res = clset->getAlarmLimits(clink, lolo, lo, hi, hihi); + + if (dlink->trace) { + printf("Link trace: %s::getAlarmLimits(%p) returned %ld (0x%lx)\n", + dlink->child_jlif->name, clink, res, res); + if (res == 0) + printf(" Got: Lolo = %g, Lo = %g, Hi = %g, Hihi = %g\n", + *lolo, *lo, *hi, *hihi); + } + + return res; +} + +static long delegate_getPrecision(const struct link *plink, short *precision) +{ + debug_link *dlink = CONTAINER(plink->value.json.jlink, + struct debug_link, jlink); + struct link *clink = &dlink->child_link; + const lset *clset = dlink->child_lset; + long res; + + if (dlink->trace) + printf("Link trace: Calling %s::getPrecision(%p)\n", + dlink->child_jlif->name, clink); + + res = clset->getPrecision(clink, precision); + + if (dlink->trace) { + printf("Link trace: %s::getPrecision(%p) returned %ld (0x%lx)\n", + dlink->child_jlif->name, clink, res, res); + if (res == 0) + printf(" Got: prec = %d\n", *precision); + } + + return res; +} + +static long delegate_getUnits(const struct link *plink, char *units, int unitsSize) +{ + debug_link *dlink = CONTAINER(plink->value.json.jlink, + struct debug_link, jlink); + struct link *clink = &dlink->child_link; + const lset *clset = dlink->child_lset; + long res; + + if (dlink->trace) + printf("Link trace: Calling %s::getUnits(%p)\n", + dlink->child_jlif->name, clink); + + res = clset->getUnits(clink, units, unitsSize); + + if (dlink->trace) { + printf("Link trace: %s::getUnits(%p) returned %ld (0x%lx)\n", + dlink->child_jlif->name, clink, res, res); + if (res == 0) + printf(" Got: Units = '%s'\n", units); + } + + return res; +} + +static long delegate_getAlarm(const struct link *plink, epicsEnum16 *stat, + epicsEnum16 *sevr) +{ + debug_link *dlink = CONTAINER(plink->value.json.jlink, + struct debug_link, jlink); + struct link *clink = &dlink->child_link; + const lset *clset = dlink->child_lset; + long res; + + if (dlink->trace) + printf("Link trace: Calling %s::getAlarm(%p)\n", + dlink->child_jlif->name, clink); + + res = clset->getAlarm(clink, stat, sevr); + + if (dlink->trace) { + printf("Link trace: %s::getAlarm(%p) returned %ld (0x%lx)\n", + dlink->child_jlif->name, clink, res, res); + if (res == 0) + printf(" Got:%s%s%s%s\n", + stat ? " Status = " : "", + stat && (*stat < ALARM_NSTATUS) ? + epicsAlarmConditionStrings[*stat] : "Bad-status", + sevr ? " Severity = " : "", + sevr && (*sevr < ALARM_NSEV) ? + epicsAlarmSeverityStrings[*sevr] : "Bad-severity" + ); + } + + return res; +} + +static long delegate_getTimeStamp(const struct link *plink, epicsTimeStamp *pstamp) +{ + debug_link *dlink = CONTAINER(plink->value.json.jlink, + struct debug_link, jlink); + struct link *clink = &dlink->child_link; + const lset *clset = dlink->child_lset; + long res; + + if (dlink->trace) + printf("Link trace: Calling %s::getTimeStamp(%p)\n", + dlink->child_jlif->name, clink); + + res = clset->getTimeStamp(clink, pstamp); + + if (dlink->trace) { + printf("Link trace: %s::getTimeStamp(%p) returned %ld (0x%lx)\n", + dlink->child_jlif->name, clink, res, res); + if (res == 0) { + char timeStr[32]; + + epicsTimeToStrftime(timeStr, sizeof(timeStr), + "%Y-%m-%d %H:%M:%S.%09f", pstamp); + printf(" Got: Timestamp = '%s'\n", timeStr); + } + } + + return res; +} + +static long delegate_putValue(struct link *plink, short dbrType, + const void *pbuffer, long nRequest) +{ + debug_link *dlink = CONTAINER(plink->value.json.jlink, + struct debug_link, jlink); + struct link *clink = &dlink->child_link; + const lset *clset = dlink->child_lset; + long res; + + if (dlink->trace) + printf("Link trace: Calling %s::putValue(%p, %s, %p, %ld)\n", + dlink->child_jlif->name, clink, + dbGetFieldTypeString(dbrType), pbuffer, nRequest); + + res = clset->putValue(clink, dbrType, pbuffer, nRequest); + + if (dlink->trace) + printf("Link trace: %s::putValue(%p) returned %ld (0x%lx)\n", + dlink->child_jlif->name, clink, res, res); + + return res; +} + +static long delegate_putAsync(struct link *plink, short dbrType, + const void *pbuffer, long nRequest) +{ + debug_link *dlink = CONTAINER(plink->value.json.jlink, + struct debug_link, jlink); + struct link *clink = &dlink->child_link; + const lset *clset = dlink->child_lset; + long res; + + if (dlink->trace) + printf("Link trace: Calling %s::putAsync(%p, %s, %p, %ld)\n", + dlink->child_jlif->name, clink, + dbGetFieldTypeString(dbrType), pbuffer, nRequest); + + res = clset->putAsync(clink, dbrType, pbuffer, nRequest); + + if (dlink->trace) + printf("Link trace: %s::putAsync(%p) returned %ld (0x%lx)\n", + dlink->child_jlif->name, clink, res, res); + + return res; +} + +static void delegate_scanForward(struct link *plink) +{ + debug_link *dlink = CONTAINER(plink->value.json.jlink, + struct debug_link, jlink); + struct link *clink = &dlink->child_link; + const lset *clset = dlink->child_lset; + + if (dlink->trace) + printf("Link trace: Calling %s::scanForward(%p)\n", + dlink->child_jlif->name, clink); + + clset->scanForward(clink); + + if (dlink->trace) + printf("Link trace: %s::scanForward(%p) returned\n", + dlink->child_jlif->name, clink); +} + +static long delegate_doLocked(struct link *plink, dbLinkUserCallback rtn, void *priv) +{ + debug_link *dlink = CONTAINER(plink->value.json.jlink, + struct debug_link, jlink); + struct link *clink = &dlink->child_link; + const lset *clset = dlink->child_lset; + long res; + + if (dlink->trace) + printf("Link trace: Calling %s::doLocked(%p, %p, %p)\n", + dlink->child_jlif->name, clink, rtn, priv); + + res = clset->doLocked(clink, rtn, priv); + + if (dlink->trace) + printf("Link trace: %s::doLocked(%p) returned %ld (0x%lx)\n", + dlink->child_jlif->name, clink, res, res); + + return res; +} + + +/************************ Debug jlif Routines ***********************/ + +static jlink* lnkDebug_alloc(short dbfType) +{ + debug_link *dlink; + + IFDEBUG(10) + printf("lnkDebug_alloc(%s)\n", dbGetFieldTypeString(dbfType)); + + dlink = calloc(1, sizeof(struct debug_link)); + if (!dlink) { + errlogPrintf("lnkDebug: calloc() failed.\n"); + return NULL; + } + + dlink->dbfType = dbfType; + + IFDEBUG(10) + printf("lnkDebug_alloc -> debug@%p\n", dlink); + + return &dlink->jlink; +} + +static jlink* lnkTrace_alloc(short dbfType) +{ + debug_link *dlink; + + IFDEBUG(10) + printf("lnkTrace_alloc(%s)\n", dbGetFieldTypeString(dbfType)); + + dlink = calloc(1, sizeof(struct debug_link)); + if (!dlink) { + errlogPrintf("lnkTrace: calloc() failed.\n"); + return NULL; + } + + dlink->dbfType = dbfType; + dlink->trace = 1; + + IFDEBUG(10) + printf("lnkTrace_alloc -> debug@%p\n", dlink); + + return &dlink->jlink; +} + +static void lnkDebug_free(jlink *pjlink) +{ + debug_link *dlink = CONTAINER(pjlink, struct debug_link, jlink); + + IFDEBUG(10) + printf("lnkDebug_free(debug@%p)\n", dlink); + + dbJLinkFree(dlink->child_link.value.json.jlink); + + free(dlink); +} + +static jlif_key_result lnkDebug_start_map(jlink *pjlink) +{ + debug_link *dlink = CONTAINER(pjlink, struct debug_link, jlink); + + IFDEBUG(10) + printf("lnkDebug_start_map(debug@%p)\n", dlink); + + switch (dlink->dbfType) { + case DBF_INLINK: + return jlif_key_child_inlink; + case DBF_OUTLINK: + return jlif_key_child_outlink; + case DBF_FWDLINK: + return jlif_key_child_outlink; + } + return jlif_key_stop; +} + +static jlif_result lnkDebug_end_map(jlink *pjlink) +{ + debug_link *dlink = CONTAINER(pjlink, struct debug_link, jlink); + + IFDEBUG(10) + printf("lnkDebug_end_map(debug@%p)\n", dlink); + + return jlif_continue; +} + +static void lnkDebug_start_child(jlink *parent, jlink *child) +{ + debug_link *dlink = CONTAINER(parent, struct debug_link, jlink); + const jlif *pif = child->pif; + const jlif delegate_jlif = { + pif->name, + pif->alloc_jlink, /* Never used */ + delegate_free, + pif->parse_null ? delegate_null : NULL, + pif->parse_boolean ? delegate_boolean : NULL, + pif->parse_integer ? delegate_integer : NULL, + pif->parse_double ? delegate_double : NULL, + pif->parse_string ? delegate_string : NULL, + pif->parse_start_map ? delegate_start_map : NULL, + pif->parse_map_key ? delegate_map_key : NULL, + pif->parse_end_map ? delegate_end_map : NULL, + pif->parse_start_array ? delegate_start_array : NULL, + pif->parse_end_array ? delegate_end_array : NULL, + pif->end_child ? delegate_end_child : NULL, + delegate_get_lset, + pif->report ? delegate_report : NULL, + pif->map_children ? delegate_map_children : NULL, + pif->start_child ? delegate_start_child : NULL + }; + + IFDEBUG(10) + printf("lnkDebug_start_child(debug@%p, %s@%p)\n", dlink, + child->pif->name, child); + + dlink->child_jlif = pif; + memcpy(&dlink->jlif, &delegate_jlif, sizeof(jlif)); + + child->debug = 1; + child->pif = &dlink->jlif; /* Replace Child's interface table */ + + IFDEBUG(15) + printf("lnkDebug_start_child: pif %p => %p\n", pif, child->pif); + + if (dlink->trace) + printf("Link trace: %s::alloc_jlink(%s) returned %p\n", + pif->name, dbGetFieldTypeString(dlink->dbfType), child); +} + +static void lnkDebug_end_child(jlink *parent, jlink *child) +{ + debug_link *dlink = CONTAINER(parent, struct debug_link, jlink); + struct link *plink = &dlink->child_link; + const lset *plset = dlink->child_jlif->get_lset(child); + lset delegate_lset = { + plset->isConstant, + plset->isVolatile, + plset->openLink ? delegate_openLink : NULL, + plset->removeLink ? delegate_removeLink : NULL, + plset->loadScalar ? delegate_loadScalar : NULL, + plset->loadLS ? delegate_loadLS : NULL, + plset->loadArray ? delegate_loadArray : NULL, + plset->isConnected ? delegate_isConnected : NULL, + plset->getDBFtype ? delegate_getDBFtype : NULL, + plset->getElements ? delegate_getElements : NULL, + plset->getValue ? delegate_getValue : NULL, + plset->getControlLimits ? delegate_getControlLimits : NULL, + plset->getGraphicLimits ? delegate_getGraphicLimits : NULL, + plset->getAlarmLimits ? delegate_getAlarmLimits : NULL, + plset->getPrecision ? delegate_getPrecision : NULL, + plset->getUnits ? delegate_getUnits : NULL, + plset->getAlarm ? delegate_getAlarm : NULL, + plset->getTimeStamp ? delegate_getTimeStamp : NULL, + plset->putValue ? delegate_putValue : NULL, + plset->putAsync ? delegate_putAsync : NULL, + plset->scanForward ? delegate_scanForward : NULL, + plset->doLocked ? delegate_doLocked : NULL + }; + + IFDEBUG(10) + printf("lnkDebug_end_child(debug@%p, %s@%p)\n", dlink, + child->pif->name, child); + + plink->type = JSON_LINK; + plink->value.json.string = NULL; + plink->value.json.jlink = child; + + dlink->child_lset = plset; + memcpy(&dlink->lset, &delegate_lset, sizeof(lset)); + + IFDEBUG(15) + printf("lnkDebug_end_child: lset %p => %p\n", plset, &dlink->lset); +} + +static struct lset* lnkDebug_get_lset(const jlink *pjlink) +{ + debug_link *dlink = CONTAINER(pjlink, struct debug_link, jlink); + + IFDEBUG(10) + printf("lnkDebug_get_lset(debug@%p)\n", pjlink); + + return &dlink->lset; +} + +static void lnkDebug_report(const jlink *pjlink, int level, int indent) +{ + debug_link *dlink = CONTAINER(pjlink, struct debug_link, jlink); + + IFDEBUG(10) + printf("lnkDebug_report(debug@%p)\n", dlink); + + if (dlink->trace) + printf("%*s'trace':\n", indent, ""); + else + printf("%*s'debug':\n", indent, ""); + + if (dlink->child_link.type == JSON_LINK) { + dbJLinkReport(dlink->child_link.value.json.jlink, level, indent + 2); + } +} + +long lnkDebug_map_children(jlink *pjlink, jlink_map_fn rtn, void *ctx) +{ + debug_link *dlink = CONTAINER(pjlink, struct debug_link, jlink); + + IFDEBUG(10) + printf("lnkDebug_map_children(debug@%p)\n", dlink); + + if (dlink->child_link.type == JSON_LINK) { + return dbJLinkMapChildren(&dlink->child_link, rtn, ctx); + } + return 0; +} + + +/************************* Interface Tables *************************/ + +static jlif lnkDebugIf = { + "debug", lnkDebug_alloc, lnkDebug_free, + NULL, NULL, NULL, NULL, NULL, + lnkDebug_start_map, NULL, lnkDebug_end_map, + NULL, NULL, lnkDebug_end_child, lnkDebug_get_lset, + lnkDebug_report, lnkDebug_map_children, lnkDebug_start_child +}; +epicsExportAddress(jlif, lnkDebugIf); + +static jlif lnkTraceIf = { + "trace", lnkTrace_alloc, lnkDebug_free, + NULL, NULL, NULL, NULL, NULL, + lnkDebug_start_map, NULL, lnkDebug_end_map, + NULL, NULL, lnkDebug_end_child, lnkDebug_get_lset, + lnkDebug_report, lnkDebug_map_children, lnkDebug_start_child +}; +epicsExportAddress(jlif, lnkTraceIf); diff --git a/modules/database/src/std/link/lnkState.c b/modules/database/src/std/link/lnkState.c new file mode 100644 index 000000000..b8791bd3b --- /dev/null +++ b/modules/database/src/std/link/lnkState.c @@ -0,0 +1,230 @@ +/*************************************************************************\ +* Copyright (c) 2017 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* lnkState.c */ + +/* Usage: + * {state:green} + */ + +#include +#include +#include +#include + +#include "alarm.h" +#include "dbDefs.h" +#include "errlog.h" +#include "epicsAssert.h" +#include "epicsString.h" +#include "epicsTypes.h" +#include "dbAccessDefs.h" +#include "dbCommon.h" +#include "dbConvertFast.h" +#include "dbLink.h" +#include "dbJLink.h" +#include "dbStaticLib.h" +#include "dbStaticPvt.h" +#include "dbState.h" +#include "recGbl.h" +#include "epicsExport.h" + + +typedef long (*FASTCONVERT)(); + +typedef struct state_link { + jlink jlink; /* embedded object */ + char *name; + short val; + short invert; + dbStateId state; +} state_link; + +static lset lnkState_lset; + + +/*************************** jlif Routines **************************/ + +static jlink* lnkState_alloc(short dbfType) +{ + state_link *slink; + + if (dbfType == DBF_FWDLINK) { + errlogPrintf("lnkState: DBF_FWDLINK not supported\n"); + return NULL; + } + + slink = calloc(1, sizeof(struct state_link)); + if (!slink) { + errlogPrintf("lnkState: calloc() failed.\n"); + return NULL; + } + + slink->name = NULL; + slink->state = NULL; + slink->invert = 0; + slink->val = 0; + + return &slink->jlink; +} + +static void lnkState_free(jlink *pjlink) +{ + state_link *slink = CONTAINER(pjlink, struct state_link, jlink); + + free(slink->name); + free(slink); +} + +static jlif_result lnkState_string(jlink *pjlink, const char *val, size_t len) +{ + state_link *slink = CONTAINER(pjlink, struct state_link, jlink); + + if (len > 1 && val[0] == '!') { + slink->invert = 1; + val++; len--; + } + + slink->name = epicsStrnDup(val, len); + return jlif_continue; +} + +static struct lset* lnkState_get_lset(const jlink *pjlink) +{ + return &lnkState_lset; +} + +static void lnkState_report(const jlink *pjlink, int level, int indent) +{ + state_link *slink = CONTAINER(pjlink, struct state_link, jlink); + + printf("%*s'state': \"%s\" = %s%s\n", indent, "", + slink->name, slink->invert ? "! " : "", slink->val ? "TRUE" : "FALSE"); +} + +/*************************** lset Routines **************************/ + +static void lnkState_open(struct link *plink) +{ + state_link *slink = CONTAINER(plink->value.json.jlink, + struct state_link, jlink); + + slink->state = dbStateCreate(slink->name); +} + +static void lnkState_remove(struct dbLocker *locker, struct link *plink) +{ + state_link *slink = CONTAINER(plink->value.json.jlink, + struct state_link, jlink); + + free(slink->name); + free(slink); + + plink->value.json.jlink = NULL; +} + +static int lnkState_getDBFtype(const struct link *plink) +{ + return DBF_SHORT; +} + +static long lnkState_getElements(const struct link *plink, long *nelements) +{ + *nelements = 1; + return 0; +} + +static long lnkState_getValue(struct link *plink, short dbrType, void *pbuffer, + long *pnRequest) +{ + state_link *slink = CONTAINER(plink->value.json.jlink, + struct state_link, jlink); + FASTCONVERT conv = dbFastPutConvertRoutine[DBR_SHORT][dbrType]; + + slink->val = slink->invert ^ dbStateGet(slink->state); + return conv(&slink->val, pbuffer, NULL); +} + +static long lnkState_putValue(struct link *plink, short dbrType, + const void *pbuffer, long nRequest) +{ + state_link *slink = CONTAINER(plink->value.json.jlink, + struct state_link, jlink); + short val; + const char *pstr; + + if (nRequest == 0) + return 0; + + switch(dbrType) { + case DBR_CHAR: + case DBR_UCHAR: + val = !! *(const epicsInt8 *) pbuffer; + break; + + case DBR_SHORT: + case DBR_USHORT: + val = !! *(const epicsInt16 *) pbuffer; + break; + + case DBR_LONG: + case DBR_ULONG: + val = !! *(const epicsInt32 *) pbuffer; + break; + + case DBR_INT64: + case DBR_UINT64: + val = !! *(const epicsInt64 *) pbuffer; + break; + + case DBR_FLOAT: + val = !! *(const epicsFloat32 *) pbuffer; + break; + + case DBR_DOUBLE: + val = !! *(const epicsFloat64 *) pbuffer; + break; + + case DBR_STRING: /* Only "" and "0" are FALSE */ + pstr = (const char *) pbuffer; + val = (pstr[0] != 0) && ((pstr[0] != '0') || (pstr[1] != 0)); + break; + + default: + return S_db_badDbrtype; + } + slink->val = val; + + val ^= slink->invert; + (val ? dbStateSet : dbStateClear)(slink->state); + + return 0; +} + +/************************* Interface Tables *************************/ + +static lset lnkState_lset = { + 0, 0, /* not constant, always connected */ + lnkState_open, lnkState_remove, + NULL, NULL, NULL, + NULL, lnkState_getDBFtype, lnkState_getElements, + lnkState_getValue, + NULL, NULL, NULL, + NULL, NULL, + NULL, NULL, + lnkState_putValue, NULL, + NULL, NULL +}; + +static jlif lnkStateIf = { + "state", lnkState_alloc, lnkState_free, + NULL, NULL, NULL, NULL, lnkState_string, + NULL, NULL, NULL, + NULL, NULL, + NULL, lnkState_get_lset, + lnkState_report, NULL, NULL +}; +epicsExportAddress(jlif, lnkStateIf); diff --git a/modules/database/src/std/rec/aaiRecord.c b/modules/database/src/std/rec/aaiRecord.c index 230c060c4..7805c9fba 100644 --- a/modules/database/src/std/rec/aaiRecord.c +++ b/modules/database/src/std/rec/aaiRecord.c @@ -2,7 +2,7 @@ * Copyright (c) 2002 Southeastern Universities Research Association, as * Operator of Thomas Jefferson National Accelerator Facility. * 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. \*************************************************************************/ /* recAai.c */ @@ -11,7 +11,7 @@ * Original Author: Dave Barker * * C E B A F - * + * * Continuous Electron Beam Accelerator Facility * Newport News, Virginia, USA. * @@ -106,7 +106,6 @@ static long init_record(struct dbCommon *pcommon, int pass) { struct aaiRecord *prec = (struct aaiRecord *)pcommon; struct aaidset *pdset = (struct aaidset *)(prec->dset); - long status; /* must have dset defined */ if (!pdset) { @@ -119,11 +118,7 @@ static long init_record(struct dbCommon *pcommon, int pass) prec->nelm = 1; if (prec->ftvl > DBF_ENUM) prec->ftvl = DBF_UCHAR; - if (prec->nelm == 1) { - prec->nord = 1; - } else { - prec->nord = 0; - } + prec->nord = (prec->nelm == 1); /* we must call pdset->init_record in pass 0 because it may set prec->bptr which must @@ -131,8 +126,10 @@ static long init_record(struct dbCommon *pcommon, int pass) */ if (pdset->init_record) { + long status = pdset->init_record(prec); + /* init_record may set the bptr to point to the data */ - if ((status = pdset->init_record(prec))) + if (status) return status; } if (!prec->bptr) { @@ -142,7 +139,7 @@ static long init_record(struct dbCommon *pcommon, int pass) } return 0; } - + recGblInitSimm(pcommon, &prec->sscn, &prec->oldsimm, &prec->simm, &prec->siml); /* must have read_aai function defined */ @@ -167,9 +164,10 @@ static long process(struct dbCommon *pcommon) } status = readValue(prec); /* read the new value */ - if (!pact && prec->pact) return 0; - prec->pact = TRUE; + if (!pact && prec->pact) + return 0; + prec->pact = TRUE; prec->udf = FALSE; recGblGetTimeStampSimm(prec, prec->simm, &prec->siol); @@ -187,17 +185,19 @@ static long special(DBADDR *paddr, int after) int special_type = paddr->special; switch(special_type) { - case(SPC_MOD): + case SPC_MOD: if (dbGetFieldIndex(paddr) == aaiRecordSIMM) { if (!after) recGblSaveSimm(prec->sscn, &prec->oldsimm, prec->simm); else - recGblCheckSimm((dbCommon *)prec, &prec->sscn, prec->oldsimm, prec->simm); - return(0); + recGblCheckSimm((dbCommon *)prec, &prec->sscn, prec->oldsimm, + prec->simm); + return 0; } + default: recGblDbaddrError(S_db_badChoice, paddr, "aai: special"); - return(S_db_badChoice); + return S_db_badChoice; } } @@ -225,10 +225,14 @@ static long get_array_info(DBADDR *paddr, long *no_elements, long *offset) static long put_array_info(DBADDR *paddr, long nNew) { aaiRecord *prec = (aaiRecord *)paddr->precord; + epicsUInt32 nord = prec->nord; prec->nord = nNew; if (prec->nord > prec->nelm) prec->nord = prec->nelm; + + if (nord != prec->nord) + db_post_events(prec, &prec->nord, DBE_VALUE | DBE_LOG); return 0; } @@ -241,7 +245,7 @@ static long get_units(DBADDR *paddr, char *units) switch (dbGetFieldIndex(paddr)) { case indexof(VAL): if (prec->ftvl == DBF_STRING || prec->ftvl == DBF_ENUM) - break; + break; case indexof(HOPR): case indexof(LOPR): strncpy(units,prec->egu,DB_UNITS_SIZE); @@ -336,48 +340,40 @@ static void monitor(aaiRecord *prec) static long readValue(aaiRecord *prec) { struct aaidset *pdset = (struct aaidset *) prec->dset; - long status = 0; + long status; - if (!prec->pact) { - status = recGblGetSimm((dbCommon *)prec, &prec->sscn, &prec->oldsimm, &prec->simm, &prec->siml); - if (status) return status; - } + /* NB: Device support must post updates to NORD */ - switch (prec->simm) { - case menuYesNoNO: - status = pdset->read_aai(prec); - break; + if (prec->pact) + goto do_read; - case menuYesNoYES: { + status = recGblGetSimm((dbCommon *)prec, &prec->sscn, &prec->oldsimm, + &prec->simm, &prec->siml); + if (status) + return status; + + if (prec->simm == menuYesNoYES) { recGblSetSevr(prec, SIMM_ALARM, prec->sims); - if (prec->pact || (prec->sdly < 0.)) { - /* Device suport is responsible for buffer - which might be read-only so we may not be - allowed to call dbGetLink on it. - Maybe also device support has an advanced - simulation mode. - Thus call device now. - Reading through SIOL is handled in Soft Channel Device Support - */ - status = pdset->read_aai(prec); - prec->pact = FALSE; - } else { /* !prec->pact && delay >= 0. */ + if (prec->sdly >= 0) { CALLBACK *pvt = prec->simpvt; - if (!pvt) { - pvt = calloc(1, sizeof(CALLBACK)); /* very lazy allocation of callback structure */ + + if (!pvt) { /* very lazy allocation of callback structure */ + pvt = calloc(1, sizeof(CALLBACK)); prec->simpvt = pvt; } - if (pvt) callbackRequestProcessCallbackDelayed(pvt, prec->prio, prec, prec->sdly); + if (pvt) + callbackRequestProcessCallbackDelayed(pvt, prec->prio, prec, + prec->sdly); prec->pact = TRUE; + return 0; } - break; } - - default: + else if (prec->simm != menuYesNoNO) { recGblSetSevr(prec, SOFT_ALARM, INVALID_ALARM); - status = -1; + return -1; } - return status; +do_read: + return pdset->read_aai(prec); } diff --git a/modules/database/src/std/rec/aaoRecord.c b/modules/database/src/std/rec/aaoRecord.c index 68a76a66a..b71750052 100644 --- a/modules/database/src/std/rec/aaoRecord.c +++ b/modules/database/src/std/rec/aaoRecord.c @@ -2,7 +2,7 @@ * Copyright (c) 2002 Southeastern Universities Research Association, as * Operator of Thomas Jefferson National Accelerator Facility. * 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. \*************************************************************************/ /* recAao.c */ @@ -11,7 +11,7 @@ * Original Author: Dave Barker * * C E B A F - * + * * Continuous Electron Beam Accelerator Facility * Newport News, Virginia, USA. * @@ -225,10 +225,14 @@ static long get_array_info(DBADDR *paddr, long *no_elements, long *offset) static long put_array_info(DBADDR *paddr, long nNew) { aaoRecord *prec = (aaoRecord *)paddr->precord; + epicsUInt32 nord = prec->nord; prec->nord = nNew; if (prec->nord > prec->nelm) prec->nord = prec->nelm; + + if (nord != prec->nord) + db_post_events(prec, &prec->nord, DBE_VALUE | DBE_LOG); return 0; } @@ -241,7 +245,7 @@ static long get_units(DBADDR *paddr, char *units) switch (dbGetFieldIndex(paddr)) { case indexof(VAL): if (prec->ftvl == DBF_STRING || prec->ftvl == DBF_ENUM) - break; + break; case indexof(HOPR): case indexof(LOPR): strncpy(units,prec->egu,DB_UNITS_SIZE); diff --git a/modules/database/src/std/rec/biRecord.dbd b/modules/database/src/std/rec/biRecord.dbd deleted file mode 100644 index 1646d5df1..000000000 --- a/modules/database/src/std/rec/biRecord.dbd +++ /dev/null @@ -1,134 +0,0 @@ -#************************************************************************* -# Copyright (c) 2002 The University of Chicago, as Operator of Argonne -# National Laboratory. -# Copyright (c) 2002 The Regents of the University of California, as -# Operator of Los Alamos National Laboratory. -# EPICS BASE is distributed subject to a Software License Agreement found -# in file LICENSE that is included with this distribution. -#************************************************************************* -recordtype(bi) { - include "dbCommon.dbd" - field(INP,DBF_INLINK) { - prompt("Input Specification") - promptgroup("40 - Input") - interest(1) - } - field(VAL,DBF_ENUM) { - prompt("Current Value") - promptgroup("40 - Input") - asl(ASL0) - pp(TRUE) - } - field(ZSV,DBF_MENU) { - prompt("Zero Error Severity") - promptgroup("70 - Alarm") - pp(TRUE) - interest(1) - menu(menuAlarmSevr) - } - field(OSV,DBF_MENU) { - prompt("One Error Severity") - promptgroup("70 - Alarm") - pp(TRUE) - interest(1) - menu(menuAlarmSevr) - } - field(COSV,DBF_MENU) { - prompt("Change of State Svr") - promptgroup("70 - Alarm") - pp(TRUE) - interest(1) - menu(menuAlarmSevr) - } - field(ZNAM,DBF_STRING) { - prompt("Zero Name") - promptgroup("80 - Display") - pp(TRUE) - interest(1) - size(26) - prop(YES) - } - field(ONAM,DBF_STRING) { - prompt("One Name") - promptgroup("80 - Display") - pp(TRUE) - interest(1) - size(26) - prop(YES) - } - field(RVAL,DBF_ULONG) { - prompt("Raw Value") - pp(TRUE) - } - field(ORAW,DBF_ULONG) { - prompt("prev Raw Value") - special(SPC_NOMOD) - interest(3) - } - field(MASK,DBF_ULONG) { - prompt("Hardware Mask") - special(SPC_NOMOD) - interest(1) - } - field(LALM,DBF_USHORT) { - prompt("Last Value Alarmed") - special(SPC_NOMOD) - interest(3) - } - field(MLST,DBF_USHORT) { - prompt("Last Value Monitored") - special(SPC_NOMOD) - interest(3) - } - field(SIOL,DBF_INLINK) { - prompt("Simulation Input Link") - promptgroup("90 - Simulate") - interest(1) - } - field(SVAL,DBF_ULONG) { - prompt("Simulation Value") - } - field(SIML,DBF_INLINK) { - prompt("Simulation Mode Link") - promptgroup("90 - Simulate") - interest(1) - } - field(SIMM,DBF_MENU) { - prompt("Simulation Mode") - special(SPC_MOD) - interest(1) - menu(menuSimm) - } - field(SIMS,DBF_MENU) { - prompt("Simulation Mode Severity") - promptgroup("90 - Simulate") - interest(2) - menu(menuAlarmSevr) - } - field(OLDSIMM,DBF_MENU) { - prompt("Prev. Simulation Mode") - special(SPC_NOMOD) - interest(4) - menu(menuSimm) - } - field(SSCN,DBF_MENU) { - prompt("Sim. Mode Scan") - promptgroup("90 - Simulate") - interest(1) - menu(menuScan) - initial("65535") - } - field(SDLY,DBF_DOUBLE) { - prompt("Sim. Mode Async Delay") - promptgroup("90 - Simulate") - interest(2) - initial("-1.0") - } - %#include "callback.h" - field(SIMPVT,DBF_NOACCESS) { - prompt("Sim. Mode Private") - special(SPC_NOMOD) - interest(4) - extra("CALLBACK *simpvt") - } -} diff --git a/modules/database/src/std/rec/biRecord.dbd.pod b/modules/database/src/std/rec/biRecord.dbd.pod new file mode 100644 index 000000000..fbf343ad4 --- /dev/null +++ b/modules/database/src/std/rec/biRecord.dbd.pod @@ -0,0 +1,491 @@ +#************************************************************************* +# 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. +#************************************************************************* + +=title Binary Input Record (bi) + +This record type is normally used to obtain a binary value of 0 or 1. Most +device support modules obtain values from hardware and place the value in +RVAL. For these devices, record processing sets VAL = (0,1) if RVAL is (0, +not 0). Device support modules may optionally read a value directly from +VAL. + +Soft device modules are provided to obtain input via database or channel +access links via dbPutField or dbPutLink requests. Two soft device support +modules are provided: C and C. The first +allows VAL to be an arbitrary unsigned short integer. The second reads the +value into RVAL just like normal hardware modules. + +=head2 Parameter Fields + +The binary input's fields fall into the following categories: + +=over + +=item * +scan Parameters + +=item * +read and convert parameters + +=item * +operator display parameters + +=item * +alarm parameters + +=item * +run-time parameters + +=back + +=recordtype bi + +=cut + +recordtype(bi) { + +=head3 Scan Parameters + +The binary input record has the standard fields for specifying under what +circumstances the record will be processed. These fields are listed in +L. In addition, L explains how these +fields are used. Note that I/O event scanning is only supported for those +card types that interrupt. + +=fields SCAN + +=head3 Read and Convert Parameters + +The read and convert fields determine where the binary input gets its +input from and how to convert the raw signal to engineering units. The INP +field contains the address from where device support retrieves the value. +If the binary input record gets its value from hardware, the address of the +card must be entered in the INP field, and the name of the device support +module must be entered in the DTYP field. See L
    for +information on the format of the hardware address. Be aware that the format +differs between types of cards. You can see a list of device support +modules currently supported at the user's local site by using C +utility (R3.13). + +For records that specify C or C device +support routines, the INP field can be a channel or a database link, or a +constant. If a constant, VAL can be changed directly by dbPuts. See +L
    for information on the format of database and +channel access addresses. Also, see L in +this chapter for information on soft device support. + +If the record gets its values from hardware or uses the C +device support, the device support routines place the value in the RVAL +field which is then converted using the process described in the next +section. + +=fields INP, DTYP, ZNAM, ONAM, RVAL, VAL + +=head3 Conversion Fields + +The VAL field is set equal to (0,1) if the RVAL field is (0, not 0), unless +the device support module reads a value directly into VAL or the +C device support is used. The value can also be fetched as one of +the strings specified in the ZNAM or ONAM fields. The ZNAM field has a +string that corresponds to the 0 state, so when the value is fetched as +this string, C will return a 0. The ONAM field hold the +string that corresponds to the 1 state, so when the value is fetched as +this string, C returns a 1. + +=fields ZNAM, ONAM + +=head3 Operator Display Parameters + +These parameters are used to present meaningful data to the operator. The +C record support routine can retrieve the state string +corresponding to the VAL's state. If the value is 1, C will +return the string in the ONAM field; and if 0, C will return +the ZNAM string. + +See L for more on the record name (NAME) +and description (DESC) fields. + +=fields ZNAM, ONAM, NAME, DESC + +=head3 Alarm Parameters + +These parameters are used to determine if the binary input is in alarm +condition and to determine the severity of that condition. The possible +alarm conditions for binary inputs are the SCAN, READ state alarms, and the +change of state alarm. The SCAN and READ alarms are called by the device +supprt routines. + +The user can choose the severity of each state in the ZSV and OSV fields. +The possible values for these fields are C, C, and +C. The ZSV field holds the severity for the zero state; OSV, for +the one state. COSV causes an alarm whenever the state changes between +0 and 1 and the severity is configured as MINOR or MAJOR. + +See L for a complete explanation of the discrete alarm +states. L lists other fields related to alarms that are +common to all record types. + +=fields ZSV, OSV, COSV + +=head3 Run-time Parameters and Simulation Mode Parameters + +These parameters are used by the run-time code for processing the binary +input. They are not configured using a database configuration tool. + +ORAW is used to determine if monitors should be triggered for RVAL at the same +time they are triggered for VAL. + +MASK is given a value by ithe device support routines. This value is used to +manipulate the record's value, but is only the concern of the hardware device +support routines. + +The LALM fields holds the value of the last occurence of the change of +state alarm. It is used to implement the change of state alarm, and thus +only has meaning if COSV is MAJOR or MINOR. + +The MSLT field is used by the C record support routine to +determine if archive and value change monitors are invoked. They are if MSLT +is not equal to VAL. + +=fields ORAW, MASK, LALM, MLST + +The following fields are used to operate the binary input in simulation +mode. See L for more information on +these fields. + +=fields SIOL, SVAL, SIML, SIMM, SIMS + + +=cut + + include "dbCommon.dbd" + field(INP,DBF_INLINK) { + prompt("Input Specification") + promptgroup("40 - Input") + interest(1) + } + field(VAL,DBF_ENUM) { + prompt("Current Value") + promptgroup("40 - Input") + asl(ASL0) + pp(TRUE) + } + field(ZSV,DBF_MENU) { + prompt("Zero Error Severity") + promptgroup("70 - Alarm") + pp(TRUE) + interest(1) + menu(menuAlarmSevr) + } + field(OSV,DBF_MENU) { + prompt("One Error Severity") + promptgroup("70 - Alarm") + pp(TRUE) + interest(1) + menu(menuAlarmSevr) + } + field(COSV,DBF_MENU) { + prompt("Change of State Svr") + promptgroup("70 - Alarm") + pp(TRUE) + interest(1) + menu(menuAlarmSevr) + } + field(ZNAM,DBF_STRING) { + prompt("Zero Name") + promptgroup("80 - Display") + pp(TRUE) + interest(1) + size(26) + prop(YES) + } + field(ONAM,DBF_STRING) { + prompt("One Name") + promptgroup("80 - Display") + pp(TRUE) + interest(1) + size(26) + prop(YES) + } + field(RVAL,DBF_ULONG) { + prompt("Raw Value") + pp(TRUE) + } + field(ORAW,DBF_ULONG) { + prompt("prev Raw Value") + special(SPC_NOMOD) + interest(3) + } + field(MASK,DBF_ULONG) { + prompt("Hardware Mask") + special(SPC_NOMOD) + interest(1) + } + field(LALM,DBF_USHORT) { + prompt("Last Value Alarmed") + special(SPC_NOMOD) + interest(3) + } + field(MLST,DBF_USHORT) { + prompt("Last Value Monitored") + special(SPC_NOMOD) + interest(3) + } + field(SIOL,DBF_INLINK) { + prompt("Simulation Input Link") + promptgroup("90 - Simulate") + interest(1) + } + field(SVAL,DBF_ULONG) { + prompt("Simulation Value") + } + field(SIML,DBF_INLINK) { + prompt("Simulation Mode Link") + promptgroup("90 - Simulate") + interest(1) + } + field(SIMM,DBF_MENU) { + prompt("Simulation Mode") + special(SPC_MOD) + interest(1) + menu(menuSimm) + } + field(SIMS,DBF_MENU) { + prompt("Simulation Mode Severity") + promptgroup("90 - Simulate") + interest(2) + menu(menuAlarmSevr) + } + field(OLDSIMM,DBF_MENU) { + prompt("Prev. Simulation Mode") + special(SPC_NOMOD) + interest(4) + menu(menuSimm) + } + field(SSCN,DBF_MENU) { + prompt("Sim. Mode Scan") + promptgroup("90 - Simulate") + interest(1) + menu(menuScan) + initial("65535") + } + field(SDLY,DBF_DOUBLE) { + prompt("Sim. Mode Async Delay") + promptgroup("90 - Simulate") + interest(2) + initial("-1.0") + } + %#include "callback.h" + field(SIMPVT,DBF_NOACCESS) { + prompt("Sim. Mode Private") + special(SPC_NOMOD) + interest(4) + extra("CALLBACK *simpvt") + } + +=head2 Record Support + +=head3 Record Support Routines + +=head2 C + +This routine initializes SIMM with the value of SIML if SIML type is a +CONSTANT link or creates a channel access link if SIML type is PV_LINK. +SVAL is likewise initialized if SIOL is a CONSTANT or PV_LINK. + +This routine next checks to see that device support is available and a +device support routine is defined. If neither exist, an error is issued and +processing is terminated. + +If device support includes C, it is called. + +=head2 C + +See next section. + +=head2 C + +Fills in the values of struct valueDes so that they refer to VAL. + +=head2 C + +Retrieves ASCII string corresponding to VAL. + +=head2 C + +Retrieves ASCII strings for ZNAM and ONAM. + +=head2 C + +Check if string matches ZNAM or ONAM, and if it does, sets VAL. + +=head2 Record Processing + +Routine process implements the following algorithm: + +=over 1 + +=item 1. +Check to see that the appropriate device support module exists. If it +doesn't, an error message is issued and processing is terminated with +the PACT field still set to TRUE. This ensures that processes will no +longer be called for this record. Thus error storms will not occur. + +=item 2. +C is called. See L for details. + +=item 3. +If PACT has been changed to TRUE, the device support read routine has +started but has not completed reading a new input value. In this case, the +processing routine merely returns, leaving PACT TRUE. + +=item 4. +Convert. + +=back + +=over 1 + +=item * +status = read_bi + +=item * +PACT = TRUE + +=item * +TIME = tslocaltime + +=item * +if status is 0, then set VAL=(0,1) if RVAL is (0, not 0) and UDF = False. + +=item * +if status is 2, set status = 0 + +=back + +=over 1 + +=item 5. +Check alarms: This routine checks to see if the new VAL causes the alarm +status and severity to change. If so, NSEV, NSTA and LALM are set. Note +that if VAL is greater than 1, no checking is performed. + +=item 6. +Check if monitors should be invoked: + +=back + +=over 1 + +=item * +Alarm monitors are invoked if the alarm status or severity has changed. + +=item * +Archive and value change monitors are invoked if MSLT is not equal to VAL. + +=item * +Monitors for RVAL are checked whenever other monitors are invoked. + +=item * +NSEV and NSTA are reset to 0. + +=back + +=over 1 + +=item 7. +Scan forward link if necessary, set PACT FALSE, and return. + +=back + +=head2 Device Support + +=head3 Fields of Interest to Device Support + +Each binary input record must have an associated set of device support +routines. The primary resposibility of the device support routines is to +obtain a new raw input value whenever C is called. The device +support routines are primarily interested in the following fields: + +=fields PACT, DPVT, UDF, NSEV, NSTA, VAL, INP, RVAL, MASK + +=head3 Device Support routines + +Device support consists of the following routines: + +=head2 C + +Not currently used. + +=head2 C + +This routine is called once during IOC initialization. + +=head2 C + +This routine is optional. If provided, it is called by the record support +C routine. + +=head2 C + +This routine is called by the C system each time the record is +added or deleted from an I/O event scan list. C has the value (0,1) if +the record is being (added to, deleted from) and I/O event list. It must be +provided for any device type that can use the ioEvent scanner. + +=head2 C + +This routine must provide a new input value. It returns the following +values: + +=over + +=item 0: +Success. A new raw value is placed in RVAL. The record support module +forces VAL to be (0,1) if RVAL is (0, not 0). + +=item 2: +Success, but don't modify VAL. + +=item Other: +Error. + +=back + +=head3 Device Support for Soft Records + +Two soft device support modules, Soft Channel and Raw Soft Channel, are +provided for input records not related to actual hardware devices. The INP +link type must be either CONSTANT, DB_LINK, or CA_LINK. + +=head3 Soft Channel + +C always returns a value of 2, which means that no conversion is +performed. + +If the INP link type is CONSTANT, then the constant value is stored in VAL +by C, and the UDF is set to FALSE. VAL can be changed via +C requests. If the INP link type is PV_LINK, the C is +called by C. + +C calls C to read the current value of VAL. +See L for details. + +If the return status of C is zero, then C sets +UDF to FALSE. The status of C is returned. + +=head3 Raw Soft Channel + +This module is like the previous except that values are read into RVAL. + +C returns a value of 0. Thus the record processing routine will +force VAL to be 0 or 1. + +=cut +} diff --git a/modules/database/src/std/rec/boRecord.dbd b/modules/database/src/std/rec/boRecord.dbd deleted file mode 100644 index 346f0f22c..000000000 --- a/modules/database/src/std/rec/boRecord.dbd +++ /dev/null @@ -1,182 +0,0 @@ -#************************************************************************* -# Copyright (c) 2002 The University of Chicago, as Operator of Argonne -# National Laboratory. -# Copyright (c) 2002 The Regents of the University of California, as -# Operator of Los Alamos National Laboratory. -# EPICS BASE is distributed subject to a Software License Agreement found -# in file LICENSE that is included with this distribution. -#************************************************************************* -recordtype(bo) { - include "dbCommon.dbd" - field(VAL,DBF_ENUM) { - prompt("Current Value") - promptgroup("50 - Output") - asl(ASL0) - pp(TRUE) - } - field(OMSL,DBF_MENU) { - prompt("Output Mode Select") - promptgroup("50 - Output") - interest(1) - menu(menuOmsl) - } - field(DOL,DBF_INLINK) { - prompt("Desired Output Loc") - promptgroup("40 - Input") - interest(1) - } - field(OUT,DBF_OUTLINK) { - prompt("Output Specification") - promptgroup("50 - Output") - interest(1) - } - field(HIGH,DBF_DOUBLE) { - prompt("Seconds to Hold High") - promptgroup("30 - Action") - interest(1) - } - field(ZNAM,DBF_STRING) { - prompt("Zero Name") - promptgroup("80 - Display") - pp(TRUE) - interest(1) - size(26) - prop(YES) - } - field(ONAM,DBF_STRING) { - prompt("One Name") - promptgroup("80 - Display") - pp(TRUE) - interest(1) - size(26) - prop(YES) - } - field(RVAL,DBF_ULONG) { - prompt("Raw Value") - pp(TRUE) - } - field(ORAW,DBF_ULONG) { - prompt("prev Raw Value") - special(SPC_NOMOD) - interest(3) - } - field(MASK,DBF_ULONG) { - prompt("Hardware Mask") - special(SPC_NOMOD) - interest(1) - } - field(RPVT,DBF_NOACCESS) { - prompt("Record Private") - special(SPC_NOMOD) - interest(4) - extra("void * rpvt") - } - field(WDPT,DBF_NOACCESS) { - prompt("Watch Dog Timer ID") - special(SPC_NOMOD) - interest(4) - extra("void * wdpt") - } - field(ZSV,DBF_MENU) { - prompt("Zero Error Severity") - promptgroup("70 - Alarm") - pp(TRUE) - interest(1) - menu(menuAlarmSevr) - } - field(OSV,DBF_MENU) { - prompt("One Error Severity") - promptgroup("70 - Alarm") - pp(TRUE) - interest(1) - menu(menuAlarmSevr) - } - field(COSV,DBF_MENU) { - prompt("Change of State Sevr") - promptgroup("70 - Alarm") - pp(TRUE) - interest(1) - menu(menuAlarmSevr) - } - field(RBV,DBF_ULONG) { - prompt("Readback Value") - special(SPC_NOMOD) - } - field(ORBV,DBF_ULONG) { - prompt("Prev Readback Value") - special(SPC_NOMOD) - interest(3) - } - field(MLST,DBF_USHORT) { - prompt("Last Value Monitored") - special(SPC_NOMOD) - interest(3) - } - field(LALM,DBF_USHORT) { - prompt("Last Value Alarmed") - special(SPC_NOMOD) - interest(3) - } - field(SIOL,DBF_OUTLINK) { - prompt("Simulation Output Link") - promptgroup("90 - Simulate") - interest(1) - } - field(SIML,DBF_INLINK) { - prompt("Simulation Mode Link") - promptgroup("90 - Simulate") - interest(1) - } - field(SIMM,DBF_MENU) { - prompt("Simulation Mode") - special(SPC_MOD) - interest(1) - menu(menuYesNo) - } - field(SIMS,DBF_MENU) { - prompt("Simulation Mode Severity") - promptgroup("90 - Simulate") - interest(2) - menu(menuAlarmSevr) - } - field(OLDSIMM,DBF_MENU) { - prompt("Prev. Simulation Mode") - special(SPC_NOMOD) - interest(4) - menu(menuSimm) - } - field(SSCN,DBF_MENU) { - prompt("Sim. Mode Scan") - promptgroup("90 - Simulate") - interest(1) - menu(menuScan) - initial("65535") - } - field(SDLY,DBF_DOUBLE) { - prompt("Sim. Mode Async Delay") - promptgroup("90 - Simulate") - interest(2) - initial("-1.0") - } - %#include "callback.h" - field(SIMPVT,DBF_NOACCESS) { - prompt("Sim. Mode Private") - special(SPC_NOMOD) - interest(4) - extra("CALLBACK *simpvt") - } - field(IVOA,DBF_MENU) { - prompt("INVALID outpt action") - promptgroup("50 - Output") - interest(2) - menu(menuIvoa) - } - field(IVOV,DBF_USHORT) { - prompt("INVALID output value") - promptgroup("50 - Output") - interest(2) - } -} - -variable(boHIGHprecision, int) -variable(boHIGHlimit, double) diff --git a/modules/database/src/std/rec/boRecord.dbd.pod b/modules/database/src/std/rec/boRecord.dbd.pod new file mode 100644 index 000000000..fe0a98e6e --- /dev/null +++ b/modules/database/src/std/rec/boRecord.dbd.pod @@ -0,0 +1,611 @@ +#************************************************************************* +# 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. +#************************************************************************* + +=title Binary Output Record (bo) + +The normal use for this record type is to store a simple bit (0 or 1) value +to be sent to a Digital Output module. It can also be used to write binary +values into other records via database or channel access links. This record +can implement both latched and momentary binary outputs depending on how +the HIGH field is configured. + +=head2 Parameter Fields + +The binary output's fields fall into the following categories: + +=over 1 + +=item * +scan parameters + +=item * +convert and write parameters + +=item * +operator display parameters + +=item * +alarm parameters + +=item * +run-time parameters + +=back + +=recordtype bo + +=cut + +recordtype(bo) { + +=head3 Scan Parameters + +The binary output record has the standard fields for specifying under what +circumstances the record will be processed. The fields are listed in +L. In addition, L explains how these +fields are used. Note that I/O event scanning is only supported for those card +types that interrupt. + +=fields SCAN + +=head3 Desired Output Parameters + +The binary output record must specify where its desired output originates. +The desired output needs to be in engineering units. + +The first field that determines where the desired output originates is the +output mode select (OMSL) field, which can have two possible values: +C or C. If C is specified, the value +in the VAL field can be set externally via dbPuts at run-time. If +C is specified, the VAL field's value is obtained from the +address specified in the desired output location (DOL) field which can be a +database link, a channel access link, or a constant. To achieve continuous +control, a database link to a control algorithm record should be entered in +the DOL field. + +L
    presents more information on database addresses +and links. L explaines the effect of database +linkage on scanning. + +=fields DOL, OMSL + +=head3 Convert and Write Parameters + +These parameters are used to determine where the binary output writes to +and how to convert the engineering units to a raw signal. After VAL is set +and forced to be either 1 or 0, as the result of either a dbPut or a new +value being retrieved from the link in the DOL field, then what happens +next depends on which device support routine is used and how the HIGH field +is configured. + +If the C device support routine is specified, then the device +support routine writes the VAL field's value to the address specified in +the OUT field. Otherwise, RVAL is the value written by the device support +routines after being converted. + +If VAL is equal to 0, then the record processing routine sets RVAL equal to +zero. When VAL is not equal to 0, then RVAL is set equal to the value +contained in the MASK field. (MASK is set by the device support routines +and is of no concern to the user.) Also, when VAL is not 0 and after RVAL is +set equal to MASK, the record processing routine checks to see if the HIGH +field is greater than 0. If it is, then the routine will process the record +again with VAL set to 0 after the number of seconds specified by HIGH. +Thus, HIGH implements a momentary output which changes the state of the +device back to 0 after I number of seconds. + +=fields DTYP, OUT, VAL, RVAL, HIGH, ZNAM, ONAM + +=head3 Conversion Parameters + +The ZNAM field has the string that corresponds to the 0 state, and the ONAM +field holds the string that corresponds to the 1 state. These fields, other +than being used to tell the operator what each state represents, are used +to perform conversions if the value fetched by DOL is a string. If it is, +VAL is set to the state which corresponds to that string. For instance, if the +value fetched is the string "Off" and the ZNAM string is "Off," then VAL is +set to 0. + +After VAL is set, if VAL is equal to 0, then the record processing routine +sets RVAL equal to zero. When VAL is not equal to 0, then RVAL is set equal +to the value contained in the MASK field. (Mask is set by the device +support routines and is of no concern to the user.) Also when VAL is equal +to 1 and after RVAL is set equal to MASK, the record processing routine checks +to see if the HIGH field is greater than 0. If it is, then the routine +processes the record again with VAL=0 after the number of seconds specified +by HIGH. Thus, HIGH implements a latched output which changes the state of +the device or link to 1, then changes it back to 0 after I number of seconds. + +=fields ZNAM, ONAM, HIGH + +=head3 Output Specification + +The OUT field specifies where the binary output record writes its output. +It must specify the address of an I/O card if the record sends its output +to hardware, and the DTYP field must contain the corresponding device +support module. Be aware that the address format differs according to the +I/O bus used. See L
    for information on the format of +hardware addresses. You can see a list of device support modules currently +supported at the user's local site by using the C utility in R3.13. + +Otherwise, if the record is configured to use the soft device support +modules, then it can be either a database link, a channel access link, or a +constant. Be aware that nothing will be written when OUT is a constant. See +L
    for information on the format of the database and +channel access addresses. Also, see L in +this chapter for more on output to other records. + +=head3 Operator Display Parameters + +These parameters are used to present meaningful data to the operator, The +C record support routine can retrieve the state string +corresponding to the VAL's state. So, if the value is 1, C +will return the string in the ONAM field: and if 0, C will +return the ZNAM string. + +See L for more on the record name (NAME) +and description (DESC) fields. + +=fields ZNAM, ONAM, NAME, DESC + +=head3 Alarm Parameters + +These parameters are used to determine the binary output's alarm condition +and to determine the severity of that condition. The possible alarm +conditions for binary outputs are the SCAN, READ, INVALID and state alarms. +The user can configure the state alarm conditions using these fields. + +The possible values for these fields are C, C, and +C. The ZSV holds the severity for the zero state; OSV for the one +state. COSV is used to cause an alarm whenever the state changes between +states (0-1, 1-0) and its severity is configured as MINOR or MAJOR. + +See L for more information on the IVOA and +IVOV fields. L lists other fields related to alarms that are +common to all record types. + +=fields ZSV, OSV, COSV, IVOA, IVOV + +=head3 Run-Time and Simulation Mode Parameters + +These parameters are used by the run-time code for processiong the binary +output. They are not configurable using a configuration tool. They +represent the current state of the binary output. + +ORAW is used to determine if monitors should be triggered for RVAL at the +same time they are triggered for VAL. + +MASK is given a value by the device support routines and should not concern +the user. + +The RBV field is also set by device support. It is the actual read back +value obtained from the hardware itself or from the associated device +driver. + +The ORBV field is used to decide if monitors should be triggered +for RBV at the same time monitors are triggered for changes in VAL. + +The LALM field holds the value of the last occurrence of the change of +state alarm. It is used to implement the change of state alarm, and thus +only has meaning if COSV is MINOR or MAJOR. + +The MLST is used by the C record support routine to determine if +archive and value change monitors are invoked. They are if MLST is not +equal to VAL. + +The WPDT field is a private field for honoring seconds to hold HIGH. + +=fields ORAW, MASK, RBV, ORBV, LALM, MLST, RPVT, WDPT + +The following fields are used to operate the binary output in the +simulation mode. See L for more +information on these fields. + +=fields SIOL, SIML, SIMM, SIMS + +=cut + + include "dbCommon.dbd" + field(VAL,DBF_ENUM) { + prompt("Current Value") + promptgroup("50 - Output") + asl(ASL0) + pp(TRUE) + } + field(OMSL,DBF_MENU) { + prompt("Output Mode Select") + promptgroup("50 - Output") + interest(1) + menu(menuOmsl) + } + field(DOL,DBF_INLINK) { + prompt("Desired Output Loc") + promptgroup("40 - Input") + interest(1) + } + field(OUT,DBF_OUTLINK) { + prompt("Output Specification") + promptgroup("50 - Output") + interest(1) + } + field(HIGH,DBF_DOUBLE) { + prompt("Seconds to Hold High") + promptgroup("30 - Action") + interest(1) + } + field(ZNAM,DBF_STRING) { + prompt("Zero Name") + promptgroup("80 - Display") + pp(TRUE) + interest(1) + size(26) + prop(YES) + } + field(ONAM,DBF_STRING) { + prompt("One Name") + promptgroup("80 - Display") + pp(TRUE) + interest(1) + size(26) + prop(YES) + } + field(RVAL,DBF_ULONG) { + prompt("Raw Value") + pp(TRUE) + } + field(ORAW,DBF_ULONG) { + prompt("prev Raw Value") + special(SPC_NOMOD) + interest(3) + } + field(MASK,DBF_ULONG) { + prompt("Hardware Mask") + special(SPC_NOMOD) + interest(1) + } + field(RPVT,DBF_NOACCESS) { + prompt("Record Private") + special(SPC_NOMOD) + interest(4) + extra("void * rpvt") + } + field(WDPT,DBF_NOACCESS) { + prompt("Watch Dog Timer ID") + special(SPC_NOMOD) + interest(4) + extra("void * wdpt") + } + field(ZSV,DBF_MENU) { + prompt("Zero Error Severity") + promptgroup("70 - Alarm") + pp(TRUE) + interest(1) + menu(menuAlarmSevr) + } + field(OSV,DBF_MENU) { + prompt("One Error Severity") + promptgroup("70 - Alarm") + pp(TRUE) + interest(1) + menu(menuAlarmSevr) + } + field(COSV,DBF_MENU) { + prompt("Change of State Sevr") + promptgroup("70 - Alarm") + pp(TRUE) + interest(1) + menu(menuAlarmSevr) + } + field(RBV,DBF_ULONG) { + prompt("Readback Value") + special(SPC_NOMOD) + } + field(ORBV,DBF_ULONG) { + prompt("Prev Readback Value") + special(SPC_NOMOD) + interest(3) + } + field(MLST,DBF_USHORT) { + prompt("Last Value Monitored") + special(SPC_NOMOD) + interest(3) + } + field(LALM,DBF_USHORT) { + prompt("Last Value Alarmed") + special(SPC_NOMOD) + interest(3) + } + field(SIOL,DBF_OUTLINK) { + prompt("Simulation Output Link") + promptgroup("90 - Simulate") + interest(1) + } + field(SIML,DBF_INLINK) { + prompt("Simulation Mode Link") + promptgroup("90 - Simulate") + interest(1) + } + field(SIMM,DBF_MENU) { + prompt("Simulation Mode") + special(SPC_MOD) + interest(1) + menu(menuYesNo) + } + field(SIMS,DBF_MENU) { + prompt("Simulation Mode Severity") + promptgroup("90 - Simulate") + interest(2) + menu(menuAlarmSevr) + } + field(OLDSIMM,DBF_MENU) { + prompt("Prev. Simulation Mode") + special(SPC_NOMOD) + interest(4) + menu(menuSimm) + } + field(SSCN,DBF_MENU) { + prompt("Sim. Mode Scan") + promptgroup("90 - Simulate") + interest(1) + menu(menuScan) + initial("65535") + } + field(SDLY,DBF_DOUBLE) { + prompt("Sim. Mode Async Delay") + promptgroup("90 - Simulate") + interest(2) + initial("-1.0") + } + %#include "callback.h" + field(SIMPVT,DBF_NOACCESS) { + prompt("Sim. Mode Private") + special(SPC_NOMOD) + interest(4) + extra("CALLBACK *simpvt") + } + field(IVOA,DBF_MENU) { + prompt("INVALID outpt action") + promptgroup("50 - Output") + interest(2) + menu(menuIvoa) + } + field(IVOV,DBF_USHORT) { + prompt("INVALID output value") + promptgroup("50 - Output") + interest(2) + } + + +=head2 Record Support + +=head3 Record Support Routines + +=head2 C + +This routine initializes SIMM if SIML is a constant or creates a channel +access link if SIML is PV_LINK. If SIOL is a PV_LINK a channel access link +is created. + +This routine next checks to see that device support is available. The +routine next checks to see if the device support write routine is defined. + +If either device support or the device support write routine does not +exist, and error message is issued and processing is terminated. + +If DOL is a constant, then VAL is initialized to 1 if its value is nonzero +or initialzed to 0 if DOL is zero, and UDF is set to FALSE. + +If device support includes C, it is called. VAL is set using +RVAL, and UDF is set to FALSE. + +=head2 C + +See next section. + +=head2 C + +Fills in the values of struct valueDes so that they refer to VAL. + +=head2 C + +Retrieves ASCII string corresponding to VAL. + +=head2 C + +Retrieves ASCII strings for ZNAM and ONAM. + +=head2 C + +Checks if string matches ZNAM or ONAM, and if it does, sets VAL. + +=head2 Record Processing + +Routine process implements the following algorithm: + +=over 1 + +=item 1. +Check to see that the appropriate device support module exists. If it +doesn't, an error message is issued and processing is terminated with +the PACT field still set to TRUE. This ensures that processes will no +longer be called for this record. Thus error storms will not occur. + +=item 2. +If PACT is FALSE + +=back + +=over + +=item * +If DOL is DB_LINK and OMSL is CLOSED_LOOP + +=over + +=item * +get values from DOL + +=item * +check for link alarm + +=item * +force VAL to be 0 or 1 + +=item * +if MASK is defined + +=over + +=item * +if VAL is 0 set RVAL = 0 + +=back + +=item * +else set RVAL = MASK + +=back + +=back + +=over + +=item 3. +Check alarms: This routine checks to see if the new VAL causes the alarm +status and severity to change. If so, NSEV, NSTA, and LALM are set. + +=item 4. +Check severity and write the new value. See L +for more information on how INVALID alarms affect output. + +=item 5. +If PACT has been changed to TRUE, the device support write output routine +has started but has not completed writing the new value. in this case, the +processing routine merely returns, leaving PACT TRUE. + +=item 6. +Check WAIT. If VAL is 1 and WAIT is greater than 0, process again with a +VAL=0 after WAIT seconds. + +=item 7. +Check to see if monitors should be invoked. + +=back + +=over 1 + +=item * +Alarm monitors are invoked if the alarm status or severity has changed. + +=item * +Archive and value change monitors are invoked if MLST is not equal to VAL. + +=item * +Monitors for RVAL and for RBV are checked whenever other monitors are +invoked. + +=item * +NSEV and NSTA are reset to 0. + +=back + +=over + +=item 8 +Scan forward link if necessary, set PACT FALSE, and return + +=back + +=head2 Device support + +=head3 Fields Of Interest To Device Support + +Each binary output record must have an associated set of device support +routines. The primary responsibility of the device support routines is to +write a new value whenever C is called. The device support routines +are primarily interested in the following fields: + +=fields PACT, DPVT, NSEV, NSTA, VAL, OUT, RVAL, MASK, RBV + +=head3 Decive Support Routines + +Device support consists of the following routines: + +=head2 C + +Not currently used. + +=head2 C + +This routine is called once during IOC initialization. + +=head2 C + +This routine is optional. If provided, it is called by record support +C routine. It should determine MASK if it is needed. + +=over + +=item * +0: Success. RVAL modified (VAL will be set accordingly) + +=item * +2: Success. VAL modified + +=item * +other: Error + +=back + +=head2 C + +This routine is called by the ioEventScan system each time the record is +added or deleted from an I/O event scan list. C has the value (0,1) if +the record is being (added to, deleted from) an I/O event list. It must be +provided for any device type that can use the ioEvent scanner. + +=head2 C + +This routine must output a new value. It returns the following values: + +=over + +=item * +0: Success + +=item * +other: Error. + +=back + +=head2 Device Support For Soft Records + +Two soft device support modules C and C are +provided for output records not related to actual hardware devices. The OUT +link type must be either CONSTANT, DB_LINK, or CA_LINK. + +=head3 Soft Channel + +This module writes the current value of VAL. + +If the OUT link type is PV_LINK, then C is called by +C. C always returns a value of 2, which means +that no conversion will ever be attempted. C calls +C to write the current value of VAL. See L +for details. + +=head3 Raw Soft Channel + +This module is like the previous except that it writes the current value of +RVAL + +=cut +} + +variable(boHIGHprecision, int) +variable(boHIGHlimit, double) diff --git a/modules/database/src/std/rec/calcRecord.dbd b/modules/database/src/std/rec/calcRecord.dbd deleted file mode 100644 index e7eb0eee3..000000000 --- a/modules/database/src/std/rec/calcRecord.dbd +++ /dev/null @@ -1,324 +0,0 @@ -#************************************************************************* -# Copyright (c) 2007 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. -#************************************************************************* -recordtype(calc) { - include "dbCommon.dbd" - field(VAL,DBF_DOUBLE) { - prompt("Result") - promptgroup("50 - Output") - asl(ASL0) - } - field(CALC,DBF_STRING) { - prompt("Calculation") - promptgroup("30 - Action") - special(SPC_CALC) - pp(TRUE) - size(80) - initial("0") - } - field(INPA,DBF_INLINK) { - prompt("Input A") - promptgroup("41 - Input A-F") - interest(1) - } - field(INPB,DBF_INLINK) { - prompt("Input B") - promptgroup("41 - Input A-F") - interest(1) - } - field(INPC,DBF_INLINK) { - prompt("Input C") - promptgroup("41 - Input A-F") - interest(1) - } - field(INPD,DBF_INLINK) { - prompt("Input D") - promptgroup("41 - Input A-F") - interest(1) - } - field(INPE,DBF_INLINK) { - prompt("Input E") - promptgroup("41 - Input A-F") - interest(1) - } - field(INPF,DBF_INLINK) { - prompt("Input F") - promptgroup("41 - Input A-F") - interest(1) - } - field(INPG,DBF_INLINK) { - prompt("Input G") - promptgroup("42 - Input G-L") - interest(1) - } - field(INPH,DBF_INLINK) { - prompt("Input H") - promptgroup("42 - Input G-L") - interest(1) - } - field(INPI,DBF_INLINK) { - prompt("Input I") - promptgroup("42 - Input G-L") - interest(1) - } - field(INPJ,DBF_INLINK) { - prompt("Input J") - promptgroup("42 - Input G-L") - interest(1) - } - field(INPK,DBF_INLINK) { - prompt("Input K") - promptgroup("42 - Input G-L") - interest(1) - } - field(INPL,DBF_INLINK) { - prompt("Input L") - promptgroup("42 - Input G-L") - interest(1) - } - field(EGU,DBF_STRING) { - prompt("Engineering Units") - promptgroup("80 - Display") - interest(1) - size(16) - prop(YES) - } - field(PREC,DBF_SHORT) { - prompt("Display Precision") - promptgroup("80 - Display") - interest(1) - prop(YES) - } - field(HOPR,DBF_DOUBLE) { - prompt("High Operating Rng") - promptgroup("80 - Display") - interest(1) - prop(YES) - } - field(LOPR,DBF_DOUBLE) { - prompt("Low Operating Range") - promptgroup("80 - Display") - interest(1) - prop(YES) - } - field(HIHI,DBF_DOUBLE) { - prompt("Hihi Alarm Limit") - promptgroup("70 - Alarm") - pp(TRUE) - interest(1) - prop(YES) - } - field(LOLO,DBF_DOUBLE) { - prompt("Lolo Alarm Limit") - promptgroup("70 - Alarm") - pp(TRUE) - interest(1) - prop(YES) - } - field(HIGH,DBF_DOUBLE) { - prompt("High Alarm Limit") - promptgroup("70 - Alarm") - pp(TRUE) - interest(1) - prop(YES) - } - field(LOW,DBF_DOUBLE) { - prompt("Low Alarm Limit") - promptgroup("70 - Alarm") - pp(TRUE) - interest(1) - prop(YES) - } - field(HHSV,DBF_MENU) { - prompt("Hihi Severity") - promptgroup("70 - Alarm") - pp(TRUE) - interest(1) - prop(YES) - menu(menuAlarmSevr) - } - field(LLSV,DBF_MENU) { - prompt("Lolo Severity") - promptgroup("70 - Alarm") - pp(TRUE) - interest(1) - prop(YES) - menu(menuAlarmSevr) - } - field(HSV,DBF_MENU) { - prompt("High Severity") - promptgroup("70 - Alarm") - pp(TRUE) - interest(1) - prop(YES) - menu(menuAlarmSevr) - } - field(LSV,DBF_MENU) { - prompt("Low Severity") - promptgroup("70 - Alarm") - pp(TRUE) - interest(1) - prop(YES) - menu(menuAlarmSevr) - } - field(AFTC, DBF_DOUBLE) { - prompt("Alarm Filter Time Constant") - promptgroup("70 - Alarm") - interest(1) - } - field(AFVL, DBF_DOUBLE) { - prompt("Alarm Filter Value") - special(SPC_NOMOD) - interest(3) - } - field(HYST,DBF_DOUBLE) { - prompt("Alarm Deadband") - promptgroup("70 - Alarm") - interest(1) - } - field(ADEL,DBF_DOUBLE) { - prompt("Archive Deadband") - promptgroup("80 - Display") - interest(1) - } - field(MDEL,DBF_DOUBLE) { - prompt("Monitor Deadband") - promptgroup("80 - Display") - interest(1) - } - field(A,DBF_DOUBLE) { - prompt("Value of Input A") - pp(TRUE) - } - field(B,DBF_DOUBLE) { - prompt("Value of Input B") - pp(TRUE) - } - field(C,DBF_DOUBLE) { - prompt("Value of Input C") - pp(TRUE) - } - field(D,DBF_DOUBLE) { - prompt("Value of Input D") - pp(TRUE) - } - field(E,DBF_DOUBLE) { - prompt("Value of Input E") - pp(TRUE) - } - field(F,DBF_DOUBLE) { - prompt("Value of Input F") - pp(TRUE) - } - field(G,DBF_DOUBLE) { - prompt("Value of Input G") - pp(TRUE) - } - field(H,DBF_DOUBLE) { - prompt("Value of Input H") - pp(TRUE) - } - field(I,DBF_DOUBLE) { - prompt("Value of Input I") - pp(TRUE) - } - field(J,DBF_DOUBLE) { - prompt("Value of Input J") - pp(TRUE) - } - field(K,DBF_DOUBLE) { - prompt("Value of Input K") - pp(TRUE) - } - field(L,DBF_DOUBLE) { - prompt("Value of Input L") - pp(TRUE) - } - field(LA,DBF_DOUBLE) { - prompt("Prev Value of A") - special(SPC_NOMOD) - interest(3) - } - field(LB,DBF_DOUBLE) { - prompt("Prev Value of B") - special(SPC_NOMOD) - interest(3) - } - field(LC,DBF_DOUBLE) { - prompt("Prev Value of C") - special(SPC_NOMOD) - interest(3) - } - field(LD,DBF_DOUBLE) { - prompt("Prev Value of D") - special(SPC_NOMOD) - interest(3) - } - field(LE,DBF_DOUBLE) { - prompt("Prev Value of E") - special(SPC_NOMOD) - interest(3) - } - field(LF,DBF_DOUBLE) { - prompt("Prev Value of F") - special(SPC_NOMOD) - interest(3) - } - field(LG,DBF_DOUBLE) { - prompt("Prev Value of G") - special(SPC_NOMOD) - interest(3) - } - field(LH,DBF_DOUBLE) { - prompt("Prev Value of H") - special(SPC_NOMOD) - interest(3) - } - field(LI,DBF_DOUBLE) { - prompt("Prev Value of I") - special(SPC_NOMOD) - interest(3) - } - field(LJ,DBF_DOUBLE) { - prompt("Prev Value of J") - special(SPC_NOMOD) - interest(3) - } - field(LK,DBF_DOUBLE) { - prompt("Prev Value of K") - special(SPC_NOMOD) - interest(3) - } - field(LL,DBF_DOUBLE) { - prompt("Prev Value of L") - special(SPC_NOMOD) - interest(3) - } - field(LALM,DBF_DOUBLE) { - prompt("Last Value Alarmed") - special(SPC_NOMOD) - interest(3) - } - field(ALST,DBF_DOUBLE) { - prompt("Last Value Archived") - special(SPC_NOMOD) - interest(3) - } - field(MLST,DBF_DOUBLE) { - prompt("Last Val Monitored") - special(SPC_NOMOD) - interest(3) - } - %#include "postfix.h" - field(RPCL,DBF_NOACCESS) { - prompt("Reverse Polish Calc") - special(SPC_NOMOD) - interest(4) - extra("char rpcl[INFIX_TO_POSTFIX_SIZE(80)]") - } -} diff --git a/modules/database/src/std/rec/calcRecord.dbd.pod b/modules/database/src/std/rec/calcRecord.dbd.pod new file mode 100644 index 000000000..c50667fb3 --- /dev/null +++ b/modules/database/src/std/rec/calcRecord.dbd.pod @@ -0,0 +1,948 @@ +#************************************************************************* +# Copyright (c) 2007 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. +#************************************************************************* + +=title Calculation Record (calc) + +The calculation or "Calc" record is used to perform algebraic, relational, +and logical operations on values retrieved from other records. The result +of its operations can then be accessed by another record so that it can +then be used. + +=head2 Parameter Fields + +The fields in the record fall into the following categories: + +=over 1 + +=item * +scan parameters + +=item * +read parameters + +=item * +expression parameters + +=item * +operator display parameters + +=item * +alarm parameters + +=item * +monitor parameters + +=item * +run-time parameters + +=back + +=recordtype calc + +=cut + +recordtype(calc) { + +=head3 Scan Parameters + +The Calc record has the standard fields for specifying under what +circumstances the record will be processed. These fields are listed in +L. In addition, L explains how these +fields are used. Since the Calc record supports no direct interfaces to +hardware, it cannot be scanned on I/O interrupt, so its SCAN field cannot +be C. + +=fields SCAN + + +=head3 Read Parameters + +The read parameters for the Calc record consist of 12 input links INPA, +INPB, ... INPL. The fields can be database links, channel access links, or +constants. If they are links, they must specify another record's field or a +channel access link. If they are constants, they will be initialized with +the value they are configured with and can be changed via C. They +cannot be hardware addresses. + +See L
    for information on how to specify database +links. + +=fields INPA, INPB, INPC, INPD, INPE, INPF, INPG, INPH, INPI, INPJ, INPK, INPL + +=head3 Expression + +At the core of the Calc record lies the CALC and RPCL fields. The CALC field +contains the infix expresion which the record routine will use when it +processes the record. The resulting value is placed in the VAL field and +can be accessed from there. The CALC expression is actually converted to +opcode and stored as Reverse Polish Notation in the RPCL field. It is this +expression which is actually used to calculate VAL. The Reverse Polish +expression is evaluated more efficiently during run-time than an infix +expression. CALC can be changed at run-time, and a special record routine +calls a function to convert it to Reverse Polish Notation. + +The infix expressions that can be used are very similar to the C expression +syntax, but with some additions and subtle differences in operator meaning +and precedence. The string may contain a series of expressions separated by +a semi-colon character ";" any one of which may actually provide the +calculation result; however, all of the other expressions included must +assign their result to a variable. All alphabetic elements described below +are case independent, so upper and lower case letters may be used and mixed +in the variable and function names as desired. Spaces may be used anywhere +within an expression except between characters that make up a single +expression element. + +The range of expressions supported by the calculation record are separated +into literals, constants, operands, algebraic operators, trigonometric operators, +relational operators, logical operators, the assignment operator, +parentheses and commas, and the question mark or '?:' operator. + +=fields CALC, RPCL + +=head3 Literals + +=over 1 + +=item * +Standard double precision floating point numbers + +=item * +Inf: Infinity + +=item * +Nan: Not a Number + +=back + +=head3 Constants + +=over 1 + +=item * +PI: returns the mathematical constant E + +=item * +D2R: evaluates to E/180 which, when used as a multiplier, converts an +angle from degrees to radians + +=item * +R2D: evaluates to 180/E which as a multiplier converts an angle from +radians to degrees + +=back + +=head3 Operands + +The expression uses the values retrieved from the INPx links as operands, +though constants can be used as operands too. These values retrieved from +the input links are stored in the A-L fields. The values to be used in the +expression are simply referenced by the field letter. For instance, the +value obtained from INPA link is stored in the field A, and the value +obtained from INPB is stored in field B. The field names can be included in +the expression which will operate on their respective values, as in A+B. +Also, the RNDM nullary function can be included as an operand in the +expression in order to generate a random number between 0 and 1. + +=fields A, B, C, D, E, F, G, H, I, J, K, L + +The keyword VAL returns the current contents of the VAL field (which can be +written to by a CA put, so it might I be the result from the last time +the expression was evaluated). + +=head3 Algebraic Operators + +=over 1 + +=item * +ABS: Absolute value (unary) + +=item * +SQR: Square root (unary) + +=item * +MIN: Minimum (any number of args) + +=item * +MAX: Maximum (any number of args) + +=item * +FINITE: returns non-zero if none of the arguments are NaN or Inf (any +number of args) + +=item * +ISNAN: returns non-zero if any of the arguments is NaN or Inf (any number +of args) + +=item * +CEIL: Ceiling (unary) + +=item * +FLOOR: Floor (unary) + +=item * +LOG: Log base 10 (unary) + +=item * +LOGE: Natural log (unary) + +=item * +LN: Natural log (unary) + +=item * +EXP: Exponential function (unary) + +=item * +^ : Exponential (binary) + +=item * +** : Exponential (binary) + +=item * ++ : Addition (binary) + +=item * +- : Subtraction (binary) + +=item * +* : Multiplication (binary) + +=item * +/ : Division (binary) + +=item * +% : Modulo (binary) + +=item * +NOT: Negate (unary) + +=back + +=head3 Trigonometric Operators + +=over 1 + +=item * +SIN: Sine + +=item * +SINH: Hyperbolic sine + +=item * +ASIN: Arc sine + +=item * +COS: Cosine + +=item * +COSH: Hyperbolic cosine + +=item * +ACOS: Arc cosine + +=item * +TAN: Tangent + +=item * +TANH: Hyperbolic tangent + +=item * +ATAN: Arc tangent + +=back + +=head3 Relational Operators + +=over 1 + +=item * +>= : Greater than or equal to + +=item * +> : Greater than + +=item * +<= : Less than or equal to + +=item * +< : Less than + +=item * +# : Not equal to + +=item * += : Equal to + +=back + +=head3 Logical Operators + +=over 1 + +=item * +&& : And + +=item * +|| : Or + +=item * +! : Not + +=back + +=head3 Bitwise Operators + +=over 1 + +=item * +| : Bitwise Or + +=item * +& : Bitwise And + +=item * +OR : Bitwise Or + +=item * +AND : Bitwise And + +=item * +XOR : Bitwise Exclusive Or + +=item * +~ : One's Complement + +=item * +<< : Left shift + +=item * +>> : Right shift + +=back + +=head3 Assignment Operator + +=over 1 + +=item * +:= : assigns a value (right hand side) to a variable (i.e. field) + +=back + +=head3 Parantheses, Comma, and Semicolon + +The open and close parentheses are supported. Nested parentheses are +supported. + +The comma is supported when used to separate the arguments of a binary +function. + +The semicolon is used to separate expressions. Although only one +traditional calculation expression is allowed, multiple assignment +expressions are allowed. + +=head3 Conditional Expression + +The C language's question mark operator is supported. The format is: +C + +=head3 Expression Examples + +=head3 Algebraic + +C + +=over 1 + +=item * +Result is A + B + 10 + +=back + +=head3 Relational + +C<(A + B) < (C + D)> + +=over 1 + +=item * +Result is 1 if (A + B) < (C + D) + +=item * +Result is 0 if (A + B) >= (C + D) + +=back + +=head3 Question Mark + +C<(A + B) < (C + D) ? E : F + L + 10> + +=over 1 + +=item * +Result is E if (A + B) < (C + D) + +=item * +Result is F + L + 10 if (A + B) >= (C + D) + +=back + +Prior to Base 3.14.9 it was legal to omit the : and the second (else) part +of the conditional, like this: + +C<(A + B)<(C + D) ? E> + +=over 1 + +=item +Result is E if (A + B)<(C + D) + +=item +Result is unchanged if (A + B)>=(C + D) + +From 3.14.9 onwards, this expresion must be written as +C<(A + B) < (C + D) ? E : VAL> + +=back + +=head3 Logical + +C + +=over 1 + +=item * +Causes the following to occur: + +=over 1 + +=item * +Convert A to integer + +=item * +Convert B to integer + +=item * +Bitwise And A and B + +=item * +Convert result to floating point + +=back + +=back + +=head3 Assignment + +C + +=over 1 + +=item * +Causes the Calc record to output the successive values of a sine curve in +1 degree intervals. + +=back + +=head3 Operator Display Parameters + +These parameters are used to present meaningful data to the operator. These +fields are used to display VAL and other parameters of the calculation +record either textually or graphically. + +The EGU field contains a string of up to 16 characters which is supplied by +the user and which describes the values being operated upon. The string is +retrieved whenever the routine C is called. The EGU string is +solely for an operator's sake and does not have to be used. + +The HOPR and LOPR fields only refer to the limits of the VAL, HIHI, HIGH, +LOW and LOLO fields. PREC controls the precision of the VAL field. + +See L for more on the record name (NAME) +and description (DESC) fields. + +=fields EGU, PREC, HOPR, LOPR, NAME, DESC + +=head3 Alarm Parameters + +The possible alarm conditions for the Calc record are the SCAN, READ, +Calculation, and limit alarms. The SCAN and READ alarms are called by the +record support routines. The Calculation alarm is called by the record +processing routine when the CALC expression is an invalid one, upon which +an error message is generated. + +The following alarm parameters which are configured by the user, define the +limit alarms for the VAL field and the severity corresponding to those +conditions. + +The HYST field defines an alarm deadband for each limit. See L +for a complete explanation of alarms of these fields. L +lists other fields related to alarms that are common to all record types. + +=fields HIHI, HIGH, LOW, LOLO, HHSV, HSV, LSV, LLSV, HYST + +=head3 Monitor Parameters + +These paramaeters are used to determine when to send monitors for the value +fields. These monitors are sent when the value field exceeds the last +monitored field by the appropriate deadband, the ADEL for archiver monitors +and the MDEL field for all other types of monitors. If these fields have a +value of zero, everytime the value changes, monitors are triggered; if they have a +value of -1, everytime the record is scanned, monitors are triggered. See +L for a complete explanation of monitors. + +=fields ADEL, MDEL + +=head3 Run-time Parameters + +These fields are not configurable using a configuration tool and none are +modifiable at run-time. They are used to process the record. + +The LALM field is used to implement the hysteresis factor for the alarm +limits. + +The LA-LL fields are used to decide when to trigger monitors for the +corresponding fields. For instance, if LA does not equal the value A, +monitors for A are triggered. The MLST and ALST fields are used in the same +manner for the VAL field. + +=fields LALM, ALST, MLST, LA, LB, LC, LD, LE, LF, LG, LH, LI, LJ, LK, LL + +=cut + + include "dbCommon.dbd" + field(VAL,DBF_DOUBLE) { + prompt("Result") + promptgroup("50 - Output") + asl(ASL0) + } + field(CALC,DBF_STRING) { + prompt("Calculation") + promptgroup("30 - Action") + special(SPC_CALC) + pp(TRUE) + size(80) + initial("0") + } + field(INPA,DBF_INLINK) { + prompt("Input A") + promptgroup("41 - Input A-F") + interest(1) + } + field(INPB,DBF_INLINK) { + prompt("Input B") + promptgroup("41 - Input A-F") + interest(1) + } + field(INPC,DBF_INLINK) { + prompt("Input C") + promptgroup("41 - Input A-F") + interest(1) + } + field(INPD,DBF_INLINK) { + prompt("Input D") + promptgroup("41 - Input A-F") + interest(1) + } + field(INPE,DBF_INLINK) { + prompt("Input E") + promptgroup("41 - Input A-F") + interest(1) + } + field(INPF,DBF_INLINK) { + prompt("Input F") + promptgroup("41 - Input A-F") + interest(1) + } + field(INPG,DBF_INLINK) { + prompt("Input G") + promptgroup("42 - Input G-L") + interest(1) + } + field(INPH,DBF_INLINK) { + prompt("Input H") + promptgroup("42 - Input G-L") + interest(1) + } + field(INPI,DBF_INLINK) { + prompt("Input I") + promptgroup("42 - Input G-L") + interest(1) + } + field(INPJ,DBF_INLINK) { + prompt("Input J") + promptgroup("42 - Input G-L") + interest(1) + } + field(INPK,DBF_INLINK) { + prompt("Input K") + promptgroup("42 - Input G-L") + interest(1) + } + field(INPL,DBF_INLINK) { + prompt("Input L") + promptgroup("42 - Input G-L") + interest(1) + } + field(EGU,DBF_STRING) { + prompt("Engineering Units") + promptgroup("80 - Display") + interest(1) + size(16) + prop(YES) + } + field(PREC,DBF_SHORT) { + prompt("Display Precision") + promptgroup("80 - Display") + interest(1) + prop(YES) + } + field(HOPR,DBF_DOUBLE) { + prompt("High Operating Rng") + promptgroup("80 - Display") + interest(1) + prop(YES) + } + field(LOPR,DBF_DOUBLE) { + prompt("Low Operating Range") + promptgroup("80 - Display") + interest(1) + prop(YES) + } + field(HIHI,DBF_DOUBLE) { + prompt("Hihi Alarm Limit") + promptgroup("70 - Alarm") + pp(TRUE) + interest(1) + prop(YES) + } + field(LOLO,DBF_DOUBLE) { + prompt("Lolo Alarm Limit") + promptgroup("70 - Alarm") + pp(TRUE) + interest(1) + prop(YES) + } + field(HIGH,DBF_DOUBLE) { + prompt("High Alarm Limit") + promptgroup("70 - Alarm") + pp(TRUE) + interest(1) + prop(YES) + } + field(LOW,DBF_DOUBLE) { + prompt("Low Alarm Limit") + promptgroup("70 - Alarm") + pp(TRUE) + interest(1) + prop(YES) + } + field(HHSV,DBF_MENU) { + prompt("Hihi Severity") + promptgroup("70 - Alarm") + pp(TRUE) + interest(1) + prop(YES) + menu(menuAlarmSevr) + } + field(LLSV,DBF_MENU) { + prompt("Lolo Severity") + promptgroup("70 - Alarm") + pp(TRUE) + interest(1) + prop(YES) + menu(menuAlarmSevr) + } + field(HSV,DBF_MENU) { + prompt("High Severity") + promptgroup("70 - Alarm") + pp(TRUE) + interest(1) + prop(YES) + menu(menuAlarmSevr) + } + field(LSV,DBF_MENU) { + prompt("Low Severity") + promptgroup("70 - Alarm") + pp(TRUE) + interest(1) + prop(YES) + menu(menuAlarmSevr) + } + field(AFTC, DBF_DOUBLE) { + prompt("Alarm Filter Time Constant") + promptgroup("70 - Alarm") + interest(1) + } + field(AFVL, DBF_DOUBLE) { + prompt("Alarm Filter Value") + special(SPC_NOMOD) + interest(3) + } + field(HYST,DBF_DOUBLE) { + prompt("Alarm Deadband") + promptgroup("70 - Alarm") + interest(1) + } + field(ADEL,DBF_DOUBLE) { + prompt("Archive Deadband") + promptgroup("80 - Display") + interest(1) + } + field(MDEL,DBF_DOUBLE) { + prompt("Monitor Deadband") + promptgroup("80 - Display") + interest(1) + } + field(A,DBF_DOUBLE) { + prompt("Value of Input A") + pp(TRUE) + } + field(B,DBF_DOUBLE) { + prompt("Value of Input B") + pp(TRUE) + } + field(C,DBF_DOUBLE) { + prompt("Value of Input C") + pp(TRUE) + } + field(D,DBF_DOUBLE) { + prompt("Value of Input D") + pp(TRUE) + } + field(E,DBF_DOUBLE) { + prompt("Value of Input E") + pp(TRUE) + } + field(F,DBF_DOUBLE) { + prompt("Value of Input F") + pp(TRUE) + } + field(G,DBF_DOUBLE) { + prompt("Value of Input G") + pp(TRUE) + } + field(H,DBF_DOUBLE) { + prompt("Value of Input H") + pp(TRUE) + } + field(I,DBF_DOUBLE) { + prompt("Value of Input I") + pp(TRUE) + } + field(J,DBF_DOUBLE) { + prompt("Value of Input J") + pp(TRUE) + } + field(K,DBF_DOUBLE) { + prompt("Value of Input K") + pp(TRUE) + } + field(L,DBF_DOUBLE) { + prompt("Value of Input L") + pp(TRUE) + } + field(LA,DBF_DOUBLE) { + prompt("Prev Value of A") + special(SPC_NOMOD) + interest(3) + } + field(LB,DBF_DOUBLE) { + prompt("Prev Value of B") + special(SPC_NOMOD) + interest(3) + } + field(LC,DBF_DOUBLE) { + prompt("Prev Value of C") + special(SPC_NOMOD) + interest(3) + } + field(LD,DBF_DOUBLE) { + prompt("Prev Value of D") + special(SPC_NOMOD) + interest(3) + } + field(LE,DBF_DOUBLE) { + prompt("Prev Value of E") + special(SPC_NOMOD) + interest(3) + } + field(LF,DBF_DOUBLE) { + prompt("Prev Value of F") + special(SPC_NOMOD) + interest(3) + } + field(LG,DBF_DOUBLE) { + prompt("Prev Value of G") + special(SPC_NOMOD) + interest(3) + } + field(LH,DBF_DOUBLE) { + prompt("Prev Value of H") + special(SPC_NOMOD) + interest(3) + } + field(LI,DBF_DOUBLE) { + prompt("Prev Value of I") + special(SPC_NOMOD) + interest(3) + } + field(LJ,DBF_DOUBLE) { + prompt("Prev Value of J") + special(SPC_NOMOD) + interest(3) + } + field(LK,DBF_DOUBLE) { + prompt("Prev Value of K") + special(SPC_NOMOD) + interest(3) + } + field(LL,DBF_DOUBLE) { + prompt("Prev Value of L") + special(SPC_NOMOD) + interest(3) + } + field(LALM,DBF_DOUBLE) { + prompt("Last Value Alarmed") + special(SPC_NOMOD) + interest(3) + } + field(ALST,DBF_DOUBLE) { + prompt("Last Value Archived") + special(SPC_NOMOD) + interest(3) + } + field(MLST,DBF_DOUBLE) { + prompt("Last Val Monitored") + special(SPC_NOMOD) + interest(3) + } + %#include "postfix.h" + field(RPCL,DBF_NOACCESS) { + prompt("Reverse Polish Calc") + special(SPC_NOMOD) + interest(4) + extra("char rpcl[INFIX_TO_POSTFIX_SIZE(80)]") + } + +=head2 Record Support + +=head3 Record Support Routines + +=head2 C + +For each constant input link, the corresponding value field is initialized +with the constant value if the input link is CONSTANT or a channel access +link is created if the input link is a PV_LINK. + +A routine postfix is called to convert the infix expression in CALC to +Reverse Polish Notation. The result is stored in RPCL. + +=head2 C + +See next section. + +=head2 C + +This is called if CALC is changed. C calls postfix. + +=head2 C + +Fills in the values of struct valueDes so that the refer to VAL. + +=head2 C + +Retrieves EGU. + +=head2 C + +Retrieves PREC. + +=head2 C + +Sets the upper display and lower display limits for a field. If the field +is VAL, HIHI, HIGH, LOW, or LOLO, the limits are set to HOPR and LOPR, else +if the field has upper and lower limits defined they will be used, else the +upper and lower maximum values for the field will be used. + +=head2 C + +Sets the upper control and the lower control limits for a field. If the +field is VAL, HIHI, HIGH, LOW, or LOLO, the limits are set to HOPR and +LOPR, else if the field has upper and lower limits defined they will be +used, else the upper and lower maximum values for the field type will be +used. + +=head2 C + +Sets the following values: + +=over 1 + +upper_alarm_limit = HIHI + +upper_warning_limit = HIGH + +lower_warning_limit = LOW + +lower_alarm_limit = LOLO + +=back + +=head3 Record Processing + +Routine process implements the following algorithm: + +=over 1 + +=item 1. +Fetch all arguments. + +=item 2. +Call routine C, which calculates VAL from the postfix version of +the expression given in CALC. If C returns success UDF is set to +FALSE. + +=item 3. +Check alarms. This routine checks to see if the new VAL causes the alarm +status and severity to change. If so, NSEV, NSTA, and LALM are set. It also +honors the alarm hysteresis factor (HYST). Thus the value must change by +at least HYST before the alarm status and severity changes. + +=item 4. +Check to see if monitors should be invoked. + +=back + +=over 1 + +=item * +Alarm monitors are invoked if the alarm status or severity has changed. + +=item * +Archive and values change monitors are invoked if ADEL and MDEL conditions +are met. + +=item * +Monitors for A-L are checked whenever other monitors are invoked. + +=item * +NSEV and NSTA are reset to 0. + +=back + +=over + +=item 5. +Scan forward link if necessary, set PACT FALSE, and return. + +=back + +=cut + +} diff --git a/modules/database/src/std/rec/calcoutRecord.dbd b/modules/database/src/std/rec/calcoutRecord.dbd deleted file mode 100644 index 5bf2e54de..000000000 --- a/modules/database/src/std/rec/calcoutRecord.dbd +++ /dev/null @@ -1,530 +0,0 @@ -#************************************************************************* -# Copyright (c) 2007 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. -#************************************************************************* -menu(calcoutOOPT) { - choice(calcoutOOPT_Every_Time,"Every Time") - choice(calcoutOOPT_On_Change,"On Change") - choice(calcoutOOPT_When_Zero,"When Zero") - choice(calcoutOOPT_When_Non_zero,"When Non-zero") - choice(calcoutOOPT_Transition_To_Zero,"Transition To Zero") - choice(calcoutOOPT_Transition_To_Non_zero,"Transition To Non-zero") -} -menu(calcoutDOPT) { - choice(calcoutDOPT_Use_VAL,"Use CALC") - choice(calcoutDOPT_Use_OVAL,"Use OCAL") -} -menu(calcoutINAV) { - choice(calcoutINAV_EXT_NC,"Ext PV NC") - choice(calcoutINAV_EXT,"Ext PV OK") - choice(calcoutINAV_LOC,"Local PV") - choice(calcoutINAV_CON,"Constant") -} -recordtype(calcout) { - include "dbCommon.dbd" - field(RPVT,DBF_NOACCESS) { - prompt("Record Private") - special(SPC_NOMOD) - interest(4) - extra("struct rpvtStruct *rpvt") - } - field(VAL,DBF_DOUBLE) { - prompt("Result") - promptgroup("50 - Output") - asl(ASL0) - } - field(PVAL,DBF_DOUBLE) { - prompt("Previous Value") - } - field(CALC,DBF_STRING) { - prompt("Calculation") - promptgroup("30 - Action") - special(SPC_CALC) - pp(TRUE) - size(80) - initial("0") - } - field(CLCV,DBF_LONG) { - prompt("CALC Valid") - interest(1) - } - field(INPA,DBF_INLINK) { - prompt("Input A") - special(SPC_MOD) - promptgroup("41 - Input A-F") - interest(1) - } - field(INPB,DBF_INLINK) { - prompt("Input B") - special(SPC_MOD) - promptgroup("41 - Input A-F") - interest(1) - } - field(INPC,DBF_INLINK) { - prompt("Input C") - special(SPC_MOD) - promptgroup("41 - Input A-F") - interest(1) - } - field(INPD,DBF_INLINK) { - prompt("Input D") - special(SPC_MOD) - promptgroup("41 - Input A-F") - interest(1) - } - field(INPE,DBF_INLINK) { - prompt("Input E") - special(SPC_MOD) - promptgroup("41 - Input A-F") - interest(1) - } - field(INPF,DBF_INLINK) { - prompt("Input F") - special(SPC_MOD) - promptgroup("41 - Input A-F") - interest(1) - } - field(INPG,DBF_INLINK) { - prompt("Input G") - special(SPC_MOD) - promptgroup("42 - Input G-L") - interest(1) - } - field(INPH,DBF_INLINK) { - prompt("Input H") - special(SPC_MOD) - promptgroup("42 - Input G-L") - interest(1) - } - field(INPI,DBF_INLINK) { - prompt("Input I") - special(SPC_MOD) - promptgroup("42 - Input G-L") - interest(1) - } - field(INPJ,DBF_INLINK) { - prompt("Input J") - special(SPC_MOD) - promptgroup("42 - Input G-L") - interest(1) - } - field(INPK,DBF_INLINK) { - prompt("Input K") - special(SPC_MOD) - promptgroup("42 - Input G-L") - interest(1) - } - field(INPL,DBF_INLINK) { - prompt("Input L") - special(SPC_MOD) - promptgroup("42 - Input G-L") - interest(1) - } - field(OUT,DBF_OUTLINK) { - prompt("Output Specification") - special(SPC_MOD) - promptgroup("50 - Output") - interest(1) - } - field(INAV,DBF_MENU) { - prompt("INPA PV Status") - special(SPC_NOMOD) - interest(1) - menu(calcoutINAV) - initial("1") - } - field(INBV,DBF_MENU) { - prompt("INPB PV Status") - special(SPC_NOMOD) - interest(1) - menu(calcoutINAV) - initial("1") - } - field(INCV,DBF_MENU) { - prompt("INPC PV Status") - special(SPC_NOMOD) - interest(1) - menu(calcoutINAV) - initial("1") - } - field(INDV,DBF_MENU) { - prompt("INPD PV Status") - special(SPC_NOMOD) - interest(1) - menu(calcoutINAV) - initial("1") - } - field(INEV,DBF_MENU) { - prompt("INPE PV Status") - special(SPC_NOMOD) - interest(1) - menu(calcoutINAV) - initial("1") - } - field(INFV,DBF_MENU) { - prompt("INPF PV Status") - special(SPC_NOMOD) - interest(1) - menu(calcoutINAV) - initial("1") - } - field(INGV,DBF_MENU) { - prompt("INPG PV Status") - special(SPC_NOMOD) - interest(1) - menu(calcoutINAV) - initial("1") - } - field(INHV,DBF_MENU) { - prompt("INPH PV Status") - special(SPC_NOMOD) - interest(1) - menu(calcoutINAV) - initial("1") - } - field(INIV,DBF_MENU) { - prompt("INPI PV Status") - special(SPC_NOMOD) - interest(1) - menu(calcoutINAV) - initial("1") - } - field(INJV,DBF_MENU) { - prompt("INPJ PV Status") - special(SPC_NOMOD) - interest(1) - menu(calcoutINAV) - initial("1") - } - field(INKV,DBF_MENU) { - prompt("INPK PV Status") - special(SPC_NOMOD) - interest(1) - menu(calcoutINAV) - initial("1") - } - field(INLV,DBF_MENU) { - prompt("INPL PV Status") - special(SPC_NOMOD) - interest(1) - menu(calcoutINAV) - initial("1") - } - field(OUTV,DBF_MENU) { - prompt("OUT PV Status") - special(SPC_NOMOD) - interest(1) - menu(calcoutINAV) - } - field(OOPT,DBF_MENU) { - prompt("Output Execute Opt") - promptgroup("50 - Output") - interest(1) - menu(calcoutOOPT) - } - field(ODLY,DBF_DOUBLE) { - prompt("Output Execute Delay") - promptgroup("50 - Output") - asl(ASL0) - interest(1) - } - field(DLYA,DBF_USHORT) { - prompt("Output Delay Active") - special(SPC_NOMOD) - asl(ASL0) - } - field(DOPT,DBF_MENU) { - prompt("Output Data Opt") - promptgroup("30 - Action") - interest(1) - menu(calcoutDOPT) - } - field(OCAL,DBF_STRING) { - prompt("Output Calculation") - promptgroup("30 - Action") - special(SPC_CALC) - pp(TRUE) - size(80) - initial("0") - } - field(OCLV,DBF_LONG) { - prompt("OCAL Valid") - interest(1) - } - field(OEVT,DBF_STRING) { - prompt("Event To Issue") - promptgroup("30 - Action") - special(SPC_MOD) - asl(ASL0) - size(40) - } - %#include "dbScan.h" - field(EPVT, DBF_NOACCESS) { - prompt("Event private") - special(SPC_NOMOD) - interest(4) - extra("EVENTPVT epvt") - } - field(IVOA,DBF_MENU) { - prompt("INVALID output action") - promptgroup("50 - Output") - interest(2) - menu(menuIvoa) - } - field(IVOV,DBF_DOUBLE) { - prompt("INVALID output value") - promptgroup("50 - Output") - interest(2) - } - field(EGU,DBF_STRING) { - prompt("Engineering Units") - promptgroup("80 - Display") - interest(1) - size(16) - prop(YES) - } - field(PREC,DBF_SHORT) { - prompt("Display Precision") - promptgroup("80 - Display") - interest(1) - prop(YES) - } - field(HOPR,DBF_DOUBLE) { - prompt("High Operating Rng") - promptgroup("80 - Display") - interest(1) - prop(YES) - } - field(LOPR,DBF_DOUBLE) { - prompt("Low Operating Range") - promptgroup("80 - Display") - interest(1) - prop(YES) - } - field(HIHI,DBF_DOUBLE) { - prompt("Hihi Alarm Limit") - promptgroup("70 - Alarm") - pp(TRUE) - interest(1) - prop(YES) - } - field(LOLO,DBF_DOUBLE) { - prompt("Lolo Alarm Limit") - promptgroup("70 - Alarm") - pp(TRUE) - interest(1) - prop(YES) - } - field(HIGH,DBF_DOUBLE) { - prompt("High Alarm Limit") - promptgroup("70 - Alarm") - pp(TRUE) - interest(1) - prop(YES) - } - field(LOW,DBF_DOUBLE) { - prompt("Low Alarm Limit") - promptgroup("70 - Alarm") - pp(TRUE) - interest(1) - prop(YES) - } - field(HHSV,DBF_MENU) { - prompt("Hihi Severity") - promptgroup("70 - Alarm") - pp(TRUE) - interest(1) - prop(YES) - menu(menuAlarmSevr) - } - field(LLSV,DBF_MENU) { - prompt("Lolo Severity") - promptgroup("70 - Alarm") - pp(TRUE) - interest(1) - prop(YES) - menu(menuAlarmSevr) - } - field(HSV,DBF_MENU) { - prompt("High Severity") - promptgroup("70 - Alarm") - pp(TRUE) - interest(1) - prop(YES) - menu(menuAlarmSevr) - } - field(LSV,DBF_MENU) { - prompt("Low Severity") - promptgroup("70 - Alarm") - pp(TRUE) - interest(1) - prop(YES) - menu(menuAlarmSevr) - } - field(HYST,DBF_DOUBLE) { - prompt("Alarm Deadband") - promptgroup("70 - Alarm") - interest(1) - } - field(ADEL,DBF_DOUBLE) { - prompt("Archive Deadband") - promptgroup("80 - Display") - interest(1) - } - field(MDEL,DBF_DOUBLE) { - prompt("Monitor Deadband") - promptgroup("80 - Display") - interest(1) - } - field(A,DBF_DOUBLE) { - prompt("Value of Input A") - pp(TRUE) - } - field(B,DBF_DOUBLE) { - prompt("Value of Input B") - pp(TRUE) - } - field(C,DBF_DOUBLE) { - prompt("Value of Input C") - pp(TRUE) - } - field(D,DBF_DOUBLE) { - prompt("Value of Input D") - pp(TRUE) - } - field(E,DBF_DOUBLE) { - prompt("Value of Input E") - pp(TRUE) - } - field(F,DBF_DOUBLE) { - prompt("Value of Input F") - pp(TRUE) - } - field(G,DBF_DOUBLE) { - prompt("Value of Input G") - pp(TRUE) - } - field(H,DBF_DOUBLE) { - prompt("Value of Input H") - pp(TRUE) - } - field(I,DBF_DOUBLE) { - prompt("Value of Input I") - pp(TRUE) - } - field(J,DBF_DOUBLE) { - prompt("Value of Input J") - pp(TRUE) - } - field(K,DBF_DOUBLE) { - prompt("Value of Input K") - pp(TRUE) - } - field(L,DBF_DOUBLE) { - prompt("Value of Input L") - pp(TRUE) - } - field(OVAL,DBF_DOUBLE) { - prompt("Output Value") - asl(ASL0) - } - field(LA,DBF_DOUBLE) { - prompt("Prev Value of A") - special(SPC_NOMOD) - interest(3) - } - field(LB,DBF_DOUBLE) { - prompt("Prev Value of B") - special(SPC_NOMOD) - interest(3) - } - field(LC,DBF_DOUBLE) { - prompt("Prev Value of C") - special(SPC_NOMOD) - interest(3) - } - field(LD,DBF_DOUBLE) { - prompt("Prev Value of D") - special(SPC_NOMOD) - interest(3) - } - field(LE,DBF_DOUBLE) { - prompt("Prev Value of E") - special(SPC_NOMOD) - interest(3) - } - field(LF,DBF_DOUBLE) { - prompt("Prev Value of F") - special(SPC_NOMOD) - interest(3) - } - field(LG,DBF_DOUBLE) { - prompt("Prev Value of G") - special(SPC_NOMOD) - interest(3) - } - field(LH,DBF_DOUBLE) { - prompt("Prev Value of H") - special(SPC_NOMOD) - interest(3) - } - field(LI,DBF_DOUBLE) { - prompt("Prev Value of I") - special(SPC_NOMOD) - interest(3) - } - field(LJ,DBF_DOUBLE) { - prompt("Prev Value of J") - special(SPC_NOMOD) - interest(3) - } - field(LK,DBF_DOUBLE) { - prompt("Prev Value of K") - special(SPC_NOMOD) - interest(3) - } - field(LL,DBF_DOUBLE) { - prompt("Prev Value of L") - special(SPC_NOMOD) - interest(3) - } - field(POVL,DBF_DOUBLE) { - prompt("Prev Value of OVAL") - asl(ASL0) - } - field(LALM,DBF_DOUBLE) { - prompt("Last Value Alarmed") - special(SPC_NOMOD) - interest(3) - } - field(ALST,DBF_DOUBLE) { - prompt("Last Value Archived") - special(SPC_NOMOD) - interest(3) - } - field(MLST,DBF_DOUBLE) { - prompt("Last Val Monitored") - special(SPC_NOMOD) - interest(3) - } - %#include "postfix.h" - field(RPCL,DBF_NOACCESS) { - prompt("Reverse Polish Calc") - special(SPC_NOMOD) - interest(4) - extra("char rpcl[INFIX_TO_POSTFIX_SIZE(80)]") - } - field(ORPC,DBF_NOACCESS) { - prompt("Reverse Polish OCalc") - special(SPC_NOMOD) - interest(4) - extra("char orpc[INFIX_TO_POSTFIX_SIZE(80)]") - } -} - -variable(calcoutODLYprecision, int) -variable(calcoutODLYlimit, double) diff --git a/modules/database/src/std/rec/calcoutRecord.dbd.pod b/modules/database/src/std/rec/calcoutRecord.dbd.pod new file mode 100644 index 000000000..6289f9043 --- /dev/null +++ b/modules/database/src/std/rec/calcoutRecord.dbd.pod @@ -0,0 +1,1271 @@ +#************************************************************************* +# Copyright (c) 2007 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. +#************************************************************************* + +=title Calculation Output Record (calcout) + +The Calculation Output or "Calcout" record is similar to the Calc record +with the added feature of having outputs (an "output link" and an "output +event") which are conditionally executed based on the result of the +calculation. This feature allows conditional branching to be implemented +within an EPICS database (e.g. process Record_A only if Record_B has a +value of 0). The Calcout record is also similar to the Wait record (with +additional features) but uses EPICS standard INLINK and OUTLINK fields +rather than the DBF_STRING fields used in the Wait record. For new +databases, it is recommended that the Calcout record be used instead of the +Wait record. + +=head2 Parameter Fields + +The fields in this record fall into these categories: + +=over 1 + +=item * +scan parameters + +=item * +read parameters + +=item * +expression parameters + +=item * +output parameters + +=item * +operator display parameters + +=item * +alarm parameters + +=item * +monitor parameters + +=item * +run-time parameters + +=back + +=recordtype calcout + +=cut + +menu(calcoutOOPT) { + choice(calcoutOOPT_Every_Time,"Every Time") + choice(calcoutOOPT_On_Change,"On Change") + choice(calcoutOOPT_When_Zero,"When Zero") + choice(calcoutOOPT_When_Non_zero,"When Non-zero") + choice(calcoutOOPT_Transition_To_Zero,"Transition To Zero") + choice(calcoutOOPT_Transition_To_Non_zero,"Transition To Non-zero") +} +menu(calcoutDOPT) { + choice(calcoutDOPT_Use_VAL,"Use CALC") + choice(calcoutDOPT_Use_OVAL,"Use OCAL") +} +menu(calcoutINAV) { + choice(calcoutINAV_EXT_NC,"Ext PV NC") + choice(calcoutINAV_EXT,"Ext PV OK") + choice(calcoutINAV_LOC,"Local PV") + choice(calcoutINAV_CON,"Constant") +} + +recordtype(calcout) { + +=head3 Scan Parameters + +The Calcout record has the standard fields for specifying under what +circumstances the record will be processed. The fields are listed in +L. In addition, L explains how these +fields are used. Since the Calcout record supports no direct interfaces to +hardware, it cannot be scanned on I/O interrupt, so its SCAN field cannot +be C. + +=head3 Read Parameters + +The read parameters for the Calcout record consists of 12 input links INPA, +INPB, ... INPL. The fields can be database links, channel access links, or +constants. If they are links, they must specify another record's field. If +they are constants, they will be initialized with the value they are +configured with and can be changed via C. These fields cannot be +hardware addresses. In addition, the Calcout record contains the INAV, +INBV, ... INLV fields which indicate the status of the link fields, for +example, whether or not the specified PV was found and a link to it +established. See L for an explanation of these +fields. + +See L
    for information on how to specify database +links. + +=fields INPA, INPB, INPC, INPD, INPE, INPF, INPG, INPH, INPI, INPJ, INPK, INPL + +=head3 Expression + +Like the Calc record, the Calcout record has a CALC field in which the +developer can enter an infix expression which the record routine will +evaluate when it processes the record. The resulting value is placed in the +VAL field. This value can then be used by the OOPT field (see +L) to determine whether or not to write to the output +link or post an output event. It can also be the value that is written to +the output link. The CALC expression is actually converted to opcode and +stored in Reverse Polish Notation in the RPCL field. It is this expression +which is actually used to calculate VAL. The Reverse Polish expression is +evaluated more efficiently during run-time than an infix expression. CALC +can be changes at run-time, and a special record routine will call a +function to convert it to Reverse Polish Notation. + +The infix expressions that can be used are very similar to the C expression +syntax, but with some additions and subtle differences in operator meaning +and precedence. The string may contain a series of expressions separated by +a semi-colon character ';' any one of which may actually provide the +calculation result; however all of the other expressions included must +assign their result to a variable. All alphabetic elements described below +are case independent, so upper and lower case letters may be used and mixed +in the variable and function names as desired. Spaces may be used anywhere +within an expression except between the characters that make up a single +expression element. + +The range of expressions supported by the calculation record are separated into +literals, constants, operands, algebraic operators, trigonometric operators, +relational operators, logical operator, the assignment operator, +parentheses and commas, and the question mark or '?:' operator. + +=fields CALC, VAL, RPCL + +=head3 Literals + +=over 1 + +=item * +Standard double precision floating point numbers + +=item * +Inf: Infinity + +=item * +Nan: Not a Number + +=back + +=head3 Constants + +=over + +=item * +PI: returns the mathematical constant E + +=item * +D2R: evaluates to E/180 which, when used as a multiplier, converts an +angle from degrees to radians + +=item * +R2D: evaluates to 180/E which, when used as a multiplier, converts an +angle from radians to degrees + +=back + +=head3 Operands + +The expression can use the values retrieved from the INPx links as +operands, though constants can be used as operands too. These values +retrieved from the input links are stored in the A-L fields. The values to +be used in the expression are simple references by the field letter. For +instance, the value obtained from the INPA link is stored in field A, and +the values obtained from the INPB link is stored in the field B. The names +can be included in the expression will operate on their respective values, +as in A+B. + +=fields A, B, C, D, E, F, G, H, I, J, K, L + +The keyword VAL returns the current contents of the expression's result +field, i.e. the VAL field for the CALC expression and the OVAL field for +the OCAL expression. (These fields can be written to by CA put, so it might +I be the result from the last time the expression was evaluated). + +=head3 Algebraic Operations + +=over 1 + +=item * +ABS: Absolute value (unary) + +=item * +SQR: Square root (unary) + +=item * +MIN: Minimum (any number of args) + +=item * +MAX: Maximum (any number of args) + +=item * +FINITE: returns non-zero if none of the arguments are NaN or Inf (any +number of args) + +=item * +ISNAN: returns non-zero if any of the arguments is NaN or Inf (any number +of args) + +=item * +CEIL: Ceiling (unary) + +=item * +FLOOR: Floor (unary) + +=item * +LOG: Log base 10 (unary) + +=item * +LOGE: Natural log (unary) + +=item * +LN: Natural log (unary) + +=item * +EXP: Exponential function (unary) + +=item * +^ : Exponential (binary) + +=item * +** : Exponential (binary) + +=item * ++ : Addition (binary) + +=item * +- : Subtraction (binary) + +=item * +* : Multiplication (binary) + +=item * +/ : Division (binary) + +=item * +% : Modulo (binary) + +=item * +NOT: Negate (unary) + +=back + +=head3 Trigonometric Operators + +=over 1 + +=item * +SIN: Sine + +=item * +SINH: Hyperbolic sine + +=item * +ASIN: Arc sine + +=item * +COS: Cosine + +=item * +COSH: Hyperbolic cosine + +=item * +ACOS: Arc cosine + +=item * +TAN: Tangent + +=item * +TANH: Hyperbolic tangent + +=item * +ATAN: Arc tangent + +=back + +=head3 Relational Operators + +=over 1 + +=item * +>= : Greater than or equal to + +=item * +> : Greater than + +=item * +<= : Less than or equal to + +=item * +< : Less than + +=item * +# : Not equal to + +=item * += : Equal to + +=back + +=head3 Logical Operators + +=over 1 + +=item * +&& : And + +=item * +|| : Or + +=item * +! : Not + +=back + +=head3 Bitwise Operators + +=over 1 + +=item * +| : Bitwise Or + +=item * +& : Bitwise And + +=item * +OR : Bitwise Or + +=item * +AND : Bitwise And + +=item * +XOR : Bitwise Exclusive Or + +=item * +~ : One's Complement + +=item * +<< : Left shift + +=item * +>> : Right shift + +=back + +=head3 Assignment Operator + +=over 1 + +=item * +:= : assigns a value (right hand side) to a variable (i.e. field) + +=back + +=head3 Parentheses and Comma + +The open and close parentheses are supported. Nested parentheses are +supported. + +The comma is supported when used to separate the arguments of a binary +function. + +=head3 Conditional Expression + +The C language's question mark operator is supported. The format is: +C + +=head3 Expression Examples + +=head3 Algebraic + +C + +=over 1 + +=item * +Result is A + B + 10 + +=back + +=head3 Relational + +C<(A + B) < (C + D)> + +=over 1 + +=item * +Result is 1 if (A + B) < (C + D) + +=item * +Result is 0 if (A + B) >= (C + D) + +=back + +=head3 Question Mark + +C<(A + B) < (C + D) ? E : F + L + 10> + +=over 1 + +=item * +Result is E if (A + B) < (C + D) + +=item * +Result is F + L + 10 if (A + B) >= (C + D) + +=back + +=head3 Logical + +C + +=over 1 + +=item * +Causes the following to occur: + +=over 1 + +=item * +Convert A to integer + +=item * +Convert B to integer + +=item * +Bitwise And A and B + +=item * +Convert result to floating point + +=back + +=back + +=head3 Output Parameters + +These parameters specify and control the output capabilities of the Calcout +record. They determine when to write the output, where to write it, and what +the output will be. The OUT link specifies the Process Variable to which +the result will be written. + +=head4 Menu calcoutOOPT + +The OOPT field determines the condition that causes the output link to be +written to. It's a menu field that has six choices: + +=menu calcoutOOPT + +=over + +=item * +C -- write output every time record is processed. + +=item * +C -- write output every time VAL changes, i.e., every time the +result of the expression changes. + +=item * +C -- when record is preocessed, write output if VAL is zero. + +=item * +C -- when record is processed, write output if VAL is +non-zero. + +=item * +C -- when record is processed, write output only if VAL +is zero and the last value was non-zero. + +=item * +C -- when record is processed, write output only if +VAL is non-zero and last value was zero. + +=back + +=head4 Menu calcoutDOPT + +The DOPT field determines what data is written to the output link when the +output is executed. The field is a menu field with two options: + +=menu calcoutDOPT + +If C is specified, when the record writes its +output it will write the result of the expression in the CALC field, that +is, it will write the value of the VAL field. If C is specified, +the record will instead write the result of the expresion in the OCAL +field, which is contained in the OVAL field. The OCAL field is exactly like +the CALC field and has the same fuctionality it can contain the string +representation of an expression which is evaluated at run-time. Thus, if +necessary, the record can use the result of the CALC expression to +determine if data should be written and can use the result of the OCAL +expression as the data to write. + +If the OEVT field specifies a non-zero integer and the condition in the +OOPT field is met, the record will post a corresponding event. If the ODLY +field is non-zero, the record pauses for the specified number of seconds +before executing the OUT link or posting the output event. During this +waiting period the record is "active" and will not be processed again until +the wait is over. The field DLYA is equal to 1 during the delay period. The +resolution of the delay entry system dependent. + +The IVOA field specifies what action to take with the OUT link if the +Calcout record eneters an INVALID alarm status. The options are +C, C, and C. +If the IVOA field is C, the data entered into the +IVOV field is written to the OUT link if the record alarm severity is +INVALID. + +=fields OUT, OOPT, DOPT, OCAL, OVAL, OEVT, ODLY, IVOA, IVOV + +=head3 Operator Display Parameter + +These parameters are used to present meaningful data to the operator. Some +are also meant to represent the status of the record at run-time. + +The EGU field contains a string of up to 16 characters which is supplied by +the user and which describes the values being operated upon. The string is +retrieved whenever the routine C is called. The EGU string is +solely for an operator's sake and does not have to be used. + +The HOPR and LOPR fields on;y refer to the limits if the VAL, HIHI, HIGH, +LOW, and LOLO fields. PREC controls the precision of the VAL field. + +=head4 Menu calcoutINAV + +The INAV-INLV fields indicate the status of the link to the PVs specified +in the INPA-INPL fields, respectfully. These field can have four possible +values: + +=menu calcoutINAV + +=over 1 + +=item * +C -- the PV wasn't found on this IOC and a Channel Access link +hasn't been established. + +=item * +C -- the PV wasn't found on this IOC and a Channel Access link +has been established. + +=item * +C -- the PV was found on this IOC. + +=item * +C -- the corresponding link field is a constant. + +=back + +The OUTV field indicates the status of the OUT link. If has the same +possible values as the INAV-INLV fields. + +The CLCV and OLCV fields indicate the validity of the expression in the +CALC and OCAL fields respectfully. If the expression in invalid, the field +is set to one. + +The DYLA field is set to one during the delay specified in ODLY. + +See L for more information on the record +name (NAME) and description (DESC) fields. + +=fields EGU, PREC, HOPR, LOPR, INAV, INBV, INCV, INDV, INEV, INFV, INGV, INHV, INIV, INJV, INKV, INLV, OUTV, CLCV, OCLV, DLYA, NAME, DESC + +=head3 Alarm Parameters + +The possible alarm conditions for the Calcout record are the SCAN, READ, +Calculation, and limit alarms. The SCAN and READ alarms are called by the +record support routines. The Calculation alarm is called by the record +processing routine when the CALC expression is an invalid one, upon which +an error message is generated. + +The following alarm parametersi, which are configured by the user, define the +limit alarms for the VAL field and the severity corresponding to those +conditions. + +The HYST field defines an alarm deadband for each limit. See +L for a complete explanation of alarms and these +fields. C lists other fields related to alarms that are +common to all record types. + +=fields HIHI, HIGH, LOW, LOLO, HHSV, HSV, LSV, LLSV, HYST + +=head3 Monitor Parameters + +These parameters are used to determine when to send monitors for the value +fields. These monitors are sent when the value field exceeds the last +monitored field by the appropriate deadband, the ADEL for archiver monitors +and the MDEL field for all aother types of monitors. If these fields have a +value of zero, every time the value changes, monitors are triggered; if +they have a value of -1, every time the record is scanned, monitors are +triggered. See L for a complete explanation of +monitors. + +=fields ADEL, MDEL + +=head3 Run-time Parameters + +These fields are not configurable using a configuration tool and none are +modifiable at run-time. They are used to process the record. + +The LALM field is used to implement the hysteresis factor for the alarm +limits. + +The LA-LL fields are used to decide when to trigger monitors for the +corresponding fields. For instance, if LA does not equal the value for A, +monitors for A are triggered. The MLST and ALST fields are used in the same +manner for the VAL field. + +=fields LALM, ALST, MLST, LA, LB, LC, LD, LE, LF, LG, LH, LI, LJ, LK, LL + +=cut + + include "dbCommon.dbd" + field(RPVT,DBF_NOACCESS) { + prompt("Record Private") + special(SPC_NOMOD) + interest(4) + extra("struct rpvtStruct *rpvt") + } + field(VAL,DBF_DOUBLE) { + prompt("Result") + promptgroup("50 - Output") + asl(ASL0) + } + field(PVAL,DBF_DOUBLE) { + prompt("Previous Value") + } + field(CALC,DBF_STRING) { + prompt("Calculation") + promptgroup("30 - Action") + special(SPC_CALC) + pp(TRUE) + size(80) + initial("0") + } + field(CLCV,DBF_LONG) { + prompt("CALC Valid") + interest(1) + } + field(INPA,DBF_INLINK) { + prompt("Input A") + special(SPC_MOD) + promptgroup("41 - Input A-F") + interest(1) + } + field(INPB,DBF_INLINK) { + prompt("Input B") + special(SPC_MOD) + promptgroup("41 - Input A-F") + interest(1) + } + field(INPC,DBF_INLINK) { + prompt("Input C") + special(SPC_MOD) + promptgroup("41 - Input A-F") + interest(1) + } + field(INPD,DBF_INLINK) { + prompt("Input D") + special(SPC_MOD) + promptgroup("41 - Input A-F") + interest(1) + } + field(INPE,DBF_INLINK) { + prompt("Input E") + special(SPC_MOD) + promptgroup("41 - Input A-F") + interest(1) + } + field(INPF,DBF_INLINK) { + prompt("Input F") + special(SPC_MOD) + promptgroup("41 - Input A-F") + interest(1) + } + field(INPG,DBF_INLINK) { + prompt("Input G") + special(SPC_MOD) + promptgroup("42 - Input G-L") + interest(1) + } + field(INPH,DBF_INLINK) { + prompt("Input H") + special(SPC_MOD) + promptgroup("42 - Input G-L") + interest(1) + } + field(INPI,DBF_INLINK) { + prompt("Input I") + special(SPC_MOD) + promptgroup("42 - Input G-L") + interest(1) + } + field(INPJ,DBF_INLINK) { + prompt("Input J") + special(SPC_MOD) + promptgroup("42 - Input G-L") + interest(1) + } + field(INPK,DBF_INLINK) { + prompt("Input K") + special(SPC_MOD) + promptgroup("42 - Input G-L") + interest(1) + } + field(INPL,DBF_INLINK) { + prompt("Input L") + special(SPC_MOD) + promptgroup("42 - Input G-L") + interest(1) + } + field(OUT,DBF_OUTLINK) { + prompt("Output Specification") + special(SPC_MOD) + promptgroup("50 - Output") + interest(1) + } + field(INAV,DBF_MENU) { + prompt("INPA PV Status") + special(SPC_NOMOD) + interest(1) + menu(calcoutINAV) + initial("1") + } + field(INBV,DBF_MENU) { + prompt("INPB PV Status") + special(SPC_NOMOD) + interest(1) + menu(calcoutINAV) + initial("1") + } + field(INCV,DBF_MENU) { + prompt("INPC PV Status") + special(SPC_NOMOD) + interest(1) + menu(calcoutINAV) + initial("1") + } + field(INDV,DBF_MENU) { + prompt("INPD PV Status") + special(SPC_NOMOD) + interest(1) + menu(calcoutINAV) + initial("1") + } + field(INEV,DBF_MENU) { + prompt("INPE PV Status") + special(SPC_NOMOD) + interest(1) + menu(calcoutINAV) + initial("1") + } + field(INFV,DBF_MENU) { + prompt("INPF PV Status") + special(SPC_NOMOD) + interest(1) + menu(calcoutINAV) + initial("1") + } + field(INGV,DBF_MENU) { + prompt("INPG PV Status") + special(SPC_NOMOD) + interest(1) + menu(calcoutINAV) + initial("1") + } + field(INHV,DBF_MENU) { + prompt("INPH PV Status") + special(SPC_NOMOD) + interest(1) + menu(calcoutINAV) + initial("1") + } + field(INIV,DBF_MENU) { + prompt("INPI PV Status") + special(SPC_NOMOD) + interest(1) + menu(calcoutINAV) + initial("1") + } + field(INJV,DBF_MENU) { + prompt("INPJ PV Status") + special(SPC_NOMOD) + interest(1) + menu(calcoutINAV) + initial("1") + } + field(INKV,DBF_MENU) { + prompt("INPK PV Status") + special(SPC_NOMOD) + interest(1) + menu(calcoutINAV) + initial("1") + } + field(INLV,DBF_MENU) { + prompt("INPL PV Status") + special(SPC_NOMOD) + interest(1) + menu(calcoutINAV) + initial("1") + } + field(OUTV,DBF_MENU) { + prompt("OUT PV Status") + special(SPC_NOMOD) + interest(1) + menu(calcoutINAV) + } + field(OOPT,DBF_MENU) { + prompt("Output Execute Opt") + promptgroup("50 - Output") + interest(1) + menu(calcoutOOPT) + } + field(ODLY,DBF_DOUBLE) { + prompt("Output Execute Delay") + promptgroup("50 - Output") + asl(ASL0) + interest(1) + } + field(DLYA,DBF_USHORT) { + prompt("Output Delay Active") + special(SPC_NOMOD) + asl(ASL0) + } + field(DOPT,DBF_MENU) { + prompt("Output Data Opt") + promptgroup("30 - Action") + interest(1) + menu(calcoutDOPT) + } + field(OCAL,DBF_STRING) { + prompt("Output Calculation") + promptgroup("30 - Action") + special(SPC_CALC) + pp(TRUE) + size(80) + initial("0") + } + field(OCLV,DBF_LONG) { + prompt("OCAL Valid") + interest(1) + } + field(OEVT,DBF_STRING) { + prompt("Event To Issue") + promptgroup("30 - Action") + special(SPC_MOD) + asl(ASL0) + size(40) + } + %#include "dbScan.h" + field(EPVT, DBF_NOACCESS) { + prompt("Event private") + special(SPC_NOMOD) + interest(4) + extra("EVENTPVT epvt") + } + field(IVOA,DBF_MENU) { + prompt("INVALID output action") + promptgroup("50 - Output") + interest(2) + menu(menuIvoa) + } + field(IVOV,DBF_DOUBLE) { + prompt("INVALID output value") + promptgroup("50 - Output") + interest(2) + } + field(EGU,DBF_STRING) { + prompt("Engineering Units") + promptgroup("80 - Display") + interest(1) + size(16) + prop(YES) + } + field(PREC,DBF_SHORT) { + prompt("Display Precision") + promptgroup("80 - Display") + interest(1) + prop(YES) + } + field(HOPR,DBF_DOUBLE) { + prompt("High Operating Rng") + promptgroup("80 - Display") + interest(1) + prop(YES) + } + field(LOPR,DBF_DOUBLE) { + prompt("Low Operating Range") + promptgroup("80 - Display") + interest(1) + prop(YES) + } + field(HIHI,DBF_DOUBLE) { + prompt("Hihi Alarm Limit") + promptgroup("70 - Alarm") + pp(TRUE) + interest(1) + prop(YES) + } + field(LOLO,DBF_DOUBLE) { + prompt("Lolo Alarm Limit") + promptgroup("70 - Alarm") + pp(TRUE) + interest(1) + prop(YES) + } + field(HIGH,DBF_DOUBLE) { + prompt("High Alarm Limit") + promptgroup("70 - Alarm") + pp(TRUE) + interest(1) + prop(YES) + } + field(LOW,DBF_DOUBLE) { + prompt("Low Alarm Limit") + promptgroup("70 - Alarm") + pp(TRUE) + interest(1) + prop(YES) + } + field(HHSV,DBF_MENU) { + prompt("Hihi Severity") + promptgroup("70 - Alarm") + pp(TRUE) + interest(1) + prop(YES) + menu(menuAlarmSevr) + } + field(LLSV,DBF_MENU) { + prompt("Lolo Severity") + promptgroup("70 - Alarm") + pp(TRUE) + interest(1) + prop(YES) + menu(menuAlarmSevr) + } + field(HSV,DBF_MENU) { + prompt("High Severity") + promptgroup("70 - Alarm") + pp(TRUE) + interest(1) + prop(YES) + menu(menuAlarmSevr) + } + field(LSV,DBF_MENU) { + prompt("Low Severity") + promptgroup("70 - Alarm") + pp(TRUE) + interest(1) + prop(YES) + menu(menuAlarmSevr) + } + field(HYST,DBF_DOUBLE) { + prompt("Alarm Deadband") + promptgroup("70 - Alarm") + interest(1) + } + field(ADEL,DBF_DOUBLE) { + prompt("Archive Deadband") + promptgroup("80 - Display") + interest(1) + } + field(MDEL,DBF_DOUBLE) { + prompt("Monitor Deadband") + promptgroup("80 - Display") + interest(1) + } + field(A,DBF_DOUBLE) { + prompt("Value of Input A") + pp(TRUE) + } + field(B,DBF_DOUBLE) { + prompt("Value of Input B") + pp(TRUE) + } + field(C,DBF_DOUBLE) { + prompt("Value of Input C") + pp(TRUE) + } + field(D,DBF_DOUBLE) { + prompt("Value of Input D") + pp(TRUE) + } + field(E,DBF_DOUBLE) { + prompt("Value of Input E") + pp(TRUE) + } + field(F,DBF_DOUBLE) { + prompt("Value of Input F") + pp(TRUE) + } + field(G,DBF_DOUBLE) { + prompt("Value of Input G") + pp(TRUE) + } + field(H,DBF_DOUBLE) { + prompt("Value of Input H") + pp(TRUE) + } + field(I,DBF_DOUBLE) { + prompt("Value of Input I") + pp(TRUE) + } + field(J,DBF_DOUBLE) { + prompt("Value of Input J") + pp(TRUE) + } + field(K,DBF_DOUBLE) { + prompt("Value of Input K") + pp(TRUE) + } + field(L,DBF_DOUBLE) { + prompt("Value of Input L") + pp(TRUE) + } + field(OVAL,DBF_DOUBLE) { + prompt("Output Value") + asl(ASL0) + } + field(LA,DBF_DOUBLE) { + prompt("Prev Value of A") + special(SPC_NOMOD) + interest(3) + } + field(LB,DBF_DOUBLE) { + prompt("Prev Value of B") + special(SPC_NOMOD) + interest(3) + } + field(LC,DBF_DOUBLE) { + prompt("Prev Value of C") + special(SPC_NOMOD) + interest(3) + } + field(LD,DBF_DOUBLE) { + prompt("Prev Value of D") + special(SPC_NOMOD) + interest(3) + } + field(LE,DBF_DOUBLE) { + prompt("Prev Value of E") + special(SPC_NOMOD) + interest(3) + } + field(LF,DBF_DOUBLE) { + prompt("Prev Value of F") + special(SPC_NOMOD) + interest(3) + } + field(LG,DBF_DOUBLE) { + prompt("Prev Value of G") + special(SPC_NOMOD) + interest(3) + } + field(LH,DBF_DOUBLE) { + prompt("Prev Value of H") + special(SPC_NOMOD) + interest(3) + } + field(LI,DBF_DOUBLE) { + prompt("Prev Value of I") + special(SPC_NOMOD) + interest(3) + } + field(LJ,DBF_DOUBLE) { + prompt("Prev Value of J") + special(SPC_NOMOD) + interest(3) + } + field(LK,DBF_DOUBLE) { + prompt("Prev Value of K") + special(SPC_NOMOD) + interest(3) + } + field(LL,DBF_DOUBLE) { + prompt("Prev Value of L") + special(SPC_NOMOD) + interest(3) + } + field(POVL,DBF_DOUBLE) { + prompt("Prev Value of OVAL") + asl(ASL0) + } + field(LALM,DBF_DOUBLE) { + prompt("Last Value Alarmed") + special(SPC_NOMOD) + interest(3) + } + field(ALST,DBF_DOUBLE) { + prompt("Last Value Archived") + special(SPC_NOMOD) + interest(3) + } + field(MLST,DBF_DOUBLE) { + prompt("Last Val Monitored") + special(SPC_NOMOD) + interest(3) + } + %#include "postfix.h" + field(RPCL,DBF_NOACCESS) { + prompt("Reverse Polish Calc") + special(SPC_NOMOD) + interest(4) + extra("char rpcl[INFIX_TO_POSTFIX_SIZE(80)]") + } + field(ORPC,DBF_NOACCESS) { + prompt("Reverse Polish OCalc") + special(SPC_NOMOD) + interest(4) + extra("char orpc[INFIX_TO_POSTFIX_SIZE(80)]") + } + +=head2 Record Support + +=head3 Record Support Routines + +=head2 C + +For each constant input link, the corresponding value field is initialized +with the constant value if the input link is CONSTANT or a channel access +link is created if the input link is PV_LINK. + +A routine postfix is called to convert the infix expression in CALC and +OCAL to Reverse Polish Notation. The result is stored in RPCL and ORPC, +respectively. + +=head2 C + +See next section. + +=head2 C + +This is called id CALC or OCAL is changed. C calls postfix. + +=head2 C + +Fills in the values of struct valueDes so that they refer to VAL. + +=head2 C + +Retrieves EGU. + +=head2 C + +Retrieves PREC. + +=head2 C + +Sets the upper display and lower display limits for a field. If the field +is VAL, HIHI, HIGH, LOW, or LOLO, the limits are set to HOPR and LOPR, else +if the field has upper and lower limits defined they will be used, else the +upper and lower macimum values for the field type will be used. + +=head2 C + +Sets the upper control and lower control limits for a field. If the VAL, +HIHI, HIGH, LOW, or LOLO, the limits are set to HOPR and LOPR, else if the +field has upper and lower limits defimed they will be used, else the upper +and lower maximum values for the field will be used. + +=head2 C + +Sets the following values: + +=over + +upper_alarm_limit = HIHI + +upper_warning_limit = HIGH + +lower warning_limit = LOW + +lower_alarm_limit = LOLO + +=back + +=head3 Record Processing + +=head2 C + +The C routine implements the following algorithm: + +=over + +=item 1. +Fetch all arguments. + +=item 2. +Call routine C, which calculates VAL from the prefix version +of the expression given in CALC. If C returns success, UDF +is set to FALSE. + +=item 3. +Check alarms. This routine checks to see if the new VAL causes the alarm +status and severity to change. If so, NSEV, NSTA and LALM are set. If also +honors the alarm hysteresis factor (HYST). Thus the value must change by at +least HYST before the alarm status and severity changes. + +=item 4. +Determin if the Output Execution Option (OOPT) is met. If it met, either +execute the output link (and output event) immediately (if ODLY = 0), or +schedule a callback after the specified interval. See the explanation for +the C routine below. + +=item 5. +Check to see if monitors should be invoked. + +=over + +=item * +Alarm monitors are invoked if the alarm status or severity has changed. + +=item * +Archive and value change monitors are invoked if ADEL and MDEL conditions +are met. + +=item * +Monitors for A-L are checked whenever other monitors are invoked. + +=item * +NSEV and NSTA are reset to 0 + +=back + +=item 6. +If no output delay was specified, scan forwark link if necessaru, set PACT +FALSE, and return. + +=back + +=head2 C + +=over + +=item 1. +If DOPT field specifies the use of OCAL, call the routine C +for the postfix version of the expression in OCAL. Otherwise, use VAL. + +=item 2. +If the Alarm Severity is INVALID, follow the option as designated by the +field IVOA. + +=item 3. +The Alarm Severity is not INVALID or IVOA specifies "Continue Normally", +put the value of OVAL to the OUT link and post the event in OEVT (if +non-zero). + +=item 4. +If an output delay was implemented, process the forwark link. + +=back + +=cut +} + +variable(calcoutODLYprecision, int) +variable(calcoutODLYlimit, double) diff --git a/modules/database/src/std/rec/compressRecord.c b/modules/database/src/std/rec/compressRecord.c index b123dcf09..e1164d81f 100644 --- a/modules/database/src/std/rec/compressRecord.c +++ b/modules/database/src/std/rec/compressRecord.c @@ -4,12 +4,12 @@ * 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. \*************************************************************************/ /* * Original Author: Bob Dalesio - * Date: 7-14-89 + * Date: 7-14-89 */ #include @@ -439,12 +439,16 @@ static long get_array_info(DBADDR *paddr, long *no_elements, long *offset) static long put_array_info(DBADDR *paddr, long nNew) { compressRecord *prec = (compressRecord *) paddr->precord; + epicsUInt32 nuse = prec->nuse; if (prec->balg == bufferingALG_FIFO) prec->off = (prec->off + nNew) % prec->nsam; prec->nuse += nNew; if (prec->nuse > prec->nsam) prec->nuse = prec->nsam; + + if (nuse != prec->nuse) + db_post_events(prec, &prec->nuse, DBE_VALUE | DBE_LOG); return 0; } diff --git a/modules/database/src/std/rec/dfanoutRecord.dbd b/modules/database/src/std/rec/dfanoutRecord.dbd deleted file mode 100644 index c2eb42a75..000000000 --- a/modules/database/src/std/rec/dfanoutRecord.dbd +++ /dev/null @@ -1,204 +0,0 @@ -#************************************************************************* -# Copyright (c) 2002 The University of Chicago, as Operator of Argonne -# National Laboratory. -# Copyright (c) 2002 The Regents of the University of California, as -# Operator of Los Alamos National Laboratory. -# EPICS BASE is distributed subject to a Software License Agreement found -# in file LICENSE that is included with this distribution. -#************************************************************************* -menu(dfanoutSELM) { - choice(dfanoutSELM_All,"All") - choice(dfanoutSELM_Specified,"Specified") - choice(dfanoutSELM_Mask,"Mask") -} -recordtype(dfanout) { - include "dbCommon.dbd" - field(VAL,DBF_DOUBLE) { - prompt("Desired Output") - promptgroup("40 - Input") - asl(ASL0) - pp(TRUE) - } - field(SELM,DBF_MENU) { - prompt("Select Mechanism") - promptgroup("30 - Action") - interest(1) - menu(dfanoutSELM) - } - field(SELN,DBF_USHORT) { - prompt("Link Selection") - interest(1) - initial("1") - } - field(SELL,DBF_INLINK) { - prompt("Link Selection Loc") - promptgroup("30 - Action") - interest(1) - } - field(OUTA,DBF_OUTLINK) { - prompt("Output Spec A") - promptgroup("50 - Output") - interest(1) - } - field(OUTB,DBF_OUTLINK) { - prompt("Output Spec B") - promptgroup("50 - Output") - interest(1) - } - field(OUTC,DBF_OUTLINK) { - prompt("Output Spec C") - promptgroup("50 - Output") - interest(1) - } - field(OUTD,DBF_OUTLINK) { - prompt("Output Spec D") - promptgroup("50 - Output") - interest(1) - } - field(OUTE,DBF_OUTLINK) { - prompt("Output Spec E") - promptgroup("50 - Output") - interest(1) - } - field(OUTF,DBF_OUTLINK) { - prompt("Output Spec F") - promptgroup("50 - Output") - interest(1) - } - field(OUTG,DBF_OUTLINK) { - prompt("Output Spec G") - promptgroup("50 - Output") - interest(1) - } - field(OUTH,DBF_OUTLINK) { - prompt("Output Spec H") - promptgroup("50 - Output") - interest(1) - } - field(DOL,DBF_INLINK) { - prompt("Desired Output Loc") - promptgroup("40 - Input") - interest(1) - } - field(OMSL,DBF_MENU) { - prompt("Output Mode Select") - promptgroup("50 - Output") - interest(1) - menu(menuOmsl) - } - field(EGU,DBF_STRING) { - prompt("Engineering Units") - promptgroup("80 - Display") - interest(1) - size(16) - prop(YES) - } - field(PREC,DBF_SHORT) { - prompt("Display Precision") - promptgroup("80 - Display") - interest(1) - prop(YES) - } - field(HOPR,DBF_DOUBLE) { - prompt("High Operating Range") - promptgroup("80 - Display") - interest(1) - prop(YES) - } - field(LOPR,DBF_DOUBLE) { - prompt("Low Operating Range") - promptgroup("80 - Display") - interest(1) - prop(YES) - } - field(HIHI,DBF_DOUBLE) { - prompt("Hihi Alarm Limit") - promptgroup("70 - Alarm") - pp(TRUE) - interest(1) - prop(YES) - } - field(LOLO,DBF_DOUBLE) { - prompt("Lolo Alarm Limit") - promptgroup("70 - Alarm") - pp(TRUE) - interest(1) - prop(YES) - } - field(HIGH,DBF_DOUBLE) { - prompt("High Alarm Limit") - promptgroup("70 - Alarm") - pp(TRUE) - interest(1) - prop(YES) - } - field(LOW,DBF_DOUBLE) { - prompt("Low Alarm Limit") - promptgroup("70 - Alarm") - pp(TRUE) - interest(1) - prop(YES) - } - field(HHSV,DBF_MENU) { - prompt("Hihi Severity") - promptgroup("70 - Alarm") - pp(TRUE) - interest(1) - prop(YES) - menu(menuAlarmSevr) - } - field(LLSV,DBF_MENU) { - prompt("Lolo Severity") - promptgroup("70 - Alarm") - pp(TRUE) - interest(1) - prop(YES) - menu(menuAlarmSevr) - } - field(HSV,DBF_MENU) { - prompt("High Severity") - promptgroup("70 - Alarm") - pp(TRUE) - interest(1) - prop(YES) - menu(menuAlarmSevr) - } - field(LSV,DBF_MENU) { - prompt("Low Severity") - promptgroup("70 - Alarm") - pp(TRUE) - interest(1) - prop(YES) - menu(menuAlarmSevr) - } - field(HYST,DBF_DOUBLE) { - prompt("Alarm Deadband") - promptgroup("70 - Alarm") - interest(1) - } - field(ADEL,DBF_DOUBLE) { - prompt("Archive Deadband") - promptgroup("80 - Display") - interest(1) - } - field(MDEL,DBF_DOUBLE) { - prompt("Monitor Deadband") - promptgroup("80 - Display") - interest(1) - } - field(LALM,DBF_DOUBLE) { - prompt("Last Value Alarmed") - special(SPC_NOMOD) - interest(3) - } - field(ALST,DBF_DOUBLE) { - prompt("Last Value Archived") - special(SPC_NOMOD) - interest(3) - } - field(MLST,DBF_DOUBLE) { - prompt("Last Val Monitored") - special(SPC_NOMOD) - interest(3) - } -} diff --git a/modules/database/src/std/rec/dfanoutRecord.dbd.pod b/modules/database/src/std/rec/dfanoutRecord.dbd.pod new file mode 100644 index 000000000..0d0487666 --- /dev/null +++ b/modules/database/src/std/rec/dfanoutRecord.dbd.pod @@ -0,0 +1,454 @@ +#************************************************************************* +# 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. +#************************************************************************* + +=title Data Fanout Record (dfanout) + +The Data Fanout or "dfanout" record is used to forward data to up to +eight other records. It's similar to the fanout record except that the +capability to forward data has been added to it. If has no associated +device support. + +=head2 Parameter Fields + +The fields in this record can be classified into the following categories: + +=over + +=item * +scan parameters + +=item * +desired output parameters + +=item * +write parameters + +=item * +operator display parameters + +=item * +alarm parameters + +=item * +monitor parameters + +=item * +run-time and simulation mode parameters + +=back + +=recordtype dfanout + +=cut + +menu(dfanoutSELM) { + choice(dfanoutSELM_All,"All") + choice(dfanoutSELM_Specified,"Specified") + choice(dfanoutSELM_Mask,"Mask") +} + +recordtype(dfanout) { + +=head3 Scan Parameters + +The data fanout record has the standard fields for specifying under what +circumstances it will be processed. These fields are listed in +L. In addition, L explains how these +fields are used. Since the data fanout record supports no direct interfaces +to hardware, it cannot be scanned on I/O interrupt, so its SCAN field +cannot be C. + +=head3 Desired Output Parameters + +The data fanout record must specify where the desired output value +originates, i.e., the data which is to be fowarded to the records in its +output links. The output mode select (OMSL) field determines whether the +output originates from another record or from run-time database access. +When set to C, the desired output is retrieved from the link +specified in the desired output (DOL) field, which can specify either a +database or a channel access link, and placed into the VAL field. When set +to C, the desired output can be written to the VAL field via +dbPuts at run-time. + +The DOL field can also be a constant in which case the VAL field is +initialized to the constant value. + +Note that there are no conversion parameters, so the desired output value +undergoes no conversions before it is sent out to the output links. + +=fields DOL, OMSL, VAL + +=head3 Write Parameters + +The OUTA-OUTH fields specify where VAL is to be sent. Each field that is to +forward data must specify an address to another record. See +L
    for information on specifying links. + +The SELL, SELM, and SELN fields specify which output links are to be +used. + +=head4 Menu dfanoutSELM + +SELM is a menu, with three choices: + +=menu dfanoutSELM + +If SELM=="All", then all output links are used, and the values of +SELL and SELN are ignored. + +If SELM=="Specified", then the value of SELN is used to specify a single +link which will be used. If SELN==0, then no link will be used; if SELN==1, +then OUTA will be used, and so on. + +SELN can either have its value set directly, or have its values retrieved +from another EPICS PV. If SELL is a valid PV link, then SELN will be set to +the values of the linked PV. + +If SELM=="Mask", then SELN will be treated as a bit mask. If bit one of +SELN is set, then OUTA will be used, if bit two is set, OUTB will be used. +Thus if SELN==5, OUTC and OUTA will be used. + +=fields OUTA, OUTB, OUTC, OUTD, OUTE, OUTF, OUTG, OUTH + +=head3 Operator Display Parameters + +These parameters are used to present meaningful data to the operator. They +display the value and other parameters of the data fanout record either +textually or graphically. + +The EGU field can contain a string of up to 16 characters describing the +value on the VAL field. + +The HOPR and LOPR fields determine the upper and lower display limits for +graphic displays and the upper and lower control limits for control +displays. They apply to the VAL, HIHI, HIGH, LOW, and LOLO fields. The +record support routines C or C +retrieve HOPR and LOPR. + +See L for more on the record name (NAME) +and description (DESC) fields. + +=fields EGU, HOPR, LOPR, NAME, DESC + +=head3 Alarm Parameters + +The possible alarm conditions for data fanouts are the SCAN, READ, INVALID, +and limit alarms. The SCAN and READ alarms are called by the record +routines. The limit alarms are configured by the user in the HIHI, LOLO, +HIGH, and LOW fields using floating point values. The limit alarms apply +only to the VAL field. The severity for each of these limits is specified +in the corresponding field (HHSV, LLSV, HSV, LSV) and can be either +NO_ALARM, MINOR, or MAJOR. In the hysteresis field (HYST) can be entered a +number which serves as the deadband on the limit alarms. + +See L for a complete explanation of alarms and these +fields. L lists other fields related to alarms that are +common to all record types. + +=fields HIHI, HIGH, LOW, LOLO, HHSV, HSV, LSV, LLSV, HYST + +=head3 Monitor Parameters + +These parameters are used to determine when to send monitors placed on the +VAL field. These monitors are sent when the value field exceeds the last +monitored fields by the specified deadband, ADEL for archivers monitors and +MDEL for all other types of monitors. If these fields have a value of zero, +everytime the value changes, a monitor will be triggered; if they have a +value of -1, everytime the record is scanned, monitors are triggered. See +L for a complete explanation of monitors. + +=fields ADEL, MDEL + +=head3 Run-Time Parameters and Simulation Mode Parameters + +These parameters are used by the run-time code for processing the data +fanout record. Ther are not configurable. They are used to implement the +hysteresis factors for monitor callbacks. + +=fields LALM, ALST, MLST + +=cut + + include "dbCommon.dbd" + field(VAL,DBF_DOUBLE) { + prompt("Desired Output") + promptgroup("40 - Input") + asl(ASL0) + pp(TRUE) + } + field(SELM,DBF_MENU) { + prompt("Select Mechanism") + promptgroup("30 - Action") + interest(1) + menu(dfanoutSELM) + } + field(SELN,DBF_USHORT) { + prompt("Link Selection") + interest(1) + initial("1") + } + field(SELL,DBF_INLINK) { + prompt("Link Selection Loc") + promptgroup("30 - Action") + interest(1) + } + field(OUTA,DBF_OUTLINK) { + prompt("Output Spec A") + promptgroup("50 - Output") + interest(1) + } + field(OUTB,DBF_OUTLINK) { + prompt("Output Spec B") + promptgroup("50 - Output") + interest(1) + } + field(OUTC,DBF_OUTLINK) { + prompt("Output Spec C") + promptgroup("50 - Output") + interest(1) + } + field(OUTD,DBF_OUTLINK) { + prompt("Output Spec D") + promptgroup("50 - Output") + interest(1) + } + field(OUTE,DBF_OUTLINK) { + prompt("Output Spec E") + promptgroup("50 - Output") + interest(1) + } + field(OUTF,DBF_OUTLINK) { + prompt("Output Spec F") + promptgroup("50 - Output") + interest(1) + } + field(OUTG,DBF_OUTLINK) { + prompt("Output Spec G") + promptgroup("50 - Output") + interest(1) + } + field(OUTH,DBF_OUTLINK) { + prompt("Output Spec H") + promptgroup("50 - Output") + interest(1) + } + field(DOL,DBF_INLINK) { + prompt("Desired Output Loc") + promptgroup("40 - Input") + interest(1) + } + field(OMSL,DBF_MENU) { + prompt("Output Mode Select") + promptgroup("50 - Output") + interest(1) + menu(menuOmsl) + } + field(EGU,DBF_STRING) { + prompt("Engineering Units") + promptgroup("80 - Display") + interest(1) + size(16) + prop(YES) + } + field(PREC,DBF_SHORT) { + prompt("Display Precision") + promptgroup("80 - Display") + interest(1) + prop(YES) + } + field(HOPR,DBF_DOUBLE) { + prompt("High Operating Range") + promptgroup("80 - Display") + interest(1) + prop(YES) + } + field(LOPR,DBF_DOUBLE) { + prompt("Low Operating Range") + promptgroup("80 - Display") + interest(1) + prop(YES) + } + field(HIHI,DBF_DOUBLE) { + prompt("Hihi Alarm Limit") + promptgroup("70 - Alarm") + pp(TRUE) + interest(1) + prop(YES) + } + field(LOLO,DBF_DOUBLE) { + prompt("Lolo Alarm Limit") + promptgroup("70 - Alarm") + pp(TRUE) + interest(1) + prop(YES) + } + field(HIGH,DBF_DOUBLE) { + prompt("High Alarm Limit") + promptgroup("70 - Alarm") + pp(TRUE) + interest(1) + prop(YES) + } + field(LOW,DBF_DOUBLE) { + prompt("Low Alarm Limit") + promptgroup("70 - Alarm") + pp(TRUE) + interest(1) + prop(YES) + } + field(HHSV,DBF_MENU) { + prompt("Hihi Severity") + promptgroup("70 - Alarm") + pp(TRUE) + interest(1) + prop(YES) + menu(menuAlarmSevr) + } + field(LLSV,DBF_MENU) { + prompt("Lolo Severity") + promptgroup("70 - Alarm") + pp(TRUE) + interest(1) + prop(YES) + menu(menuAlarmSevr) + } + field(HSV,DBF_MENU) { + prompt("High Severity") + promptgroup("70 - Alarm") + pp(TRUE) + interest(1) + prop(YES) + menu(menuAlarmSevr) + } + field(LSV,DBF_MENU) { + prompt("Low Severity") + promptgroup("70 - Alarm") + pp(TRUE) + interest(1) + prop(YES) + menu(menuAlarmSevr) + } + field(HYST,DBF_DOUBLE) { + prompt("Alarm Deadband") + promptgroup("70 - Alarm") + interest(1) + } + field(ADEL,DBF_DOUBLE) { + prompt("Archive Deadband") + promptgroup("80 - Display") + interest(1) + } + field(MDEL,DBF_DOUBLE) { + prompt("Monitor Deadband") + promptgroup("80 - Display") + interest(1) + } + field(LALM,DBF_DOUBLE) { + prompt("Last Value Alarmed") + special(SPC_NOMOD) + interest(3) + } + field(ALST,DBF_DOUBLE) { + prompt("Last Value Archived") + special(SPC_NOMOD) + interest(3) + } + field(MLST,DBF_DOUBLE) { + prompt("Last Val Monitored") + special(SPC_NOMOD) + interest(3) + } + +=head2 Record Support + +=head3 Record Support Routines + +=head2 C + +This routine initializes all output links that are defined. Then it initializes +DOL if DOL is a constant or a PV_LINK. When initializing the output links +and the DOL link, a non-zero value is returned if an error occurs. + +=head2 C + +See next section. + +=head2 C + +This routine fills in the members of C with the VAL fields +value and characteristics. + +=head2 C + +The routine copies the string specified in the EGU field to the location +specified by a pointer which is passed to the routine. + +=head2 C + +If the referenced field is VAL, HIHI, HIGH, LOW, or LOLO, this routine sets +the C member of the C structure to the +HOPR and the C member to the LOPR. If the referenced +field is not one of the above fields, then C +routine is called. + +=head2 C + +Same as the C routine except that it uses the +C structure. + +=head2 C + +This sets the members of the C structure to the specified +alarm limits if the referenced field is VAL: + +=over + +upper_alarm_limit = HIHI + +upper_warning_limit = HIGH + +lower_warning_limit = LOW + +lower_alarm_limit = LOLO + +=back + +If the referenced field is not VAL, the C routine +is called. + +=head3 Record Processing + +=over + +=item 1. +The C routine first retrieves a value for DOL and places it in +VAL if OMSL is set to colsed loop mode. If an error occurs, then UDF is set +to FALSE. + +=item 2. +PACT is set TRUE + +=item 3. +VAL is then sent to all the records specified in the OUTA-OUTH fields by +calling C for each link. + +=item 4. +Alarms are checked and monitors are called if conditions apply. + +=item 5. +The data fanout's own forward link is then processed. + +=item 6. +PACT is set FALSE, and the C routine returns. A -1 is returned +if there was an error writing values to one of the output links. + +=back + +=cut +} diff --git a/modules/database/src/std/rec/int64inRecord.c b/modules/database/src/std/rec/int64inRecord.c index 865d05fbb..c76d96c67 100644 --- a/modules/database/src/std/rec/int64inRecord.c +++ b/modules/database/src/std/rec/int64inRecord.c @@ -183,17 +183,16 @@ static long special(DBADDR *paddr, int after) #define indexof(field) int64inRecord##field -static long get_units(DBADDR *paddr,char *units) +static long get_units(DBADDR *paddr, char *units) { - int64inRecord *prec=(int64inRecord *)paddr->precord; + int64inRecord *prec = (int64inRecord *) paddr->precord; - if(paddr->pfldDes->field_type == DBF_LONG) { - strncpy(units,prec->egu,DB_UNITS_SIZE); + if (paddr->pfldDes->field_type == DBF_INT64) { + strncpy(units, prec->egu, DB_UNITS_SIZE); } - return(0); + return 0; } - static long get_graphic_double(DBADDR *paddr, struct dbr_grDouble *pgd) { int64inRecord *prec=(int64inRecord *)paddr->precord; @@ -255,7 +254,7 @@ static long get_alarm_double(DBADDR *paddr, struct dbr_alDouble *pad) static void checkAlarms(int64inRecord *prec, epicsTimeStamp *timeLast) { - enum { + enum { range_Lolo = 1, range_Low, range_Normal, diff --git a/modules/database/src/std/rec/subArrayRecord.c b/modules/database/src/std/rec/subArrayRecord.c index d9d1c33e1..6de514766 100644 --- a/modules/database/src/std/rec/subArrayRecord.c +++ b/modules/database/src/std/rec/subArrayRecord.c @@ -2,17 +2,17 @@ * Copyright (c) 2002 Lawrence Berkeley Laboratory,The Control Systems * Group, Systems Engineering Department * 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. \*************************************************************************/ -/* recSubArray.c - Record Support Routines for SubArray records +/* recSubArray.c - Record Support Routines for SubArray records * * * Author: Carl Lionberger * Date: 090293 * * NOTES: - * Derived from waveform record. + * Derived from waveform record. * Modification Log: * ----------------- */ @@ -126,7 +126,7 @@ static long init_record(struct dbCommon *pcommon, int pass) } if (pdset->init_record) - return (*pdset->init_record)(prec); + return pdset->init_record(prec); return 0; } @@ -194,11 +194,14 @@ static long get_array_info(DBADDR *paddr, long *no_elements, long *offset) static long put_array_info(DBADDR *paddr, long nNew) { subArrayRecord *prec = (subArrayRecord *) paddr->precord; + epicsUInt32 nord = prec->nord; - if (nNew > prec->malm) - nNew = prec->malm; prec->nord = nNew; + if (prec->nord > prec->malm) + prec->nord = prec->malm; + if (nord != prec->nord) + db_post_events(prec, &prec->nord, DBE_VALUE | DBE_LOG); return 0; } @@ -211,7 +214,7 @@ static long get_units(DBADDR *paddr, char *units) switch (dbGetFieldIndex(paddr)) { case indexof(VAL): if (prec->ftvl == DBF_STRING || prec->ftvl == DBF_ENUM) - break; + break; case indexof(HOPR): case indexof(LOPR): strncpy(units,prec->egu,DB_UNITS_SIZE); @@ -321,4 +324,3 @@ static long readValue(subArrayRecord *prec) return status; } - diff --git a/modules/database/src/std/rec/waveformRecord.c b/modules/database/src/std/rec/waveformRecord.c index c987cfbc4..19f4fbf7b 100644 --- a/modules/database/src/std/rec/waveformRecord.c +++ b/modules/database/src/std/rec/waveformRecord.c @@ -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. \*************************************************************************/ /* recWaveform.c - Record Support Routines for Waveform records */ @@ -97,18 +97,14 @@ static long init_record(struct dbCommon *pcommon, int pass) struct waveformRecord *prec = (struct waveformRecord *)pcommon; struct wfdset *pdset; - if (pass == 0){ + if (pass == 0) { if (prec->nelm <= 0) prec->nelm = 1; if (prec->ftvl > DBF_ENUM) prec->ftvl = DBF_UCHAR; prec->bptr = callocMustSucceed(prec->nelm, dbValueSize(prec->ftvl), "waveform calloc failed"); - if (prec->nelm == 1) { - prec->nord = 1; - } else { - prec->nord = 0; - } + prec->nord = (prec->nelm == 1); return 0; } @@ -124,16 +120,18 @@ static long init_record(struct dbCommon *pcommon, int pass) recGblRecordError(S_dev_missingSup,(void *)prec,"wf: init_record"); return S_dev_missingSup; } - if (! pdset->init_record) return 0; + if (!pdset->init_record) + return 0; - return (*pdset->init_record)(prec); + return pdset->init_record(prec); } - + static long process(struct dbCommon *pcommon) { struct waveformRecord *prec = (struct waveformRecord *)pcommon; struct wfdset *pdset = (struct wfdset *)(prec->dset); - unsigned char pact=prec->pact; + unsigned char pact=prec->pact; + long status; if ((pdset==NULL) || (pdset->read_wf==NULL)) { prec->pact=TRUE; @@ -141,13 +139,14 @@ static long process(struct dbCommon *pcommon) return S_dev_missingSup; } - if (pact && prec->busy) return 0; + if (pact && prec->busy) + return 0; - readValue(prec); /* read the new value */ - if (!pact && prec->pact) return 0; + status = readValue(prec); /* read the new value */ + if (!pact && prec->pact) + return 0; prec->udf = FALSE; - prec->pact = TRUE; recGblGetTimeStampSimm(prec, prec->simm, &prec->siol); monitor(prec); @@ -156,26 +155,27 @@ static long process(struct dbCommon *pcommon) recGblFwdLink(prec); prec->pact=FALSE; - return 0; + return status; } static long special(DBADDR *paddr, int after) { waveformRecord *prec = (waveformRecord *)(paddr->precord); - int special_type = paddr->special; + int special_type = paddr->special; switch(special_type) { - case(SPC_MOD): + case SPC_MOD: if (dbGetFieldIndex(paddr) == waveformRecordSIMM) { if (!after) recGblSaveSimm(prec->sscn, &prec->oldsimm, prec->simm); else - recGblCheckSimm((dbCommon *)prec, &prec->sscn, prec->oldsimm, prec->simm); - return(0); + recGblCheckSimm((dbCommon *)prec, &prec->sscn, prec->oldsimm, + prec->simm); + return 0; } default: recGblDbaddrError(S_db_badChoice, paddr, "waveform: special"); - return(S_db_badChoice); + return S_db_badChoice; } } @@ -205,12 +205,14 @@ static long get_array_info(DBADDR *paddr, long *no_elements, long *offset) static long put_array_info(DBADDR *paddr, long nNew) { waveformRecord *prec = (waveformRecord *) paddr->precord; + epicsUInt32 nord = prec->nord; prec->nord = nNew; if (prec->nord > prec->nelm) prec->nord = prec->nelm; - db_post_events(prec, &prec->nord, DBE_VALUE | DBE_LOG); + if (nord != prec->nord) + db_post_events(prec, &prec->nord, DBE_VALUE | DBE_LOG); return 0; } @@ -223,7 +225,7 @@ static long get_units(DBADDR *paddr, char *units) switch (dbGetFieldIndex(paddr)) { case indexof(VAL): if (prec->ftvl == DBF_STRING || prec->ftvl == DBF_ENUM) - break; + break; case indexof(HOPR): case indexof(LOPR): strncpy(units,prec->egu,DB_UNITS_SIZE); @@ -323,15 +325,17 @@ static void monitor(waveformRecord *prec) db_post_events(prec, &prec->val, monitor_mask); } } - + static long readValue(waveformRecord *prec) { struct wfdset *pdset = (struct wfdset *) prec->dset; long status = 0; if (!prec->pact) { - status = recGblGetSimm((dbCommon *)prec, &prec->sscn, &prec->oldsimm, &prec->simm, &prec->siml); - if (status) return status; + status = recGblGetSimm((dbCommon *)prec, &prec->sscn, &prec->oldsimm, + &prec->simm, &prec->siml); + if (status) + return status; } switch (prec->simm) { @@ -348,22 +352,27 @@ static long readValue(waveformRecord *prec) long nRequest = prec->nelm; recGblSetSevr(prec, SIMM_ALARM, prec->sims); - if (prec->pact || (prec->sdly < 0.)) { + if (prec->pact || (prec->sdly < 0)) { status = dbGetLink(&prec->siol, prec->ftvl, prec->bptr, 0, &nRequest); - if (status == 0) prec->udf = FALSE; - /* nord set only for db links: needed for old db_access */ - if (!dbLinkIsConstant(&prec->siol)) { + if (status == 0) + prec->udf = FALSE; + + if (nRequest != prec->nord) { prec->nord = nRequest; db_post_events(prec, &prec->nord, DBE_VALUE | DBE_LOG); } prec->pact = FALSE; - } else { /* !prec->pact && delay >= 0. */ + } + else { /* !prec->pact && delay >= 0 */ CALLBACK *pvt = prec->simpvt; - if (!pvt) { - pvt = calloc(1, sizeof(CALLBACK)); /* very lazy allocation of callback structure */ + + if (!pvt) { /* very lazy allocation of callback structure */ + pvt = calloc(1, sizeof(CALLBACK)); prec->simpvt = pvt; } - if (pvt) callbackRequestProcessCallbackDelayed(pvt, prec->prio, prec, prec->sdly); + if (pvt) + callbackRequestProcessCallbackDelayed(pvt, prec->prio, prec, + prec->sdly); prec->pact = TRUE; } break; diff --git a/modules/database/src/std/softIoc/makeInstallDir.pl b/modules/database/src/std/softIoc/makeInstallDir.pl index 61f271f15..b090280eb 100644 --- a/modules/database/src/std/softIoc/makeInstallDir.pl +++ b/modules/database/src/std/softIoc/makeInstallDir.pl @@ -1,5 +1,4 @@ -eval 'exec perl -S $0 ${1+"$@"}' # -*- Mode: perl -*- - if $running_under_some_shell; +#!/usr/bin/env perl #************************************************************************* # Copyright (c) 2010 UChicago Argonne LLC, as Operator of Argonne # National Laboratory. diff --git a/modules/database/src/tools/DBD/Parser.pm b/modules/database/src/tools/DBD/Parser.pm index 6065486c8..4b845b0d1 100644 --- a/modules/database/src/tools/DBD/Parser.pm +++ b/modules/database/src/tools/DBD/Parser.pm @@ -97,7 +97,7 @@ sub ParseDBD { my $rtyp = $dbd->recordtype($record_type); if (!defined $rtyp) { $rtyp = DBD::Recordtype->new($record_type); - warn "Device using undefined record type '$record_type', place-holder created\n"; + warn "Device using unknown record type '$record_type', declaration created\n"; $dbd->add($rtyp); } $rtyp->add_device(DBD::Device->new($link_type, $dset, $choice)); @@ -218,7 +218,12 @@ sub parse_breaktable { sub parse_recordtype { my ($dbd, $record_type) = @_; pushContext("recordtype($record_type)"); - my $rtyp = DBD::Recordtype->new($record_type); + # Re-use a matching declaration record type if one exists + my $rtyp = $dbd->recordtype($record_type); + if (!defined($rtyp) || $rtyp->fields) { + # Earlier record type is not a declaration, don't re-use it + $rtyp = DBD::Recordtype->new($record_type); + } while(1) { parseCommon($rtyp); if (m/\G field \s* \( \s* $RXstr \s* , \s* $RXstr \s* \) \s* \{/xgc) { diff --git a/modules/database/src/tools/DBD/Recfield.pm b/modules/database/src/tools/DBD/Recfield.pm index f7bfbae46..3e109d2d1 100644 --- a/modules/database/src/tools/DBD/Recfield.pm +++ b/modules/database/src/tools/DBD/Recfield.pm @@ -130,7 +130,18 @@ sub attribute { } sub equals { - dieContext("Record field objects are not comparable"); + my ($l, $r) = @_; + return 1 if $l eq $r; + return 0 if + $l->{NAME} ne $r->{NAME} || + $l->{DBF_TYPE} ne $r->{DBF_TYPE}; + my ($la, $ra) = ($l->{ATTR_INDEX}, $r->{ATTR_INDEX}); + my @keys = sort keys %$la; + return 0 if join(',', @keys) ne join(',', sort keys %$ra); + foreach my $k (@keys) { + return 0 if $la->{$k} ne $ra->{$k}; + } + return 1; } sub check_valid { diff --git a/modules/database/src/tools/DBD/Recordtype.pm b/modules/database/src/tools/DBD/Recordtype.pm index e4b0d5481..649c6a21b 100644 --- a/modules/database/src/tools/DBD/Recordtype.pm +++ b/modules/database/src/tools/DBD/Recordtype.pm @@ -104,9 +104,19 @@ sub pod { sub equals { my ($new, $known) = @_; - return 0 if ! $known->fields; - return 1 if ! $new->fields; - dieContext("Duplicate definition of record type '$known->{NAME}'"); + return 1 if $new eq $known; + return 0 if $new->{NAME} ne $known->{NAME}; + return 1 if ! $new->fields; # Later declarations always match + # NB: Definition after declaration is handled in parse_recordtype() + my @nf = @{$new->{FIELD_LIST}}; + my @kf = @{$known->{FIELD_LIST}}; + return 0 if scalar @nf != scalar @kf; + while (@nf) { + my $nf = shift @nf; + my $kf = shift @kf; + return 0 if ! $nf->equals($kf); + } + return 1; } sub toDeclaration { diff --git a/modules/database/src/tools/EPICS/IOC.pm b/modules/database/src/tools/EPICS/IOC.pm new file mode 100644 index 000000000..9eae9d4fd --- /dev/null +++ b/modules/database/src/tools/EPICS/IOC.pm @@ -0,0 +1,489 @@ +###################################################################### +# EPICS BASE is distributed subject to a Software License Agreement +# found in file LICENSE that is included with this distribution. +# +# Original Author: Shantha Condamoor, SLAC +# Creation Date: 1-Sep-2011 +# Current Author: Andrew Johnson + +use strict; +use warnings; + +package EPICS::IOC; +require 5.010; + +=head1 NAME + +EPICS::IOC - Manage an EPICS IOC + +=head1 SYNOPSIS + + use EPICS::IOC; + + my $ioc = EPICS::IOC->new; + $ioc->debug(1); # Show IOC stdio streams + + $ioc->start('bin/@ARCH@/ioc', 'iocBoot/ioc/st.cmd'); + $ioc->cmd; # Wait for the iocsh prompt + + my @records = $ioc->dbl; + my @values = map { $ioc->dbgf($_); } @records; + + $ioc->kill; + +=head1 DESCRIPTION + +This module provides an object-oriented API for starting, interacting with and +stopping one or more EPICS IOCs under program control, and is generally intended +for writing test programs. + +The IOC should not be configured to emit unsolicited messages on stdout as this +could interfere with the ability of the software to detect an end-of-command +from the IOC shell, which is achieved by setting the prompt to a known string +(normally C<__END__>). Unsolicited messages on stderr will not cause problems, +but can't be seen on Windows systems. + +=head1 CONSTRUCTOR + +=over 4 + +=cut + +use Symbol 'gensym'; +use IPC::Open3; +use IO::Select; + + +=item new () + +Calling C creates an C object that can be used to start and +interact with a single IOC. After this IOC has been shut down (by calling its +C method) the C object may be reused for another IOC. + +=back + +=cut + +sub new { + my $proto = shift; + my $class = ref $proto || $proto; + + my $self = { + pid => undef, + stdin => gensym, + stdout => gensym, + stderr => gensym, + select => IO::Select->new(), + errbuf => '', + debug => 0, + terminator => '__END__' + }; + + bless $self, $class; +} + +=head1 METHODS + +=over 4 + +=item debug ( [FLAG] ) + +Each C object has its own debug flag which when non-zero causes all +IOC console traffic sent or read by other methods to be printed to stdout along +with the IOC's pid and a direction indicator. The C method optionally +sets and returns the value of this flag. + +The optional FLAG is treated as a true/false value. If provided this sets the +debug flag value. + +The method's return value is the current (new if given) value of the flag. + +=cut + +sub debug { + my $self = shift; + + $self->{debug} = shift if scalar @_; + return $self->{debug}; +} + + +=item start ( EXECUTABLE [, ARGUMENTS ...] ) + +Launch an IOC binary given by EXECUTABLE with ARGUMENTS. The method dies if it +can't run the program as given, or if the IOC is already running. + +In most cases the C method should be called next with no command string, +which waits for the IOC's boot process to finish and the first iocsh prompt to +be displayed. + +The C method sets two environment variables that control how the IOC +shell behaves: C is set to prevent it calling the GNU +Readline library, and C is set to a known string which is used as a +terminator for the previous command. + +=cut + +sub start { + my ($self, $exe, @args) = @_; + + croak("IOC already running") if $self->started; + + # Turn off readline or its equivalents + local $ENV{IOCSH_HISTEDIT_DISABLE} = "TRUE"; + + # The iocsh prompt marks the end of the previous command + local $ENV{IOCSH_PS1} = $self->{terminator} . "\n"; + + # Run the IOC as a child process + $self->{pid} = open3($self->{stdin}, $self->{stdout}, $self->{stderr}, + $exe, @args) + or die "can't start $exe: $!"; + + $self->{select}->add($self->{stderr}); + + printf "#%d running %s\n", $self->{pid}, $exe if $self->{debug}; +} + + +=item pid () + +Returns the process-ID of the IOC process, or undef if the IOC process has not +yet been started. + +=cut + +sub pid { + my $self = shift; + + return $self->{pid}; +} + + +=item started () + +Returns a true value if the IOC has been started and not yet killed. This state +will not change if the IOC dies by itself, it indicates that the start method +has been called without the kill method. + +=cut + +sub started { + my $self = shift; + + return defined($self->pid); +} + + +=item _send ( COMMAND ) + +The C<_send> method is a primitive for internal use that sends a COMMAND string +to the IOC shell, and prints it to stdout if the debug flag is set. + +=cut + +sub _send { + my ($self, $cmd) = @_; + my $stdin = $self->{stdin}; + + printf "#%d << %s", $self->{pid}, $cmd if $self->{debug}; + + local $SIG{PIPE} = sub { + printf "#%d << \n", $self->{pid} if $self->{debug}; + }; + + print $stdin $cmd; +} + + +=item _getline () + +The C<_getline> method is also designed for internal use, it fetches a single +line output by the IOC, and prints it to stdout if the debug flag is set. + +Any CR/LF is stripped from the line before returning it. If the stream gets +closed because the IOC shuts down an C debug message may be shown and an +undef value will be returned. + +=cut + +sub _getline { + my $self = shift; + + my $line = readline $self->{stdout}; + if (defined $line) { + $line =~ s/[\r\n]+ $//x; # chomp broken on Windows? + printf "#%d >> %s\n", $self->{pid}, $line if $self->{debug}; + } + elsif (eof($self->{stdout})) { + printf "#%d >> \n", $self->{pid} if $self->{debug}; + } + else { + printf "#%d Error: %s\n", $self->{pid}, $! if $self->{debug}; + } + return $line; +} + + +=item _getlines ( [TERM] ) + +Another internal method C<_getlines> fetches multiple lines from the IOC. It +takes an optional TERM string or regexp parameter which is matched against each +input line in turn to determine when the IOC's output has been completed. +Termination also occurs on an EOF from the output stream. + +The return value is a list of all the lines received (with the final CR/LF +stripped) including the line that matched the terminator. + +=cut + +sub _getlines { + my ($self, $term) = @_; + + my @response = (); + + while (my $line = $self->_getline) { + push @response, $line; + last if defined $term && $line =~ $term; + } + return @response; +} + + +=item _geterrors ( ) + +Returns a list of lines output by the IOC to stderr since last called. Only +complete lines are included, and trailing newlines have been removed. + +NOTE: This doesn't work on Windows because it uses select which Perl doesn't +support on that OS, but it doesn't seem to cause any problems for short-lived +IOCs at least, it just never returns any text from the IOC's stderr output. + +=cut + +sub _geterrors { + my ($self) = @_; + my @errors; + + while ($self->{select}->can_read(0.01)) { + sysread $self->{stderr}, my $errbuf, 1024; + push @errors, split m/\n/, $self->{errbuf} . $errbuf, -1; + last unless @errors; + $self->{errbuf} = pop @errors; + } + return @errors; +} + +=item cmd ( [COMMAND [, ARGUMENTS ...]] ) + +If the C method is given an optional COMMAND string along with any number +of ARGUMENTS it constructs a command-line, quoting each argument as necessary. +This is sent to the IOC and a line read back and discarded if it matches the +command-line. + +With no COMMAND string the method starts here; it then collects lines from the +IOC until one matches the terminator. A list of all the lines received prior to +the terminator line is returned. + +=cut + +sub cmd { + my ($self, $cmd, @args) = @_; + + my @response; + my $term = $self->{terminator}; + + if (defined $cmd) { + if (@args) { + # FIXME: This quoting stuff isn't quite right + my @qargs = map { + m/^ (?: -? [0-9.eE+\-]+ ) | (?: " .* " ) $/x ? $_ : "'$_'" + } @args; + $cmd .= ' ' . join(' ', @qargs); + } + $self->_send("$cmd\n"); + + my $echo = $self->_getline; + return @response unless defined $echo; # undef => reached EOF + if ($echo ne $cmd) { + return @response if $echo =~ $term; + push @response, $echo; + } + } + + push @response, $self->_getlines($term); + pop @response if @response and $response[-1] =~ $term; + + my @errors = $self->_geterrors; + if (scalar @errors && $self->{debug}) { + my $indent = sprintf "#%d e>", $self->{pid}; + print map {"$indent $_\n"} @errors; + } + + return @response; +} + +=item kill () + +The C method attempts to stop an IOC that is still running in several +ways. First it sends an C command to the IOC shell. Next it closes the +IOC's stdin stream which will trigger an end-of-file on that stream, and it +fetches any remaining lines from the IOC's stdout stream before closing both +that and the stderr stream. Finally (unless running on MS-Windows) it sends a +SIGTERM signal to the child process and waits for it to clean up. + +=cut + +sub kill { + my $self = shift; + + return () + unless $self->started; + + $self->_send("exit\n"); # Don't wait + + close $self->{stdin}; + $self->{stdin} = gensym; + + my @response = $self->_getlines; # No terminator + close $self->{stdout}; + $self->{stdout} = gensym; + + $self->{select}->remove($self->{stderr}); + close $self->{stderr}; + $self->{stderr} = gensym; + + if ($^O ne "MSWin32") { + kill 'TERM', $self->{pid}; + waitpid $self->{pid}, 0; + } + $self->{pid} = undef; + + return @response; +} + +=item DESTROY () + +C objects have a destructor which calls the C method, but it +is not recommended that this be relied on to terminate an IOC process. Better to +use an C block and/or trap the necessary signals to explicitly kill the +IOC. + +=cut + +sub DESTROY { + shift->kill; +} + + +=back + +=head1 CONVENIENCE METHODS + +The following methods provide an easy way to perform various common IOC +operations. + +=over 4 + +=item dbLoadRecords ( FILE [, MACROS] ) + +Instructs the IOC to load a database (.db) from FILE. If provided, the MACROS +parameter is a single string containing one or more comma-separated assignment +statements like C for macros that are used in the database file. + +This method can also be used to load a database definition (.dbd) file. + +=cut + +sub dbLoadRecords { + my ($self, $file, $macros) = @_; + + $macros = '' unless defined $macros; + $self->cmd('dbLoadRecords', $file, $macros); +} + +=item iocInit () + +Start the IOC executing. + +=cut + +sub iocInit { + shift->cmd('iocInit'); +} + +=item dbl ( [RECORDTYPE]) + +This method uses the C command to fetch a list of all of the record names +the IOC has loaded. If a RECORDTYPE name is given, the list will only comprise +records of that type. + +=cut + +sub dbl { + my ($self, $rtyp) = @_; + + return $self->cmd('dbl', $rtyp) +} + +=item dbgf ( PV ) + +The C method returns the value of the process variable PV, or C if +the PV doesn't exist. This only works when the PV holds a scalar or an array +with one element. + +=cut + +# Regexps for the output from dbgf, currently supporting scalars only +my $RXdbfstr = qr/ DB[FR]_(STRING) : \s* " ( (?> \\. | [^"\\] )* ) " /x; +my $RXdbfint = qr/ DB[FR]_(U?(?:CHAR|SHORT|LONG|INT64)) : \s* ( -? [0-9]+ ) /x; +my $RXdbfflt = qr/ DB[FR]_(FLOAT|DOUBLE) : \s* ( [0-9.eE+\-]+ ) /x; +my $RXdbf = qr/ (?| $RXdbfstr | $RXdbfint | $RXdbfflt ) /x; + +sub dbgf { + my ($self, $pv) = @_; + + my @res = $self->cmd('dbgf', $pv); + return undef unless scalar @res; + + my ($type, $result) = ($res[0] =~ m/^ $RXdbf /x); + $result =~ s/\\([\\"'])/$1/gx + if $type eq 'STRING'; + return $result; +} + +=item dbpf ( PV, VALUE ) + +This method sets PV to VALUE, and returns the new value, or C if the PV +doesn't exist. If the put fails the return value is the previous value of the +PV. As with the C method this only works for scalar or single-element +arrays, but PV may be an array field which will be set to one element. + +=cut + +sub dbpf { + my ($self, $pv, $val) = @_; + + my @res = $self->cmd('dbpf', $pv, $val); + return undef unless scalar @res; + + my ($type, $result) = ($res[0] =~ m/^ $RXdbf /x); + $result =~ s/\\([\\"'])/$1/gx + if $type eq 'STRING'; + return $result; +} + +=back + +=head1 COPYRIGHT AND LICENSE + +Portions Copyright (C) 2011 UChicago Argonne LLC, as Operator of Argonne +National Laboratory. + +This software is distributed under the terms of the EPICS Open License. + +=cut + +1; + +__END__ diff --git a/modules/database/src/tools/Makefile b/modules/database/src/tools/Makefile index d61727661..e19c0c58f 100644 --- a/modules/database/src/tools/Makefile +++ b/modules/database/src/tools/Makefile @@ -1,5 +1,5 @@ #************************************************************************* -# Copyright (c) 2012 UChicago Argonne LLC, as Operator of Argonne +# Copyright (c) 2018 UChicago Argonne LLC, as Operator of Argonne # National Laboratory. # EPICS BASE is distributed subject to a Software License Agreement found # in file LICENSE that is included with this distribution. @@ -24,6 +24,9 @@ PERL_MODULES += DBD/Record.pm PERL_MODULES += DBD/Registrar.pm PERL_MODULES += DBD/Variable.pm +PERL_MODULES += EPICS/IOC.pm +HTMLS += EPICS/IOC.html + PERL_SCRIPTS += databaseModuleDirs.pm PERL_SCRIPTS += makeIncludeDbd.pl @@ -37,4 +40,27 @@ PERL_SCRIPTS += registerRecordDeviceDriver.pl HTMLS += dbdToHtml.html +# Build Package Config Files + +FINAL_LOCATION ?= $(shell $(PERL) $(TOOLS)/fullPathName.pl $(INSTALL_LOCATION)) +C_CFLAGS += $(filter-out -g,$(filter-out -O%,$(filter-out -W%,$(CPPFLAGS)))) +C_CFLAGS += $(filter-out -g,$(filter-out -O%,$(filter-out -W%,$(CFLAGS)))) +PKGVARS += FINAL_LOCATION OS_CLASS CMPLR_CLASS C_CFLAGS LDFLAGS LDLIBS +PKGVARS += EPICS_VERSION EPICS_REVISION EPICS_MODIFICATION EPICS_PATCH_LEVEL +PKGVARS += CC CCC CPP AR LD +PKGVARS += EPICS_BASE_HOST_LIBS EPICS_BASE_IOC_LIBS + +EXPANDFLAGS += $(foreach var,$(PKGVARS),-D$(var)="$(strip $($(var)))") +PKGCONFIG += epics-base-$(T_A).pc +ifeq ($(T_A),$(EPICS_HOST_ARCH)) +PKGCONFIG += epics-base.pc +endif + +EXPAND += $(PKGCONFIG:%=%@) +CLEANS += epics-base-$(T_A).pc@ + include $(TOP)/configure/RULES + +epics-base-$(T_A).pc@: ../epics-base-arch.pc@ + @$(RM) $@ + @$(CP) $< $@ diff --git a/modules/database/src/tools/dbdToHtml.pl b/modules/database/src/tools/dbdToHtml.pl index c51c793b5..e9711f757 100644 --- a/modules/database/src/tools/dbdToHtml.pl +++ b/modules/database/src/tools/dbdToHtml.pl @@ -127,6 +127,33 @@ if ($opt_D) { # Output dependencies only open my $out, '>', $opt_o or die "Can't create $opt_o: $!\n"; +my $podHtml; +my $idify; + +if ($::XHTML) { + $podHtml = Pod::Simple::XHTML->new(); + $podHtml->html_doctype(<< '__END_DOCTYPE'); + + +__END_DOCTYPE + $podHtml->html_header_tags($podHtml->html_header_tags . + "\n"); + + $idify = sub { + my $title = shift; + return $podHtml->idify($title, 1); + } +} else { # Fall back to HTML + $podHtml = Pod::Simple::HTML->new(); + $podHtml->html_css('style.css'); + + $idify = sub { + my $title = shift; + return Pod::Simple::HTML::esc($podHtml->section_escape($title)); + } +} + # Parse the Pod text from the root DBD object my $pod = join "\n", '=for html
    ', '', map { @@ -156,22 +183,6 @@ my $pod = join "\n", '=for html
    ', '', } $dbd->pod, '=for html
    ', ''; -my $podHtml; - -if ($::XHTML) { - $podHtml = Pod::Simple::XHTML->new(); - $podHtml->html_doctype(<< '__END_DOCTYPE'); - - -__END_DOCTYPE - $podHtml->html_header_tags($podHtml->html_header_tags . - "\n"); -} else { # Fall back to HTML - $podHtml = Pod::Simple::HTML->new(); - $podHtml->html_css('style.css'); -} - $podHtml->force_title(encode_entities($title)); $podHtml->perldoc_url_prefix(''); $podHtml->perldoc_url_postfix('.html'); @@ -249,7 +260,7 @@ sub fieldTableRow { if ($type eq 'MENU') { my $mn = $fld->attribute('menu'); my $menu = $dbd->menu($mn); - my $url = $menu ? "#Menu_$mn" : "${mn}.html"; + my $url = $menu ? '#' . &$idify("Menu $mn") : "${mn}.html"; $html .= " (
    $mn)"; } $html .= ''; @@ -377,6 +388,8 @@ can be found in the aai and aSub record types. If you look at the L file you'll see that the POD there starts by documenting a record-specific menu definition. The "menu" keyword generates a -table that lists all the choices found in the named menu. +table that lists all the choices found in the named menu. Any MENU fields in the +field tables that refer to a locally-defined menu will generate a link to a +document section which must be titled "Menu [menuName]". =cut diff --git a/src/tools/epics-base-arch.pc@ b/modules/database/src/tools/epics-base-arch.pc@ similarity index 95% rename from src/tools/epics-base-arch.pc@ rename to modules/database/src/tools/epics-base-arch.pc@ index 8e30cd30e..7dde33dea 100644 --- a/src/tools/epics-base-arch.pc@ +++ b/modules/database/src/tools/epics-base-arch.pc@ @@ -12,6 +12,7 @@ ARCH=@ARCH@ OS_CLASS=@OS_CLASS@ CMPLR_CLASS=@CMPLR_CLASS@ +EPICS_BASE_HOST_LIBS=@EPICS_BASE_HOST_LIBS@ EPICS_BASE_IOC_LIBS=@EPICS_BASE_IOC_LIBS@ # Directories diff --git a/src/tools/epics-base.pc@ b/modules/database/src/tools/epics-base.pc@ similarity index 100% rename from src/tools/epics-base.pc@ rename to modules/database/src/tools/epics-base.pc@ diff --git a/modules/database/test/Makefile b/modules/database/test/Makefile index b47389f94..0f2c64263 100644 --- a/modules/database/test/Makefile +++ b/modules/database/test/Makefile @@ -14,6 +14,7 @@ DIRS += ioc/db DIRS += ioc/dbtemplate DIRS += std/rec +DIRS += std/link DIRS += std/filters DIRS += tools diff --git a/modules/database/test/ioc/db/Makefile b/modules/database/test/ioc/db/Makefile index 840fd7394..e1f06cd3b 100644 --- a/modules/database/test/ioc/db/Makefile +++ b/modules/database/test/ioc/db/Makefile @@ -180,6 +180,8 @@ testHarness_SRCS += epicsRunDbTests.c dbTestHarness_SRCS += $(testHarness_SRCS) dbTestHarness_SRCS_RTEMS += rtemsTestHarness.c +PROD_SRCS_RTEMS += rtemsTestData.c + PROD_vxWorks = dbTestHarness PROD_RTEMS = dbTestHarness @@ -187,6 +189,10 @@ TESTSPEC_vxWorks = dbTestHarness.munch; epicsRunDbTests TESTSPEC_RTEMS = dbTestHarness.boot; epicsRunDbTests TESTSCRIPTS_HOST += $(TESTS:%=%.t) +ifneq ($(filter $(T_A),$(CROSS_COMPILER_RUNTEST_ARCHS)),) +TESTPROD_RTEMS = $(TESTPROD_HOST) +TESTSCRIPTS_RTEMS += $(TESTS:%=%.t) +endif include $(TOP)/configure/RULES @@ -197,3 +203,6 @@ dbStressLock$(DEP): $(COMMON_DIR)/xRecord.h devx$(DEP): $(COMMON_DIR)/xRecord.h scanIoTest$(DEP): $(COMMON_DIR)/xRecord.h xRecord$(DEP): $(COMMON_DIR)/xRecord.h + +rtemsTestData.c : $(TESTFILES) $(TOOLS)/epicsMakeMemFs.pl + $(PERL) $(TOOLS)/epicsMakeMemFs.pl $@ epicsRtemsFSImage $(TESTFILES) diff --git a/modules/database/test/ioc/db/dbBadLink.db b/modules/database/test/ioc/db/dbBadLink.db index 6c41e854b..b38d2ccef 100644 --- a/modules/database/test/ioc/db/dbBadLink.db +++ b/modules/database/test/ioc/db/dbBadLink.db @@ -14,3 +14,8 @@ record(x, "eINST_IO") { field(DTYP, "Unit Test INST_IO") field(INP, "hello") } + +record(x, "eINST_IO2") { + field(DTYP, "Unit Test INST_IO") + field(INP, "RL @no") +} diff --git a/modules/database/test/ioc/db/dbPutLinkTest.c b/modules/database/test/ioc/db/dbPutLinkTest.c index d6748a9eb..179b913ce 100644 --- a/modules/database/test/ioc/db/dbPutLinkTest.c +++ b/modules/database/test/ioc/db/dbPutLinkTest.c @@ -60,7 +60,9 @@ static const struct testParseDataT { TEST_PV_LINK(" world MSICP", "world", pvlOptMSI|pvlOptCP), {"#C14 S145 @testing", {VME_IO, "testing", 0, "CS", {14, 145}}}, + {"#C14 S145", {VME_IO, "", 0, "CS", {14, 145}}}, {"#B11 C12 N13 A14 F15 @cparam", {CAMAC_IO, "cparam", 0, "BCNAF", {11, 12, 13, 14, 15}}}, + {"#B11 C12 N13 A14 F15", {CAMAC_IO, "", 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, "", /*{}*/}}, {" {\"x\":true} ", {JSON_LINK, "{\"x\":true}", 0, "", /*{}*/}}, @@ -87,11 +89,15 @@ static void testLinkParse(void) for (;td->str; td++) { int i, N; testDiag("Parsing \"%s\"", td->str); - testOk(dbParseLink(td->str, DBF_INLINK, &info, 0) == 0, "Parser returned OK"); + testOk(dbParseLink(td->str, DBF_INLINK, &info) == 0, "Parser returned OK"); if (!testOk(info.ltype == td->info.ltype, "Link type value")) testDiag("Expected %d, got %d", td->info.ltype, info.ltype); - if (td->info.target) + if (td->info.target && info.target) testStrcmp(0, info.target, td->info.target); + else if(!!td->info.target ^ !!info.target) + testFail("info target NULL mis-match %s %s", info.target, td->info.target); + else + testPass("info target NULL as expected"); if (info.ltype == td->info.ltype) { switch (info.ltype) { case PV_LINK: @@ -125,7 +131,6 @@ static const char *testParseFailData[] = { "#A0 B @", "#A0 B C @", "#R1 M2 D3 E4 @oops", /* RF_IO has no parm */ - "#C1 S2", /* VME_IO needs parm */ NULL }; @@ -147,7 +152,7 @@ static void testLinkFailParse(void) eltc(1); for(;*td; td++) { - testOk(dbParseLink(*td, DBF_INLINK, &info, 0) == S_dbLib_badField, + testOk(dbParseLink(*td, DBF_INLINK, &info) == S_dbLib_badField, "dbParseLink correctly rejected \"%s\"", *td); } @@ -480,6 +485,13 @@ static void testLinkInitFail(void) testOk1(plink->type == INST_IO); testOk1(plink->value.instio.string != NULL); + testdbGetFieldEqual("eINST_IO2.INP", DBR_STRING, "@"); + + prec = (xRecord *) testdbRecordPtr("eINST_IO2"); + plink = &prec->inp; + testOk1(plink->type == INST_IO); + testOk1(plink->value.instio.string != NULL); + testIocShutdownOk(); testdbCleanup(); @@ -684,7 +696,7 @@ void testTSEL(void) MAIN(dbPutLinkTest) { - testPlan(320); + testPlan(337); testLinkParse(); testLinkFailParse(); testCADBSet(); diff --git a/modules/database/test/ioc/db/dbServerTest.c b/modules/database/test/ioc/db/dbServerTest.c index f0d5bf6ac..ddb22339f 100644 --- a/modules/database/test/ioc/db/dbServerTest.c +++ b/modules/database/test/ioc/db/dbServerTest.c @@ -79,6 +79,11 @@ dbServer one = { oneInit, oneRun, onePause, oneStop }; +dbServer one2 = { + ELLNODE_INIT, "one", + oneReport, oneStats, oneClient, + oneInit, oneRun, onePause, oneStop +}; /* Server layer for testing NULL methods */ @@ -123,7 +128,7 @@ MAIN(dbServerTest) char *theName = "The One"; int status; - testPlan(24); + testPlan(25); /* Prove that we handle substring names properly */ epicsEnvSet("EPICS_IOC_IGNORE_SERVERS", "none ones"); @@ -132,7 +137,8 @@ MAIN(dbServerTest) testOk(dbRegisterServer(&one) == 0, "Registered 'one'"); testOk1(oneState == NOTHING_CALLED); - testOk(dbRegisterServer(&one) != 0, "Duplicate registration rejected"); + testOk(dbRegisterServer(&one) == 0, "Duplicate registration accepted"); + testOk(dbRegisterServer(&one2) != 0, "change registration rejected"); testOk(dbRegisterServer(&illegal) != 0, "Illegal registration rejected"); testDiag("Registering dbServer 'no-routines'"); diff --git a/modules/database/test/ioc/db/dbStressLock.c b/modules/database/test/ioc/db/dbStressLock.c index aed0116e0..827464840 100644 --- a/modules/database/test/ioc/db/dbStressLock.c +++ b/modules/database/test/ioc/db/dbStressLock.c @@ -219,6 +219,11 @@ MAIN(dbStressTest) testPlan(80+nworkers*3); +#if defined(__rtems__) + testSkip(80+nworkers*3, "Test assumes time sliced preempting scheduling"); + return testDone(); +#endif + priv = callocMustSucceed(nworkers, sizeof(*priv), "no memory"); testDiag("lock set stress test"); diff --git a/modules/database/test/ioc/db/jlinkz.c b/modules/database/test/ioc/db/jlinkz.c index dd6919ffb..fc98ff3b3 100644 --- a/modules/database/test/ioc/db/jlinkz.c +++ b/modules/database/test/ioc/db/jlinkz.c @@ -246,7 +246,8 @@ static jlif jlifZ = { NULL, /* end child */ &z_lset, NULL, /* report */ - NULL /* map child */ + NULL, /* map child */ + NULL /* start child */ }; epicsExportAddress(jlif, jlifZ); diff --git a/modules/database/test/ioc/db/xLink.c b/modules/database/test/ioc/db/xLink.c index 12175f44e..2d7c3c043 100644 --- a/modules/database/test/ioc/db/xLink.c +++ b/modules/database/test/ioc/db/xLink.c @@ -2,7 +2,7 @@ * Copyright (c) 2016 UChicago Argonne LLC, as Operator of Argonne * 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. \*************************************************************************/ /* xLink.c */ @@ -82,7 +82,6 @@ static jlif xlinkIf = { NULL, NULL, NULL, NULL, NULL, NULL, xlink_get_lset, - NULL, NULL + NULL, NULL, NULL }; epicsExportAddress(jlif, xlinkIf); - diff --git a/modules/database/test/ioc/dbtemplate/msi.plt b/modules/database/test/ioc/dbtemplate/msi.plt index ab49fd9cd..54cc56aa2 100644 --- a/modules/database/test/ioc/dbtemplate/msi.plt +++ b/modules/database/test/ioc/dbtemplate/msi.plt @@ -31,18 +31,18 @@ ok(msi('-S ../t5-substitute.txt ../t5-template.txt'), slurp('../t5-result.txt')) # Substitution file, pattern format ok(msi('-S../t6-substitute.txt ../t6-template.txt'), slurp('../t6-result.txt')); -# Output option -o +# Output option -o and verbose option -V my $out = 't7-output.txt'; my $count = 5; # Try up to 5 times... my $result; do { unlink $out; - msi("-I.. -o $out ../t1-template.txt"); + msi("-I.. -V -o $out ../t1-template.txt"); $result = slurp($out); print "# msi output file empty, retrying\n" if $result eq ''; } while ($result eq '') && (--$count > 0); -ok($result, slurp('../t1-result.txt')); +ok($result, slurp('../t7-result.txt')); # Dependency generation, include/substitute model ok(msi('-I.. -D -o t8.txt ../t1-template.txt'), slurp('../t8-result.txt')); diff --git a/modules/database/test/ioc/dbtemplate/t1-result.txt b/modules/database/test/ioc/dbtemplate/t1-result.txt index fcd065585..abe94ebf7 100644 --- a/modules/database/test/ioc/dbtemplate/t1-result.txt +++ b/modules/database/test/ioc/dbtemplate/t1-result.txt @@ -1,6 +1,6 @@ This is t1-template.txt -With $(a,undefined) & $(b,undefined): +With $(a) & $(b): This is t1-include.txt a = default value used when a is undefined b = default value used when b is undefined diff --git a/modules/database/test/ioc/dbtemplate/t2-result.txt b/modules/database/test/ioc/dbtemplate/t2-result.txt index 5239e6a05..123d2d92f 100644 --- a/modules/database/test/ioc/dbtemplate/t2-result.txt +++ b/modules/database/test/ioc/dbtemplate/t2-result.txt @@ -1,6 +1,6 @@ -a = va1-a b = def-b c = def-c d = $(d,undefined) -a = va2-a b = va2-b c = def-c d = $(d,undefined) -a = va3-a b = va3-b c = va3-c d = $(d,undefined) -a = va4-a b = va4-b c = def-c d = $(d,undefined) -a = va5-a b = def-b c = def-c d = $(d,undefined) -a = pt3-a b = pt3-b c = pt3-c d = $(d,undefined) +a = va1-a b = def-b c = def-c d = $(d) +a = va2-a b = va2-b c = def-c d = $(d) +a = va3-a b = va3-b c = va3-c d = $(d) +a = va4-a b = va4-b c = def-c d = $(d) +a = va5-a b = def-b c = def-c d = $(d) +a = pt3-a b = pt3-b c = pt3-c d = $(d) diff --git a/modules/database/test/ioc/dbtemplate/t3-result.txt b/modules/database/test/ioc/dbtemplate/t3-result.txt index c6961507f..d7823e541 100644 --- a/modules/database/test/ioc/dbtemplate/t3-result.txt +++ b/modules/database/test/ioc/dbtemplate/t3-result.txt @@ -1,28 +1,28 @@ -a = gb1-a b = gb1-b c = def-c d = $(d,undefined) -a = va1-a b = gb1-b c = def-c d = $(d,undefined) -a = va2-a b = va2-b c = def-c d = $(d,undefined) -a = va3-a b = va3-b c = va3-c d = $(d,undefined) -a = va4-a b = va4-b c = def-c d = $(d,undefined) -a = va5-a b = gb1-b c = def-c d = $(d,undefined) -a = gb1-a b = gb1-b c = def-c d = $(d,undefined) -a = gb2-a b = gb2-b c = def-c d = $(d,undefined) -a = va1-a b = gb2-b c = def-c d = $(d,undefined) -a = va2-a b = va2-b c = def-c d = $(d,undefined) -a = va3-a b = va3-b c = va3-c d = $(d,undefined) -a = va4-a b = va4-b c = def-c d = $(d,undefined) -a = va5-a b = gb2-b c = def-c d = $(d,undefined) -a = gb2-a b = gb2-b c = def-c d = $(d,undefined) -a = gb3-a b = gb3-b c = def-c d = $(d,undefined) -a = pt1-a b = gb3-b c = def-c d = $(d,undefined) -a = pt2-a b = pt2-b c = def-c d = $(d,undefined) -a = pt3-a b = pt3-b c = pt3-c d = $(d,undefined) -a = pt4-a b = pt4-b c = def-c d = $(d,undefined) -a = pt5-a b = gb3-b c = def-c d = $(d,undefined) -a = gb3-a b = gb3-b c = def-c d = $(d,undefined) -a = gb4-a b = gb4-b c = def-c d = $(d,undefined) -a = pt1-a b = gb4-b c = def-c d = $(d,undefined) -a = pt2-a b = pt2-b c = def-c d = $(d,undefined) -a = pt3-a b = pt3-b c = pt3-c d = $(d,undefined) -a = pt4-a b = pt4-b c = def-c d = $(d,undefined) -a = pt5-a b = gb4-b c = def-c d = $(d,undefined) -a = gb4-a b = gb4-b c = def-c d = $(d,undefined) +a = gb1-a b = gb1-b c = def-c d = $(d) +a = va1-a b = gb1-b c = def-c d = $(d) +a = va2-a b = va2-b c = def-c d = $(d) +a = va3-a b = va3-b c = va3-c d = $(d) +a = va4-a b = va4-b c = def-c d = $(d) +a = va5-a b = gb1-b c = def-c d = $(d) +a = gb1-a b = gb1-b c = def-c d = $(d) +a = gb2-a b = gb2-b c = def-c d = $(d) +a = va1-a b = gb2-b c = def-c d = $(d) +a = va2-a b = va2-b c = def-c d = $(d) +a = va3-a b = va3-b c = va3-c d = $(d) +a = va4-a b = va4-b c = def-c d = $(d) +a = va5-a b = gb2-b c = def-c d = $(d) +a = gb2-a b = gb2-b c = def-c d = $(d) +a = gb3-a b = gb3-b c = def-c d = $(d) +a = pt1-a b = gb3-b c = def-c d = $(d) +a = pt2-a b = pt2-b c = def-c d = $(d) +a = pt3-a b = pt3-b c = pt3-c d = $(d) +a = pt4-a b = pt4-b c = def-c d = $(d) +a = pt5-a b = gb3-b c = def-c d = $(d) +a = gb3-a b = gb3-b c = def-c d = $(d) +a = gb4-a b = gb4-b c = def-c d = $(d) +a = pt1-a b = gb4-b c = def-c d = $(d) +a = pt2-a b = pt2-b c = def-c d = $(d) +a = pt3-a b = pt3-b c = pt3-c d = $(d) +a = pt4-a b = pt4-b c = def-c d = $(d) +a = pt5-a b = gb4-b c = def-c d = $(d) +a = gb4-a b = gb4-b c = def-c d = $(d) diff --git a/modules/database/test/ioc/dbtemplate/t4-result.txt b/modules/database/test/ioc/dbtemplate/t4-result.txt index 664872013..6ee8aa0f3 100644 --- a/modules/database/test/ioc/dbtemplate/t4-result.txt +++ b/modules/database/test/ioc/dbtemplate/t4-result.txt @@ -1,6 +1,6 @@ -a = va1-a b = def-b c = def-c d = $(d,undefined) -a = va2-a b = va2-b c = def-c d = $(d,undefined) -a = va3-a b = va3-b c = va3-c d = $(d,undefined) -a = va4-a b = va4-b c = va3-c d = $(d,undefined) -a = va5-a b = va4-b c = va3-c d = $(d,undefined) -a = pt3-a b = pt3-b c = pt3-c d = $(d,undefined) +a = va1-a b = def-b c = def-c d = $(d) +a = va2-a b = va2-b c = def-c d = $(d) +a = va3-a b = va3-b c = va3-c d = $(d) +a = va4-a b = va4-b c = va3-c d = $(d) +a = va5-a b = va4-b c = va3-c d = $(d) +a = pt3-a b = pt3-b c = pt3-c d = $(d) diff --git a/modules/database/test/ioc/dbtemplate/t5-result.txt b/modules/database/test/ioc/dbtemplate/t5-result.txt index 19a57c87c..2ae214948 100644 --- a/modules/database/test/ioc/dbtemplate/t5-result.txt +++ b/modules/database/test/ioc/dbtemplate/t5-result.txt @@ -2,19 +2,19 @@ a = 111 b = 222 c = xx -d = $(d,undefined) +d = $(d) # comment line a = aaa b = bbb c = ccc -d = $(d,undefined) +d = $(d) # comment line a = AA b = BB c = xx -d = $(d,undefined) +d = $(d) # comment line a = aaa b = bbb c = yy -d = $(d,undefined) +d = $(d) diff --git a/modules/database/test/ioc/dbtemplate/t6-result.txt b/modules/database/test/ioc/dbtemplate/t6-result.txt index 19a57c87c..2ae214948 100644 --- a/modules/database/test/ioc/dbtemplate/t6-result.txt +++ b/modules/database/test/ioc/dbtemplate/t6-result.txt @@ -2,19 +2,19 @@ a = 111 b = 222 c = xx -d = $(d,undefined) +d = $(d) # comment line a = aaa b = bbb c = ccc -d = $(d,undefined) +d = $(d) # comment line a = AA b = BB c = xx -d = $(d,undefined) +d = $(d) # comment line a = aaa b = bbb c = yy -d = $(d,undefined) +d = $(d) diff --git a/modules/database/test/ioc/dbtemplate/t7-result.txt b/modules/database/test/ioc/dbtemplate/t7-result.txt new file mode 100644 index 000000000..fcd065585 --- /dev/null +++ b/modules/database/test/ioc/dbtemplate/t7-result.txt @@ -0,0 +1,21 @@ +This is t1-template.txt + +With $(a,undefined) & $(b,undefined): +This is t1-include.txt + a = default value used when a is undefined + b = default value used when b is undefined +End of t1-include.txt + +On defining a=aaa & b=bbb: +This is t1-include.txt again + a = aaa + b = bbb +End of t1-include.txt + +On setting a="aa": +This is t1-include.txt again + a = "aa" + b = bbb +End of t1-include.txt + +End of t1-template.txt diff --git a/modules/database/test/std/filters/Makefile b/modules/database/test/std/filters/Makefile index 540a4d76c..718b8d523 100644 --- a/modules/database/test/std/filters/Makefile +++ b/modules/database/test/std/filters/Makefile @@ -63,6 +63,8 @@ testHarness_SRCS += epicsRunFilterTests.c filterTestHarness_SRCS += $(testHarness_SRCS) filterTestHarness_SRCS_RTEMS += rtemsTestHarness.c +PROD_SRCS_RTEMS += rtemsTestData.c + PROD_vxWorks = filterTestHarness PROD_RTEMS = filterTestHarness @@ -70,6 +72,10 @@ TESTSPEC_vxWorks = filterTestHarness.munch; epicsRunFilterTests TESTSPEC_RTEMS = filterTestHarness.boot; epicsRunFilterTests TESTSCRIPTS_HOST += $(TESTS:%=%.t) +ifneq ($(filter $(T_A),$(CROSS_COMPILER_RUNTEST_ARCHS)),) +TESTPROD_RTEMS = $(TESTPROD_HOST) +TESTSCRIPTS_RTEMS += $(TESTS:%=%.t) +endif include $(TOP)/configure/RULES @@ -79,3 +85,6 @@ dbndTest$(DEP): $(COMMON_DIR)/xRecord.h syncTest$(DEP): $(COMMON_DIR)/xRecord.h arrRecord$(DEP): $(COMMON_DIR)/arrRecord.h arrTest$(DEP): $(COMMON_DIR)/arrRecord.h + +rtemsTestData.c : $(TESTFILES) $(TOOLS)/epicsMakeMemFs.pl + $(PERL) $(TOOLS)/epicsMakeMemFs.pl $@ epicsRtemsFSImage $(TESTFILES) diff --git a/modules/database/test/std/link/Makefile b/modules/database/test/std/link/Makefile new file mode 100644 index 000000000..8540cdf84 --- /dev/null +++ b/modules/database/test/std/link/Makefile @@ -0,0 +1,60 @@ +#************************************************************************* +# Copyright (c) 2018 UChicago Argonne LLC, as Operator of Argonne +# National Laboratory. +# EPICS BASE is distributed subject to a Software License Agreement found +# in the file LICENSE that is included with this distribution. +#************************************************************************* +TOP=../../.. + +include $(TOP)/configure/CONFIG + +TESTLIBRARY = Recs + +Recs_SRCS += ioRecord.c +Recs_LIBS += dbCore ca Com + +PROD_LIBS = Recs dbRecStd dbCore ca Com + +DBDDEPENDS_FILES += linkTest.dbd$(DEP) +TARGETS += $(COMMON_DIR)/linkTest.dbd +linkTest_DBD += menuGlobal.dbd +linkTest_DBD += menuConvert.dbd +linkTest_DBD += menuScan.dbd +linkTest_DBD += links.dbd +linkTest_DBD += ioRecord.dbd +TESTFILES += $(COMMON_DIR)/linkTest.dbd +TESTFILES += ../ioRecord.db + +testHarness_SRCS += linkTest_registerRecordDeviceDriver.cpp + +TESTPROD_HOST += lnkStateTest +lnkStateTest_SRCS += lnkStateTest.c +lnkStateTest_SRCS += linkTest_registerRecordDeviceDriver.cpp +testHarness_SRCS += lnkStateTest.c +TESTS += lnkStateTest + +TESTPROD_HOST += lnkCalcTest +lnkCalcTest_SRCS += lnkCalcTest.c +lnkCalcTest_SRCS += linkTest_registerRecordDeviceDriver.cpp +testHarness_SRCS += lnkCalcTest.c +TESTS += lnkCalcTest + +# epicsRunLinkTests runs all the test programs in a known working order. +testHarness_SRCS += epicsRunLinkTests.c + +linkTestHarness_SRCS += $(testHarness_SRCS) +linkTestHarness_SRCS_RTEMS += rtemsTestHarness.c + +PROD_vxWorks = linkTestHarness +PROD_RTEMS = linkTestHarness + +TESTSPEC_vxWorks = linkTestHarness.munch; epicsRunLinkTests +TESTSPEC_RTEMS = linkTestHarness.boot; epicsRunLinkTests + +TESTSCRIPTS_HOST += $(TESTS:%=%.t) + +include $(TOP)/configure/RULES + +ioRecord$(DEP): $(COMMON_DIR)/ioRecord.h +lnkStateTest$(DEP): $(COMMON_DIR)/ioRecord.h +lnkCalcTest$(DEP): $(COMMON_DIR)/ioRecord.h diff --git a/modules/database/test/std/link/epicsRunLinkTests.c b/modules/database/test/std/link/epicsRunLinkTests.c new file mode 100644 index 000000000..a0f3b7c72 --- /dev/null +++ b/modules/database/test/std/link/epicsRunLinkTests.c @@ -0,0 +1,29 @@ +/*************************************************************************\ +* Copyright (c) 2018 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in the file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* + * Run filter tests as a batch. + */ + +#include "epicsUnitTest.h" +#include "epicsExit.h" +#include "dbmf.h" + +int lnkStateTest(void); +int lnkCalcTest(void); + +void epicsRunLinkTests(void) +{ + testHarness(); + + runTest(lnkStateTest); + runTest(lnkCalcTest); + + dbmfFreeChunks(); + + epicsExit(0); /* Trigger test harness */ +} diff --git a/modules/database/test/std/link/ioRecord.c b/modules/database/test/std/link/ioRecord.c new file mode 100644 index 000000000..1807c9171 --- /dev/null +++ b/modules/database/test/std/link/ioRecord.c @@ -0,0 +1,22 @@ +/*************************************************************************\ +* Copyright (c) 2018 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in the file LICENSE that is included with this distribution. + \*************************************************************************/ + +/* + * Author: Andrew Johnson + */ + +#include +#include + +#define GEN_SIZE_OFFSET +#include "ioRecord.h" +#undef GEN_SIZE_OFFSET + +#include + +static rset ioRSET; +epicsExportAddress(rset,ioRSET); diff --git a/modules/database/test/std/link/ioRecord.db b/modules/database/test/std/link/ioRecord.db new file mode 100644 index 000000000..7a95a1025 --- /dev/null +++ b/modules/database/test/std/link/ioRecord.db @@ -0,0 +1 @@ +record(io, io) {} diff --git a/modules/database/test/std/link/ioRecord.dbd b/modules/database/test/std/link/ioRecord.dbd new file mode 100644 index 000000000..efaec10a0 --- /dev/null +++ b/modules/database/test/std/link/ioRecord.dbd @@ -0,0 +1,14 @@ +# This is a soft record type with both input and output links + +recordtype(io) { + include "dbCommon.dbd" + field(VAL, DBF_LONG) { + prompt("Value") + } + field(INPUT, DBF_INLINK) { + prompt("Input Link") + } + field(OUTPUT, DBF_OUTLINK) { + prompt("Output Link") + } +} diff --git a/modules/database/test/std/link/lnkCalcTest.c b/modules/database/test/std/link/lnkCalcTest.c new file mode 100644 index 000000000..3fe8382d4 --- /dev/null +++ b/modules/database/test/std/link/lnkCalcTest.c @@ -0,0 +1,164 @@ +/*************************************************************************\ +* Copyright (c) 2018 Andrew Johnson +* EPICS BASE is distributed subject to a Software License Agreement found +* in the file LICENSE that is included with this distribution. +\*************************************************************************/ + +#include + +#include "dbAccess.h" +#include "alarm.h" +#include "dbUnitTest.h" +#include "errlog.h" +#include "epicsThread.h" +#include "dbLink.h" +#include "dbState.h" +#include "recGbl.h" +#include "testMain.h" +#include "ioRecord.h" + +#define testPutLongStr(PV, VAL) \ + testdbPutArrFieldOk(PV, DBF_CHAR, sizeof(VAL), VAL); + +void linkTest_registerRecordDeviceDriver(struct dbBase *); + +static void startTestIoc(const char *dbfile) +{ + testdbPrepare(); + testdbReadDatabase("linkTest.dbd", NULL, NULL); + linkTest_registerRecordDeviceDriver(pdbbase); + testdbReadDatabase(dbfile, NULL, NULL); + + eltc(0); + testIocInitOk(); + eltc(1); +} + +static void testCalc() +{ + ioRecord *pio; + DBLINK *pinp, *pout; + long status; + epicsFloat64 f64; + + startTestIoc("ioRecord.db"); + + pio = (ioRecord *) testdbRecordPtr("io"); + pinp = &pio->input; + pout = &pio->output; + + testDiag("testing lnkCalc input"); + + { + dbStateId red; + + testPutLongStr("io.INPUT", "{\"calc\":{" + "\"expr\":\"a\"," + "\"args\":[{\"state\":\"red\"}]" + "}}"); + if (testOk1(pinp->type == JSON_LINK)) + testDiag("Link was set to '%s'", pinp->value.json.string); + red = dbStateFind("red"); + testOk(!!red, "State red was created"); + + dbStateSet(red); + status = dbGetLink(pinp, DBF_DOUBLE, &f64, NULL, NULL); + testOk(!status, "dbGetLink succeeded (status = %ld)", status); + testOk(f64, "Got TRUE (%g)", f64); + + dbStateClear(red); + status = dbGetLink(pinp, DBF_DOUBLE, &f64, NULL, NULL); + testOk(!status, "dbGetLink succeeded (status = %ld)", status); + testOk(!f64, "Got FALSE (%g)", f64); + } + + { + dbStateId major = dbStateCreate("major"); + dbStateId minor = dbStateCreate("minor"); + epicsEnum16 stat, sevr; + + testPutLongStr("io.INPUT", "{\"calc\":{" + "\"expr\":\"0\"," + "\"major\":\"A\"," + "\"minor\":\"B\"," + "\"args\":[{\"state\":\"major\"},{\"state\":\"minor\"}]" + "}}"); + if (testOk1(pinp->type == JSON_LINK)) + testDiag("Link was set to '%s'", pinp->value.json.string); + + dbStateSet(major); + dbStateSet(minor); + status = dbGetLink(pinp, DBF_DOUBLE, &f64, NULL, NULL); + testOk(!status, "dbGetLink succeeded (status = %ld)", status); + testOk(f64 == 0.0, "Got zero (%g)", f64); + testOk(recGblResetAlarms(pio) & DBE_ALARM, "Record alarm was raised"); + status = dbGetAlarm(pinp, &stat, &sevr); + testOk(!status, "dbGetAlarm succeeded (status = %ld)", status); + testOk(stat == LINK_ALARM, "Alarm status = LINK (%d)", stat); + testOk(sevr == MAJOR_ALARM, "Alarm severity = MAJOR (%d)", sevr); + + dbStateClear(major); + status = dbGetLink(pinp, DBF_DOUBLE, &f64, NULL, NULL); + testOk(!status, "dbGetLink succeeded (status = %ld)", status); + testOk(recGblResetAlarms(pio) & DBE_ALARM, "Record alarm was raised"); + status = dbGetAlarm(pinp, &stat, &sevr); + testOk(!status, "dbGetAlarm succeeded (status = %ld)", status); + testOk(stat == LINK_ALARM, "Alarm status = LINK (%d)", stat); + testOk(sevr == MINOR_ALARM, "Alarm severity = MINOR (%d)", sevr); + } + + testDiag("testing lnkCalc output"); + + { + dbStateId red = dbStateFind("red"); + dbStateId out = dbStateCreate("out"); + + testPutLongStr("io.OUTPUT", "{\"calc\":{" + "\"expr\":\"!a\"," + "\"out\":{\"state\":\"out\"}," + "\"args\":[{\"state\":\"red\"}]," + "\"units\":\"things\"," + "\"prec\":3" + "}}"); + if (testOk1(pout->type == JSON_LINK)) + testDiag("Link was set to '%s'", pout->value.json.string); + + dbStateSet(red); + f64 = 1.0; + status = dbPutLink(pout, DBF_DOUBLE, &f64, 1); + testOk(!status, "dbPutLink succeeded (status = %ld)", status); + testOk(!dbStateGet(out), "output was cleared"); + + dbStateClear(red); + status = dbPutLink(pout, DBF_DOUBLE, &f64, 1); + testOk(!status, "dbGetLink succeeded (status = %ld)", status); + testOk(dbStateGet(out), "output was set"); + } + + { + char units[20] = {0}; + short prec = 0; + + status = dbGetUnits(pout, units, sizeof(units)); + testOk(!status, "dbGetUnits succeeded (status = %ld)", status); + testOk(!strcmp(units, "things"), "Units string correct (%s)", units); + + status = dbGetPrecision(pout, &prec); + testOk(!status, "dbGetPrecision succeeded (status = %ld)", status); + testOk(prec == 3, "Precision correct (%d)", prec); + } + + testIocShutdownOk(); + + testdbCleanup(); +} + + +MAIN(lnkCalcTest) +{ + testPlan(0); + + testCalc(); + + return testDone(); +} diff --git a/modules/database/test/std/link/lnkStateTest.c b/modules/database/test/std/link/lnkStateTest.c new file mode 100644 index 000000000..d3da06f5c --- /dev/null +++ b/modules/database/test/std/link/lnkStateTest.c @@ -0,0 +1,130 @@ +/*************************************************************************\ +* Copyright (c) 2018 Andrew Johnson +* EPICS BASE is distributed subject to a Software License Agreement found +* in the file LICENSE that is included with this distribution. +\*************************************************************************/ + +#include + +#include "dbAccess.h" +#include "alarm.h" +#include "dbUnitTest.h" +#include "errlog.h" +#include "epicsThread.h" +#include "dbLink.h" +#include "dbState.h" +#include "ioRecord.h" + +#include "testMain.h" + +void linkTest_registerRecordDeviceDriver(struct dbBase *); + +static void startTestIoc(const char *dbfile) +{ + testdbPrepare(); + testdbReadDatabase("linkTest.dbd", NULL, NULL); + linkTest_registerRecordDeviceDriver(pdbbase); + testdbReadDatabase(dbfile, NULL, NULL); + + eltc(0); + testIocInitOk(); + eltc(1); +} + +static void testState() +{ + dbStateId red; + ioRecord *pio; + DBLINK *pinp, *pout; + long status; + + testDiag("testing lnkState"); + + startTestIoc("ioRecord.db"); + + pio = (ioRecord *) testdbRecordPtr("io"); + pinp = &pio->input; + pout = &pio->output; + + red = dbStateFind("red"); + testOk(!red, "No state red exists"); + + testdbPutFieldOk("io.INPUT", DBF_STRING, "{\"state\":\"red\"}"); + if (testOk1(pinp->type == JSON_LINK)) + testDiag("Link was set to '%s'", pinp->value.json.string); + red = dbStateFind("red"); + testOk(!!red, "state red exists"); + + { + epicsInt16 i16; + + dbStateSet(red); + status = dbGetLink(pinp, DBF_SHORT, &i16, NULL, NULL); + testOk(!status, "dbGetLink succeeded (status = %ld)", status); + testOk(i16, "Got TRUE"); + + testdbPutFieldOk("io.INPUT", DBF_STRING, "{\"state\":\"!red\"}"); + if (testOk1(pinp->type == JSON_LINK)) + testDiag("Link was set to '%s'", pinp->value.json.string); + + status = dbGetLink(pinp, DBF_SHORT, &i16, NULL, NULL); + testOk(!status, "dbGetLink succeeded (status = %ld)", status); + testOk(!i16, "Got FALSE"); + + testdbPutFieldOk("io.OUTPUT", DBF_STRING, "{\"state\":\"red\"}"); + if (testOk1(pout->type == JSON_LINK)) + testDiag("Link was set to '%s'", pout->value.json.string); + + i16 = 0; + status = dbPutLink(pout, DBF_SHORT, &i16, 1); + testOk(!status, "dbPutLink %d succeeded (status = %ld)", i16, status); + testOk(!dbStateGet(red), "state was cleared"); + + i16 = 0x8000; + status = dbPutLink(pout, DBF_SHORT, &i16, 1); + testOk(!status, "dbPutLink 0x%hx succeeded (status = %ld)", i16, status); + testOk(dbStateGet(red), "state was set"); + } + + status = dbPutLink(pout, DBF_STRING, "", 1); + testOk(!status, "dbPutLink '' succeeded (status = %ld)", status); + testOk(!dbStateGet(red), "state was cleared"); + + status = dbPutLink(pout, DBF_STRING, "FALSE", 1); /* Not really... */ + testOk(!status, "dbPutLink 'FALSE' succeeded (status = %ld)", status); + testOk(dbStateGet(red), "state was set"); + + status = dbPutLink(pout, DBF_STRING, "0", 1); + testOk(!status, "dbPutLink '0' succeeded (status = %ld)", status); + testOk(!dbStateGet(red), "state was cleared"); + + { + epicsFloat64 f64 = 0.1; + + status = dbPutLink(pout, DBF_DOUBLE, &f64, 1); + testOk(!status, "dbPutLink %g succeeded (status = %ld)", f64, status); + testOk(dbStateGet(red), "state was set"); + + testdbPutFieldOk("io.OUTPUT", DBF_STRING, "{\"state\":\"!red\"}"); + if (testOk1(pout->type == JSON_LINK)) + testDiag("Link was set to '%s'", pout->value.json.string); + + status = dbPutLink(pout, DBF_DOUBLE, &f64, 1); + testOk(!status, "dbPutLink %g succeeded (status = %ld)", f64, status); + testOk(!dbStateGet(red), "state was cleared"); + } + + testIocShutdownOk(); + + testdbCleanup(); +} + + +MAIN(lnkStateTest) +{ + testPlan(28); + + testState(); + + return testDone(); +} diff --git a/modules/database/test/std/link/rtemsTestHarness.c b/modules/database/test/std/link/rtemsTestHarness.c new file mode 100644 index 000000000..7397f74d6 --- /dev/null +++ b/modules/database/test/std/link/rtemsTestHarness.c @@ -0,0 +1,14 @@ +/*************************************************************************\ +* Copyright (c) 2018 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in the file LICENSE that is included with this distribution. +\*************************************************************************/ + +extern void epicsRunLinkTests(void); + +int main(int argc, char **argv) +{ + epicsRunLinkTests(); /* calls epicsExit(0) */ + return 0; +} diff --git a/modules/database/test/std/rec/Makefile b/modules/database/test/std/rec/Makefile index 7c78ac93c..185c2c0c1 100644 --- a/modules/database/test/std/rec/Makefile +++ b/modules/database/test/std/rec/Makefile @@ -114,6 +114,7 @@ TESTPROD_HOST += regressTest regressTest_SRCS += regressTest.c regressTest_SRCS += regressTest_registerRecordDeviceDriver.cpp TESTFILES += $(COMMON_DIR)/regressTest.dbd ../regressArray1.db ../regressHex.db ../regressLinkMS.db +TESTFILES += ../badCaLink.db TESTS += regressTest TARGETS += $(COMMON_DIR)/simmTest.dbd @@ -136,12 +137,27 @@ testHarness_SRCS += mbbioDirectTest.c TESTFILES += ../mbbioDirectTest.db TESTS += mbbioDirectTest +TARGETS += $(COMMON_DIR)/asyncproctest.dbd +DBDDEPENDS_FILES += asyncproctest.dbd$(DEP) +asyncproctest_DBD += base.dbd +TESTPROD_HOST += asyncproctest +asyncproctest_SRCS += asyncproctest.c +asyncproctest_SRCS += asyncproctest_registerRecordDeviceDriver.cpp +TESTFILES += $(COMMON_DIR)/asyncproctest.dbd ../asyncproctest.db +TESTS += asyncproctest + +ifeq ($(T_A),$(EPICS_HOST_ARCH)) +# Host-only tests of softIoc/softIocPVA, caget and pvget (if present) +TESTS += netget +endif # epicsRunRecordTests runs all the test programs in a known working order. testHarness_SRCS += epicsRunRecordTests.c recordTestHarness_SRCS += $(testHarness_SRCS) recordTestHarness_SRCS_RTEMS += rtemsTestHarness.c +PROD_SRCS_RTEMS += rtemsTestData.c + PROD_vxWorks = recordTestHarness PROD_RTEMS = recordTestHarness @@ -149,5 +165,12 @@ TESTSPEC_vxWorks = recordTestHarness.munch; epicsRunRecordTests TESTSPEC_RTEMS = recordTestHarness.boot; epicsRunRecordTests TESTSCRIPTS_HOST += $(TESTS:%=%.t) +ifneq ($(filter $(T_A),$(CROSS_COMPILER_RUNTEST_ARCHS)),) +TESTPROD_RTEMS = $(TESTPROD_HOST) +TESTSCRIPTS_RTEMS += $(TESTS:%=%.t) +endif include $(TOP)/configure/RULES + +rtemsTestData.c : $(TESTFILES) $(TOOLS)/epicsMakeMemFs.pl + $(PERL) $(TOOLS)/epicsMakeMemFs.pl $@ epicsRtemsFSImage $(TESTFILES) diff --git a/modules/database/test/std/rec/asyncproctest.c b/modules/database/test/std/rec/asyncproctest.c new file mode 100644 index 000000000..a8b09a928 --- /dev/null +++ b/modules/database/test/std/rec/asyncproctest.c @@ -0,0 +1,107 @@ +/*************************************************************************\ +* Copyright (c) 2018 Michael Davidsaver +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* This test covers some situations where asynchronous records are + * dbProcess()'d while busy (PACT==1). + */ + +#include +#include +#include +#include +#include +#include +#include +#include "registryFunction.h" +#include + +epicsEventId done; +static int waitFor; + +static +long doneSubr(subRecord *prec) +{ + if (--waitFor <= 0) + epicsEventMustTrigger(done); + return 0; +} + +void asyncproctest_registerRecordDeviceDriver(struct dbBase *); + +MAIN(asyncproctest) +{ + testPlan(21); + + done = epicsEventMustCreate(epicsEventEmpty); + + testdbPrepare(); + + testdbReadDatabase("asyncproctest.dbd", NULL, NULL); + asyncproctest_registerRecordDeviceDriver(pdbbase); + registryFunctionAdd("doneSubr", (REGISTRYFUNCTION) doneSubr); + testdbReadDatabase("asyncproctest.db", NULL, "TPRO=0"); + + dbAccessDebugPUTF = 1; + + testIocInitOk(); + testDiag("===== Chain 1 ======"); + + waitFor = 2; + testdbPutFieldOk("chain1.B", DBF_LONG, 6); + testdbPutFieldOk("chain1.B", DBF_LONG, 7); + + if (epicsEventWaitWithTimeout(done, 10.0) != epicsEventOK) + testAbort("Processing timed out"); + + testdbGetFieldEqual("chain1", DBF_LONG, 7); + testdbGetFieldEqual("chain1.A", DBF_LONG, 2); + + testDiag("===== Chain 2 ======"); + + waitFor = 2; + testdbPutFieldOk("chain2:1.B", DBF_LONG, 6); + testdbPutFieldOk("chain2:1.B", DBF_LONG, 7); + + if (epicsEventWaitWithTimeout(done, 10.0) != epicsEventOK) + testAbort("Processing timed out"); + + testdbGetFieldEqual("chain2:1", DBF_LONG, 7); + testdbGetFieldEqual("chain2:2", DBF_LONG, 7); + testdbGetFieldEqual("chain2:1.A", DBF_LONG, 2); + testdbGetFieldEqual("chain2:2.A", DBF_LONG, 2); + + testDiag("===== Chain 2 again ======"); + + waitFor = 2; + testdbPutFieldOk("chain2:1.B", DBF_LONG, 6); + testdbPutFieldOk("chain2:1.B", DBF_LONG, 7); + testdbPutFieldOk("chain2:1.B", DBF_LONG, 8); + + if (epicsEventWaitWithTimeout(done, 10.0) != epicsEventOK) + testAbort("Processing timed out"); + + testdbGetFieldEqual("chain2:1", DBF_LONG, 8); + testdbGetFieldEqual("chain2:2", DBF_LONG, 8); + testdbGetFieldEqual("chain2:1.A", DBF_LONG, 5); + testdbGetFieldEqual("chain2:2.A", DBF_LONG, 4); + + testDiag("===== Chain 3 ======"); + + waitFor = 2; + testdbPutFieldOk("chain3.B", DBF_LONG, 6); + testdbPutFieldOk("chain3.B", DBF_LONG, 7); + + if (epicsEventWaitWithTimeout(done, 10.0) != epicsEventOK) + testAbort("Processing timed out"); + + testdbGetFieldEqual("chain3", DBF_LONG, 7); + testdbGetFieldEqual("chain3.A", DBF_LONG, 2); + + testIocShutdownOk(); + + testdbCleanup(); + + return testDone(); +} diff --git a/modules/database/test/std/rec/asyncproctest.db b/modules/database/test/std/rec/asyncproctest.db new file mode 100644 index 000000000..352b6ca56 --- /dev/null +++ b/modules/database/test/std/rec/asyncproctest.db @@ -0,0 +1,55 @@ + +# simple case +# stand alone async record +record(calcout, "chain1") { + field(CALC, "A:=A+1;B") + field(ODLY, "0.1") + field(TPRO, "$(TPRO=)") + field(FLNK, "done1") +} +record(sub, "done1") { + field(SNAM, "doneSubr") + field(TPRO, "$(TPRO=)") +} + + +# original problem case +# async record chained after syncronous record +record(calcout, "chain2:1") { + field(CALC, "A:=A+1;B") + # ODLY=0 synchronous + field(OUT , "chain2:2.B PP") + field(TPRO, "$(TPRO=)") +} + +record(ai, "chain2:3") { + field(INP, "chain2:1 CPP") + field(FLNK, "chain2:2") + field(TPRO, "$(TPRO=)") +} + +record(calcout, "chain2:2") { + field(CALC, "A:=A+1;B") + field(ODLY, "0.5") + field(TPRO, "$(TPRO=)") + field(FLNK, "done2") +} +record(sub, "done2") { + field(SNAM, "doneSubr") + field(TPRO, "$(TPRO=)") +} + + +# ANJ's error case +# async record FLNK's to itself (via done3) +record(calcout, "chain3") { + field(CALC, "A:=A+1;B") + field(ODLY, "0.1") + field(FLNK, "done3") + field(TPRO, "$(TPRO=)") +} +record(sub, "done3") { + field(SNAM, "doneSubr") + field(FLNK, "chain3") + field(TPRO, "$(TPRO=)") +} diff --git a/modules/database/test/std/rec/badCaLink.db b/modules/database/test/std/rec/badCaLink.db new file mode 100644 index 000000000..c71bc9d2f --- /dev/null +++ b/modules/database/test/std/rec/badCaLink.db @@ -0,0 +1,4 @@ +record(ai, "ai:disconn") { + field(INP , "invalid CA") + field(UDF , "0") +} diff --git a/modules/database/test/std/rec/linkInitTest.c b/modules/database/test/std/rec/linkInitTest.c index 7225beb1c..09ff40942 100644 --- a/modules/database/test/std/rec/linkInitTest.c +++ b/modules/database/test/std/rec/linkInitTest.c @@ -7,11 +7,14 @@ #include #include "dbAccess.h" +#include "devSup.h" #include "alarm.h" #include "dbUnitTest.h" #include "errlog.h" #include "epicsThread.h" +#include "longinRecord.h" + #include "testMain.h" void recTestIoc_registerRecordDeviceDriver(struct dbBase *); @@ -28,12 +31,21 @@ static void startTestIoc(const char *dbfile) eltc(1); } +/* testing here instead of ioc/db/test as xRecord has no INP/OUT */ +static void testdbGetDevLink(void) +{ + longinRecord *rec = (longinRecord*)testdbRecordPtr("li1"); + testOk1(dbGetDevLink((dbCommon*)rec) == &rec->inp); +} + static void testLongStringInit() { testDiag("testLongStringInit"); startTestIoc("linkInitTest.db"); + testdbGetDevLink(); + { const char buf[] = "!----------------------------------------------!"; testdbGetArrFieldEqual("longstr1.VAL$", DBF_CHAR, NELEMENTS(buf)+2, NELEMENTS(buf), buf); @@ -230,7 +242,7 @@ void testInt64Inputs(void) MAIN(linkInitTest) { - testPlan(77); + testPlan(78); testLongStringInit(); testCalcInit(); diff --git a/modules/database/test/std/rec/netget.plt b/modules/database/test/std/rec/netget.plt new file mode 100644 index 000000000..75a4f4565 --- /dev/null +++ b/modules/database/test/std/rec/netget.plt @@ -0,0 +1,140 @@ +#!/usr/bin/env perl + +use strict; +use warnings; + +use lib '@TOP@/lib/perl'; + +use Test::More tests => 3; +use EPICS::IOC; + +$ENV{HARNESS_ACTIVE} = 1 if scalar @ARGV && shift eq '-tap'; + +# Keep traffic local and avoid duplicates over multiple interfaces +$ENV{EPICS_CA_AUTO_ADDR_LIST} = 'NO'; +$ENV{EPICS_CA_ADDR_LIST} = 'localhost'; +$ENV{EPICS_CA_SERVER_PORT} = 55064; +$ENV{EPICS_CAS_BEACON_PORT} = 55065; +$ENV{EPICS_CAS_INTF_ADDR_LIST} = 'localhost'; + +$ENV{EPICS_PVA_AUTO_ADDR_LIST} = 'NO'; +$ENV{EPICS_PVA_ADDR_LIST} = 'localhost'; +$ENV{EPICS_PVAS_SERVER_PORT} = 55075; +$ENV{EPICS_PVA_BROADCAST_PORT} = 55076; +$ENV{EPICS_PVAS_INTF_ADDR_LIST} = 'localhost'; + +my $bin = "@TOP@/bin/@ARCH@"; +my $exe = ($^O =~ m/^(MSWin32|cygwin)$/x) ? '.exe' : ''; +my $prefix = "test-$$"; + +my $ioc = EPICS::IOC->new(); +#$ioc->debug(1); + +$SIG{__DIE__} = $SIG{INT} = $SIG{QUIT} = sub { + $ioc->kill; + BAIL_OUT('Caught signal'); +}; + + +# Watchdog utilities + +sub kill_bail { + my $doing = shift; + return sub { + $ioc->kill; + BAIL_OUT("Timeout $doing"); + } +} + +sub watchdog (&$$) { + my ($do, $timeout, $abort) = @_; + $SIG{ALRM} = $abort; + alarm $timeout; + &$do; + alarm 0; +} + + +# Start the IOC + +my $softIoc = "$bin/softIocPVA$exe"; +$softIoc = "$bin/softIoc$exe" + unless -x $softIoc; +BAIL_OUT("Can't find a softIoc executable") + unless -x $softIoc; + +watchdog { + $ioc->start($softIoc, '-x', $prefix); + $ioc->cmd; # Wait for command prompt +} 10, kill_bail('starting softIoc'); + + +# Get Base Version number from PV + +my $pv = "$prefix:BaseVersion"; + +watchdog { + my @pvs = $ioc->dbl('stringin'); + grep(m/^ $pv $/x, @pvs) + or BAIL_OUT('No BaseVersion record found'); +} 10, kill_bail('running dbl'); + +my $version; +watchdog { + $version = $ioc->dbgf("$pv"); +} 10, kill_bail('getting BaseVersion'); +like($version, qr/^ \d+ \. \d+ \. \d+ /x, + "Got BaseVersion '$version' from iocsh"); + + +# Channel Access + +SKIP: { + my $caget = "$bin/caget$exe"; + skip "caget not available", 1 + unless -x $caget; + + # CA Server Diagnostics + + watchdog { + note("CA server configuration:\n", + map(" $_\n", $ioc->cmd('casr', 1))); + } 10, kill_bail('running casr'); + + # CA Client test + + watchdog { + my $caVersion = `$caget -w5 $pv`; + like($caVersion, qr/^ $pv \s+ \Q$version\E $/x, + 'Got same BaseVersion from caget'); + } 10, kill_bail('doing caget'); +} + + +# PV Access + +SKIP: { + my $pvget = "$bin/pvget$exe"; + skip "softIocPVA not available", 1 + if $softIoc eq "$bin/softIoc$exe"; + + # PVA Server Diagnostics + + watchdog { + note("PVA server configuration:\n", + map(" $_\n", $ioc->cmd('pvasr'))); + } 10, kill_bail('running pvasr'); + + skip "pvget not available", 1 + unless -x $pvget; + + # PVA Client test + + watchdog { + my $pvaVersion = `$pvget -w5 $pv`; + like($pvaVersion, qr/^ $pv \s .* \Q$version\E \s* $/x, + 'Got same BaseVersion from pvget'); + } 10, kill_bail('doing pvget'); +} + +$ioc->kill; diff --git a/modules/database/test/std/rec/regressTest.c b/modules/database/test/std/rec/regressTest.c index 15010cebf..661463984 100644 --- a/modules/database/test/std/rec/regressTest.c +++ b/modules/database/test/std/rec/regressTest.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #include @@ -123,12 +124,26 @@ void testLinkMS(void) testdbCleanup(); } +/* lp:1798855 disconnected CA link must alarm */ +static +void testCADisconn(void) +{ + testDiag("In testCADisconn()"); + + startRegressTestIoc("badCaLink.db"); + + testdbPutFieldOk("ai:disconn.PROC", DBF_LONG, 1); + testdbGetFieldEqual("ai:disconn.SEVR", DBF_LONG, INVALID_ALARM); + testdbGetFieldEqual("ai:disconn.STAT", DBF_LONG, LINK_ALARM); +} + MAIN(regressTest) { - testPlan(31); + testPlan(34); testArrayLength1(); testHexConstantLinks(); testLinkMS(); + testCADisconn(); return testDone(); } diff --git a/modules/database/test/tools/Recordtype.plt b/modules/database/test/tools/Recordtype.plt index ec189a92c..d9580b62b 100644 --- a/modules/database/test/tools/Recordtype.plt +++ b/modules/database/test/tools/Recordtype.plt @@ -2,7 +2,7 @@ use lib '@TOP@/lib/perl'; -use Test::More tests => 17; +use Test::More tests => 23; use DBD::Recordtype; use DBD::Recfield; @@ -13,6 +13,11 @@ isa_ok $rtyp, 'DBD::Recordtype'; is $rtyp->name, 'test', 'Record name'; is $rtyp->fields, 0, 'No fields yet'; +is $rtyp->equals($rtyp), 1, 'A declaration == itself'; + +my $rt2 = DBD::Recordtype->new('test'); +is $rtyp->equals($rt2), 1, 'A declaration == a different declaration'; + my $fld1 = DBD::Recfield->new('NAME', 'DBF_STRING'); $fld1->add_attribute("size", "41"); $fld1->check_valid; @@ -26,6 +31,18 @@ is $rtyp->fields, 1, 'First field added'; $rtyp->add_field($fld2); is $rtyp->fields, 2, 'Second field added'; +is $rtyp->equals($rtyp), 1, 'A definition == itself'; +is $rt2->equals($rtyp), 1, 'A declaration == a definition'; + +$rt2->add_field($fld1); +my $fld3 = DBD::Recfield->new('DTYP', 'DBF_DEVICE'); +$fld3->check_valid; +$rt2->add_field($fld3); +is $rt2->equals($rtyp), 1, 'Identical definitions are equal'; + +$fld3->add_attribute("pp", "TRUE"); +is $rt2->equals($rtyp), 0, 'Different definitions are not equal'; + my @fields = $rtyp->fields; is_deeply \@fields, [$fld1, $fld2], 'Field list'; diff --git a/modules/libcom/RTEMS/Makefile b/modules/libcom/RTEMS/Makefile index b0f3b6fd9..311c25024 100644 --- a/modules/libcom/RTEMS/Makefile +++ b/modules/libcom/RTEMS/Makefile @@ -11,8 +11,14 @@ TOP = .. include $(TOP)/configure/CONFIG include $(TOP)/configure/CONFIG_LIBCOM_VERSION -INC += epicsRtemsInitHooks.h +PERL_SCRIPTS += epicsMakeMemFs.pl +INC += epicsRtemsInitHooks.h +INC += epicsMemFs.h + +ifeq ($(RTEMS_QEMU_FIXUPS),YES) +rtems_init_CPPFLAGS += -DQEMU_FIXUPS +endif rtemsCom_SRCS += rtems_init.c rtemsCom_SRCS += rtems_config.c rtemsCom_SRCS += rtems_netconfig.c @@ -20,6 +26,11 @@ rtemsCom_SRCS += rtems_util.c rtemsCom_SRCS += setBootConfigFromNVRAM.c rtemsCom_SRCS += epicsRtemsInitHookPre.c rtemsCom_SRCS += epicsRtemsInitHookPost.c +rtemsCom_SRCS += epicsMemFs.c + +ifeq ($(RTEMS_BSP),pc386) +rtemsCom_SRCS += ne2kpci.c +endif LIBRARY_RTEMS = rtemsCom diff --git a/modules/libcom/RTEMS/epicsMakeMemFs.pl b/modules/libcom/RTEMS/epicsMakeMemFs.pl new file mode 100644 index 000000000..a7470bdc4 --- /dev/null +++ b/modules/libcom/RTEMS/epicsMakeMemFs.pl @@ -0,0 +1,85 @@ +#!/usr/bin/env perl +# + +use File::Basename; +use Text::Wrap; + +use strict; + +my $outfile = shift; +my $varname = shift; + +open(my $DST, '>', $outfile) + or die "Failed to open $outfile"; + +my $inputs = join "\n * ", @ARGV; +print $DST < + +EOF + +my $N = 0; + +$Text::Wrap::break = ','; +$Text::Wrap::columns = 78; +$Text::Wrap::separator = ",\n"; + +for my $fname (@ARGV) { + my $realfname = $fname; + + # strip leading "../" "./" or "/" + $fname =~ s(^\.{0,2}/)()g; + + my $file = basename($fname); + my @dirs = split('/', dirname($fname)); + + print $DST "/* $realfname */\n", + "static const char * const file_${N}_dir[] = {", + map("\"$_\", ", @dirs), "NULL};\n", + "static const char file_${N}_data[] = {\n", + " "; + + open(my $SRC, '<', $realfname) + or die "Failed to open $realfname"; + binmode $SRC; + + my ($buf, @bufs); + while (read($SRC, $buf, 4096)) { + @bufs[-1] .= ',' if @bufs; # Need ',' between buffers + push @bufs, join(",", map(ord, split(//, $buf))); + } + print $DST wrap('', ' ', @bufs); + + close $SRC; + + print $DST < +#include +#include + +#include +#include +#include +#include + +#include "epicsMemFs.h" + +#ifndef PATH_MAX +# define PATH_MAX 100 +#endif + +int epicsMemFsLoad(const epicsMemFS *fs) +{ + char initdir[PATH_MAX]; + const epicsMemFile * const *fileptr = fs->files; + + if(getcwd(initdir, sizeof(initdir)-1)==NULL) { + perror("getcwd"); + return errno; + } + initdir[sizeof(initdir)-1] = '\0'; + + for(;*fileptr; fileptr++) { + const epicsMemFile *curfile = *fileptr; + int fd; + ssize_t ret; + size_t sofar; + const char * const *dir = curfile->directory; + /* jump back to the root each time, + * slow but simple. + */ + if(chdir(initdir)) { + perror("chdir"); + return errno; + } + + printf("-> /"); + + /* traverse directory tree, creating as necessary */ + for(;*dir; dir++) { + int ret; + if(**dir=='.') continue; /* ignore '.' and '..' */ + printf("%s/", *dir); + ret = chdir(*dir); + if(ret==-1 && errno==ENOENT) { + /* this directory doesn't exist */ + if(mkdir(*dir,0744)==-1) { + printf("\n"); + perror("mkdir"); + return errno; + } + if(chdir(*dir)==-1) { + printf("\n"); + perror("chdir2"); + return errno; + } + } else if(ret==-1) { + printf("\n"); + perror("chdir1"); + return errno; + } + } + + /* no file name creates an empty directory */ + if(!curfile->name) { + printf("\n"); + continue; + } + printf("%s", curfile->name); + + /* create or overwrite */ + fd = open(curfile->name, O_WRONLY|O_CREAT|O_TRUNC, 0644); + + if(fd==-1) { + printf("\n"); + perror("open"); + return errno; + } + + sofar = 0; + + while(sofarsize) { + ret = write(fd, curfile->data+sofar, curfile->size-sofar); + if(ret<=0) { + printf("\n"); + perror("write"); + return errno; + } + sofar += ret; + } + + close(fd); + printf(" - ok\n"); + } + + if(chdir(initdir)) + perror("chdir"); + + return 0; +} diff --git a/modules/libcom/RTEMS/epicsMemFs.h b/modules/libcom/RTEMS/epicsMemFs.h new file mode 100644 index 000000000..c57f4d793 --- /dev/null +++ b/modules/libcom/RTEMS/epicsMemFs.h @@ -0,0 +1,24 @@ +/*************************************************************************\ +* Copyright (c) 2014 Brookhaven National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +#ifndef EPICSMEMFS_H +#define EPICSMEMFS_H + +#include + +typedef struct { + const char * const *directory; /* NULL terminated list of directories */ + const char *name; /* file name */ + const char *data; /* file contents */ + size_t size; /* size of file contents in bytes */ +} epicsMemFile; + +typedef struct { + const epicsMemFile * const *files; +} epicsMemFS; + +int epicsMemFsLoad(const epicsMemFS *fs); + +#endif // EPICSMEMFS_H diff --git a/modules/libcom/RTEMS/epicsRtemsInitHooks.h b/modules/libcom/RTEMS/epicsRtemsInitHooks.h index b7f09c100..4313f19e3 100644 --- a/modules/libcom/RTEMS/epicsRtemsInitHooks.h +++ b/modules/libcom/RTEMS/epicsRtemsInitHooks.h @@ -21,3 +21,5 @@ extern char *env_nfsMountPoint; */ int epicsRtemsInitPreSetBootConfigFromNVRAM(struct rtems_bsdnet_config *config); int epicsRtemsInitPostSetBootConfigFromNVRAM(struct rtems_bsdnet_config *config); +/* Return 0 if local file system was setup, or non-zero (will fall back to network */ +int epicsRtemsMountLocalFilesystem(char **argv); diff --git a/modules/libcom/RTEMS/ne2kpci.c b/modules/libcom/RTEMS/ne2kpci.c new file mode 100644 index 000000000..909d885f1 --- /dev/null +++ b/modules/libcom/RTEMS/ne2kpci.c @@ -0,0 +1,58 @@ +/*************************************************************************\ +* Copyright (c) 2015 Brookhaven Science Associates, 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. +\*************************************************************************/ +/* + * Wrapper around the ISA ne2000 driver to support detection of the PCI variant. + * Can be used with the ne2k_pci device provided by QEMU. + * + * eg. "qemu-system-i386 ... -net nic,model=ne2k_pci" + */ + +#include +#include +#include +#include +#include + +/* The plain ISA driver attach() + * which doesn't (can't?) do any test to see if + * the HW is really present + */ +extern int +rtems_ne_driver_attach (struct rtems_bsdnet_ifconfig *config, int attach); + +int +rtems_ne2kpci_driver_attach (struct rtems_bsdnet_ifconfig *config, int attach) +{ + uint8_t irq; + uint32_t bar0; + int B, D, F, ret; + printk("Probing for NE2000 on PCI (aka. Realtek 8029)\n"); + + if(pci_find_device(PCI_VENDOR_ID_REALTEK, PCI_DEVICE_ID_REALTEK_8029, 0, &B, &D, &F)) + { + printk("Not found\n"); + return 0; + } + + printk("Found %d:%d.%d\n", B, D, F); + + ret = pci_read_config_dword(B, D, F, PCI_BASE_ADDRESS_0, &bar0); + ret|= pci_read_config_byte(B, D, F, PCI_INTERRUPT_LINE, &irq); + + if(ret || (bar0&PCI_BASE_ADDRESS_SPACE)!=PCI_BASE_ADDRESS_SPACE_IO) + { + printk("Failed reading card config\n"); + return 0; + } + + config->irno = irq; + config->port = bar0&PCI_BASE_ADDRESS_IO_MASK; + + printk("Using port=0x%x irq=%u\n", (unsigned)config->port, config->irno); + + return rtems_ne_driver_attach(config, attach); +} diff --git a/modules/libcom/RTEMS/rtems_init.c b/modules/libcom/RTEMS/rtems_init.c index 82871da0d..c9194e38f 100644 --- a/modules/libcom/RTEMS/rtems_init.c +++ b/modules/libcom/RTEMS/rtems_init.c @@ -33,6 +33,7 @@ #include #include +#include "epicsVersion.h" #include "epicsThread.h" #include "epicsTime.h" #include "epicsExit.h" @@ -42,9 +43,12 @@ #include "osiUnistd.h" #include "iocsh.h" #include "osdTime.h" +#include "epicsMemFs.h" #include "epicsRtemsInitHooks.h" +#define RTEMS_VERSION_INT VERSION_INT(__RTEMS_MAJOR__, __RTEMS_MINOR__, 0, 0) + /* * Prototypes for some functions not in header files */ @@ -138,6 +142,31 @@ mustMalloc(int size, const char *msg) # include #endif +const epicsMemFS *epicsRtemsFSImage __attribute__((weak)); +const epicsMemFS *epicsRtemsFSImage = (void*)&epicsRtemsFSImage; + +/* hook to allow app specific FS setup */ +int +epicsRtemsMountLocalFilesystem(char **argv) __attribute__((weak)); +int +epicsRtemsMountLocalFilesystem(char **argv) +{ + if(epicsRtemsFSImage==(void*)&epicsRtemsFSImage) + return -1; /* no FS image provided. */ + else if(epicsRtemsFSImage==NULL) + return 0; /* no FS image provided, but none is needed. */ + else { + printf("***** Using compiled in file data *****\n"); + if (epicsMemFsLoad(epicsRtemsFSImage) != 0) { + printf("Can't unpack tar filesystem\n"); + return -1; + } else { + argv[1] = "/"; + return 0; + } + } +} + static int initialize_local_filesystem(char **argv) { @@ -146,7 +175,9 @@ initialize_local_filesystem(char **argv) extern char _FlashSize[] __attribute__((weak)); argv[0] = rtems_bsdnet_bootp_boot_file_name; - if (_FlashSize && (_DownloadLocation || _FlashBase)) { + if (epicsRtemsMountLocalFilesystem(argv)==0) { + return 1; /* FS setup successful */ + } else if (_FlashSize && (_DownloadLocation || _FlashBase)) { extern char _edata[]; size_t flashIndex = _edata - _DownloadLocation; char *header = _FlashBase + flashIndex; @@ -478,6 +509,25 @@ static void nfsMountCallFunc(const iocshArgBuf *args) } #endif + +void zoneset(const char *zone) +{ + if(zone) + setenv("TZ", zone, 1); + else + unsetenv("TZ"); + tzset(); +} + +static const iocshArg zonesetArg0 = {"zone string", iocshArgString}; +static const iocshArg * const zonesetArgs[1] = {&zonesetArg0}; +static const iocshFuncDef zonesetFuncDef = {"zoneset",1,zonesetArgs}; +static void zonesetCallFunc(const iocshArgBuf *args) +{ + zoneset(args[0].sval); +} + + /* * Register RTEMS-specific commands */ @@ -488,6 +538,7 @@ static void iocshRegisterRTEMS (void) #ifndef OMIT_NFS_SUPPORT iocshRegister(&nfsMountFuncDef, nfsMountCallFunc); #endif + iocshRegister(&zonesetFuncDef, &zonesetCallFunc); } /* @@ -596,6 +647,7 @@ Init (rtems_task_argument ignored) } printf("\n***** Initializing network *****\n"); rtems_bsdnet_initialize_network(); + printf("\n***** Setting up file system *****\n"); initialize_remote_filesystem(argv, initialize_local_filesystem(argv)); fixup_hosts(); @@ -666,3 +718,37 @@ Init (rtems_task_argument ignored) epicsThreadSleep(1.0); epicsExit(result); } + +#if defined(QEMU_FIXUPS) +/* Override some hooks (weak symbols) + * if BSP defaults aren't configured for running tests. + */ + + +/* Ensure that stdio goes to serial (so it can be captured) */ +#if defined(__i386__) && !USE_COM1_AS_CONSOLE +#include +extern int BSPPrintkPort; +void bsp_predriver_hook(void) +{ + BSPConsolePort = BSP_CONSOLE_PORT_COM1; + BSPPrintkPort = BSP_CONSOLE_PORT_COM1; +} +#endif + +/* reboot immediately when done. */ +#if defined(__i386__) && BSP_PRESS_KEY_FOR_RESET +void bsp_cleanup(void) +{ +#if RTEMS_VERSION_INT>=VERSION_INT(4,10,0,0) + void bsp_reset(); + bsp_reset(); +#else + rtemsReboot(); +#endif +} +#endif + +#endif /* QEMU_FIXUPS */ + +int cexpdebug __attribute__((weak)); diff --git a/modules/libcom/RTEMS/rtems_netconfig.c b/modules/libcom/RTEMS/rtems_netconfig.c index 832a6646b..d0c500c92 100644 --- a/modules/libcom/RTEMS/rtems_netconfig.c +++ b/modules/libcom/RTEMS/rtems_netconfig.c @@ -36,11 +36,19 @@ static struct rtems_bsdnet_ifconfig loopback_config = { * application directory and make the appropriate changes. */ #if defined(__i386__) + +extern int +rtems_ne2kpci_driver_attach (struct rtems_bsdnet_ifconfig *config, int attach); +static struct rtems_bsdnet_ifconfig ne2k_driver_config = { + "ne2", /* name */ + rtems_ne2kpci_driver_attach, /* attach function */ + &loopback_config, /* link to next interface */ +}; extern int rtems_fxp_attach (struct rtems_bsdnet_ifconfig *, int); static struct rtems_bsdnet_ifconfig fxp_driver_config = { "fxp1", /* name */ rtems_fxp_attach, /* attach function */ - &loopback_config, /* link to next interface */ + &ne2k_driver_config, /* link to next interface */ }; extern int rtems_3c509_driver_attach (struct rtems_bsdnet_ifconfig *, int); static struct rtems_bsdnet_ifconfig e3c509_driver_config = { diff --git a/modules/libcom/configure/CONFIG_LIBCOM_VERSION b/modules/libcom/configure/CONFIG_LIBCOM_VERSION index a4fa88918..668c4fe1b 100644 --- a/modules/libcom/configure/CONFIG_LIBCOM_VERSION +++ b/modules/libcom/configure/CONFIG_LIBCOM_VERSION @@ -1,4 +1,4 @@ EPICS_LIBCOM_MAJOR_VERSION = 3 EPICS_LIBCOM_MINOR_VERSION = 17 -EPICS_LIBCOM_MAINTENANCE_VERSION = 1 -EPICS_LIBCOM_DEVELOPMENT_FLAG = 1 +EPICS_LIBCOM_MAINTENANCE_VERSION = 2 +EPICS_LIBCOM_DEVELOPMENT_FLAG = 0 diff --git a/modules/libcom/src/Makefile b/modules/libcom/src/Makefile index 13d1409b8..d61b26803 100644 --- a/modules/libcom/src/Makefile +++ b/modules/libcom/src/Makefile @@ -66,7 +66,7 @@ EXPANDVARS += EPICS_LIBCOM_DEVELOPMENT_FLAG EXPANDFLAGS += $(foreach var,$(EXPANDVARS),-D$(var)="$(strip $($(var)))") # shared library ABI version. -SHRLIB_VERSION = 3.17.0 +SHRLIB_VERSION = $(EPICS_LIBCOM_MAJOR_VERSION).$(EPICS_LIBCOM_MINOR_VERSION).$(EPICS_LIBCOM_MAINTENANCE_VERSION) include $(TOP)/configure/RULES diff --git a/modules/libcom/src/as/asLib.h b/modules/libcom/src/as/asLib.h index 95fed7a9a..261e5ed7d 100644 --- a/modules/libcom/src/as/asLib.h +++ b/modules/libcom/src/as/asLib.h @@ -66,6 +66,7 @@ epicsShareFunc long epicsShareAPI asInitialize(ASINPUTFUNCPTR inputfunction); epicsShareFunc long epicsShareAPI asInitFile( const char *filename,const char *substitutions); epicsShareFunc long epicsShareAPI asInitFP(FILE *fp,const char *substitutions); +epicsShareFunc long epicsShareAPI asInitMem(const char *acf, const char *substitutions); /*caller must provide permanent storage for asgName*/ epicsShareFunc long epicsShareAPI asAddMember( ASMEMBERPVT *asMemberPvt,const char *asgName); diff --git a/modules/libcom/src/as/asLibRoutines.c b/modules/libcom/src/as/asLibRoutines.c index e7f1d90bf..3f5713efc 100644 --- a/modules/libcom/src/as/asLibRoutines.c +++ b/modules/libcom/src/as/asLibRoutines.c @@ -250,7 +250,35 @@ long epicsShareAPI asInitFP(FILE *fp,const char *substitutions) } return(status); } - + +static const char* membuf; + +static int memInputFunction(char *buf, int max_size) +{ + int ret = 0; + if(!membuf) return ret; + + while(max_size && *membuf) { + *buf++ = *membuf++; + max_size--; + ret++; + } + + return ret; +} + +long epicsShareAPI asInitMem(const char *acf, const char *substitutions) +{ + long ret = S_asLib_InitFailed; + if(!acf) return ret; + + membuf = acf; + ret = asInitialize(&memInputFunction); + membuf = NULL; + + return ret; +} + long epicsShareAPI asAddMember(ASMEMBERPVT *pasMemberPvt,const char *asgName) { long status; diff --git a/modules/libcom/src/env/envDefs.h b/modules/libcom/src/env/envDefs.h index 13df94733..8be00a9ea 100644 --- a/modules/libcom/src/env/envDefs.h +++ b/modules/libcom/src/env/envDefs.h @@ -99,6 +99,7 @@ epicsShareFunc long epicsShareAPI envGetBoolConfigParam(const ENV_PARAM *pParam, int *pBool); epicsShareFunc long epicsShareAPI epicsPrtEnvParams(void); epicsShareFunc void epicsShareAPI epicsEnvSet (const char *name, const char *value); +epicsShareFunc void epicsShareAPI epicsEnvUnset (const char *name); epicsShareFunc void epicsShareAPI epicsEnvShow (const char *name); #ifdef __cplusplus diff --git a/modules/libcom/src/flex/flex.c b/modules/libcom/src/flex/flex.c index 196f4e4cc..008e5e670 100644 --- a/modules/libcom/src/flex/flex.c +++ b/modules/libcom/src/flex/flex.c @@ -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. \*************************************************************************/ /* flex - tool to generate fast lexical analyzers */ @@ -14,7 +14,7 @@ * * This code is derived from software contributed to Berkeley by * Vern Paxson. - * + * * The United States Government has rights in this work pursuant * to contract no. DE-AC03-76SF00098 between the United States * Department of Energy and the University of California. @@ -98,7 +98,6 @@ int num_backtracking, bol_needed; FILE *temp_action_file; FILE *backtrack_file; int end_of_buffer_state; -char action_file_name[256>L_tmpnam?256:L_tmpnam]; char **input_files; int num_input_files; char *program_name; @@ -210,10 +209,7 @@ void flexend(int status) else if ( fclose( temp_action_file ) ) flexfatal( "error occurred when closing temporary action file" ); - - else if ( unlink( action_file_name ) ) - flexfatal( "error occurred when deleting temporary action file" ); - } + } if ( status != 0 && outfile_created ) { @@ -495,6 +491,13 @@ void flexinit(int argc, char **argv) /* stupid do-nothing deprecated option */ break; + case 'o': + if ( i != 1 ) + flexerror( "-o flag must be given separately" ); + + outfile = arg + i + 1; + goto get_next_arg; + case 'p': performance_report = true; break; @@ -591,15 +594,11 @@ get_next_arg: /* used by -C and -S flags in lieu of a "continue 2" control */ if ( (skelfile = fopen( skelname, "r" )) == NULL ) lerrsf( "can't open skeleton file %s", skelname ); - epicsTempName ( action_file_name, sizeof ( action_file_name ) ); - if ( action_file_name[0] == '\0' ) + if ( ( temp_action_file = epicsTempFile () ) == NULL ) { - lerrsf( "can't create temporary file name", "" ); + lerrsf( "can't create temporary action file", "" ); } - if ( ( temp_action_file = fopen ( action_file_name, "w" ) ) == NULL ) - lerrsf( "can't open temporary action file %s", action_file_name ); - lastdfa = lastnfa = num_rules = numas = numsnpairs = tmpuses = 0; numecs = numeps = eps2 = num_reallocs = hshcol = dfaeql = totnst = 0; numuniq = numdup = hshsave = eofseen = datapos = dataline = 0; diff --git a/modules/libcom/src/flex/flexdef.h b/modules/libcom/src/flex/flexdef.h index 79cfa9427..cf972b274 100644 --- a/modules/libcom/src/flex/flexdef.h +++ b/modules/libcom/src/flex/flexdef.h @@ -350,7 +350,6 @@ extern int yymore_really_used, reject_really_used; * temp_action_file - temporary file to hold actions * backtrack_file - file to summarize backtracking states to * infilename - name of input file - * action_file_name - name of the temporary file * input_files - array holding names of input files * num_input_files - size of input_files array * program_name - name with which program was invoked @@ -359,7 +358,6 @@ extern int yymore_really_used, reject_really_used; extern int datapos, dataline, linenum; extern FILE *skelfile, *yyin, *temp_action_file, *backtrack_file; extern char *infilename; -extern char action_file_name[]; extern char **input_files; extern int num_input_files; extern char *program_name; diff --git a/modules/libcom/src/flex/gen.c b/modules/libcom/src/flex/gen.c index aad884074..d0219ec94 100644 --- a/modules/libcom/src/flex/gen.c +++ b/modules/libcom/src/flex/gen.c @@ -1164,13 +1164,8 @@ void make_tables(void) if ( ferror( temp_action_file ) ) flexfatal( "error occurred when writing temporary action file" ); - else if ( fclose( temp_action_file ) ) - flexfatal( "error occurred when closing temporary action file" ); - - temp_action_file = fopen( action_file_name, "r" ); - - if ( temp_action_file == NULL ) - flexfatal( "could not re-open temporary action file" ); + else if ( fseek( temp_action_file, 0L, SEEK_SET) != 0 ) + flexfatal( "error occurred when rewinding temporary action file" ); /* copy prolog from action_file to output file */ action_out(); diff --git a/modules/libcom/src/iocsh/Makefile b/modules/libcom/src/iocsh/Makefile index 766577354..274b02b48 100644 --- a/modules/libcom/src/iocsh/Makefile +++ b/modules/libcom/src/iocsh/Makefile @@ -9,8 +9,10 @@ SRC_DIRS += $(LIBCOM)/iocsh INC += iocsh.h +INC += initHooks.h INC += registry.h INC += libComRegister.h Com_SRCS += iocsh.cpp +Com_SRCS += initHooks.c Com_SRCS += registry.c Com_SRCS += libComRegister.c diff --git a/modules/database/src/ioc/db/initHooks.c b/modules/libcom/src/iocsh/initHooks.c similarity index 100% rename from modules/database/src/ioc/db/initHooks.c rename to modules/libcom/src/iocsh/initHooks.c index 9070c53f5..0884bdd0a 100644 --- a/modules/database/src/ioc/db/initHooks.c +++ b/modules/libcom/src/iocsh/initHooks.c @@ -16,12 +16,12 @@ #include #include +#define epicsExportSharedSymbols #include "dbDefs.h" #include "ellLib.h" #include "epicsMutex.h" #include "epicsThread.h" -#define epicsExportSharedSymbols #include "initHooks.h" typedef struct initHookLink { diff --git a/modules/database/src/ioc/db/initHooks.h b/modules/libcom/src/iocsh/initHooks.h similarity index 100% rename from modules/database/src/ioc/db/initHooks.h rename to modules/libcom/src/iocsh/initHooks.h diff --git a/modules/libcom/src/iocsh/iocsh.cpp b/modules/libcom/src/iocsh/iocsh.cpp index d6626de89..0de90c87a 100644 --- a/modules/libcom/src/iocsh/iocsh.cpp +++ b/modules/libcom/src/iocsh/iocsh.cpp @@ -178,9 +178,11 @@ void epicsShareAPI iocshRegisterVariable (const iocshVarDef *piocshVarDef) for (l = NULL, p = iocshVariableHead ; p != NULL ; l = p, p = p->next) { i = strcmp (piocshVarDef->name, p->pVarDef->name); if (i == 0) { - errlogPrintf("Warning: iocshRegisterVariable redefining %s.\n", - piocshVarDef->name); - p->pVarDef = piocshVarDef; + if (p->pVarDef != piocshVarDef) { + errlogPrintf("Warning: iocshRegisterVariable redefining %s.\n", + piocshVarDef->name); + p->pVarDef = piocshVarDef; + } found = 1; break; } @@ -599,12 +601,12 @@ iocshBody (const char *pathname, const char *commandLine, const char *macros) macPushScope(handle); macInstallMacros(handle, defines); + wasOkToBlock = epicsThreadIsOkToBlock(); + epicsThreadSetOkToBlock(1); + /* * Read commands till EOF or exit */ - argc = 0; - wasOkToBlock = epicsThreadIsOkToBlock(); - epicsThreadSetOkToBlock(1); for (;;) { /* * Read a line @@ -679,15 +681,15 @@ iocshBody (const char *pathname, const char *commandLine, const char *macros) redirect = NULL; for (;;) { if (argc >= argvCapacity) { - char **av; - argvCapacity += 50; - av = (char **)realloc (argv, argvCapacity * sizeof *argv); - if (av == NULL) { + int newCapacity = argvCapacity + 20; + char **newv = (char **)realloc (argv, newCapacity * sizeof *argv); + if (newv == NULL) { fprintf (epicsGetStderr(), "Out of memory!\n"); argc = -1; break; } - argv = av; + argv = newv; + argvCapacity = newCapacity; } c = line[icin++]; if (c == '\0') @@ -836,16 +838,14 @@ iocshBody (const char *pathname, const char *commandLine, const char *macros) break; } if (iarg >= argBufCapacity) { - void *np; - - argBufCapacity += 20; - np = realloc (argBuf, argBufCapacity * sizeof *argBuf); - if (np == NULL) { + int newCapacity = argBufCapacity + 20; + void *newBuf = realloc(argBuf, newCapacity * sizeof *argBuf); + if (newBuf == NULL) { fprintf (epicsGetStderr(), "Out of memory!\n"); - argBufCapacity -= 20; break; } - argBuf = (iocshArgBuf *)np; + argBuf = (iocshArgBuf *) newBuf; + argBufCapacity = newCapacity; } if (piocshFuncDef->arg[iarg]->type == iocshArgArgv) { argBuf[iarg].aval.ac = argc-iarg; @@ -890,7 +890,7 @@ iocshBody (const char *pathname, const char *commandLine, const char *macros) errlogFlush(); if (readlineContext) epicsReadlineEnd(readlineContext); - epicsThreadSetOkToBlock( wasOkToBlock); + epicsThreadSetOkToBlock(wasOkToBlock); return 0; } diff --git a/modules/libcom/src/iocsh/libComRegister.c b/modules/libcom/src/iocsh/libComRegister.c index 88abdc671..737d7e9c2 100644 --- a/modules/libcom/src/iocsh/libComRegister.c +++ b/modules/libcom/src/iocsh/libComRegister.c @@ -113,6 +113,21 @@ static void epicsEnvSetCallFunc(const iocshArgBuf *args) epicsEnvSet (name, value); } +/* epicsEnvUnset */ +static const iocshArg epicsEnvUnsetArg0 = { "name",iocshArgString}; +static const iocshArg * const epicsEnvUnsetArgs[1] = {&epicsEnvUnsetArg0}; +static const iocshFuncDef epicsEnvUnsetFuncDef = {"epicsEnvUnset",1,epicsEnvUnsetArgs}; +static void epicsEnvUnsetCallFunc(const iocshArgBuf *args) +{ + char *name = args[0].sval; + + if (name == NULL) { + fprintf(stderr, "Missing environment variable name argument.\n"); + return; + } + epicsEnvUnset (name); +} + /* epicsParamShow */ IOCSH_STATIC_FUNC void epicsParamShow() { @@ -221,6 +236,7 @@ static const iocshFuncDef errlogFuncDef = {"errlog",1,errlogArgs}; static void errlogCallFunc(const iocshArgBuf *args) { errlog(args[0].sval); + errlogFlush(); } /* iocLogPrefix */ @@ -385,6 +401,7 @@ void epicsShareAPI libComRegister(void) iocshRegister(&pwdFuncDef, pwdCallFunc); iocshRegister(&epicsEnvSetFuncDef, epicsEnvSetCallFunc); + iocshRegister(&epicsEnvUnsetFuncDef, epicsEnvUnsetCallFunc); iocshRegister(&epicsParamShowFuncDef, epicsParamShowCallFunc); iocshRegister(&epicsPrtEnvParamsFuncDef, epicsPrtEnvParamsCallFunc); iocshRegister(&epicsEnvShowFuncDef, epicsEnvShowCallFunc); diff --git a/modules/libcom/src/log/iocLogServer.c b/modules/libcom/src/log/iocLogServer.c index f5694fc0f..f42aa4563 100644 --- a/modules/libcom/src/log/iocLogServer.c +++ b/modules/libcom/src/log/iocLogServer.c @@ -347,11 +347,6 @@ static int openLogFile (struct ioc_log_server *pserver) { enum TF_RETURN ret; - if (ioc_log_file_limit==0u) { - pserver->poutfile = stderr; - return IOCLS_ERROR; - } - if (pserver->poutfile && pserver->poutfile != stderr){ fclose (pserver->poutfile); pserver->poutfile = NULL; @@ -627,7 +622,7 @@ static void writeMessagesToLog (struct iocLogClient *pclient) strlen(pclient->ascii_time) + nchar + 3u; assert (nTotChar <= INT_MAX); ntci = (int) nTotChar; - if ( pclient->pserver->filePos+ntci >= pclient->pserver->max_file_size ) { + if ( pclient->pserver->max_file_size && pclient->pserver->filePos+ntci >= pclient->pserver->max_file_size ) { if ( pclient->pserver->max_file_size >= pclient->pserver->filePos ) { unsigned nPadChar; /* @@ -771,7 +766,7 @@ static int getConfig(void) &EPICS_IOC_LOG_FILE_LIMIT, &ioc_log_file_limit); if(status>=0){ - if (ioc_log_file_limit<=0) { + if (ioc_log_file_limit < 0) { envFailureNotify (&EPICS_IOC_LOG_FILE_LIMIT); return IOCLS_ERROR; } diff --git a/modules/libcom/src/macLib/macCore.c b/modules/libcom/src/macLib/macCore.c index 55d31719e..6f84d6b84 100644 --- a/modules/libcom/src/macLib/macCore.c +++ b/modules/libcom/src/macLib/macCore.c @@ -902,11 +902,16 @@ static void refer ( MAC_HANDLE *handle, MAC_ENTRY *entry, int level, } } - /* Bad reference, insert $(name,errval) */ + /* Bad reference, insert either $(name,) or $(name) */ if ( v < valend ) *v++ = '$'; if ( v < valend ) *v++ = '('; cpy2val( refname, &v, valend ); - cpy2val( errval, &v, valend ); + if (handle->flags & FLAG_SUPPRESS_WARNINGS) { + if ( v < valend ) *v++ = ')'; + *v = '\0'; + } + else + cpy2val( errval, &v, valend ); cleanup: if (pop) { diff --git a/modules/libcom/src/misc/testMain.h b/modules/libcom/src/misc/testMain.h index 4db72c39e..7c4d9b29b 100644 --- a/modules/libcom/src/misc/testMain.h +++ b/modules/libcom/src/misc/testMain.h @@ -27,7 +27,13 @@ * } */ -#if defined(vxWorks) || defined(__rtems__) +#if defined(__rtems__) + #ifdef __cplusplus + #define MAIN(prog) extern "C" int prog(void); extern "C" int main() __attribute__((weak, alias(#prog))); extern "C" int prog(void) + #else + #define MAIN(prog) int prog(); int main() __attribute__((weak, alias(#prog))); int prog() + #endif +#elif defined(vxWorks) #ifdef __cplusplus #define MAIN(prog) extern "C" int prog(void) #else diff --git a/modules/libcom/src/misc/truncateFile.c b/modules/libcom/src/misc/truncateFile.c index ccb1450c2..dc074d9fb 100644 --- a/modules/libcom/src/misc/truncateFile.c +++ b/modules/libcom/src/misc/truncateFile.c @@ -27,7 +27,6 @@ */ epicsShareFunc enum TF_RETURN truncateFile (const char *pFileName, unsigned long size) { - char tmpName[256>L_tmpnam?256:L_tmpnam]; long filePos; FILE *pFile; FILE *ptmp; @@ -67,18 +66,10 @@ epicsShareFunc enum TF_RETURN truncateFile (const char *pFileName, unsigned lon return TF_OK; } - epicsTempName ( tmpName, sizeof (tmpName) ); - if ( tmpName[0] == '\0' ) { - fprintf (stderr,"Unable to create tmp file name?\n"); - fclose (pFile); - return TF_ERROR; - } - - ptmp = fopen (tmpName, "w"); + ptmp = epicsTempFile(); if (!ptmp) { fprintf (stderr, - "File access problems to `%s' because `%s'\n", - tmpName, + "File access problems to temp file because `%s'\n", strerror(errno)); fclose (pFile); return TF_ERROR; @@ -89,48 +80,59 @@ epicsShareFunc enum TF_RETURN truncateFile (const char *pFileName, unsigned lon c = getc (pFile); if (c==EOF) { fprintf (stderr, - "File access problems to `%s' because `%s'\n", - pFileName, + "File access problems to temp file because `%s'\n", strerror(errno)); fclose (pFile); fclose (ptmp); - remove (tmpName); return TF_ERROR; } status = putc (c, ptmp); if (status==EOF) { fprintf(stderr, - "File access problems to `%s' because `%s'\n", - tmpName, + "File access problems to temp file because `%s'\n", strerror(errno)); fclose (pFile); fclose (ptmp); - remove (tmpName); return TF_ERROR; } charNo++; } fclose (pFile); - fclose (ptmp); - status = remove (pFileName); - if (status!=TF_OK) { + pFile = fopen(pFileName, "w"); + if (!pFile) { fprintf (stderr, - "Unable to remove `%s' during truncate because `%s'\n", + "File access problems to `%s' because `%s'\n", pFileName, strerror(errno)); - remove (tmpName); + fclose (ptmp); return TF_ERROR; } - status = rename (tmpName, pFileName); - if (status!=TF_OK) { - fprintf (stderr, - "Unable to rename %s to `%s' because `%s'\n", - tmpName, - pFileName, - strerror(errno)); - remove (tmpName); - return TF_ERROR; + rewind (ptmp); + charNo = 0u; + while (charNo= NUM_TIME_EVENTS) && - (eventNumber != epicsTimeEventBestTime)) + if (eventNumber < epicsTimeEventBestTime) return S_time_badEvent; epicsMutexMustLock(gtPvt.eventListLock); @@ -245,7 +245,9 @@ static int generalTimeGetEventPriority(epicsTimeStamp *pDest, int eventNumber, if (pPrio) *pPrio = ptp->priority; - if (eventNumber == epicsTimeEventBestTime) { + if (eventNumber >= NUM_TIME_EVENTS) { + *pDest = ts; + } else if (eventNumber == epicsTimeEventBestTime) { if (epicsTimeGreaterThanEqual(&ts, >Pvt.lastProvidedBestTime)) { *pDest = ts; diff --git a/modules/libcom/src/osi/epicsGeneralTime.h b/modules/libcom/src/osi/epicsGeneralTime.h index 3adc23705..6bbb0b21b 100644 --- a/modules/libcom/src/osi/epicsGeneralTime.h +++ b/modules/libcom/src/osi/epicsGeneralTime.h @@ -17,7 +17,11 @@ extern "C" { #endif #define NUM_TIME_EVENTS 256 -/* Time Events are numbered 0 through (NUM_TIME_EVENTS-1) */ +/* Time Events numbered 0 through (NUM_TIME_EVENTS-1) are validated by */ +/* code in epicsGeneralTime.c that tests for advancing timestamps and */ +/* enforces that restriction. Event numbers greater than or equal to */ +/* NUM_TIME_EVENTS are now allowed if supported by a custom time provider */ +/* which should provide its own advancing timestamp validation. */ epicsShareFunc void generalTime_Init(void); diff --git a/modules/libcom/src/osi/epicsStdio.h b/modules/libcom/src/osi/epicsStdio.h index 172d5cf87..9ef0b2239 100644 --- a/modules/libcom/src/osi/epicsStdio.h +++ b/modules/libcom/src/osi/epicsStdio.h @@ -34,20 +34,22 @@ extern "C" { /* Make printf, puts and putchar use *our* version of stdout */ -#ifdef printf -# undef printf -#endif /* printf */ -#define printf epicsStdoutPrintf +#ifndef epicsStdioStdPrintfEtc +# ifdef printf +# undef printf +# endif +# define printf epicsStdoutPrintf -#ifdef puts -# undef puts -#endif /* puts */ -#define puts epicsStdoutPuts +# ifdef puts +# undef puts +# endif +# define puts epicsStdoutPuts -#ifdef putchar -# undef putchar -#endif /* putchar */ -#define putchar epicsStdoutPutchar +# ifdef putchar +# undef putchar +# endif +# define putchar epicsStdoutPutchar +#endif epicsShareFunc int epicsShareAPI epicsSnprintf( char *str, size_t size, const char *format, ...) EPICS_PRINTF_STYLE(3,4); @@ -87,6 +89,19 @@ epicsShareFunc int epicsShareAPI epicsStdoutPutchar(int c); #ifdef __cplusplus } -#endif + +/* Also pull functions into the std namespace (see lp:1786927) */ +#if !defined(__GNUC__) || (__GNUC__ > 2) +namespace std { +using ::epicsGetStdin; +using ::epicsGetStdout; +using ::epicsGetStderr; +using ::epicsStdoutPrintf; +using ::epicsStdoutPuts; +using ::epicsStdoutPutchar; +} +#endif /* __GNUC__ > 2 */ + +#endif /* __cplusplus */ #endif /* epicsStdioh */ diff --git a/modules/libcom/src/osi/epicsTempFile.h b/modules/libcom/src/osi/epicsTempFile.h index c48e336ec..e20879d04 100644 --- a/modules/libcom/src/osi/epicsTempFile.h +++ b/modules/libcom/src/osi/epicsTempFile.h @@ -20,7 +20,6 @@ extern "C" { #endif -epicsShareFunc void epicsShareAPI epicsTempName(char *pbuf, size_t bufLen); epicsShareFunc FILE * epicsShareAPI epicsTempFile(void); #ifdef __cplusplus diff --git a/modules/libcom/src/osi/os/Darwin/osdEnv.c b/modules/libcom/src/osi/os/Darwin/osdEnv.c index e6206c3d8..ea2662ddc 100644 --- a/modules/libcom/src/osi/os/Darwin/osdEnv.c +++ b/modules/libcom/src/osi/os/Darwin/osdEnv.c @@ -40,6 +40,16 @@ epicsShareFunc void epicsShareAPI epicsEnvSet (const char *name, const char *val setenv(name, value, 1); } +/* + * Unset an environment variable + */ + +epicsShareFunc void epicsShareAPI epicsEnvUnset (const char *name) +{ + iocshEnvClear(name); + unsetenv(name); +} + /* * Show the value of the specified, or all, environment variables */ diff --git a/modules/libcom/src/osi/os/Darwin/osdSock.h b/modules/libcom/src/osi/os/Darwin/osdSock.h index e7c344062..0122d16de 100644 --- a/modules/libcom/src/osi/os/Darwin/osdSock.h +++ b/modules/libcom/src/osi/os/Darwin/osdSock.h @@ -32,6 +32,7 @@ typedef int SOCKET; typedef int osiSockIoctl_t; typedef socklen_t osiSocklen_t; typedef int osiSockOptMcastLoop_t; +typedef unsigned char osiSockOptMcastTTL_t; #define FD_IN_FDSET(FD) ((FD) +#include + +#define epicsExportSharedSymbols +#include "epicsStdio.h" +#include "envDefs.h" +#include "osiUnistd.h" +#include "iocsh.h" + +/* + * Set the value of an environment variable + */ +epicsShareFunc void epicsShareAPI epicsEnvSet (const char *name, const char *value) +{ + iocshEnvClear(name); + setenv(name, value, 1); +} + +/* + * Unset an environment variable + */ + +epicsShareFunc void epicsShareAPI epicsEnvUnset (const char *name) +{ + iocshEnvClear(name); + unsetenv(name); +} + +/* + * Show the value of the specified, or all, environment variables + */ +epicsShareFunc void epicsShareAPI epicsEnvShow (const char *name) +{ + if (name == NULL) { + extern char **environ; + char **sp; + + for (sp = environ ; (sp != NULL) && (*sp != NULL) ; sp++) + printf ("%s\n", *sp); + } + else { + const char *cp = getenv (name); + if (cp == NULL) + printf ("%s is not an environment variable.\n", name); + else + printf ("%s=%s\n", name, cp); + } +} diff --git a/modules/libcom/src/osi/os/RTEMS/osdSock.h b/modules/libcom/src/osi/os/RTEMS/osdSock.h index 6177c3033..ec43b72ef 100644 --- a/modules/libcom/src/osi/os/RTEMS/osdSock.h +++ b/modules/libcom/src/osi/os/RTEMS/osdSock.h @@ -43,6 +43,7 @@ typedef int SOCKET; typedef int osiSockIoctl_t; typedef socklen_t osiSocklen_t; typedef char osiSockOptMcastLoop_t; +typedef unsigned char osiSockOptMcastTTL_t; #define FD_IN_FDSET(FD) ((FD) strlen ( pName ) ) { - strncpy ( pNameBuf, pName, nameBufLength ); - } - free ( pName ); - } - } -} - // // epicsTmpFile // diff --git a/modules/libcom/src/osi/os/WIN32/osdEnv.c b/modules/libcom/src/osi/os/WIN32/osdEnv.c new file mode 100644 index 000000000..4c5587bb2 --- /dev/null +++ b/modules/libcom/src/osi/os/WIN32/osdEnv.c @@ -0,0 +1,89 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Saskatchewan +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* osdEnv.c */ +/* + * Author: Eric Norum + * Date: May 7, 2001 + * + * Routines to modify/display environment variables and EPICS parameters + * + */ + +#include +#include +#include +#include +#include + +#define epicsExportSharedSymbols +#include "epicsStdio.h" +#include "errlog.h" +#include "cantProceed.h" +#include "envDefs.h" +#include "osiUnistd.h" +#include "epicsFindSymbol.h" +#include "iocsh.h" + +/* + * Set the value of an environment variable + * Leaks memory, but the assumption is that this routine won't be + * called often enough for the leak to be a problem. + */ +epicsShareFunc void epicsShareAPI epicsEnvSet (const char *name, const char *value) +{ + char *cp; + + iocshEnvClear(name); + + cp = mallocMustSucceed (strlen (name) + strlen (value) + 2, "epicsEnvSet"); + strcpy (cp, name); + strcat (cp, "="); + strcat (cp, value); + if (putenv (cp) < 0) { + errPrintf( + -1L, + __FILE__, + __LINE__, + "Failed to set environment parameter \"%s\" to \"%s\": %s\n", + name, + value, + strerror (errno)); + free (cp); + } +} + +/* + * Unset an environment variable + * Using putenv with a an existing name plus "=" (without value) deletes + */ + +epicsShareFunc void epicsShareAPI epicsEnvUnset (const char *name) +{ + iocshEnvClear(name); + if (getenv(name) != NULL) + epicsEnvSet((char*)name, ""); +} + +/* + * Show the value of the specified, or all, environment variables + */ +epicsShareFunc void epicsShareAPI epicsEnvShow (const char *name) +{ + if (name == NULL) { + extern char **environ; + char **sp; + + for (sp = environ ; (sp != NULL) && (*sp != NULL) ; sp++) + printf ("%s\n", *sp); + } + else { + const char *cp = getenv (name); + if (cp == NULL) + printf ("%s is not an environment variable.\n", name); + else + printf ("%s=%s\n", name, cp); + } +} diff --git a/modules/libcom/src/osi/os/WIN32/osdProcess.c b/modules/libcom/src/osi/os/WIN32/osdProcess.c index 6e69c0485..fbe68ba91 100644 --- a/modules/libcom/src/osi/os/WIN32/osdProcess.c +++ b/modules/libcom/src/osi/os/WIN32/osdProcess.c @@ -19,13 +19,13 @@ #endif #include +#include #define STRICT #include #define epicsExportSharedSymbols #include "osiProcess.h" -#include "errlog.h" epicsShareFunc osiGetUserNameReturn epicsShareAPI osiGetUserName (char *pBuf, unsigned bufSizeIn) { @@ -94,16 +94,11 @@ epicsShareFunc osiSpawnDetachedProcessReturn epicsShareAPI osiSpawnDetachedProce pFmtArgs[1] = (char *) pBaseExecutableName; pFmtArgs[2] = errStrMsgBuf; pFmtArgs[3] = "Changes may be required in your \"path\" environment variable."; - pFmtArgs[4] = "PATH = "; - pFmtArgs[5] = getenv ("path"); - if ( pFmtArgs[5] == NULL ) { - pFmtArgs[5] = ""; - } W32status = FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_STRING | FORMAT_MESSAGE_ARGUMENT_ARRAY | 80, - "%1 \"%2\". %3 %4 %5 \"%6\"", + "%1 \"%2\". %3 %4", 0, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), /* Default language */ (LPTSTR) &complteMsgBuf, @@ -111,24 +106,20 @@ epicsShareFunc osiSpawnDetachedProcessReturn epicsShareAPI osiSpawnDetachedProce pFmtArgs ); if (W32status) { - /* Display the string. */ - MessageBox (NULL, complteMsgBuf, "Configuration Problem", - MB_OK | MB_ICONINFORMATION); + fprintf (stderr, "%s\n", (char *) complteMsgBuf); LocalFree (complteMsgBuf); } else { - /* Display the string. */ - MessageBox (NULL, errStrMsgBuf, "Failed to start executable", - MB_OK | MB_ICONINFORMATION); + fprintf (stderr, "%s\n", (char *) errStrMsgBuf); } /* Free the buffer. */ LocalFree (errStrMsgBuf); } else { - errlogPrintf ("!!WARNING!!\n"); - errlogPrintf ("Unable to locate executable \"%s\".\n", pBaseExecutableName); - errlogPrintf ("You may need to modify your \"path\" environment variable.\n"); + fprintf (stderr, "!!WARNING!!\n"); + fprintf (stderr, "Unable to locate executable \"%s\".\n", pBaseExecutableName); + fprintf (stderr, "You may need to modify your \"path\" environment variable.\n"); } return osiSpawnDetachedProcessFail; } diff --git a/modules/libcom/src/osi/os/WIN32/osdSock.h b/modules/libcom/src/osi/os/WIN32/osdSock.h index 2ff23c4a6..c1bc1d9b1 100644 --- a/modules/libcom/src/osi/os/WIN32/osdSock.h +++ b/modules/libcom/src/osi/os/WIN32/osdSock.h @@ -29,6 +29,7 @@ typedef u_long FAR osiSockIoctl_t; typedef int osiSocklen_t; typedef BOOL osiSockOptMcastLoop_t; +typedef DWORD osiSockOptMcastTTL_t; #ifndef SHUT_RD # define SHUT_RD SD_RECEIVE diff --git a/modules/libcom/src/osi/os/cygwin32/osdSock.h b/modules/libcom/src/osi/os/cygwin32/osdSock.h index 75288c865..123f2404f 100644 --- a/modules/libcom/src/osi/os/cygwin32/osdSock.h +++ b/modules/libcom/src/osi/os/cygwin32/osdSock.h @@ -35,6 +35,8 @@ typedef int SOCKET; typedef int osiSockIoctl_t; typedef int osiSocklen_t; typedef int osiSockOptMcastLoop_t; +typedef int osiSockOptMcastTTL_t; + #define FD_IN_FDSET(FD) ((FD)=0) #ifndef SHUT_RD #define SHUT_RD 0 diff --git a/modules/libcom/src/osi/os/default/osdEnv.c b/modules/libcom/src/osi/os/default/osdEnv.c index b284a86af..b74856dbf 100644 --- a/modules/libcom/src/osi/os/default/osdEnv.c +++ b/modules/libcom/src/osi/os/default/osdEnv.c @@ -56,6 +56,18 @@ epicsShareFunc void epicsShareAPI epicsEnvSet (const char *name, const char *val } } +/* + * Unset an environment variable + * Using putenv with a an existing name but without "=..." deletes + */ + +epicsShareFunc void epicsShareAPI epicsEnvUnset (const char *name) +{ + iocshEnvClear(name); + if (getenv(name) != NULL) + putenv((char*)name); +} + /* * Show the value of the specified, or all, environment variables */ diff --git a/modules/libcom/src/osi/os/freebsd/osdSock.h b/modules/libcom/src/osi/os/freebsd/osdSock.h index b402ec120..2e6951399 100644 --- a/modules/libcom/src/osi/os/freebsd/osdSock.h +++ b/modules/libcom/src/osi/os/freebsd/osdSock.h @@ -37,6 +37,7 @@ typedef int SOCKET; typedef int osiSockIoctl_t; typedef socklen_t osiSocklen_t; typedef int osiSockOptMcastLoop_t; +typedef unsigned char osiSockOptMcastTTL_t; #define FD_IN_FDSET(FD) ((FD) strlen ( nameBuf ) ) { - strncpy ( pNameBuf, nameBuf, nameBufLength ); - } - } - } -} - - extern "C" epicsShareFunc FILE * epicsShareAPI epicsTempFile ( void ) { diff --git a/modules/libcom/src/osi/os/posix/osdThread.c b/modules/libcom/src/osi/os/posix/osdThread.c index 755390eed..c08dea40e 100644 --- a/modules/libcom/src/osi/os/posix/osdThread.c +++ b/modules/libcom/src/osi/os/posix/osdThread.c @@ -422,9 +422,24 @@ void epicsThreadRealtimeLock(void) int status = mlockall(MCL_CURRENT | MCL_FUTURE); if (status) { - fprintf(stderr, "epicsThreadRealtimeLock " - "Warning: Unable to lock the virtual address space.\n" - "VM page faults may harm real-time performance.\n"); + const int err = errno; + switch(err) { +#ifdef __linux__ + case ENOMEM: + fprintf(stderr, "epicsThreadRealtimeLock " + "Warning: unable to lock memory. RLIMIT_MEMLOCK is too small or missing CAP_IPC_LOCK\n"); + break; + case EPERM: + fprintf(stderr, "epicsThreadRealtimeLock " + "Warning: unable to lock memory. missing CAP_IPC_LOCK\n"); + break; +#endif + default: + fprintf(stderr, "epicsThreadRealtimeLock " + "Warning: Unable to lock the virtual address space.\n" + "VM page faults may harm real-time performance. errno=%d\n", + err); + } } } #endif diff --git a/modules/libcom/src/osi/os/solaris/osdEnv.c b/modules/libcom/src/osi/os/solaris/osdEnv.c new file mode 100644 index 000000000..c356a0a13 --- /dev/null +++ b/modules/libcom/src/osi/os/solaris/osdEnv.c @@ -0,0 +1,62 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Saskatchewan +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* osdEnv.c */ +/* + * Author: Eric Norum + * Date: May 7, 2001 + * + * Routines to modify/display environment variables and EPICS parameters + * + */ + +#include +#include + +#define epicsExportSharedSymbols +#include "epicsStdio.h" +#include "envDefs.h" +#include "osiUnistd.h" +#include "iocsh.h" + +/* + * Set the value of an environment variable + */ +epicsShareFunc void epicsShareAPI epicsEnvSet (const char *name, const char *value) +{ + iocshEnvClear(name); + setenv(name, value, 1); +} + +/* + * Unset an environment variable + */ + +epicsShareFunc void epicsShareAPI epicsEnvUnset (const char *name) +{ + iocshEnvClear(name); + unsetenv(name); +} + +/* + * Show the value of the specified, or all, environment variables + */ +epicsShareFunc void epicsShareAPI epicsEnvShow (const char *name) +{ + if (name == NULL) { + extern char **environ; + char **sp; + + for (sp = environ ; (sp != NULL) && (*sp != NULL) ; sp++) + printf ("%s\n", *sp); + } + else { + const char *cp = getenv (name); + if (cp == NULL) + printf ("%s is not an environment variable.\n", name); + else + printf ("%s=%s\n", name, cp); + } +} diff --git a/modules/libcom/src/osi/os/solaris/osdSock.h b/modules/libcom/src/osi/os/solaris/osdSock.h index 96ea2a983..adf3d9bad 100644 --- a/modules/libcom/src/osi/os/solaris/osdSock.h +++ b/modules/libcom/src/osi/os/solaris/osdSock.h @@ -43,6 +43,7 @@ typedef int osiSockIoctl_t; typedef int osiSocklen_t; #endif typedef char osiSockOptMcastLoop_t; +typedef unsigned char osiSockOptMcastTTL_t; #define DOES_NOT_ACCEPT_ZERO_LENGTH_UDP diff --git a/modules/libcom/src/osi/os/vxWorks/osdEnv.c b/modules/libcom/src/osi/os/vxWorks/osdEnv.c index 9245ae735..9cfd1f7aa 100644 --- a/modules/libcom/src/osi/os/vxWorks/osdEnv.c +++ b/modules/libcom/src/osi/os/vxWorks/osdEnv.c @@ -56,6 +56,25 @@ epicsShareFunc void epicsShareAPI epicsEnvSet (const char *name, const char *val } } +/* + * Unset an environment variable + * Basically destroy the name of that variable because vxWorks does not + * support to really unset an environment variable. + */ + +epicsShareFunc void epicsShareAPI epicsEnvUnset (const char *name) +{ + char* var; + + if (!name) return; + iocshEnvClear(name); + var = getenv(name); + if (!var) return; + var -= strlen(name); + var --; + *var = 0; +} + /* * Show the value of the specified, or all, environment variables */ diff --git a/modules/libcom/src/osi/os/vxWorks/osdSock.h b/modules/libcom/src/osi/os/vxWorks/osdSock.h index d949b25f1..272371523 100644 --- a/modules/libcom/src/osi/os/vxWorks/osdSock.h +++ b/modules/libcom/src/osi/os/vxWorks/osdSock.h @@ -66,6 +66,7 @@ typedef int SOCKET; typedef int osiSockIoctl_t; typedef int osiSocklen_t; typedef int osiSockOptMcastLoop_t; +typedef char osiSockOptMcastTTL_t; #define FD_IN_FDSET(FD) ((FD)=0) diff --git a/modules/libcom/src/osi/osiClockTime.c b/modules/libcom/src/osi/osiClockTime.c index 408a93fc0..b0f4c35eb 100644 --- a/modules/libcom/src/osi/osiClockTime.c +++ b/modules/libcom/src/osi/osiClockTime.c @@ -41,16 +41,18 @@ static epicsThreadOnceId onceId = EPICS_THREAD_ONCE_INIT; #if defined(CLOCK_REALTIME) && !defined(_WIN32) -/* This code is not used on systems without Posix CLOCK_REALTIME such - * as Darwin, but the only way to detect that is from the OS headers, - * so the Makefile can't exclude building this file on those systems. +/* This code is not used on systems without Posix CLOCK_REALTIME, + * but the only way to detect that is from the OS headers, so the + * Makefile can't exclude compiling this file on those systems. */ /* Forward references */ static int ClockTimeGetCurrent(epicsTimeStamp *pDest); -static void ClockTimeSync(void *dummy); +#if defined(vxWorks) || defined(__rtems__) +static void ClockTimeSync(void *dummy); +#endif /* ClockTime_Report iocsh command */ static const iocshArg ReportArg0 = { "interest_level", iocshArgArgv}; @@ -98,12 +100,18 @@ void ClockTime_Init(int synchronize) if (synchronize == CLOCKTIME_SYNC) { if (ClockTimePvt.synchronize == CLOCKTIME_NOSYNC) { + +#if defined(vxWorks) || defined(__rtems__) /* Start synchronizing */ ClockTimePvt.synchronize = synchronize; epicsThreadCreate("ClockTimeSync", epicsThreadPriorityHigh, epicsThreadGetStackSize(epicsThreadStackSmall), ClockTimeSync, NULL); +#else + errlogPrintf("Clock synchronization must be performed by the OS\n"); +#endif + } else { /* No change, sync thread should already be running */ @@ -139,6 +147,7 @@ void ClockTime_GetProgramStart(epicsTimeStamp *pDest) /* Synchronization thread */ +#if defined(vxWorks) || defined(__rtems__) static void ClockTimeSync(void *dummy) { taskwdInsert(0, NULL, NULL); @@ -177,6 +186,7 @@ static void ClockTimeSync(void *dummy) ClockTimePvt.synchronized = 0; taskwdRemove(0); } +#endif /* Time Provider Routine */ @@ -188,6 +198,7 @@ static int ClockTimeGetCurrent(epicsTimeStamp *pDest) /* If a Hi-Res clock is available and works, use it */ #ifdef CLOCK_REALTIME_HR clock_gettime(CLOCK_REALTIME_HR, &clockNow) && + /* Note: Uses the lo-res clock below if the above call fails */ #endif clock_gettime(CLOCK_REALTIME, &clockNow); @@ -195,9 +206,15 @@ static int ClockTimeGetCurrent(epicsTimeStamp *pDest) clockNow.tv_sec < POSIX_TIME_AT_EPICS_EPOCH) { clockNow.tv_sec = POSIX_TIME_AT_EPICS_EPOCH + 86400; clockNow.tv_nsec = 0; + +#if defined(vxWorks) || defined(__rtems__) clock_settime(CLOCK_REALTIME, &clockNow); errlogPrintf("WARNING: OS Clock time was read before being set.\n" "Using 1990-01-02 00:00:00.000000 UTC\n"); +#else + errlogPrintf("WARNING: OS Clock pre-dates the EPICS epoch!\n" + "Using 1990-01-02 00:00:00.000000 UTC\n"); +#endif } epicsTimeFromTimespec(pDest, &clockNow); diff --git a/modules/libcom/src/ring/epicsRingBytes.c b/modules/libcom/src/ring/epicsRingBytes.c index cb7e52e83..ab048e467 100644 --- a/modules/libcom/src/ring/epicsRingBytes.c +++ b/modules/libcom/src/ring/epicsRingBytes.c @@ -38,6 +38,7 @@ typedef struct ringPvt { volatile int nextPut; volatile int nextGet; int size; + int highWaterMark; volatile char buffer[1]; /* actually larger */ }ringPvt; @@ -47,6 +48,7 @@ epicsShareFunc epicsRingBytesId epicsShareAPI epicsRingBytesCreate(int size) if(!pring) return NULL; pring->size = size + SLOP; + pring->highWaterMark = 0; pring->nextGet = 0; pring->nextPut = 0; pring->lock = 0; @@ -118,7 +120,7 @@ epicsShareFunc int epicsShareAPI epicsRingBytesPut( { ringPvt *pring = (ringPvt *)id; int nextGet, nextPut, size; - int freeCount, copyCount, topCount; + int freeCount, copyCount, topCount, used; if (pring->lock) epicsSpinLock(pring->lock); nextGet = pring->nextGet; @@ -131,8 +133,9 @@ epicsShareFunc int epicsShareAPI epicsRingBytesPut( if (pring->lock) epicsSpinUnlock(pring->lock); return 0; } - if (nbytes) + if (nbytes) { memcpy ((void *)&pring->buffer[nextPut], value, nbytes); + } nextPut += nbytes; } else { @@ -143,8 +146,9 @@ epicsShareFunc int epicsShareAPI epicsRingBytesPut( } topCount = size - nextPut; copyCount = (nbytes > topCount) ? topCount : nbytes; - if (copyCount) + if (copyCount) { memcpy ((void *)&pring->buffer[nextPut], value, copyCount); + } nextPut += copyCount; if (nextPut == size) { int nLeft = nbytes - copyCount; @@ -155,6 +159,10 @@ epicsShareFunc int epicsShareAPI epicsRingBytesPut( } pring->nextPut = nextPut; + used = nextPut - nextGet; + if (used < 0) used += pring->size; + if (used > pring->highWaterMark) pring->highWaterMark = used; + if (pring->lock) epicsSpinUnlock(pring->lock); return nbytes; } @@ -224,3 +232,20 @@ epicsShareFunc int epicsShareAPI epicsRingBytesIsFull(epicsRingBytesId id) { return (epicsRingBytesFreeBytes(id) <= 0); } + +epicsShareFunc int epicsShareAPI epicsRingBytesHighWaterMark(epicsRingBytesIdConst id) +{ + ringPvt *pring = (ringPvt *)id; + return pring->highWaterMark; +} + +epicsShareFunc void epicsShareAPI epicsRingBytesResetHighWaterMark(epicsRingBytesId id) +{ + ringPvt *pring = (ringPvt *)id; + int used; + if (pring->lock) epicsSpinLock(pring->lock); + used = pring->nextGet - pring->nextPut; + if (used < 0) used += pring->size; + pring->highWaterMark = used; + if (pring->lock) epicsSpinUnlock(pring->lock); +} diff --git a/modules/libcom/src/ring/epicsRingBytes.h b/modules/libcom/src/ring/epicsRingBytes.h index 011829bfe..3dc0081ad 100644 --- a/modules/libcom/src/ring/epicsRingBytes.h +++ b/modules/libcom/src/ring/epicsRingBytes.h @@ -24,6 +24,7 @@ extern "C" { #include "shareLib.h" typedef void *epicsRingBytesId; +typedef void const *epicsRingBytesIdConst; epicsShareFunc epicsRingBytesId epicsShareAPI epicsRingBytesCreate(int nbytes); /* Same, but secured by a spinlock */ @@ -39,6 +40,8 @@ epicsShareFunc int epicsShareAPI epicsRingBytesUsedBytes(epicsRingBytesId id); epicsShareFunc int epicsShareAPI epicsRingBytesSize(epicsRingBytesId id); epicsShareFunc int epicsShareAPI epicsRingBytesIsEmpty(epicsRingBytesId id); epicsShareFunc int epicsShareAPI epicsRingBytesIsFull(epicsRingBytesId id); +epicsShareFunc int epicsShareAPI epicsRingBytesHighWaterMark(epicsRingBytesIdConst id); +epicsShareFunc void epicsShareAPI epicsRingBytesResetHighWaterMark(epicsRingBytesId id); #ifdef __cplusplus } diff --git a/modules/libcom/src/ring/epicsRingPointer.cpp b/modules/libcom/src/ring/epicsRingPointer.cpp index 9c144cec1..709ab6509 100644 --- a/modules/libcom/src/ring/epicsRingPointer.cpp +++ b/modules/libcom/src/ring/epicsRingPointer.cpp @@ -90,3 +90,15 @@ epicsShareFunc int epicsShareAPI epicsRingPointerIsFull(epicsRingPointerId id) voidPointer *pvoidPointer = reinterpret_cast(id); return((pvoidPointer->isFull()) ? 1 : 0); } + +epicsShareFunc int epicsShareAPI epicsRingPointerGetHighWaterMark(epicsRingPointerIdConst id) +{ + voidPointer const *pvoidPointer = reinterpret_cast(id); + return(pvoidPointer->getHighWaterMark()); +} + +epicsShareFunc void epicsShareAPI epicsRingPointerResetHighWaterMark(epicsRingPointerId id) +{ + voidPointer *pvoidPointer = reinterpret_cast(id); + pvoidPointer->resetHighWaterMark(); +} diff --git a/modules/libcom/src/ring/epicsRingPointer.h b/modules/libcom/src/ring/epicsRingPointer.h index 48d62036d..68bf8f5a6 100644 --- a/modules/libcom/src/ring/epicsRingPointer.h +++ b/modules/libcom/src/ring/epicsRingPointer.h @@ -40,18 +40,22 @@ public: /* Functions */ int getSize() const; bool isEmpty() const; bool isFull() const; + int getHighWaterMark() const; + void resetHighWaterMark(); private: /* Prevent compiler-generated member functions */ /* default constructor, copy constructor, assignment operator */ epicsRingPointer(); epicsRingPointer(const epicsRingPointer &); epicsRingPointer& operator=(const epicsRingPointer &); + int getUsedNoLock() const; private: /* Data */ epicsSpinId lock; volatile int nextPush; volatile int nextPop; int size; + int highWaterMark; T * volatile * buffer; }; @@ -59,6 +63,7 @@ extern "C" { #endif /*__cplusplus */ typedef void *epicsRingPointerId; +typedef void const *epicsRingPointerIdConst; epicsShareFunc epicsRingPointerId epicsShareAPI epicsRingPointerCreate(int size); /* Same, but secured by a spinlock */ @@ -74,6 +79,8 @@ epicsShareFunc int epicsShareAPI epicsRingPointerGetUsed(epicsRingPointerId id) epicsShareFunc int epicsShareAPI epicsRingPointerGetSize(epicsRingPointerId id); epicsShareFunc int epicsShareAPI epicsRingPointerIsEmpty(epicsRingPointerId id); epicsShareFunc int epicsShareAPI epicsRingPointerIsFull(epicsRingPointerId id); +epicsShareFunc int epicsShareAPI epicsRingPointerGetHighWaterMark(epicsRingPointerIdConst id); +epicsShareFunc void epicsShareAPI epicsRingPointerResetHighWaterMark(epicsRingPointerId id); /* This routine was incorrectly named in previous releases */ #define epicsRingPointerSize epicsRingPointerGetSize @@ -95,7 +102,8 @@ epicsShareFunc int epicsShareAPI epicsRingPointerIsFull(epicsRingPointerId id); template inline epicsRingPointer::epicsRingPointer(int sz, bool locked) : - lock(0), nextPush(0), nextPop(0), size(sz+1), buffer(new T* [sz+1]) + lock(0), nextPush(0), nextPop(0), size(sz+1), highWaterMark(0), + buffer(new T* [sz+1]) { if (locked) lock = epicsSpinCreate(); @@ -121,6 +129,8 @@ inline bool epicsRingPointer::push(T *p) } buffer[next] = p; nextPush = newNext; + int used = getUsedNoLock(); + if (used > highWaterMark) highWaterMark = used; if (lock) epicsSpinUnlock(lock); return(true); } @@ -161,12 +171,19 @@ inline int epicsRingPointer::getFree() const return n; } +template +inline int epicsRingPointer::getUsedNoLock() const +{ + int n = nextPush - nextPop; + if (n < 0) n += size; + return n; +} + template inline int epicsRingPointer::getUsed() const { if (lock) epicsSpinLock(lock); - int n = nextPush - nextPop; - if (n < 0) n += size; + int n = getUsedNoLock(); if (lock) epicsSpinUnlock(lock); return n; } @@ -196,6 +213,20 @@ inline bool epicsRingPointer::isFull() const return((count == 0) || (count == size)); } +template +inline int epicsRingPointer::getHighWaterMark() const +{ + return highWaterMark; +} + +template +inline void epicsRingPointer::resetHighWaterMark() +{ + if (lock) epicsSpinLock(lock); + highWaterMark = getUsedNoLock(); + if (lock) epicsSpinUnlock(lock); +} + #endif /* __cplusplus */ #endif /* INCepicsRingPointerh */ diff --git a/modules/libcom/test/Makefile b/modules/libcom/test/Makefile index e72c46c61..1c26c44b7 100755 --- a/modules/libcom/test/Makefile +++ b/modules/libcom/test/Makefile @@ -14,11 +14,18 @@ PROD_LIBS += Com PROD_SYS_LIBS_WIN32 += ws2_32 advapi32 user32 PROD_SYS_LIBS_solaris += socket nsl +PROD_SRCS_RTEMS += rtemsTestData.c + +ifeq ($(EPICS_HOST_ARCH),$(T_A)) +# skip except for host arch due to custom .plt + TESTPROD_HOST += epicsUnitTestTest epicsUnitTestTest_SRCS += epicsUnitTestTest.c # Not much point running this on vxWorks or RTEMS... TESTS += epicsUnitTestTest +endif + TESTPROD_HOST += epicsTypesTest epicsTypesTest_SRCS += epicsTypesTest.cpp testHarness_SRCS += epicsTypesTest.cpp @@ -62,6 +69,11 @@ epicsEnvTest_SRCS += epicsEnvTest.c testHarness_SRCS += epicsEnvTest.c TESTS += epicsEnvTest +TESTPROD_HOST += epicsEnvUnsetTest +epicsEnvUnsetTest_SRCS += epicsEnvUnsetTest.c +testHarness_SRCS += epicsEnvUnsetTest.c +TESTS += epicsEnvUnsetTest + TESTPROD_HOST += epicsErrlogTest epicsErrlogTest_SRCS += epicsErrlogTest.c testHarness_SRCS += epicsErrlogTest.c @@ -182,6 +194,11 @@ macLibTest_SRCS += macLibTest.c testHarness_SRCS += macLibTest.c TESTS += macLibTest +TESTPROD_HOST += aslibtest +aslibtest_SRCS += aslibtest.c +testHarness_SRCS += aslibtest.c +TESTS += aslibtest + # Perl module tests: TESTS += macLib @@ -215,12 +232,14 @@ osiSockTest_SRCS += osiSockTest.c testHarness_SRCS += osiSockTest.c TESTS += osiSockTest +ifeq ($(BUILD_CLASS),HOST) ifneq ($(OS_CLASS),WIN32) # This test can only be run on a build host, and is broken on Windows TESTPROD_HOST += yajl_test yajl_test_SRCS += yajl_test.c TESTS += yajlTest endif +endif # The testHarness runs all the test programs in a known working order. testHarness_SRCS += epicsRunLibComTests.c @@ -235,6 +254,10 @@ TESTSPEC_vxWorks = libComTestHarness.munch; epicsRunLibComTests TESTSPEC_RTEMS = libComTestHarness.boot; epicsRunLibComTests TESTSCRIPTS_HOST += $(TESTS:%=%.t) +ifneq ($(filter $(T_A),$(CROSS_COMPILER_RUNTEST_ARCHS)),) +TESTPROD_RTEMS = $(TESTPROD_HOST) +TESTSCRIPTS_RTEMS += $(filter-out epicsUnitTestTest.t, $(TESTS:%=%.t)) +endif # The following are not test programs, they measure performance. diff --git a/modules/libcom/test/aslibtest.c b/modules/libcom/test/aslibtest.c new file mode 100644 index 000000000..875aa56fd --- /dev/null +++ b/modules/libcom/test/aslibtest.c @@ -0,0 +1,119 @@ +/*************************************************************************\ +* Copyright (c) 2018 Michael Davidsaver +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +#include +#include + +#include +#include + +#include +#include +#include +#include + +#include + +static char *asUser, + *asHost; +static int asAsl; + +static void setUser(const char *name) +{ + free(asUser); + asUser = epicsStrDup(name); +} + +static void setHost(const char *name) +{ + free(asHost); + asHost = epicsStrDup(name); +} + +static void testAccess(const char *asg, unsigned mask) +{ + ASMEMBERPVT asp = 0; /* aka dbCommon::asp */ + ASCLIENTPVT client = 0; + long ret; + + ret = asAddMember(&asp, asg); + if(ret) { + testFail("testAccess(ASG:%s, USER:%s, HOST:%s, ASL:%d) -> asAddMember error: %s", + asg, asUser, asHost, asAsl, errSymMsg(ret)); + } else { + ret = asAddClient(&client, asp, asAsl, asUser, asHost); + } + if(ret) { + testFail("testAccess(ASG:%s, USER:%s, HOST:%s, ASL:%d) -> asAddClient error: %s", + asg, asUser, asHost, asAsl, errSymMsg(ret)); + } else { + unsigned actual = 0; + actual |= asCheckGet(client) ? 1 : 0; + actual |= asCheckPut(client) ? 2 : 0; + testOk(actual==mask, "testAccess(ASG:%s, USER:%s, HOST:%s, ASL:%d) -> %x == %x", + asg, asUser, asHost, asAsl, actual, mask); + } + if(client) asRemoveClient(&client); + if(asp) asRemoveMember(&asp); +} + +static void testSyntaxErrors(void) +{ + static const char empty[] = "\n#almost empty file\n\n"; + long ret; + + testDiag("testSyntaxErrors()"); + + eltc(0); + ret = asInitMem(empty, NULL); + testOk(ret==S_asLib_badConfig, "load \"empty\" config -> %s", errSymMsg(ret)); + eltc(1); +} +static const char hostname_config[] = "" + "HAG(foo) {localhost}\n" + "ASG(DEFAULT) {RULE(0, NONE)}\n" + "ASG(ro) {RULE(0, NONE)RULE(1, READ) {HAG(foo)}}\n" + "ASG(rw) {RULE(1, WRITE) {HAG(foo)}}\n" + ; + +static void testHostNames(void) +{ + + testDiag("testHostNames()"); + + testOk1(asInitMem(hostname_config, NULL)==0); + + setUser("testing"); + setHost("localhost"); + asAsl = 0; + + testAccess("invalid", 0); + testAccess("DEFAULT", 0); + testAccess("ro", 1); + testAccess("rw", 3); + + setHost("127.0.0.1"); + + testAccess("invalid", 0); + testAccess("DEFAULT", 0); + testAccess("ro", 0); + testAccess("rw", 0); + + setHost("nosuchhost"); + + testAccess("invalid", 0); + testAccess("DEFAULT", 0); + testAccess("ro", 0); + testAccess("rw", 0); +} +MAIN(aslibtest) +{ + testPlan(14); + testSyntaxErrors(); + testHostNames(); + errlogFlush(); + return testDone(); +} diff --git a/modules/libcom/test/epicsEnvUnsetTest.c b/modules/libcom/test/epicsEnvUnsetTest.c new file mode 100644 index 000000000..e9d0f8603 --- /dev/null +++ b/modules/libcom/test/epicsEnvUnsetTest.c @@ -0,0 +1,37 @@ +#include +#include +#include +#include + +#include "macLib.h" +#include "envDefs.h" +#include "errlog.h" +#include "epicsUnitTest.h" +#include "testMain.h" + +static void check(const char* variable, const char* expected) +{ + const char* value; + + value = getenv(variable); + if (!testOk((!expected && !value) || (expected && value && strcmp(expected, value) == 0), + "%s = \"%s\"", variable, value)) + { + testDiag("should have been \"%s\"", expected); + } +} + +MAIN(epicsEnvUnsetTest) +{ + eltc(0); + testPlan(3); + + check("TEST_VAR_A",NULL); + epicsEnvSet("TEST_VAR_A","test value"); + check("TEST_VAR_A","test value"); + epicsEnvUnset("TEST_VAR_A"); + check("TEST_VAR_A",NULL); + + testDone(); + return 0; +} diff --git a/modules/libcom/test/epicsRunLibComTests.c b/modules/libcom/test/epicsRunLibComTests.c index 29c5eda3f..94fe3d76f 100644 --- a/modules/libcom/test/epicsRunLibComTests.c +++ b/modules/libcom/test/epicsRunLibComTests.c @@ -16,6 +16,7 @@ #include #include +int aslibtest(void); int blockingSockTest(void); int epicsAlgorithm(void); int epicsAtomicTest(void); @@ -73,6 +74,7 @@ void epicsRunLibComTests(void) /* * Run the regular tests in alphabetical order */ + runTest(aslibtest); runTest(blockingSockTest); runTest(epicsAlgorithm); runTest(epicsAtomicTest); diff --git a/modules/libcom/test/macLibTest.c b/modules/libcom/test/macLibTest.c index 7f1e2c544..742fc79e2 100644 --- a/modules/libcom/test/macLibTest.c +++ b/modules/libcom/test/macLibTest.c @@ -65,11 +65,11 @@ static void ovcheck(void) MAIN(macLibTest) { - testPlan(91); + testPlan(93); if (macCreateHandle(&h, NULL)) testAbort("macCreateHandle() failed"); - macSuppressWarning(h, TRUE); + eltc(0); check("FOO", " FOO"); @@ -215,6 +215,10 @@ MAIN(macLibTest) check("${FOO,FOO=${FOO}}", "!$(FOO,recursive)"); check("${FOO=GRIBBLE,FOO=${FOO}}", "!$(FOO,recursive)"); + macSuppressWarning(h, TRUE); + check("$(CRUX)", "!$(CRUX)"); + check("${FOO}", "!$(BAR)"); + ovcheck(); return testDone(); diff --git a/modules/libcom/test/osiSockTest.c b/modules/libcom/test/osiSockTest.c index 39eb0ba2b..6672cdbd3 100644 --- a/modules/libcom/test/osiSockTest.c +++ b/modules/libcom/test/osiSockTest.c @@ -42,6 +42,21 @@ void multiCastLoop(SOCKET s, int put) "getsockopt MULTICAST_LOOP => %d", (int) flag); } +void multiCastTTL(SOCKET s, int put) +{ + int status; + osiSockOptMcastTTL_t flag = put; + osiSocklen_t len = sizeof(flag); + + status = setsockopt(s, IPPROTO_IP, IP_MULTICAST_TTL, + (char *)&flag, len); + testOk(status >= 0, "setsockopt IP_MULTICAST_TTL := %d", put); + + status = getsockopt(s, IPPROTO_IP, IP_MULTICAST_TTL, (char *)&flag, &len); + testOk(status >= 0 && len == sizeof(flag) && !flag == !put, + "getsockopt IP_MULTICAST_TTL => %d", (int) flag); +} + void udpSockTest(void) { SOCKET s; @@ -55,6 +70,9 @@ void udpSockTest(void) multiCastLoop(s, 1); multiCastLoop(s, 0); + multiCastTTL(s, 1); + multiCastTTL(s, 0); + epicsSocketDestroy(s); } @@ -62,7 +80,7 @@ void udpSockTest(void) MAIN(osiSockTest) { int status; - testPlan(10); + testPlan(14); status = osiSockAttach(); testOk(status, "osiSockAttach"); diff --git a/modules/libcom/test/ringBytesTest.c b/modules/libcom/test/ringBytesTest.c index 6cef93334..bb91d0201 100644 --- a/modules/libcom/test/ringBytesTest.c +++ b/modules/libcom/test/ringBytesTest.c @@ -30,7 +30,8 @@ typedef struct info { epicsRingBytesId ring; }info; -static void check(epicsRingBytesId ring, int expectedFree) +static void check(epicsRingBytesId ring, int expectedFree, + int expectedHighWaterMark) { int expectedUsed = RINGSIZE - expectedFree; int expectedEmpty = (expectedUsed == 0); @@ -39,11 +40,14 @@ static void check(epicsRingBytesId ring, int expectedFree) int nUsed = epicsRingBytesUsedBytes(ring); int isEmpty = epicsRingBytesIsEmpty(ring); int isFull = epicsRingBytesIsFull(ring); + int highWaterMark = epicsRingBytesHighWaterMark(ring); testOk(nFree == expectedFree, "Free: %d == %d", nFree, expectedFree); testOk(nUsed == expectedUsed, "Used: %d == %d", nUsed, expectedUsed); testOk(isEmpty == expectedEmpty, "Empty: %d == %d", isEmpty, expectedEmpty); testOk(isFull == expectedFull, "Full: %d == %d", isFull, expectedFull); + testOk(highWaterMark == expectedHighWaterMark, "HighWaterMark: %d == %d", + highWaterMark, expectedHighWaterMark); } MAIN(ringBytesTest) @@ -55,7 +59,7 @@ MAIN(ringBytesTest) char get[RINGSIZE+1]; epicsRingBytesId ring; - testPlan(245); + testPlan(292); pinfo = calloc(1,sizeof(info)); if (!pinfo) { @@ -70,50 +74,54 @@ MAIN(ringBytesTest) if (!ring) { testAbort("epicsRingBytesCreate failed"); } - check(ring, RINGSIZE); + check(ring, RINGSIZE, 0); for (i = 0 ; i < sizeof(put) ; i++) put[i] = i; for(i = 0 ; i < RINGSIZE ; i++) { n = epicsRingBytesPut(ring, put, i); testOk(n==i, "ring put %d", i); - check(ring, RINGSIZE-i); + check(ring, RINGSIZE-i, i); n = epicsRingBytesGet(ring, get, i); testOk(n==i, "ring get %d", i); - check(ring, RINGSIZE); + check(ring, RINGSIZE, i); testOk(memcmp(put,get,i)==0, "get matches write"); } + epicsRingBytesResetHighWaterMark(ring); + for(i = 0 ; i < RINGSIZE ; i++) { n = epicsRingBytesPut(ring, put+i, 1); testOk(n==1, "ring put 1, %d", i); - check(ring, RINGSIZE-1-i); + check(ring, RINGSIZE-1-i, i + 1); } n = epicsRingBytesPut(ring, put+RINGSIZE, 1); testOk(n==0, "put to full ring"); - check(ring, 0); + check(ring, 0, RINGSIZE); for(i = 0 ; i < RINGSIZE ; i++) { n = epicsRingBytesGet(ring, get+i, 1); testOk(n==1, "ring get 1, %d", i); - check(ring, 1+i); + check(ring, 1+i, RINGSIZE); } testOk(memcmp(put,get,RINGSIZE)==0, "get matches write"); n = epicsRingBytesGet(ring, get+RINGSIZE, 1); testOk(n==0, "get from empty ring"); - check(ring, RINGSIZE); + check(ring, RINGSIZE, RINGSIZE); + + epicsRingBytesResetHighWaterMark(ring); n = epicsRingBytesPut(ring, put, RINGSIZE+1); testOk(n==0, "ring put beyond ring capacity (%d, expected 0)",n); - check(ring, RINGSIZE); + check(ring, RINGSIZE, 0); n = epicsRingBytesPut(ring, put, 1); testOk(n==1, "ring put %d", 1); - check(ring, RINGSIZE-1); + check(ring, RINGSIZE-1, 1); n = epicsRingBytesPut(ring, put, RINGSIZE); testOk(n==0, "ring put beyond ring capacity (%d, expected 0)",n); - check(ring, RINGSIZE-1); + check(ring, RINGSIZE-1, 1); n = epicsRingBytesGet(ring, get, 1); testOk(n==1, "ring get %d", 1); - check(ring, RINGSIZE); + check(ring, RINGSIZE, 1); epicsRingBytesDelete(ring); epicsEventDestroy(consumerEvent); diff --git a/modules/libcom/test/ringPointerTest.c b/modules/libcom/test/ringPointerTest.c index 65a349489..d351708b5 100644 --- a/modules/libcom/test/ringPointerTest.c +++ b/modules/libcom/test/ringPointerTest.c @@ -64,6 +64,7 @@ static void testSingle(void) testOk1(epicsRingPointerGetFree(ring)==rsize); testOk1(epicsRingPointerGetSize(ring)==rsize); testOk1(epicsRingPointerGetUsed(ring)==0); + testOk1(epicsRingPointerGetHighWaterMark(ring)==0); testOk1(epicsRingPointerPop(ring)==NULL); @@ -75,6 +76,10 @@ static void testSingle(void) testOk1(epicsRingPointerGetFree(ring)==rsize-1); testOk1(epicsRingPointerGetSize(ring)==rsize); testOk1(epicsRingPointerGetUsed(ring)==1); + testOk1(epicsRingPointerGetHighWaterMark(ring)==1); + + epicsRingPointerResetHighWaterMark(ring); + testOk1(epicsRingPointerGetHighWaterMark(ring)==1); testDiag("Fill it up"); for(i=2; i<2*rsize; i++) { @@ -92,6 +97,7 @@ static void testSingle(void) testOk1(epicsRingPointerGetFree(ring)==0); testOk1(epicsRingPointerGetSize(ring)==rsize); testOk1(epicsRingPointerGetUsed(ring)==rsize); + testOk1(epicsRingPointerGetHighWaterMark(ring)==rsize); testDiag("Drain it out"); for(i=1; i<2*rsize; i++) { @@ -108,6 +114,7 @@ static void testSingle(void) testOk1(epicsRingPointerGetFree(ring)==rsize); testOk1(epicsRingPointerGetSize(ring)==rsize); testOk1(epicsRingPointerGetUsed(ring)==0); + testOk1(epicsRingPointerGetHighWaterMark(ring)==rsize); testDiag("Fill it up again"); for(i=2; i<2*rsize; i++) { @@ -236,7 +243,7 @@ MAIN(ringPointerTest) { int prio = epicsThreadGetPrioritySelf(); - testPlan(37); + testPlan(42); testSingle(); if (prio) epicsThreadSetPriority(epicsThreadGetIdSelf(), epicsThreadPriorityScanLow); diff --git a/modules/libcom/test/rtemsTestData.c b/modules/libcom/test/rtemsTestData.c new file mode 100644 index 000000000..7b68976c5 --- /dev/null +++ b/modules/libcom/test/rtemsTestData.c @@ -0,0 +1,6 @@ +#include "epicsMemFs.h" + +/* no local files needed for these tests, + * so skip local FS setup + */ +const epicsMemFS *epicsRtemsFSImage = NULL; diff --git a/modules/normativeTypes b/modules/normativeTypes index 80920ae0b..41d56fdf8 160000 --- a/modules/normativeTypes +++ b/modules/normativeTypes @@ -1 +1 @@ -Subproject commit 80920ae0b0cf1a0e41bb77f70bc2aca4df4e1ac3 +Subproject commit 41d56fdf89a09617f2d5fe726fa0ecb579ca551f diff --git a/modules/pvAccess b/modules/pvAccess index 281447c90..5e7934221 160000 --- a/modules/pvAccess +++ b/modules/pvAccess @@ -1 +1 @@ -Subproject commit 281447c909c732b01b68a5b6bd013f8005799a07 +Subproject commit 5e793422198d4223a03164787cacbfb6cf9575fc diff --git a/modules/pvData b/modules/pvData index a02a60c65..d776f6eaf 160000 --- a/modules/pvData +++ b/modules/pvData @@ -1 +1 @@ -Subproject commit a02a60c6580572f1e2945b3ebccb7c1eabfc40f8 +Subproject commit d776f6eaf03c058597c05b3308636d95223b6c6e diff --git a/modules/pvDatabase b/modules/pvDatabase index e667e3957..07a951c3a 160000 --- a/modules/pvDatabase +++ b/modules/pvDatabase @@ -1 +1 @@ -Subproject commit e667e39573eabef2162917fd89875fe65962e490 +Subproject commit 07a951c3a2bd3e95b1de24e3224e310225e3e45a diff --git a/modules/pva2pva b/modules/pva2pva index 5f434aec5..a245adb5c 160000 --- a/modules/pva2pva +++ b/modules/pva2pva @@ -1 +1 @@ -Subproject commit 5f434aec59c5d377111fc8f4dfdd61ead98b7ca1 +Subproject commit a245adb5cfc9b4a0b0d04b630032963ecc3410d0 diff --git a/modules/pvaClient b/modules/pvaClient index 4e4554af4..b1c101578 160000 --- a/modules/pvaClient +++ b/modules/pvaClient @@ -1 +1 @@ -Subproject commit 4e4554af4e86fa6d7814392e6d95d901c676287b +Subproject commit b1c101578bea3610f777a1f51229eb2967dc89f4 diff --git a/src/tools/EpicsHostArch.pl b/src/tools/EpicsHostArch.pl new file mode 100644 index 000000000..e8e49bc5e --- /dev/null +++ b/src/tools/EpicsHostArch.pl @@ -0,0 +1,55 @@ +#!/usr/bin/env perl +#************************************************************************* +# Copyright (c) 2018 UChicago Argonne LLC, as Operator of Argonne +# National Laboratory. +# EPICS BASE is distributed subject to a Software License Agreement found +# in file LICENSE that is included with this distribution. +#************************************************************************* + +# Returns an architecture name for EPICS_HOST_ARCH that should be +# appropriate for the CPU that this version of Perl was built for. +# Any arguments to the program will be appended with separator '-' +# to allow flags like -gnu -debug and/or -static to be added. + +# Before Base has been built, use a command like this: +# bash$ export EPICS_HOST_ARCH=`perl src/tools/EpicsHostArch.pl` +# +# If Base is already built, use +# tcsh% setenv EPICS_HOST_ARCH `perl base/lib/perl/EpicsHostArch.pl` + +# If your architecture is not recognized by this script, please send +# the output from running 'perl --version' to the EPICS tech-talk +# mailing list to have it added. + +use strict; + +use Config; +use POSIX; + +print join('-', HostArch(), @ARGV), "\n"; + +sub HostArch { + my $arch = $Config{archname}; + for ($arch) { + return 'linux-x86_64' if m/^x86_64-linux/; + return 'linux-x86' if m/^i[3-6]86-linux/; + return 'linux-arm' if m/^arm-linux/; + return 'windows-x64' if m/^MSWin32-x64/; + return 'win32-x86' if m/^MSWin32-x86/; + return "cygwin-x86_64" if m/^x86_64-cygwin/; + return "cygwin-x86" if m/^i[3-6]86-cygwin/; + return 'solaris-sparc' if m/^sun4-solaris/; + return 'solaris-x86' if m/^i86pc-solaris/; + + my ($kernel, $hostname, $release, $version, $cpu) = uname; + if (m/^darwin/) { + for ($cpu) { + return 'darwin-x86' if m/^(i386|x86_64)/; + return 'darwin-ppc' if m/Power Macintosh/; + } + die "$0: macOS CPU type '$cpu' not recognized\n"; + } + + die "$0: Architecture '$arch' not recognized\n"; + } +} diff --git a/src/tools/Makefile b/src/tools/Makefile index d07092271..0df179781 100644 --- a/src/tools/Makefile +++ b/src/tools/Makefile @@ -17,9 +17,13 @@ PERL_MODULES += EPICS/Release.pm PERL_MODULES += EPICS/Readfile.pm PERL_MODULES += EPICS/Getopts.pm +# This goes into lib/perl, not bin/ +PERL_MODULES += EpicsHostArch.pl + PERL_SCRIPTS += assembleSnippets.pl PERL_SCRIPTS += convertRelease.pl PERL_SCRIPTS += cvsclean.pl +PERL_SCRIPTS += depclean.pl PERL_SCRIPTS += dos2unix.pl PERL_SCRIPTS += expandVars.pl PERL_SCRIPTS += fullPathName.pl @@ -44,27 +48,4 @@ HTMLS += munch.html HTMLS += podToHtml.html HTMLS += podRemove.html -# Build Package Config Files - -FINAL_LOCATION ?= $(shell $(PERL) $(TOOLS)/fullPathName.pl $(INSTALL_LOCATION)) -C_CFLAGS += $(filter-out -g,$(filter-out -O%,$(filter-out -W%,$(CPPFLAGS)))) -C_CFLAGS += $(filter-out -g,$(filter-out -O%,$(filter-out -W%,$(CFLAGS)))) -PKGVARS += FINAL_LOCATION OS_CLASS CMPLR_CLASS C_CFLAGS LDFLAGS LDLIBS -PKGVARS += EPICS_VERSION EPICS_REVISION EPICS_MODIFICATION EPICS_PATCH_LEVEL -PKGVARS += CC CCC CPP AR LD -PKGVARS += EPICS_BASE_IOC_LIBS - -EXPANDFLAGS += $(foreach var,$(PKGVARS),-D$(var)="$(strip $($(var)))") -PKGCONFIG += epics-base-$(T_A).pc -ifeq ($(T_A),$(EPICS_HOST_ARCH)) -PKGCONFIG += epics-base.pc -endif - -EXPAND += $(PKGCONFIG:%=%@) -CLEANS += epics-base-$(T_A).pc@ - include $(TOP)/configure/RULES - -epics-base-$(T_A).pc@: ../epics-base-arch.pc@ - @$(RM) $@ - @$(CP) $< $@ diff --git a/src/tools/depclean.pl b/src/tools/depclean.pl new file mode 100644 index 000000000..cbf6077d1 --- /dev/null +++ b/src/tools/depclean.pl @@ -0,0 +1,20 @@ +#!/usr/bin/env perl +#************************************************************************* +# Copyright (c) 2018 UChicago Argonne LLC, as Operator of Argonne +# National Laboratory. +# This file is distributed subject to a Software License Agreement found +# in the file LICENSE that is included with this distribution. +#************************************************************************* + +# Find and delete dependency files from all build dirs in the source tree. +# The extension for dependency files is assumed to be .d (currently true). + +use File::Find; + +@ARGV = ('.') unless @ARGV; + +sub check { + unlink if -f && m(/O\.[^/]+/[^/]+\.d$); +} + +find({ wanted => \&check, no_chdir => 1 }, @ARGV); diff --git a/src/tools/genVersionHeader.pl b/src/tools/genVersionHeader.pl index c61f14c98..24e08abf9 100644 --- a/src/tools/genVersionHeader.pl +++ b/src/tools/genVersionHeader.pl @@ -23,18 +23,20 @@ my $tfmt = '%Y-%m-%dT%H:%M'; $tfmt .= '%z' unless $^O eq 'MSWin32'; # %z returns zone name on Windows my $now = strftime($tfmt, localtime); -our ($opt_h, $opt_v, $opt_q); +our ($opt_d, $opt_h, $opt_i, $opt_v, $opt_q); our $opt_t = '.'; our $opt_N = 'VCSVERSION'; our $opt_V = $now; my $vcs; -getopts('hvqt:N:V:') && @ARGV == 1 +getopts('dhivqt:N:V:') && @ARGV == 1 or HELP_MESSAGE(); my ($outfile) = @ARGV; +if ($opt_d) { exit 0 } # exit if make is run in dry-run mode + if (!$vcs && -d "$opt_t/_darcs") { # Darcs print "== Found /_darcs directory\n" if $opt_v; # v1-4-dirty @@ -135,19 +137,36 @@ if (open($DST, '+<', $outfile)) { print "== Current:\n$actual==\n" if $opt_v; if ($actual eq $output) { - print "Keeping VCS header $outfile\n $opt_N = \"$opt_V\"\n" + close $DST; + print "Keeping VCS header $outfile\n", + " $opt_N = \"$opt_V\"\n" unless $opt_q; exit 0; } - print "Updating VCS header $outfile\n $opt_N = \"$opt_V\"\n" - unless $opt_q; + + # This regexp must match the #define in $output above: + $actual =~ m/#define (\w+) ("[^"]*")\n/; + if ($opt_i) { + print "Outdated VCS header $outfile\n", + " has: $1 = $2\n", + " needs: $opt_N = \"$opt_V\"\n"; + } + else { + print "Updating VCS header $outfile\n", + " from: $1 = $2\n", + " to: $opt_N = \"$opt_V\"\n" + unless $opt_q; + } } else { - print "Creating VCS header $outfile\n $opt_N = \"$opt_V\"\n" + print "Creating VCS header $outfile\n", + " $opt_N = \"$opt_V\"\n" unless $opt_q; open($DST, '>', $outfile) or die "Can't create $outfile: $!\n"; } +if ($opt_i) { exit 1 }; # exit if make is run in "question" mode + seek $DST, 0, 0; truncate $DST, 0; print $DST $output; @@ -158,9 +177,11 @@ sub HELP_MESSAGE { Usage: genVersionHeader.pl -h Display this Usage message - genVersionHeader.pl [-v] [-q] [-t top] [-N NAME] [-V version] output.h"; + genVersionHeader.pl [-v] [-d] [-q] [-t top] [-N NAME] [-V version] output.h"; Generate or update the header file output.h -v - Verbose (debugging messages) + -d - Dry-run + -i - Question mode -q - Quiet -t top - Path to the module's top (default '$opt_t') -N NAME - Macro name to be defined (default '$opt_N') diff --git a/src/tools/makeTestfile.pl b/src/tools/makeTestfile.pl index be4648258..7d298768f 100644 --- a/src/tools/makeTestfile.pl +++ b/src/tools/makeTestfile.pl @@ -15,18 +15,43 @@ # If the script is given an argument -tap it sets HARNESS_ACTIVE in the # environment to make the epicsUnitTest code generate strict TAP output. -# Usage: makeTestfile.pl target.t executable +# Usage: makeTestfile.pl target.t executable +# target-arch and host-arch are EPICS build target names (eg. linux-x86) # target.t is the name of the Perl script to generate # executable is the name of the file the script runs use strict; -my ($target, $exe) = @ARGV; +my ($TA, $HA, $target, $exe) = @ARGV; +my $exec; + +# Use WINE to run windows target executables on non-windows host +if( $TA =~ /^win32-x86/ && $HA !~ /^win/ ) { + # new deb. derivatives have wine32 and wine64 + # older have wine and wine64 + # prefer wine32 if present + my $wine32 = "/usr/bin/wine32"; + $wine32 = "/usr/bin/wine" if ! -x $wine32; + $exec = "$wine32 $exe"; +} elsif( $TA =~ /^windows-x64/ && $HA !~ /^win/ ) { + $exec = "wine64 $exe"; + +# Run pc386 test harness w/ QEMU +} elsif( $TA =~ /^RTEMS-pc386-qemu$/ ) { + $exec = "qemu-system-i386 -m 64 -no-reboot -serial stdio -display none -net nic,model=ne2k_pci -net user,restrict=yes -kernel $exe"; + +# Explicitly fail for other RTEMS targets +} elsif( $TA =~ /^RTEMS-/ ) { + die "$0: I don't know how to create scripts for testing $TA on $HA\n"; + +} else { + $exec = "./$exe"; +} open(my $OUT, '>', $target) or die "Can't create $target: $!\n"; print $OUT <&2 +exit 1 diff --git a/startup/EpicsHostArch.pl b/startup/EpicsHostArch.pl deleted file mode 100755 index 09f7ffddd..000000000 --- a/startup/EpicsHostArch.pl +++ /dev/null @@ -1,47 +0,0 @@ -eval 'exec perl -S $0 ${1+"$@"}' # -*- Mode: perl -*- - if $running_under_some_shell; # EpicsHostArch.pl -#************************************************************************* -# Copyright (c) 2011 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. -#************************************************************************* - -# Returns the Epics host architecture suitable -# for assigning to the EPICS_HOST_ARCH variable - -use Config; -use POSIX; - -$suffix=""; -$suffix="-".$ARGV[0] if ($ARGV[0] ne ""); - -$EpicsHostArch = GetEpicsHostArch(); -print "$EpicsHostArch$suffix"; - -sub GetEpicsHostArch { # no args - $arch=$Config{'archname'}; - if ($arch =~ /sun4-solaris/) { return "solaris-sparc"; - } elsif ($arch =~ m/i86pc-solaris/) { return "solaris-x86"; - } elsif ($arch =~ m/i[3-6]86-linux/){ return "linux-x86"; - } elsif ($arch =~ m/x86_64-linux/) { return "linux-x86_64"; - } elsif ($arch =~ m/arm-linux/) { return "linux-arm"; - } elsif ($arch =~ m/MSWin32-x86/) { return "win32-x86"; - } elsif ($arch =~ m/MSWin32-x64/) { return "windows-x64"; - } elsif ($arch =~ m/cygwin/) { - my($kernel, $hostname, $release, $version, $cpu) = POSIX::uname(); - if ($cpu =~ m/x86_64/) { return "cygwin-x86_64"; } - return "cygwin-x86"; - } elsif ($arch =~ m/darwin/) { - my($kernel, $hostname, $release, $version, $cpu) = POSIX::uname(); - if ($cpu =~ m/Power Macintosh/) { return "darwin-ppc"; } - elsif ($cpu =~ m/i386/) { return "darwin-x86"; } - elsif ($cpu =~ m/x86_64/) { return "darwin-x86"; } - else { return "unsupported"; } - } else { return "unsupported"; } -} - -#EOF EpicsHostArch.pl - diff --git a/startup/Site.cshrc b/startup/Site.cshrc deleted file mode 100755 index 24f34abb9..000000000 --- a/startup/Site.cshrc +++ /dev/null @@ -1,83 +0,0 @@ -#!/bin/csh -f -#************************************************************************* -# Copyright (c) 2002 The University of Chicago, as Operator of Argonne -# National Laboratory. -# Copyright (c) 2002 The Regents of the University of California, as -# Operator of Los Alamos National Laboratory. -# EPICS BASE Versions 3.13.7 -# and higher are distributed subject to a Software License Agreement found -# in file LICENSE that is included with this distribution. -#************************************************************************* -# Site-specific EPICS environment settings -# -# sites should modify these definitions - -# Location of epics base -if ( ! $?EPICS_BASE ) then - set EPICS_BASE=/usr/local/epics/base -endif - -# Location of epics extensions -if ( ! $?EPICS_EXTENSIONS ) then - setenv EPICS_EXTENSIONS /usr/local/epics/extensions -endif - -# Postscript printer definition needed by some extensions (eg medm, dp, dm, ...) -if ( ! $?PSPRINTTER ) then - setenv PSPRINTER lp -endif - -# Needed only by medm extension -#setenv EPICS_DISPLAY_PATH -# Needed only by medm extension -setenv BROWSER firefox - -# Needed only by orbitscreen extension -if ( ! $?ORBITSCREENHOME ) then - setenv ORBITSCREENHOME $EPICS_EXTENSIONS/src/orbitscreen -endif - -# Needed only by adt extension -if ( ! $?ADTHOME ) then - setenv ADTHOME /usr/local/oag/apps/src/appconfig/adt - echo $ADTHOME -endif - -# Needed only by ar extension (archiver) -setenv EPICS_AR_PORT 7002 - -# Needed for java extensions -if ( $?CLASSPATH ) then - setenv CLASSPATH "${CLASSPATH}:${EPICS_EXTENSIONS}/javalib" -else - setenv CLASSPATH "${EPICS_EXTENSIONS}/javalib" -endif - -# Allow private versions of extensions without a bin subdir -if ( $?EPICS_EXTENSIONS_PVT ) then - set path = ( $path $EPICS_EXTENSIONS_PVT) -endif - -################################################################## - -# Start of set R3.14 environment variables - -setenv EPICS_HOST_ARCH `$EPICS_BASE/startup/EpicsHostArch.pl` - -# Allow private versions of base -if ( $?EPICS_BASE_PVT ) then - if ( -e $EPICS_BASE_PVT/bin/$EPICS_HOST_ARCH ) then - set path = ( $path $EPICS_BASE_PVT/bin/$EPICS_HOST_ARCH) - endif -endif - -# Allow private versions of extensions -if ( $?EPICS_EXTENSIONS_PVT ) then - if ( -e $EPICS_EXTENSIONS_PVT/bin/$EPICS_HOST_ARCH ) then - set path = ( $path $EPICS_EXTENSIONS_PVT/bin/$EPICS_HOST_ARCH) - endif -endif -set path = ( $path $EPICS_EXTENSIONS/bin/$EPICS_HOST_ARCH ) - -# End of set R3.14 environment variables -################################################################## diff --git a/startup/Site.profile b/startup/Site.profile deleted file mode 100755 index 677001401..000000000 --- a/startup/Site.profile +++ /dev/null @@ -1,88 +0,0 @@ -#!/bin/sh -#************************************************************************* -# Copyright (c) 2002 The University of Chicago, as Operator of Argonne -# National Laboratory. -# Copyright (c) 2002 The Regents of the University of California, as -# Operator of Los Alamos National Laboratory. -# EPICS BASE Versions 3.13.7 -# and higher are distributed subject to a Software License Agreement found -# in file LICENSE that is included with this distribution. -#************************************************************************* -# Site-specific EPICS environment settings -# -# sites should modify these definitions - -# Location of epics base -if [ -z "${MY_EPICS_BASE}" ] ; then - MY_EPICS_BASE=/usr/local/epics/base -fi - -# Location of epics extensions (medm, msi, etc.) -if [ -z "${EPICS_EXTENSIONS}" ] ; then - EPICS_EXTENSIONS=/usr/local/epics/extensions -fi - -# Postscript printer definition needed by some extensions (eg medm, dp, dm, ...) -if [ -z "${PSPRINTER}" ] ; then - export PSPRINTER=lp -fi - -#Needed only by the idl and ezcaIDL extensions. -#export EPICS_EXTENSIONS - -# Needed only by medm extension -#export EPICS_DISPLAY_PATH=/path/to/adl/files -export BROWSER=firefox - -# Needed only by orbitscreen extension -#if [ -z "${ORBITSCREENHOME}" ] ; then -# export "ORBITSCREENHOME=${EPICS_EXTENSIONS/src/orbitscreen}" -#fi - -# Needed only by adt extension -#if [ -z "${ADTHOME}" ] ; then -# ADTHOME= -# export ADTHOME -#fi - -# Needed only by ar extension (archiver) -#EPICS_AR_PORT=7002 -#export EPICS_AR_PORT - -# Needed for java extensions -if [ -z "${CLASSPATH}" ] ; then - CLASSPATH="${EPICS_EXTENSIONS}/javalib" -else - CLASSPATH="${CLASSPATH}:${EPICS_EXTENSIONS}/javalib" -fi -export CLASSPATH - -# Allow private versions of extensions without a bin subdir -if [ -n "${EPICS_EXTENSIONS_PVT}" ] ; then - PATH="${PATH}:${EPICS_EXTENSIONS_PVT}" -fi - -#--------------------------------------------------------------- -# Start of set R3.14 environment variables -# -EPICS_HOST_ARCH=`"${MY_EPICS_BASE}"/startup/EpicsHostArch.pl` -export EPICS_HOST_ARCH - -# Allow private versions of base -if [ -n "${EPICS_BASE_PVT}" ] ; then - if [ -d "${EPICS_BASE_PVT}/bin/${EPICS_HOST_ARCH}" ]; then - PATH="${PATH}:${EPICS_BASE_PVT}/bin/${EPICS_HOST_ARCH}" - fi -fi - -# Allow private versions of extensions -if [ -n "${EPICS_EXTENSIONS_PVT}" ] ; then - if [ -d "${EPICS_EXTENSIONS_PVT}/bin/${EPICS_HOST_ARCH}" ]; then - PATH="${PATH}:${EPICS_EXTENSIONS_PVT}/bin/${EPICS_HOST_ARCH}" - fi -fi -PATH="${PATH}:${EPICS_EXTENSIONS}/bin/${EPICS_HOST_ARCH}" - -# End of set R3.14 environment variables - -#--------------------------------------------------------------- diff --git a/startup/cygwin.bat b/startup/cygwin.bat deleted file mode 100755 index ff75b5335..000000000 --- a/startup/cygwin.bat +++ /dev/null @@ -1,122 +0,0 @@ -@ECHO OFF -REM ************************************************************************* -REM Copyright (c) 2002 The University of Chicago, as Operator of Argonne -REM National Laboratory. -REM Copyright (c) 2002 The Regents of the University of California, as -REM Operator of Los Alamos National Laboratory. -REM EPICS BASE Versions 3.13.7 -REM and higher are distributed subject to a Software License Agreement found -REM in file LICENSE that is included with this distribution. -REM ************************************************************************* -REM -REM Site-specific EPICS environment settings -REM -REM sites should modify these definitions - -REM ====================================================== -REM ====== REQUIRED ENVIRONMENT VARIABLES FOLLOW ====== -REM ====================================================== - -REM ====================================================== -REM ---------------- WINDOWS --------------------------- -REM ====================================================== -REM ----- WIN95 ----- -REM set PATH=C:\WINDOWS;C:\WINDOWS\COMMAND -REM ----- WINNT, WIN2000 ----- -REM set PATH=C:\WINNT;C:\WINNT\SYSTEM32 -REM ----- WINXP, Vista, Windows 7 ----- -set PATH=C:\WINDOWS\system32;C:\WINDOWS;C:\WINDOWS\SYSTEM32\Wbem - -REM ====================================================== -REM ---------------- make and perl --------------------- -REM ====================================================== - -REM --------------- ActiveState perl ------------------- -set PATH=C:\Perl\bin;%PATH% - -REM --------------- mingw make ------------------------ -REM set PATH=C:\mingw-make\bin;%PATH% -REM set PATH=C:\mingw-make82-3\bin;%PATH% - -REM --------------- gnuwin32 make ---------------------- -set PATH=C:\gnuwin32\bin;%PATH% - -REM ====================================================== -REM ---------------- cygwin tools ------------------------ -REM ====================================================== -REM (make & perl if above perl and make are REMs) -REM Dont use cygwin GNU make and Perl! -REM cygwin contains tk/tcl, vim, perl, and many unix tools -REM need grep from here NOT from cvs directory -set PATH=%PATH%;.;.. -set PATH=%PATH%;c:\cygwin\bin - -REM ====================================================== -REM --------------- EPICS -------------------------------- -REM ====================================================== -set EPICS_HOST_ARCH=cygwin-x86 -set PATH=%PATH%;G:\epics\base\bin\%EPICS_HOST_ARCH% -set PATH=%PATH%;G:\epics\extensions\bin\%EPICS_HOST_ARCH% - -REM ====================================================== -REM ------- OPTIONAL ENVIRONMENT VARIABLES FOLLOW -------- -REM ====================================================== - -REM ====================================================== -REM ----------------- remote CVS ------------------------- -REM ====================================================== -REM set CVS_RSH=c:/cygwin/bin/ssh.exe -REM set CVSROOT=:ext:jba@aps.anl.gov:/usr/local/epicsmgr/cvsroot -REM set HOME=c:/users/%USERNAME% -REM set HOME=c:/users/jba - -REM ====================================================== -REM ------------------- Bazaar --------------------------- -REM ====================================================== -set PATH=%PATH%;C:\Program files\Bazaar - -REM ====================================================== -REM ----------------- GNU make flags --------------------- -REM ====================================================== -set MAKEFLAGS=-w - -REM ====================================================== -REM -------------- vim (use cygwin vim ) ----------------- -REM ====================================================== -REM HOME needed by vim to write .viminfo file. -REM VIM needed by vim to find _vimrc file. -REM set VIM=c:\cygwin - -REM ====================================================== -REM --------------- Epics Channel Access ----------------- -REM Modify and uncomment the following lines -REM to override the base/configure/CONFIG_ENV defaults -REM ====================================================== -REM set EPICS_CA_ADDR_LIST=n.n.n.n n.n.n.n -REM set EPICS_CA_AUTO_ADDR_LIST=YES - -REM set EPICS_CA_CONN_TMO=30.0 -REM set EPICS_CA_BEACON_PERIOD=15.0 -REM set EPICS_CA_REPEATER_PORT=5065 -REM set EPICS_CA_SERVER_PORT=5064 -REM set EPICS_TS_MIN_WEST=420 - -REM ====================================================== -REM --------------- JAVA --------------------------------- -REM ====================================================== -REM Needed for java extensions -REM set CLASSPATH=G:\epics\extensions\javalib -REM set PATH=%PATH%;C:\j2sdk1.4.1_01\bin -REM set CLASSPATH=%CLASSPATH%;C:\j2sdk1.4.1_01\lib\tools.jar - -REM ====================================================== -REM --------------- Exceed ------------------------------- -REM Needed for X11 extensions -REM ====================================================== -REM set EX_VER=7.10 -REM set EX_VER=12.00 -REM set EX_VER=14.00 -REM set PATH=%PATH%;C:\Exceed%EX_VER%\XDK\ -REM set PATH=%PATH%;C:\Program Files\Hummingbird\Connectivity\%EX_VER%\Exceed\ - - diff --git a/startup/unix.csh b/startup/unix.csh new file mode 100644 index 000000000..788a639e6 --- /dev/null +++ b/startup/unix.csh @@ -0,0 +1,96 @@ +#************************************************************************* +# Copyright (c) 2002 The University of Chicago, as Operator of Argonne +# National Laboratory. +# Copyright (c) 2002 The Regents of the University of California, as +# Operator of Los Alamos National Laboratory. +# EPICS BASE Versions 3.13.7 +# and higher are distributed subject to a Software License Agreement found +# in file LICENSE that is included with this distribution. +#************************************************************************* +# +# Site-specific EPICS environment settings +# +# Attempts to set EPICS_HOST_ARCH. Optionally, adds the EPICS Base +# install host architecture bin directory to PATH. +# + +#----------------------------------------------------------------------- +# Site serviceable parts (These definitions may be modified) +#----------------------------------------------------------------------- + +# Automatically set up the environment when possible ("yes" or "no"). +# If set to yes, as much of the environment will be set up as possible. +# If set to no, just the minimum environment will be set up. More +# specific _auto_* definitions take precedence over this definition. +set _auto=no + +# Automatically append to PATH ("yes" or "no"). If set to yes, the +# EPICS Base install host architecture bin directory will be added to +# PATH if possible. If set to no, the bin directory will not be added +# to PATH. +set _auto_path_append=$_auto + +# The program used to run Perl scripts (pathname). +set _perl_prog=perl + +# The EPICS host architecture specification for EPICS_HOST_ARCH +# (-[-] as defined in configure/CONFIG_SITE). If +# nonempty, the value will be used as the value of EPICS_HOST_ARCH. If +# empty, an attempt will be made to automatically determine the value +# with EpicsHostArch.pl. +set _epics_host_arch= + +# The install location of EPICS Base (pathname). If nonempty, the +# EpicsHostArch.pl script from it, if it exists, will be used to +# determine EPICS_HOST_ARCH. If nonempty and EPICS_HOST_ARCH was +# determined successfully, it will be used to add the host architecture +# bin directory to PATH if _auto_path_append is yes. +set _epics_base= + +# The source location of EPICS Base (pathname). If nonempty, the +# EpicsHostArch.pl script from it, if it exists and _epics_base is empty +# or it did not exist in the _epics_base location, will be used to +# determine EPICS_HOST_ARCH. +set _epics_base_src= + +#----------------------------------------------------------------------- +# Internal parts (There is typically no need to modify these) +#----------------------------------------------------------------------- + +# Define the possible locations of EpicsHostArch.pl +set _epics_host_arch_pl= +set _src_epics_host_arch_pl= +if ("$_epics_base" != '') then + set _epics_host_arch_pl="$_epics_base/lib/perl/EpicsHostArch.pl" +endif +if ("$_epics_base_src" != '') then + set _src_epics_host_arch_pl="$_epics_base_src/src/tools/EpicsHostArch.pl" +endif + +# Set the EPICS host architecture specification +if ("$_epics_host_arch" != '') then + setenv EPICS_HOST_ARCH "$_epics_host_arch" +else if (-e "$_epics_host_arch_pl") then + set _epics_host_arch=`"$_perl_prog" "$_epics_host_arch_pl"` + setenv EPICS_HOST_ARCH "$_epics_host_arch" +else if (-e "$_src_epics_host_arch_pl") then + set _epics_host_arch=`"$_perl_prog" "$_src_epics_host_arch_pl"` + setenv EPICS_HOST_ARCH "$_epics_host_arch" +endif + +# Add the EPICS Base host architecture bin directory to PATH +if ("$_auto_path_append" == yes) then + if ("$_epics_base" != '' && "$_epics_host_arch" != '') then + setenv PATH "${PATH}:$_epics_base/bin/$_epics_host_arch" + endif +endif + +# Don't leak variables into the environment +unset _auto +unset _auto_path_append +unset _perl_prog +unset _epics_host_arch +unset _epics_base +unset _epics_base_src +unset _epics_host_arch_pl +unset _src_epics_host_arch_pl diff --git a/startup/unix.sh b/startup/unix.sh new file mode 100644 index 000000000..a8d8328ec --- /dev/null +++ b/startup/unix.sh @@ -0,0 +1,100 @@ +#************************************************************************* +# Copyright (c) 2002 The University of Chicago, as Operator of Argonne +# National Laboratory. +# Copyright (c) 2002 The Regents of the University of California, as +# Operator of Los Alamos National Laboratory. +# EPICS BASE Versions 3.13.7 +# and higher are distributed subject to a Software License Agreement found +# in file LICENSE that is included with this distribution. +#************************************************************************* +# +# Site-specific EPICS environment settings +# +# Attempts to set EPICS_HOST_ARCH. Optionally, adds the EPICS Base +# install host architecture bin directory to PATH. +# + +#----------------------------------------------------------------------- +# Site serviceable parts (These definitions may be modified) +#----------------------------------------------------------------------- + +# Automatically set up the environment when possible ("yes" or "no"). +# If set to yes, as much of the environment will be set up as possible. +# If set to no, just the minimum environment will be set up. More +# specific _auto_* definitions take precedence over this definition. +_auto=no + +# Automatically append to PATH ("yes" or "no"). If set to yes, the +# EPICS Base install host architecture bin directory will be added to +# PATH if possible. If set to no, the bin directory will not be added +# to PATH. +_auto_path_append=$_auto + +# The program used to run Perl scripts (pathname). +_perl_prog=perl + +# The EPICS host architecture specification for EPICS_HOST_ARCH +# (-[-] as defined in configure/CONFIG_SITE). If +# nonempty, the value will be used as the value of EPICS_HOST_ARCH. If +# empty, an attempt will be made to automatically determine the value +# with EpicsHostArch.pl. +_epics_host_arch= + +# The install location of EPICS Base (pathname). If nonempty, the +# EpicsHostArch.pl script from it, if it exists, will be used to +# determine EPICS_HOST_ARCH. If nonempty and EPICS_HOST_ARCH was +# determined successfully, it will be used to add the host architecture +# bin directory to PATH if _auto_path_append is yes. +_epics_base= + +# The source location of EPICS Base (pathname). If nonempty, the +# EpicsHostArch.pl script from it, if it exists and _epics_base is empty +# or it did not exist in the _epics_base location, will be used to +# determine EPICS_HOST_ARCH. +_epics_base_src= + +#----------------------------------------------------------------------- +# Internal parts (There is typically no need to modify these) +#----------------------------------------------------------------------- + +# Define the possible locations of EpicsHostArch.pl +_epics_host_arch_pl= +_src_epics_host_arch_pl= +if [ -n "$_epics_base" ]; then + _epics_host_arch_pl="$_epics_base/lib/perl/EpicsHostArch.pl" +fi +if [ -n "$_epics_base_src" ]; then + _src_epics_host_arch_pl="$_epics_base_src/src/tools/EpicsHostArch.pl" +fi + +# Set the EPICS host architecture specification +if [ -n "$_epics_host_arch" ]; then + EPICS_HOST_ARCH=$_epics_host_arch + export EPICS_HOST_ARCH +elif [ -e "$_epics_host_arch_pl" ]; then + _epics_host_arch=$("$_perl_prog" "$_epics_host_arch_pl") + EPICS_HOST_ARCH=$_epics_host_arch + export EPICS_HOST_ARCH +elif [ -e "$_src_epics_host_arch_pl" ]; then + _epics_host_arch=$("$_perl_prog" "$_src_epics_host_arch_pl") + EPICS_HOST_ARCH=$_epics_host_arch + export EPICS_HOST_ARCH +fi + +# Add the EPICS Base host architecture bin directory to PATH +if [ "$_auto_path_append" = yes ]; then + if [ -n "$_epics_base" ] && [ -n "$_epics_host_arch" ]; then + PATH="$PATH:$_epics_base/bin/$_epics_host_arch" + export PATH + fi +fi + +# Don't leak variables into the environment +unset _auto +unset _auto_path_append +unset _perl_prog +unset _epics_host_arch +unset _epics_base +unset _epics_base_src +unset _epics_host_arch_pl +unset _src_epics_host_arch_pl diff --git a/startup/win32.bat b/startup/win32.bat index af9155dd7..6652fc97c 100755 --- a/startup/win32.bat +++ b/startup/win32.bat @@ -1,147 +1,105 @@ -@ECHO OFF -REM ************************************************************************* -REM Copyright (c) 2002 The University of Chicago, as Operator of Argonne -REM National Laboratory. -REM Copyright (c) 2002 The Regents of the University of California, as -REM Operator of Los Alamos National Laboratory. -REM EPICS BASE Versions 3.13.7 -REM and higher are distributed subject to a Software License Agreement found -REM in file LICENSE that is included with this distribution. -REM ************************************************************************* -REM -REM Site-specific EPICS environment settings -REM -REM sites should modify these definitions - -REM ====================================================== -REM ====== REQUIRED ENVIRONMENT VARIABLES FOLLOW ====== -REM ====================================================== - -REM ====================================================== -REM ---------------- WINDOWS --------------------------- -REM ====================================================== -REM ----- WIN95 ----- -REM set PATH=C:\WINDOWS;C:\WINDOWS\COMMAND -REM ----- WINNT, WIN2000 ----- -REM set PATH=C:\WINNT;C:\WINNT\SYSTEM32 -REM ----- WINXP, Vista, Windows 7 ----- -set PATH=C:\WINDOWS\system32;C:\WINDOWS;C:\WINDOWS\SYSTEM32\Wbem - -REM ====================================================== -REM ---------------- make and perl --------------------- -REM ====================================================== - -REM --------------- ActiveState perl ------------------- -set PATH=C:\Perl\bin;%PATH% - -REM --------------- mingw make ------------------------ -REM set PATH=C:\mingw-make\bin;%PATH% -REM set PATH=C:\mingw-make82-3\bin;%PATH% - -REM --------------- gnuwin32 make ---------------------- -set PATH=C:\gnuwin32\bin;%PATH% - -REM ====================================================== -REM ---------------- cygwin tools ------------------------ -REM ====================================================== -REM (make & perl if above perl and make are REMs) -REM Dont use cygwin GNU make and Perl! -REM cygwin contains tk/tcl, vim, perl, and many unix tools -REM need grep from here NOT from cvs directory -REM set PATH=%PATH%;.;.. -REM set PATH=%PATH%;c:\cygwin\bin - -REM ====================================================== -REM --------------- Visual c++ ------------------------- -REM ====================================================== - -REM ------ Microsoft Visual Studio 2005 ------ -REM call "C:\Program files\Microsoft Visual Studio 8\VC\vcvarsall.bat" x86_amd64 -REM set PATH=%PATH%;C:\Program Files\Microsoft SDKs\Windows\v6.0A\bin -REM set INCLUDE=%INCLUDE%;C:\Program Files\Microsoft SDKs\Windows\v6.0A\include -REM REM set LIBPATH=%LIBPATH%;C:\Program Files\Microsoft SDKs\Windows\v6.0A\lib -REM set LIB=%LIB%;C:\Program Files\Microsoft SDKs\Windows\v6.0A\lib - -REM ------ Microsoft Visual Studio 2008 ------ -REM call "C:\Program files\Microsoft Visual Studio 9.0\VC\bin\vcvars32.bat" -REM call "C:\Program files\Microsoft Visual Studio 9.0\VC\vcvarsall.bat" x86_amd64 -REM set PATH=C:\Program Files\Microsoft SDKs\Windows\v7.0\bin;%PATH% -REM set INCLUDE=C:\Program Files\Microsoft SDKs\Windows\v7.0\include;%INCLUDE% -REM set LIBPATH=C:\Program Files\Microsoft SDKs\Windows\v7.0\lib;%LIBPATH% -REM set LIB=C:\Program Files\Microsoft SDKs\Windows\v7.0\lib;%LIB% - -REM ----- Visual Studion 2010 ----- -REM -- windows-x64 --- -REM call "C:\Program files\Microsoft Visual Studio 10.0\VC\vcvarsall.bat" x64 -REM -- win32-x86 --- -call "C:\Program files\Microsoft Visual Studio 10.0\VC\vcvarsall.bat" x86 - -REM ====================================================== -REM --------------- EPICS -------------------------------- -REM ====================================================== -REM set EPICS_HOST_ARCH=windows-x64 -set EPICS_HOST_ARCH=win32-x86 -set PATH=%PATH%;G:\epics\base\bin\%EPICS_HOST_ARCH% -set PATH=%PATH%;G:\epics\extensions\bin\%EPICS_HOST_ARCH% - -REM ====================================================== -REM ------- OPTIONAL ENVIRONMENT VARIABLES FOLLOW -------- -REM ====================================================== - -REM ====================================================== -REM ----------------- remote CVS ------------------------- -REM ====================================================== -REM set CVS_RSH=c:/cygwin/bin/ssh.exe -REM set CVSROOT=:ext:jba@aps.anl.gov:/usr/local/epicsmgr/cvsroot -REM set HOME=c:/users/%USERNAME% -REM set HOME=c:/users/jba - -REM ====================================================== -REM ------------------- Bazaar --------------------------- -REM ====================================================== -set PATH=%PATH%;C:\Program files\Bazaar - -REM ====================================================== -REM ----------------- GNU make flags --------------------- -REM ====================================================== -set MAKEFLAGS=-w - -REM ====================================================== -REM -------------- vim (use cygwin vim ) ----------------- -REM ====================================================== -REM HOME needed by vim to write .viminfo file. -REM VIM needed by vim to find _vimrc file. -REM set VIM=c:\cygwin - -REM ====================================================== -REM --------------- Epics Channel Access ----------------- -REM Modify and uncomment the following lines -REM to override the base/configure/CONFIG_ENV defaults -REM ====================================================== -REM set EPICS_CA_ADDR_LIST=n.n.n.n n.n.n.n -REM set EPICS_CA_AUTO_ADDR_LIST=YES - -REM set EPICS_CA_CONN_TMO=30.0 -REM set EPICS_CA_BEACON_PERIOD=15.0 -REM set EPICS_CA_REPEATER_PORT=5065 -REM set EPICS_CA_SERVER_PORT=5064 -REM set EPICS_TS_MIN_WEST=420 - -REM ====================================================== -REM --------------- JAVA --------------------------------- -REM ====================================================== -REM Needed for java extensions -REM set CLASSPATH=G:\epics\extensions\javalib -REM set PATH=%PATH%;C:\j2sdk1.4.1_01\bin -REM set CLASSPATH=%CLASSPATH%;C:\j2sdk1.4.1_01\lib\tools.jar - -REM ====================================================== -REM --------------- Exceed ------------------------------- -REM Needed for X11 extensions -REM ====================================================== -REM set EX_VER=7.10 -REM set EX_VER=12.00 -REM set EX_VER=14.00 -REM set PATH=%PATH%;C:\Exceed%EX_VER%\XDK\ -REM set PATH=%PATH%;C:\Program Files\Hummingbird\Connectivity\%EX_VER%\Exceed\ - +@echo off +rem ************************************************************************* +rem Copyright (c) 2017 UChicago Argonne LLC, as Operator of Argonne +rem National Laboratory. +rem Copyright (c) 2002 The Regents of the University of California, as +rem Operator of Los Alamos National Laboratory. +rem EPICS BASE is distributed subject to a Software License Agreement found +rem in file LICENSE that is included with this distribution. +rem ************************************************************************* +rem +rem Site-specific EPICS environment settings +rem +rem Sets EPICS_HOST_ARCH and the environment for Microsoft Visual Studio. +rem Optionally, resets PATH, adds Strawberry Perl to PATH, and adds the +rem EPICS Base install host architecture bin directory to PATH. +rem + +rem ---------------------------------------------------------------------- +rem Site serviceable parts (These definitions may be modified) +rem ---------------------------------------------------------------------- + +rem The values of the definitions in this section must not contain +rem double-quotes. +rem +rem * Right: set _foo=C:\foo +rem * Right: set "_foo=C:\foo" +rem * Wrong: set _foo="C:\foo" + +rem Automatically set up the environment when possible ("yes" or "no"). +rem If set to yes, as much of the environment will be set up as possible. +rem If set to no, just the minimum environment will be set up. More +rem specific _auto_* definitions take precedence over this definition. +set _auto=no + +rem Automatically reset PATH ("yes" or "no"). If set to yes, PATH will +rem be reset to the value of _path_new. If set to no, PATH will not be +rem reset. +set _auto_path_reset=%_auto% + +rem Automatically append to PATH ("yes" or "no"). If set to yes, the +rem EPICS Base install host architecture bin directory will be added to +rem PATH if possible. If set to no, the bin directory will not be added +rem to PATH. +set _auto_path_append=%_auto% + +rem The new value for PATH. If _auto_path_reset is yes, PATH will be set +rem to it. +set _path_new=C:\Windows\System32;C:\Windows;C:\Windows\System32\wbem + +rem The location of Strawberry Perl (pathname). If empty, Strawberry Perl +rem is assumed to already be in PATH and will not be added. If nonempty, +rem Strawberry Perl will be added to PATH. +set _strawberry_perl_home=C:\Strawberry + +rem The location of Microsoft Visual Studio (pathname). +set _visual_studio_home=C:\Program Files (x86)\Microsoft Visual Studio 14.0 + +rem The EPICS host architecture specification for EPICS_HOST_ARCH +rem (-[-] as defined in configure/CONFIG_SITE). +set _epics_host_arch=win32-x86 + +rem The install location of EPICS Base (pathname). If nonempty and +rem _auto_path_append is yes, it will be used to add the host architecture +rem bin directory to PATH. +set _epics_base= + +rem ---------------------------------------------------------------------- +rem Internal parts (There is typically no need to modify these) +rem ---------------------------------------------------------------------- + +rem Reset PATH +if "%_auto_path_reset%" == "yes" ( + set "PATH=%_path_new%" +) + +rem Add Strawberry Perl to PATH +if "%_strawberry_perl_home%" == "" goto after_add_strawberry_perl +rem Can't do this inside parentheses because PATH would be read only once +set "PATH=%PATH%;%_strawberry_perl_home%\c\bin" +set "PATH=%PATH%;%_strawberry_perl_home%\perl\site\bin" +set "PATH=%PATH%;%_strawberry_perl_home%\perl\bin" +:after_add_strawberry_perl + +rem Set the environment for Microsoft Visual Studio +call "%_visual_studio_home%\VC\vcvarsall.bat" x86 + +rem Set the EPICS host architecture specification +set "EPICS_HOST_ARCH=%_epics_host_arch%" + +rem Add the EPICS Base host architecture bin directory to PATH +if "%_auto_path_append%" == "yes" ( + if not "%_epics_base%" == "" ( + set "PATH=%PATH%;%_epics_base%\bin\%_epics_host_arch%" + ) +) + +rem Don't leak variables into the environment +set _auto= +set _auto_path_reset= +set _auto_path_append= +set _path_new= +set _strawberry_perl_home= +set _visual_studio_home= +set _epics_host_arch= +set _epics_base= diff --git a/startup/windows.bat b/startup/windows.bat new file mode 100644 index 000000000..877c0d5a9 --- /dev/null +++ b/startup/windows.bat @@ -0,0 +1,105 @@ +@echo off +rem ************************************************************************* +rem Copyright (c) 2017 UChicago Argonne LLC, as Operator of Argonne +rem National Laboratory. +rem Copyright (c) 2002 The Regents of the University of California, as +rem Operator of Los Alamos National Laboratory. +rem EPICS BASE is distributed subject to a Software License Agreement found +rem in file LICENSE that is included with this distribution. +rem ************************************************************************* +rem +rem Site-specific EPICS environment settings +rem +rem Sets EPICS_HOST_ARCH and the environment for Microsoft Visual Studio. +rem Optionally, resets PATH, adds Strawberry Perl to PATH, and adds the +rem EPICS Base install host architecture bin directory to PATH. +rem + +rem ---------------------------------------------------------------------- +rem Site serviceable parts (These definitions may be modified) +rem ---------------------------------------------------------------------- + +rem The values of the definitions in this section must not contain +rem double-quotes. +rem +rem * Right: set _foo=C:\foo +rem * Right: set "_foo=C:\foo" +rem * Wrong: set _foo="C:\foo" + +rem Automatically set up the environment when possible ("yes" or "no"). +rem If set to yes, as much of the environment will be set up as possible. +rem If set to no, just the minimum environment will be set up. More +rem specific _auto_* definitions take precedence over this definition. +set _auto=no + +rem Automatically reset PATH ("yes" or "no"). If set to yes, PATH will +rem be reset to the value of _path_new. If set to no, PATH will not be +rem reset. +set _auto_path_reset=%_auto% + +rem Automatically append to PATH ("yes" or "no"). If set to yes, the +rem EPICS Base install host architecture bin directory will be added to +rem PATH if possible. If set to no, the bin directory will not be added +rem to PATH. +set _auto_path_append=%_auto% + +rem The new value for PATH. If _auto_path_reset is yes, PATH will be set +rem to it. +set _path_new=C:\Windows\System32;C:\Windows;C:\Windows\System32\wbem + +rem The location of Strawberry Perl (pathname). If empty, Strawberry Perl +rem is assumed to already be in PATH and will not be added. If nonempty, +rem Strawberry Perl will be added to PATH. +set _strawberry_perl_home=C:\Strawberry + +rem The location of Microsoft Visual Studio (pathname). +set _visual_studio_home=C:\Program Files (x86)\Microsoft Visual Studio 14.0 + +rem The EPICS host architecture specification for EPICS_HOST_ARCH +rem (-[-] as defined in configure/CONFIG_SITE). +set _epics_host_arch=windows-x64 + +rem The install location of EPICS Base (pathname). If nonempty and +rem _auto_path_append is yes, it will be used to add the host architecture +rem bin directory to PATH. +set _epics_base= + +rem ---------------------------------------------------------------------- +rem Internal parts (There is typically no need to modify these) +rem ---------------------------------------------------------------------- + +rem Reset PATH +if "%_auto_path_reset%" == "yes" ( + set "PATH=%_path_new%" +) + +rem Add Strawberry Perl to PATH +if "%_strawberry_perl_home%" == "" goto after_add_strawberry_perl +rem Can't do this inside parentheses because PATH would be read only once +set "PATH=%PATH%;%_strawberry_perl_home%\c\bin" +set "PATH=%PATH%;%_strawberry_perl_home%\perl\site\bin" +set "PATH=%PATH%;%_strawberry_perl_home%\perl\bin" +:after_add_strawberry_perl + +rem Set the environment for Microsoft Visual Studio +call "%_visual_studio_home%\VC\vcvarsall.bat" x64 + +rem Set the EPICS host architecture specification +set "EPICS_HOST_ARCH=%_epics_host_arch%" + +rem Add the EPICS Base host architecture bin directory to PATH +if "%_auto_path_append%" == "yes" ( + if not "%_epics_base%" == "" ( + set "PATH=%PATH%;%_epics_base%\bin\%_epics_host_arch%" + ) +) + +rem Don't leak variables into the environment +set _auto= +set _auto_path_reset= +set _auto_path_append= +set _path_new= +set _strawberry_perl_home= +set _visual_studio_home= +set _epics_host_arch= +set _epics_base=