diff --git a/.ci/travis-build.sh b/.ci/travis-build.sh index e7c2531b3..1de103106 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 @@ -86,5 +73,5 @@ make -j2 $EXTRA if [ "$TEST" != "NO" ] then make -j2 tapfiles - make -s test-results + make -j2 -s test-results fi 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_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_COMMON b/configure/CONFIG_COMMON index 67cf33e1d..eef4d6745 100644 --- a/configure/CONFIG_COMMON +++ b/configure/CONFIG_COMMON @@ -80,6 +80,7 @@ IOCS_APPL_TOP = $(shell $(FULLPATHNAME) $(INSTALL_LOCATION)) NOP = : ECHO = @$(if $(findstring s,$(patsubst T_A=%,,$(MAKEFLAGS))),$(NOP),echo) QUIET_FLAG := $(if $(findstring s,$(MAKEFLAGS)),-q,) +QUESTION_FLAG := $(if $(findstring q,$(MAKEFLAGS)),-i,) #------------------------------------------------------- ifdef T_A 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 9b8cbf094..21a838790 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 @@ -79,9 +80,9 @@ else host: endif --include $(CONFIG)/RULES_FILE_TYPE +include $(CONFIG)/RULES_FILE_TYPE --include $(CONFIG)/RULES.Db +include $(CONFIG)/RULES.Db #--------------------------------------------------------------- # Include defines and rules for prod, library and test* targets @@ -185,7 +186,7 @@ endif # RELEASE file consistency checking checkRelease: - $(CONVERTRELEASE) checkRelease + +$(CONVERTRELEASE) checkRelease warnRelease: -$(CONVERTRELEASE) checkRelease noCheckRelease: @@ -256,15 +257,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 @@ -392,7 +391,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 @@ -531,7 +530,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) @@ -540,5 +539,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/os/CONFIG.Common.RTEMS b/configure/os/CONFIG.Common.RTEMS index c22e37a66..214ba794d 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) 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.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_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/documentation/README.1st b/documentation/README.1st index 042cede60..070b3d948 100644 --- a/documentation/README.1st +++ b/documentation/README.1st @@ -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..3e5b4ad71 100644 --- a/documentation/README.html +++ b/documentation/README.html @@ -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 c60ab9575..22a291601 100644 --- a/documentation/RELEASE_NOTES.html +++ b/documentation/RELEASE_NOTES.html @@ -97,6 +97,115 @@ be happy to try and answer them!

    +

    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

    A new iocsh command dbli lists the info fields defined in the @@ -113,15 +222,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

    @@ -763,11 +875,45 @@ 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

    + +

    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 @@ -799,15 +945,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 diff --git a/modules/ca/src/client/udpiiu.cpp b/modules/ca/src/client/udpiiu.cpp index 73d4ee4cb..00d764d7c 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; 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/database/src/ioc/db/dbAccess.c b/modules/database/src/ioc/db/dbAccess.c index cd7535194..143c427da 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); @@ -713,6 +702,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 */ @@ -1040,7 +1041,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; @@ -1213,8 +1214,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 { @@ -1347,4 +1348,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..5cf0b26ab 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 */ diff --git a/modules/database/src/ioc/db/dbCa.c b/modules/database/src/ioc/db/dbCa.c index e844ad826..65a8327cf 100644 --- a/modules/database/src/ioc/db/dbCa.c +++ b/modules/database/src/ioc/db/dbCa.c @@ -842,6 +842,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 +873,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 +897,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/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/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.h b/modules/database/src/ioc/db/dbScan.h index 028d09ec8..d483a0c30 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; 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/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..7cc2645c1 100644 --- a/modules/database/src/ioc/dbStatic/dbStaticLib.c +++ b/modules/database/src/ioc/dbStatic/dbStaticLib.c @@ -2241,7 +2241,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 +2270,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 +2306,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 +2353,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 +2645,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) { 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..5ef746faa 100644 --- a/modules/database/src/ioc/rsrv/caservertask.c +++ b/modules/database/src/ioc/rsrv/caservertask.c @@ -324,7 +324,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; 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..183769c2e 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. * @@ -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,33 +340,22 @@ 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 */ @@ -370,14 +363,14 @@ static long readValue(aaiRecord *prec) } 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/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/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..ece675b27 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 */ @@ -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); @@ -346,15 +348,18 @@ static long readValue(waveformRecord *prec) case menuYesNoYES: { long nRequest = prec->nelm; + epicsUInt32 nord = prec->nord; recGblSetSevr(prec, SIMM_ALARM, prec->sims); 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 (status == 0) + prec->udf = FALSE; + if (!dbLinkIsConstant(&prec->siol)) { prec->nord = nRequest; - db_post_events(prec, &prec->nord, DBE_VALUE | DBE_LOG); + if (nord != prec->nord) + db_post_events(prec, &prec->nord, DBE_VALUE | DBE_LOG); } prec->pact = FALSE; } else { /* !prec->pact && delay >= 0. */ 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/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/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..ae3741977 100644 --- a/modules/database/test/std/rec/Makefile +++ b/modules/database/test/std/rec/Makefile @@ -136,12 +136,23 @@ 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 + # 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 +160,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/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/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..a97074e0e --- /dev/null +++ b/modules/libcom/RTEMS/epicsMakeMemFs.pl @@ -0,0 +1,83 @@ +#!/usr/bin/env perl +# + +use File::Basename; + +use strict; + +my $outfile = shift; +my $varname = shift; + +open(my $DST, '>', $outfile) or die "Failed to open $outfile"; + +print $DST < +EOF + +my $N = 0; + +for my $fname (@ARGV) { + print "<- $fname\n"; + my $realfname = $fname; + + # strip leading "../" "./" or "/" + while ($fname =~ /^\.*\/(.*)$/) { $fname = $1; } + + my $file = basename($fname); + my @dirs = split('/', dirname($fname)); + + print $DST "/* begin $realfname */\nstatic const char * const file_${N}_dir[] = {"; + for my $dpart (@dirs) { + print $DST "\"$dpart\", "; + } + print $DST "NULL};\nstatic const char file_${N}_data[] = {\n "; + + open(my $SRC, '<', $realfname) or die "Failed to open $realfname"; + binmode($SRC); + + my $buf; + my $total = 0; + while (my $num = read($SRC, $buf, 32)) { + if($total != 0) { + print $DST ",\n "; + } + $total += $num; + my $out = join(",",map(ord,split(//,$buf))); + print $DST "$out"; + } + + 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..7dbf2594f 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; @@ -596,6 +627,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 +698,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/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..8d0400c2b 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. @@ -495,6 +495,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; diff --git a/modules/libcom/src/iocsh/libComRegister.c b/modules/libcom/src/iocsh/libComRegister.c index d3a5cfac4..d8429fa9f 100644 --- a/modules/libcom/src/iocsh/libComRegister.c +++ b/modules/libcom/src/iocsh/libComRegister.c @@ -110,6 +110,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 */ static const iocshFuncDef epicsParamShowFuncDef = {"epicsParamShow",0,NULL}; static void epicsParamShowCallFunc(const iocshArgBuf *args) @@ -367,6 +382,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/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/os/Darwin/osdEnv.c b/modules/libcom/src/osi/os/Darwin/osdEnv.c index ab3f93644..f7ff12ced 100644 --- a/modules/libcom/src/osi/os/Darwin/osdEnv.c +++ b/modules/libcom/src/osi/os/Darwin/osdEnv.c @@ -39,6 +39,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) +#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 682bcc934..0f6fb250d 100644 --- a/modules/libcom/src/osi/os/default/osdEnv.c +++ b/modules/libcom/src/osi/os/default/osdEnv.c @@ -55,6 +55,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) +#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 c81f49316..88e0ba078 100644 --- a/modules/libcom/src/osi/os/vxWorks/osdEnv.c +++ b/modules/libcom/src/osi/os/vxWorks/osdEnv.c @@ -51,6 +51,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..fb9d1532f 100644 --- a/modules/libcom/src/osi/osiClockTime.c +++ b/modules/libcom/src/osi/osiClockTime.c @@ -40,17 +40,19 @@ static struct { 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. +#ifdef CLOCK_REALTIME +/* 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/test/Makefile b/modules/libcom/test/Makefile index e72c46c61..429d446c7 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 @@ -215,12 +227,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 +249,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/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/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/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/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 b7d6582cb..39497a417 100644 --- a/src/tools/Makefile +++ b/src/tools/Makefile @@ -17,6 +17,9 @@ 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 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=