Merge branch 'database/master' back
This commit is contained in:
Executable
+21
@@ -0,0 +1,21 @@
|
||||
#!/bin/sh
|
||||
set -e -x
|
||||
|
||||
# set RTEMS to eg. "4.9" or "4.10"
|
||||
# requires qemu, bison, flex, texinfo, install-info
|
||||
if [ -n "$RTEMS" ]
|
||||
then
|
||||
# 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
|
||||
|
||||
if [ "$TEST" != "NO" ]
|
||||
then
|
||||
make -j2 tapfiles
|
||||
make -s test-results
|
||||
fi
|
||||
Executable
+133
@@ -0,0 +1,133 @@
|
||||
#!/bin/sh
|
||||
set -e -x
|
||||
|
||||
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 << EOF > configure/RELEASE.local
|
||||
EPICS_BASE=$HOME/.source/epics-base
|
||||
EOF
|
||||
|
||||
install -d "$HOME/.source"
|
||||
cd "$HOME/.source"
|
||||
|
||||
add_base_module() {
|
||||
MODULE=$1
|
||||
BRANCH=$2
|
||||
( cd epics-base/modules && \
|
||||
git clone --quiet --depth 5 --branch "$MODULE"/"$BRANCH" https://github.com/${REPOBASE:-epics-base}/epics-base.git "$MODULE" && \
|
||||
cd "$MODULE" && git log -n1 )
|
||||
}
|
||||
|
||||
git clone --quiet --depth 5 --branch core/"${BRCORE:-master}" https://github.com/${REPOBASE:-epics-base}/epics-base.git epics-base
|
||||
( cd epics-base && git log -n1 )
|
||||
add_base_module libcom "${BRLIBCOM:-master}"
|
||||
add_base_module ca "${BRCA:-master}"
|
||||
|
||||
EPICS_HOST_ARCH=`sh epics-base/startup/EpicsHostArch`
|
||||
|
||||
# requires wine and g++-mingw-w64-i686
|
||||
if [ "$WINE" = "32" ]
|
||||
then
|
||||
echo "Cross mingw32"
|
||||
sed -i -e '/CMPLR_PREFIX/d' epics-base/configure/os/CONFIG_SITE.linux-x86.win32-x86-mingw
|
||||
cat << EOF >> epics-base/configure/os/CONFIG_SITE.linux-x86.win32-x86-mingw
|
||||
CMPLR_PREFIX=i686-w64-mingw32-
|
||||
EOF
|
||||
cat << EOF >> epics-base/configure/CONFIG_SITE
|
||||
CROSS_COMPILER_TARGET_ARCHS+=win32-x86-mingw
|
||||
EOF
|
||||
fi
|
||||
|
||||
if [ "$STATIC" = "YES" ]
|
||||
then
|
||||
echo "Build static libraries/executables"
|
||||
cat << EOF >> epics-base/configure/CONFIG_SITE
|
||||
SHARED_LIBRARIES=NO
|
||||
STATIC_BUILD=YES
|
||||
EOF
|
||||
fi
|
||||
|
||||
case "$CMPLR" in
|
||||
clang)
|
||||
echo "Host compiler is clang"
|
||||
cat << EOF >> epics-base/configure/os/CONFIG_SITE.Common.$EPICS_HOST_ARCH
|
||||
GNU = NO
|
||||
CMPLR_CLASS = clang
|
||||
CC = clang
|
||||
CCC = clang++
|
||||
EOF
|
||||
|
||||
# hack
|
||||
sed -i -e 's/CMPLR_CLASS = gcc/CMPLR_CLASS = clang/' epics-base/configure/CONFIG.gnuCommon
|
||||
|
||||
clang --version
|
||||
;;
|
||||
*)
|
||||
echo "Host compiler is default"
|
||||
gcc --version
|
||||
;;
|
||||
esac
|
||||
|
||||
cat <<EOF >> epics-base/configure/CONFIG_SITE
|
||||
USR_CPPFLAGS += $USR_CPPFLAGS
|
||||
USR_CFLAGS += $USR_CFLAGS
|
||||
USR_CXXFLAGS += $USR_CXXFLAGS
|
||||
EOF
|
||||
|
||||
# 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
|
||||
|
||||
sed -i -e '/^RTEMS_VERSION/d' -e '/^RTEMS_BASE/d' epics-base/configure/os/CONFIG_SITE.Common.RTEMS
|
||||
cat << EOF >> epics-base/configure/os/CONFIG_SITE.Common.RTEMS
|
||||
RTEMS_VERSION=$RTEMS
|
||||
RTEMS_BASE=/home/travis/.cache/rtems${RTEMS}-i386
|
||||
EOF
|
||||
cat << EOF >> epics-base/configure/CONFIG_SITE
|
||||
CROSS_COMPILER_TARGET_ARCHS+=RTEMS-pc386
|
||||
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 -C epics-base $EXTRA
|
||||
@@ -0,0 +1,26 @@
|
||||
sudo: false
|
||||
dist: trusty
|
||||
language: c
|
||||
compiler:
|
||||
- gcc
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
- libreadline6-dev
|
||||
- libncurses5-dev
|
||||
- perl
|
||||
- clang
|
||||
- g++-mingw-w64-i686
|
||||
install:
|
||||
- ./.ci/travis-prepare.sh
|
||||
script:
|
||||
- ./.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
|
||||
@@ -0,0 +1,19 @@
|
||||
#*************************************************************************
|
||||
# Copyright (c) 2002 The University of Chicago, as Operator of Argonne
|
||||
# National Laboratory.
|
||||
# Copyright (c) 2002 The Regents of the University of California, as
|
||||
# Operator of Los Alamos National Laboratory.
|
||||
# EPICS BASE is distributed subject to a Software License Agreement found
|
||||
# in file LICENSE that is included with this distribution.
|
||||
#*************************************************************************
|
||||
|
||||
TOP = .
|
||||
include $(TOP)/configure/CONFIG
|
||||
|
||||
DIRS += configure src
|
||||
src_DEPEND_DIRS = configure
|
||||
|
||||
DIRS += test
|
||||
test_DEPEND_DIRS = src
|
||||
|
||||
include $(TOP)/configure/RULES_TOP
|
||||
@@ -0,0 +1,36 @@
|
||||
# CONFIG - Load build configuration data
|
||||
#
|
||||
# Do not make changes to this file!
|
||||
|
||||
# Allow user to override where the build rules come from
|
||||
RULES = $(EPICS_BASE)
|
||||
|
||||
# RELEASE files point to other application tops
|
||||
include $(TOP)/configure/RELEASE
|
||||
-include $(TOP)/configure/RELEASE.$(EPICS_HOST_ARCH).Common
|
||||
ifdef T_A
|
||||
-include $(TOP)/configure/RELEASE.Common.$(T_A)
|
||||
-include $(TOP)/configure/RELEASE.$(EPICS_HOST_ARCH).$(T_A)
|
||||
endif
|
||||
|
||||
BUILDING_DATABASE = DEFINED
|
||||
|
||||
CONFIG = $(RULES)/configure
|
||||
include $(CONFIG)/CONFIG
|
||||
|
||||
# Override the Base definition:
|
||||
INSTALL_LOCATION = $(TOP)
|
||||
|
||||
# Use new RSET definition
|
||||
BASE_CPPFLAGS += -DUSE_TYPED_RSET
|
||||
|
||||
# Shared library ABI version.
|
||||
SHRLIB_VERSION = 3.17.0
|
||||
|
||||
# CONFIG_SITE files contain other build configuration settings
|
||||
include $(TOP)/configure/CONFIG_SITE
|
||||
-include $(TOP)/configure/CONFIG_SITE.$(EPICS_HOST_ARCH).Common
|
||||
ifdef T_A
|
||||
-include $(TOP)/configure/CONFIG_SITE.Common.$(T_A)
|
||||
-include $(TOP)/configure/CONFIG_SITE.$(EPICS_HOST_ARCH).$(T_A)
|
||||
endif
|
||||
@@ -0,0 +1,26 @@
|
||||
#*************************************************************************
|
||||
# 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.
|
||||
#*************************************************************************
|
||||
|
||||
# Set EPICS_DATABASE if necessary
|
||||
ifndef EPICS_DATABASE
|
||||
EPICS_DATABASE = $(if $(BUILDING_DATABASE),$(INSTALL_LOCATION),$(EPICS_BASE))
|
||||
|
||||
# Paths to tools built here
|
||||
EPICS_DATABASE_HOST_BIN = $(EPICS_DATABASE)/bin/$(EPICS_HOST_ARCH)
|
||||
endif
|
||||
|
||||
# Set location of locally-built tools
|
||||
MAKEBPT = $(EPICS_DATABASE_HOST_BIN)/makeBpt$(HOSTEXE)
|
||||
DBEXPAND = $(PERL) $(EPICS_DATABASE_HOST_BIN)/dbdExpand.pl
|
||||
DBTORECORDTYPEH = $(PERL) $(EPICS_DATABASE_HOST_BIN)/dbdToRecordtypeH.pl
|
||||
DBTOMENUH = $(PERL) $(EPICS_DATABASE_HOST_BIN)/dbdToMenuH.pl
|
||||
DBDTOHTML = $(PERL) $(EPICS_DATABASE_HOST_BIN)/dbdToHtml.pl
|
||||
REGISTERRECORDDEVICEDRIVER = $(PERL) $(EPICS_DATABASE_HOST_BIN)/registerRecordDeviceDriver.pl
|
||||
MSI3_15 = $(EPICS_DATABASE_HOST_BIN)/msi$(HOSTEXE)
|
||||
|
||||
# Libraries needed to link a basic IOC
|
||||
EPICS_BASE_IOC_LIBS = dbRecStd dbCore ca Com
|
||||
@@ -0,0 +1,4 @@
|
||||
EPICS_DATABASE_MAJOR_VERSION = 3
|
||||
EPICS_DATABASE_MINOR_VERSION = 17
|
||||
EPICS_DATABASE_MAINTENANCE_VERSION = 1
|
||||
EPICS_DATABASE_DEVELOPMENT_FLAG = 1
|
||||
@@ -0,0 +1,42 @@
|
||||
# CONFIG_SITE
|
||||
|
||||
# Make any application-specific changes to the EPICS build
|
||||
# configuration variables in this file.
|
||||
#
|
||||
# Host/target specific settings can be specified in files named
|
||||
# CONFIG_SITE.$(EPICS_HOST_ARCH).Common
|
||||
# CONFIG_SITE.Common.$(T_A)
|
||||
# CONFIG_SITE.$(EPICS_HOST_ARCH).$(T_A)
|
||||
|
||||
# CHECK_RELEASE controls the consistency checking of the support
|
||||
# applications pointed to by the RELEASE* files.
|
||||
# Normally CHECK_RELEASE should be set to YES.
|
||||
# Set CHECK_RELEASE to NO to disable checking completely.
|
||||
# Set CHECK_RELEASE to WARN to perform consistency checking but
|
||||
# continue building even if conflicts are found.
|
||||
CHECK_RELEASE = YES
|
||||
|
||||
# Set this when you only want to compile this application
|
||||
# for a subset of the cross-compiled target architectures
|
||||
# that Base is built for.
|
||||
#CROSS_COMPILER_TARGET_ARCHS = vxWorks-ppc32
|
||||
|
||||
# To install files into a location other than $(TOP) define
|
||||
# INSTALL_LOCATION here.
|
||||
#INSTALL_LOCATION=</absolute/path/to/install/top>
|
||||
|
||||
# Set this when the IOC and build host use different paths
|
||||
# to the install location. This may be needed to boot from
|
||||
# a Microsoft FTP server say, or on some NFS configurations.
|
||||
#IOCS_APPL_TOP = </IOC's/absolute/path/to/install/top>
|
||||
|
||||
# For application debugging purposes, override the HOST_OPT and/
|
||||
# or CROSS_OPT settings from base/configure/CONFIG_SITE
|
||||
#HOST_OPT = NO
|
||||
#CROSS_OPT = NO
|
||||
|
||||
# These allow developers to override the CONFIG_SITE variable
|
||||
# settings without having to modify the configure/CONFIG_SITE
|
||||
# file itself.
|
||||
-include $(TOP)/../CONFIG_SITE.local
|
||||
-include $(TOP)/configure/CONFIG_SITE.local
|
||||
@@ -0,0 +1,15 @@
|
||||
#*************************************************************************
|
||||
# EPICS BASE is distributed subject to a Software License Agreement found
|
||||
# in file LICENSE that is included with this distribution.
|
||||
#*************************************************************************
|
||||
TOP = ..
|
||||
|
||||
include $(TOP)/configure/CONFIG
|
||||
|
||||
TARGETS = $(CONFIG_TARGETS)
|
||||
CONFIGS += $(subst ../,,$(wildcard $(CONFIG_INSTALLS)))
|
||||
|
||||
CFG += CONFIG_DATABASE_MODULE
|
||||
CFG += CONFIG_DATABASE_VERSION
|
||||
|
||||
include $(TOP)/configure/RULES
|
||||
@@ -0,0 +1,40 @@
|
||||
# RELEASE - Location of external support modules
|
||||
#
|
||||
# IF YOU CHANGE ANY PATHS in this file or make API changes to
|
||||
# any modules it refers to, you should do a "make rebuild" in
|
||||
# this application's top level directory.
|
||||
#
|
||||
# The EPICS build process does not check dependencies against
|
||||
# any files from outside the application, so it is safest to
|
||||
# rebuild it completely if any modules it depends on change.
|
||||
#
|
||||
# Host- or target-specific settings can be given in files named
|
||||
# RELEASE.$(EPICS_HOST_ARCH).Common
|
||||
# RELEASE.Common.$(T_A)
|
||||
# RELEASE.$(EPICS_HOST_ARCH).$(T_A)
|
||||
#
|
||||
# This file is parsed by both GNUmake and an EPICS Perl script,
|
||||
# so it may ONLY contain definititions of paths to other support
|
||||
# modules, variable definitions that are used in module paths,
|
||||
# and include statements that pull in other RELEASE files.
|
||||
# Variables may be used before their values have been set.
|
||||
# Build variables that are NOT used in paths should be set in
|
||||
# the CONFIG_SITE file.
|
||||
|
||||
# Variables and paths to dependent modules:
|
||||
#MODULES = /path/to/modules
|
||||
#MYMODULE = $(MODULES)/my-module
|
||||
|
||||
# If building the EPICS modules individually, set these:
|
||||
#EPICS_CA = $(MODULES)/ca-4.13.1
|
||||
#EPICS_LIBCOM = $(MODULES)/libcom-3.17.0
|
||||
#EPICS_BASE = $(MODULES)/core-7.0.1
|
||||
|
||||
# Set RULES here if you want to use build rules from elsewhere:
|
||||
#RULES = $(MODULES)/build-rules
|
||||
|
||||
# These lines allow developers to override these RELEASE settings
|
||||
# without having to modify this file directly.
|
||||
-include $(TOP)/../RELEASE.local
|
||||
-include $(TOP)/../RELEASE.$(EPICS_HOST_ARCH).local
|
||||
-include $(TOP)/configure/RELEASE.local
|
||||
@@ -0,0 +1,6 @@
|
||||
# RULES
|
||||
|
||||
include $(CONFIG)/RULES
|
||||
|
||||
# Library should be rebuilt because LIBOBJS may have changed.
|
||||
$(LIBNAME): ../Makefile
|
||||
@@ -0,0 +1,2 @@
|
||||
#RULES.ioc
|
||||
include $(CONFIG)/RULES.ioc
|
||||
@@ -0,0 +1,2 @@
|
||||
#RULES_DIRS
|
||||
include $(CONFIG)/RULES_DIRS
|
||||
@@ -0,0 +1,2 @@
|
||||
#RULES_TOP
|
||||
include $(CONFIG)/RULES_TOP
|
||||
@@ -0,0 +1,31 @@
|
||||
#*************************************************************************
|
||||
# Copyright (c) 2008 UChicago Argonne LLC, as Operator of Argonne
|
||||
# National Laboratory.
|
||||
# Copyright (c) 2002 The Regents of the University of California, as
|
||||
# Operator of Los Alamos National Laboratory.
|
||||
# EPICS BASE is distributed subject to a Software License Agreement found
|
||||
# in the file LICENSE that is included with this distribution.
|
||||
#*************************************************************************
|
||||
|
||||
TOP = ..
|
||||
include $(TOP)/configure/CONFIG
|
||||
|
||||
# PDB Tools
|
||||
|
||||
DIRS += tools
|
||||
|
||||
# PDB Core
|
||||
|
||||
DIRS += ioc
|
||||
ioc_DEPEND_DIRS = tools
|
||||
|
||||
# PDB Standard Record Definitions
|
||||
|
||||
DIRS += std
|
||||
std_DEPEND_DIRS = ioc
|
||||
|
||||
# Templates
|
||||
|
||||
DIRS += template
|
||||
|
||||
include $(TOP)/configure/RULES_DIRS
|
||||
@@ -0,0 +1,60 @@
|
||||
#*************************************************************************
|
||||
# Copyright (c) 2007 UChicago Argonne LLC, as Operator of Argonne
|
||||
# National Laboratory.
|
||||
# Copyright (c) 2002 The Regents of the University of California, as
|
||||
# Operator of Los Alamos National Laboratory.
|
||||
# EPICS BASE is distributed subject to a Software License Agreement found
|
||||
# in file LICENSE that is included with this distribution.
|
||||
#*************************************************************************
|
||||
TOP=../..
|
||||
|
||||
include $(TOP)/configure/CONFIG
|
||||
|
||||
SRC = $(TOP)/src
|
||||
IOCDIR = $(SRC)/ioc
|
||||
|
||||
LIBRARY_IOC += dbCore
|
||||
dbCore_LIBS += ca Com
|
||||
dbCore_SYS_LIBS_WIN32 += ws2_32
|
||||
|
||||
dbCore_RCS += dbCore.rc
|
||||
dbStaticHost_RCS = dbStaticHost.rc
|
||||
|
||||
INC += databaseVersion.h
|
||||
INC += databaseVersionNum.h
|
||||
|
||||
PROD_LIBS = Com
|
||||
|
||||
EPICS_DATABASE_MAJOR_VERSION = 3
|
||||
EPICS_DATABASE_MINOR_VERSION = 17
|
||||
EPICS_DATABASE_MAINTENANCE_VERSION = 0
|
||||
EPICS_DATABASE_DEVELOPMENT_FLAG = 1
|
||||
|
||||
include $(IOCDIR)/as/Makefile
|
||||
include $(IOCDIR)/bpt/Makefile
|
||||
include $(IOCDIR)/db/Makefile
|
||||
include $(IOCDIR)/dbStatic/Makefile
|
||||
include $(IOCDIR)/dbtemplate/Makefile
|
||||
include $(IOCDIR)/misc/Makefile
|
||||
include $(IOCDIR)/registry/Makefile
|
||||
include $(IOCDIR)/rsrv/Makefile
|
||||
|
||||
EXPANDVARS += EPICS_DATABASE_MAJOR_VERSION
|
||||
EXPANDVARS += EPICS_DATABASE_MINOR_VERSION
|
||||
EXPANDVARS += EPICS_DATABASE_MAINTENANCE_VERSION
|
||||
EXPANDVARS += EPICS_DATABASE_DEVELOPMENT_FLAG
|
||||
|
||||
EXPANDFLAGS += $(foreach var,$(EXPANDVARS),-D$(var)="$(strip $($(var)))")
|
||||
|
||||
include $(TOP)/configure/RULES
|
||||
|
||||
include $(IOCDIR)/dbStatic/RULES
|
||||
include $(IOCDIR)/bpt/RULES
|
||||
include $(IOCDIR)/db/RULES
|
||||
include $(IOCDIR)/dbtemplate/RULES
|
||||
|
||||
# Can't use EXPAND as generated headers must appear
|
||||
# in O.Common, but EXPAND emits rules for O.$(T_A)
|
||||
../O.Common/databaseVersionNum.h: ../databaseVersionNum.h@
|
||||
$(MKDIR) $(COMMON_DIR)
|
||||
$(EXPAND_TOOL) $(EXPANDFLAGS) $($@_EXPANDFLAGS) $< $@
|
||||
@@ -0,0 +1,26 @@
|
||||
#*************************************************************************
|
||||
# Copyright (c) 2011 UChicago Argonne LLC, as Operator of Argonne
|
||||
# National Laboratory.
|
||||
# Copyright (c) 2010 Brookhaven Science Associates, as Operator of
|
||||
# Brookhaven National Lab.
|
||||
# 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.
|
||||
#*************************************************************************
|
||||
|
||||
# This is a Makefile fragment, see src/ioc/Makefile.
|
||||
|
||||
SRC_DIRS += $(IOCDIR)/as
|
||||
|
||||
INC += asDbLib.h
|
||||
INC += asCa.h
|
||||
INC += asIocRegister.h
|
||||
|
||||
dbCore_SRCS += asDbLib.c
|
||||
dbCore_SRCS += asCa.c
|
||||
dbCore_SRCS += asIocRegister.c
|
||||
|
||||
PROD_HOST += ascheck
|
||||
ascheck_SRCS = ascheck.c
|
||||
ascheck_LIBS = dbCore ca Com
|
||||
@@ -0,0 +1,335 @@
|
||||
/*asCa.c*/
|
||||
/*************************************************************************\
|
||||
* Copyright (c) 2008 UChicago Argonne LLC, as Operator of Argonne
|
||||
* National Laboratory.
|
||||
* Copyright (c) 2002 The Regents of the University of California, as
|
||||
* Operator of Los Alamos National Laboratory.
|
||||
* EPICS BASE is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
\*************************************************************************/
|
||||
/* Author: Marty Kraimer Date: 10-15-93 */
|
||||
|
||||
/*This module is separate from asDbLib because CA uses old database access*/
|
||||
#include <stdlib.h>
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "alarm.h"
|
||||
#include "asLib.h"
|
||||
#include "cantProceed.h"
|
||||
#include "db_access.h"
|
||||
#include "dbDefs.h"
|
||||
#include "ellLib.h"
|
||||
#include "epicsEvent.h"
|
||||
#include "epicsMutex.h"
|
||||
#include "epicsStdio.h"
|
||||
#include "epicsThread.h"
|
||||
#include "errlog.h"
|
||||
#include "taskwd.h"
|
||||
|
||||
#include "cadef.h"
|
||||
#include "caerr.h"
|
||||
#include "caeventmask.h"
|
||||
|
||||
#define epicsExportSharedSymbols
|
||||
#include "asCa.h"
|
||||
#include "asDbLib.h"
|
||||
#include "callback.h"
|
||||
#include "epicsExport.h"
|
||||
|
||||
int asCaDebug = 0;
|
||||
epicsExportAddress(int,asCaDebug);
|
||||
static int firstTime = TRUE;
|
||||
static epicsThreadId threadid=0;
|
||||
static int caInitializing=FALSE;
|
||||
static epicsMutexId asCaTaskLock; /*lock access to task */
|
||||
static epicsEventId asCaTaskWait; /*Wait for task to respond*/
|
||||
static epicsEventId asCaTaskAddChannels; /*Tell asCaTask to add channels*/
|
||||
static epicsEventId asCaTaskClearChannels;/*Tell asCaTask to clear channels*/
|
||||
|
||||
typedef struct {
|
||||
struct dbr_sts_double rtndata;
|
||||
chid chid;
|
||||
} CAPVT;
|
||||
|
||||
static void exceptionCallback(struct exception_handler_args args)
|
||||
{
|
||||
chid chid = args.chid;
|
||||
long stat = args.stat; /* Channel access status code*/
|
||||
const char *channel;
|
||||
const char *context;
|
||||
static char *unknown = "unknown";
|
||||
const char *nativeType;
|
||||
const char *requestType;
|
||||
long nativeCount;
|
||||
long requestCount;
|
||||
int readAccess;
|
||||
int writeAccess;
|
||||
|
||||
channel = (chid ? ca_name(chid) : unknown);
|
||||
context = (args.ctx ? args.ctx : unknown);
|
||||
nativeType = dbr_type_to_text((chid ? ca_field_type(chid) : -1));
|
||||
requestType = dbr_type_to_text(args.type);
|
||||
nativeCount = (chid ? ca_element_count(chid) : 0);
|
||||
requestCount = args.count;
|
||||
readAccess = (chid ? ca_read_access(chid) : 0);
|
||||
writeAccess = (chid ? ca_write_access(chid) : 0);
|
||||
|
||||
errlogPrintf("dbCa:exceptionCallback stat \"%s\" channel \"%s\""
|
||||
" context \"%s\"\n"
|
||||
" nativeType %s requestType %s"
|
||||
" nativeCount %ld requestCount %ld %s %s\n",
|
||||
ca_message(stat),channel,context,
|
||||
nativeType,requestType,
|
||||
nativeCount,requestCount,
|
||||
(readAccess ? "readAccess" : "noReadAccess"),
|
||||
(writeAccess ? "writeAccess" : "noWriteAccess"));
|
||||
}
|
||||
|
||||
/*connectCallback only handles disconnects*/
|
||||
static void connectCallback(struct connection_handler_args arg)
|
||||
{
|
||||
chid chid = arg.chid;
|
||||
ASGINP *pasginp = (ASGINP *)ca_puser(chid);
|
||||
ASG *pasg = pasginp->pasg;
|
||||
|
||||
if(ca_state(chid)!=cs_conn) {
|
||||
if(!(pasg->inpBad & (1<<pasginp->inpIndex))) {
|
||||
/*was good so lets make it bad*/
|
||||
pasg->inpBad |= (1<<pasginp->inpIndex);
|
||||
if(!caInitializing) asComputeAsg(pasg);
|
||||
if(asCaDebug) printf("as connectCallback disconnect %s\n",
|
||||
ca_name(chid));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void eventCallback(struct event_handler_args arg)
|
||||
{
|
||||
int caStatus = arg.status;
|
||||
chid chid = arg.chid;
|
||||
ASGINP *pasginp = (ASGINP *)arg.usr;
|
||||
ASG *pasg;
|
||||
CAPVT *pcapvt;
|
||||
const struct dbr_sts_double *pdata;
|
||||
|
||||
if(caStatus!=ECA_NORMAL) {
|
||||
if(chid) {
|
||||
epicsPrintf("asCa: eventCallback error %s channel %s\n",
|
||||
ca_message(caStatus),ca_name(chid));
|
||||
} else {
|
||||
epicsPrintf("asCa: eventCallback error %s chid is null\n",
|
||||
ca_message(caStatus));
|
||||
}
|
||||
return;
|
||||
}
|
||||
pasg = pasginp->pasg;
|
||||
pcapvt = (CAPVT *)pasginp->capvt;
|
||||
if(chid!=pcapvt->chid) {
|
||||
epicsPrintf("asCa: eventCallback error pcapvt->chid != arg.chid\n");
|
||||
return;
|
||||
}
|
||||
if(ca_state(chid)!=cs_conn || !ca_read_access(chid)) {
|
||||
if(!(pasg->inpBad & (1<<pasginp->inpIndex))) {
|
||||
/*was good so lets make it bad*/
|
||||
pasg->inpBad |= (1<<pasginp->inpIndex);
|
||||
if(!caInitializing) asComputeAsg(pasg);
|
||||
if(asCaDebug) {
|
||||
printf("as eventCallback %s inpBad ca_state %d"
|
||||
" ca_read_access %d\n",
|
||||
ca_name(chid),ca_state(chid),ca_read_access(chid));
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
pdata = arg.dbr;
|
||||
pcapvt->rtndata = *pdata; /*structure copy*/
|
||||
if(pdata->severity==INVALID_ALARM) {
|
||||
pasg->inpBad |= (1<<pasginp->inpIndex);
|
||||
if(asCaDebug)
|
||||
printf("as eventCallback %s inpBad because INVALID_ALARM\n",
|
||||
ca_name(chid));
|
||||
} else {
|
||||
pasg->inpBad &= ~((1<<pasginp->inpIndex));
|
||||
pasg->pavalue[pasginp->inpIndex] = pdata->value;
|
||||
if(asCaDebug)
|
||||
printf("as eventCallback %s inpGood data %f\n",
|
||||
ca_name(chid),pdata->value);
|
||||
}
|
||||
pasg->inpChanged |= (1<<pasginp->inpIndex);
|
||||
if(!caInitializing) asComputeAsg(pasg);
|
||||
}
|
||||
|
||||
static void asCaTask(void)
|
||||
{
|
||||
ASG *pasg;
|
||||
ASGINP *pasginp;
|
||||
CAPVT *pcapvt;
|
||||
int status;
|
||||
|
||||
taskwdInsert(epicsThreadGetIdSelf(),NULL,NULL);
|
||||
SEVCHK(ca_context_create(ca_enable_preemptive_callback),
|
||||
"asCaTask calling ca_context_create");
|
||||
SEVCHK(ca_add_exception_event(exceptionCallback,NULL),
|
||||
"ca_add_exception_event");
|
||||
while(TRUE) {
|
||||
epicsEventMustWait(asCaTaskAddChannels);
|
||||
caInitializing = TRUE;
|
||||
pasg = (ASG *)ellFirst(&pasbase->asgList);
|
||||
while(pasg) {
|
||||
pasginp = (ASGINP *)ellFirst(&pasg->inpList);
|
||||
while(pasginp) {
|
||||
pasg->inpBad |= (1<<pasginp->inpIndex);
|
||||
pcapvt = pasginp->capvt = asCalloc(1,sizeof(CAPVT));
|
||||
/*Note calls connectCallback immediately for local Pvs*/
|
||||
status = ca_search_and_connect(pasginp->inp,&pcapvt->chid,
|
||||
connectCallback,pasginp);
|
||||
if(status!=ECA_NORMAL) {
|
||||
epicsPrintf("asCa ca_search_and_connect error %s\n",
|
||||
ca_message(status));
|
||||
}
|
||||
/*Note calls eventCallback immediately for local Pvs*/
|
||||
status = ca_add_event(DBR_STS_DOUBLE,pcapvt->chid,
|
||||
eventCallback,pasginp,0);
|
||||
if(status!=ECA_NORMAL) {
|
||||
epicsPrintf("asCa ca_add_event error %s\n",
|
||||
ca_message(status));
|
||||
}
|
||||
pasginp = (ASGINP *)ellNext((ELLNODE *)pasginp);
|
||||
}
|
||||
pasg = (ASG *)ellNext((ELLNODE *)pasg);
|
||||
}
|
||||
SEVCHK(ca_flush_io(),"asCaTask");
|
||||
caInitializing = FALSE;
|
||||
asComputeAllAsg();
|
||||
if(asCaDebug) printf("asCaTask initialized\n");
|
||||
epicsEventSignal(asCaTaskWait);
|
||||
epicsEventMustWait(asCaTaskClearChannels);
|
||||
pasg = (ASG *)ellFirst(&pasbase->asgList);
|
||||
while(pasg) {
|
||||
pasginp = (ASGINP *)ellFirst(&pasg->inpList);
|
||||
while(pasginp) {
|
||||
pcapvt = (CAPVT *)pasginp->capvt;
|
||||
status = ca_clear_channel(pcapvt->chid);
|
||||
if(status!=ECA_NORMAL) {
|
||||
epicsPrintf("asCa ca_clear_channel error %s\n",
|
||||
ca_message(status));
|
||||
}
|
||||
free(pasginp->capvt);
|
||||
pasginp->capvt = 0;
|
||||
pasginp = (ASGINP *)ellNext((ELLNODE *)pasginp);
|
||||
}
|
||||
pasg = (ASG *)ellNext((ELLNODE *)pasg);
|
||||
}
|
||||
if(asCaDebug) printf("asCaTask has cleared all channels\n");
|
||||
epicsEventSignal(asCaTaskWait);
|
||||
}
|
||||
}
|
||||
|
||||
void asCaStart(void)
|
||||
{
|
||||
if(asCaDebug) printf("asCaStart called\n");
|
||||
if(firstTime) {
|
||||
firstTime = FALSE;
|
||||
asCaTaskLock=epicsMutexMustCreate();
|
||||
asCaTaskWait=epicsEventMustCreate(epicsEventEmpty);
|
||||
asCaTaskAddChannels=epicsEventMustCreate(epicsEventEmpty);
|
||||
asCaTaskClearChannels=epicsEventMustCreate(epicsEventEmpty);
|
||||
threadid = epicsThreadCreate("asCaTask",
|
||||
(epicsThreadPriorityScanLow - 3),
|
||||
epicsThreadGetStackSize(epicsThreadStackBig),
|
||||
(EPICSTHREADFUNC)asCaTask,0);
|
||||
if(threadid==0) {
|
||||
errMessage(0,"asCaStart: taskSpawn Failure\n");
|
||||
}
|
||||
}
|
||||
epicsMutexMustLock(asCaTaskLock);
|
||||
epicsEventSignal(asCaTaskAddChannels);
|
||||
epicsEventMustWait(asCaTaskWait);
|
||||
if(asCaDebug) printf("asCaStart done\n");
|
||||
epicsMutexUnlock(asCaTaskLock);
|
||||
}
|
||||
|
||||
void asCaStop(void)
|
||||
{
|
||||
if(threadid==0) return;
|
||||
if(asCaDebug) printf("asCaStop called\n");
|
||||
epicsMutexMustLock(asCaTaskLock);
|
||||
epicsEventSignal(asCaTaskClearChannels);
|
||||
epicsEventMustWait(asCaTaskWait);
|
||||
if(asCaDebug) printf("asCaStop done\n");
|
||||
epicsMutexUnlock(asCaTaskLock);
|
||||
}
|
||||
|
||||
int ascar(int level) { return ascarFP(stdout,level);}
|
||||
|
||||
int ascarFP(FILE *fp,int level)
|
||||
{
|
||||
ASG *pasg;
|
||||
int n=0,nbad=0;
|
||||
enum channel_state state;
|
||||
|
||||
if(!pasbase) {
|
||||
fprintf(fp,"access security not started\n");
|
||||
return(0);
|
||||
}
|
||||
pasg = (ASG *)ellFirst(&pasbase->asgList);
|
||||
while(pasg) {
|
||||
ASGINP *pasginp;
|
||||
pasginp = (ASGINP *)ellFirst(&pasg->inpList);
|
||||
while(pasginp) {
|
||||
CAPVT *pcapvt = (CAPVT *)pasginp->capvt;
|
||||
chid chid = pcapvt->chid;
|
||||
pcapvt = pasginp->capvt;
|
||||
++n;
|
||||
state = ca_state(chid);
|
||||
if(state!=cs_conn) ++nbad;
|
||||
if(level>1 || (level==1 && state!=cs_conn)) {
|
||||
fprintf(fp,"connected:");
|
||||
if(state==cs_never_conn) fprintf(fp,"never ");
|
||||
else if(state==cs_prev_conn) fprintf(fp,"prev ");
|
||||
else if(state==cs_conn) fprintf(fp,"yes ");
|
||||
else if(state==cs_closed) fprintf(fp,"closed");
|
||||
else fprintf(fp,"unknown");
|
||||
fprintf(fp," read:%s write:%s",
|
||||
(ca_read_access(chid) ? "yes" : "no "),
|
||||
(ca_write_access(chid) ? "yes" : "no "));
|
||||
fprintf(fp," %s %s\n", ca_name(chid),ca_host_name(chid));
|
||||
}
|
||||
pasginp = (ASGINP *)ellNext((ELLNODE *)pasginp);
|
||||
}
|
||||
pasg = (ASG *)ellNext((ELLNODE *)pasg);
|
||||
}
|
||||
fprintf(fp,"%d channels %d not connected\n",n,nbad);
|
||||
return(0);
|
||||
}
|
||||
|
||||
void ascaStats(int *pchans, int *pdiscon)
|
||||
{
|
||||
ASG *pasg;
|
||||
int n = 0;
|
||||
int nbad = 0;
|
||||
|
||||
if(!pasbase) {
|
||||
if (pchans) *pchans = n;
|
||||
if (pdiscon) *pdiscon = nbad;
|
||||
return;
|
||||
}
|
||||
pasg = (ASG *)ellFirst(&pasbase->asgList);
|
||||
while (pasg) {
|
||||
ASGINP *pasginp;
|
||||
pasginp = (ASGINP *)ellFirst(&pasg->inpList);
|
||||
while (pasginp) {
|
||||
CAPVT *pcapvt = (CAPVT *)pasginp->capvt;
|
||||
chid chid = pcapvt->chid;
|
||||
++n;
|
||||
if (ca_state(chid) != cs_conn) ++nbad;
|
||||
pasginp = (ASGINP *)ellNext((ELLNODE *)pasginp);
|
||||
}
|
||||
pasg = (ASG *)ellNext((ELLNODE *)pasg);
|
||||
}
|
||||
if (pchans) *pchans = n;
|
||||
if (pdiscon) *pdiscon = nbad;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
/*************************************************************************\
|
||||
* Copyright (c) 2008 UChicago Argonne LLC, as Operator of Argonne
|
||||
* National Laboratory.
|
||||
* Copyright (c) 2002 The Regents of the University of California, as
|
||||
* Operator of Los Alamos National Laboratory.
|
||||
* EPICS BASE is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
\*************************************************************************/
|
||||
/* asCa.h */
|
||||
|
||||
#ifndef INCasCah
|
||||
#define INCasCah
|
||||
|
||||
#include "shareLib.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
epicsShareFunc void asCaStart(void);
|
||||
epicsShareFunc void asCaStop(void);
|
||||
epicsShareFunc int ascar(int level);
|
||||
epicsShareFunc int ascarFP(FILE *fp, int level);
|
||||
epicsShareFunc void ascaStats(int *pchans, int *pdiscon);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /*INCasCah*/
|
||||
@@ -0,0 +1,336 @@
|
||||
/*************************************************************************\
|
||||
* Copyright (c) 2012 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.
|
||||
\*************************************************************************/
|
||||
/* Author: Marty Kraimer Date: 02-11-94*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "alarm.h"
|
||||
#include "asLib.h"
|
||||
#include "cantProceed.h"
|
||||
#include "dbDefs.h"
|
||||
#include "epicsStdio.h"
|
||||
#include "epicsThread.h"
|
||||
#include "errlog.h"
|
||||
#include "taskwd.h"
|
||||
|
||||
#include "caeventmask.h"
|
||||
|
||||
#define epicsExportSharedSymbols
|
||||
#include "asCa.h"
|
||||
#include "asDbLib.h"
|
||||
#include "callback.h"
|
||||
#include "dbAccess.h"
|
||||
#include "dbChannel.h"
|
||||
#include "dbCommon.h"
|
||||
#include "dbEvent.h"
|
||||
#include "db_field_log.h"
|
||||
#include "dbStaticLib.h"
|
||||
#include "recSup.h"
|
||||
|
||||
static char *pacf=NULL;
|
||||
static char *psubstitutions=NULL;
|
||||
static epicsThreadId asInitTheadId=0;
|
||||
static int firstTime = TRUE;
|
||||
|
||||
static long asDbAddRecords(void)
|
||||
{
|
||||
DBENTRY dbentry;
|
||||
DBENTRY *pdbentry=&dbentry;
|
||||
long status;
|
||||
dbCommon *precord;
|
||||
|
||||
dbInitEntry(pdbbase,pdbentry);
|
||||
status = dbFirstRecordType(pdbentry);
|
||||
while(!status) {
|
||||
status = dbFirstRecord(pdbentry);
|
||||
while(!status) {
|
||||
precord = pdbentry->precnode->precord;
|
||||
if(!precord->asp) {
|
||||
status = asAddMember(&precord->asp, precord->asg);
|
||||
if(status) errMessage(status,"asDbAddRecords:asAddMember");
|
||||
asPutMemberPvt(precord->asp,precord);
|
||||
}
|
||||
status = dbNextRecord(pdbentry);
|
||||
}
|
||||
status = dbNextRecordType(pdbentry);
|
||||
}
|
||||
dbFinishEntry(pdbentry);
|
||||
return(0);
|
||||
}
|
||||
|
||||
int asSetFilename(const char *acf)
|
||||
{
|
||||
if (pacf)
|
||||
free (pacf);
|
||||
if (acf) {
|
||||
pacf = calloc(1, strlen(acf)+1);
|
||||
if (!pacf) {
|
||||
errMessage(0, "asSetFilename calloc failure");
|
||||
} else {
|
||||
strcpy(pacf, acf);
|
||||
if (*pacf != '/' && !strchr(pacf, ':')) {
|
||||
printf("asSetFilename: Warning - relative paths won't usually "
|
||||
"work\n");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
pacf = NULL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int asSetSubstitutions(const char *substitutions)
|
||||
{
|
||||
if(psubstitutions) free ((void *)psubstitutions);
|
||||
if(substitutions) {
|
||||
psubstitutions = calloc(1,strlen(substitutions)+1);
|
||||
if(!psubstitutions) {
|
||||
errMessage(0,"asSetSubstitutions calloc failure");
|
||||
} else {
|
||||
strcpy(psubstitutions,substitutions);
|
||||
}
|
||||
} else {
|
||||
psubstitutions = NULL;
|
||||
}
|
||||
return(0);
|
||||
}
|
||||
|
||||
static void asSpcAsCallback(struct dbCommon *precord)
|
||||
{
|
||||
asChangeGroup(&precord->asp, precord->asg);
|
||||
}
|
||||
|
||||
static void asInitCommonOnce(void *arg)
|
||||
{
|
||||
int *firstTime = (int *)arg;
|
||||
*firstTime = FALSE;
|
||||
}
|
||||
|
||||
static long asInitCommon(void)
|
||||
{
|
||||
long status;
|
||||
int asWasActive = asActive;
|
||||
int wasFirstTime = firstTime;
|
||||
static epicsThreadOnceId asInitCommonOnceFlag = EPICS_THREAD_ONCE_INIT;
|
||||
|
||||
|
||||
epicsThreadOnce(&asInitCommonOnceFlag,asInitCommonOnce,(void *)&firstTime);
|
||||
if(wasFirstTime) {
|
||||
if(!pacf) return(0); /*access security will NEVER be turned on*/
|
||||
} else {
|
||||
if(!asActive) {
|
||||
printf("Access security is NOT enabled."
|
||||
" Was asSetFilename specified before iocInit?\n");
|
||||
return(S_asLib_asNotActive);
|
||||
}
|
||||
if(pacf) {
|
||||
asCaStop();
|
||||
} else { /*Just leave everything as is */
|
||||
return(S_asLib_badConfig);
|
||||
}
|
||||
}
|
||||
status = asInitFile(pacf,psubstitutions);
|
||||
if(asActive) {
|
||||
if(!asWasActive) {
|
||||
dbSpcAsRegisterCallback(asSpcAsCallback);
|
||||
asDbAddRecords();
|
||||
}
|
||||
asCaStart();
|
||||
}
|
||||
return(status);
|
||||
}
|
||||
|
||||
int asInit(void)
|
||||
{
|
||||
return(asInitCommon());
|
||||
}
|
||||
|
||||
int asShutdown(void) {
|
||||
volatile ASBASE *pbase = pasbase;
|
||||
pasbase = NULL;
|
||||
firstTime = TRUE;
|
||||
if(pbase)
|
||||
asFreeAll((ASBASE*)pbase);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void wdCallback(void *arg)
|
||||
{
|
||||
ASDBCALLBACK *pcallback = (ASDBCALLBACK *)arg;
|
||||
pcallback->status = S_asLib_InitFailed;
|
||||
callbackRequest(&pcallback->callback);
|
||||
}
|
||||
|
||||
static void asInitTask(ASDBCALLBACK *pcallback)
|
||||
{
|
||||
long status;
|
||||
|
||||
taskwdInsert(epicsThreadGetIdSelf(), wdCallback, (void *)pcallback);
|
||||
status = asInitCommon();
|
||||
taskwdRemove(epicsThreadGetIdSelf());
|
||||
asInitTheadId = 0;
|
||||
if(pcallback) {
|
||||
pcallback->status = status;
|
||||
callbackRequest(&pcallback->callback);
|
||||
}
|
||||
}
|
||||
|
||||
int asInitAsyn(ASDBCALLBACK *pcallback)
|
||||
{
|
||||
if(!pacf) return(0);
|
||||
if(asInitTheadId) {
|
||||
errMessage(-1,"asInit: asInitTask already active");
|
||||
if(pcallback) {
|
||||
pcallback->status = S_asLib_InitFailed;
|
||||
callbackRequest(&pcallback->callback);
|
||||
}
|
||||
return(-1);
|
||||
}
|
||||
asInitTheadId = epicsThreadCreate("asInitTask",
|
||||
(epicsThreadPriorityCAServerHigh + 1),
|
||||
epicsThreadGetStackSize(epicsThreadStackBig),
|
||||
(EPICSTHREADFUNC)asInitTask,(void *)pcallback);
|
||||
if(asInitTheadId==0) {
|
||||
errMessage(0,"asInit: epicsThreadCreate Error");
|
||||
if(pcallback) {
|
||||
pcallback->status = S_asLib_InitFailed;
|
||||
callbackRequest(&pcallback->callback);
|
||||
}
|
||||
asInitTheadId = 0;
|
||||
}
|
||||
return(0);
|
||||
}
|
||||
|
||||
int asDbGetAsl(struct dbChannel *chan)
|
||||
{
|
||||
return dbChannelFldDes(chan)->as_level;
|
||||
}
|
||||
|
||||
void * asDbGetMemberPvt(struct dbChannel *chan)
|
||||
{
|
||||
return dbChannelRecord(chan)->asp;
|
||||
}
|
||||
|
||||
static void astacCallback(ASCLIENTPVT clientPvt,asClientStatus status)
|
||||
{
|
||||
char *recordname;
|
||||
|
||||
recordname = (char *)asGetClientPvt(clientPvt);
|
||||
printf("astac callback %s: status=%d",recordname,status);
|
||||
printf(" get %s put %s\n",(asCheckGet(clientPvt) ? "Yes" : "No"),
|
||||
(asCheckPut(clientPvt) ? "Yes" : "No"));
|
||||
}
|
||||
|
||||
int astac(const char *pname,const char *user,const char *location)
|
||||
{
|
||||
DBADDR *paddr;
|
||||
long status;
|
||||
ASCLIENTPVT *pasclientpvt=NULL;
|
||||
dbCommon *precord;
|
||||
dbFldDes *pflddes;
|
||||
char *puser;
|
||||
char *plocation;
|
||||
|
||||
paddr = dbCalloc(1,sizeof(DBADDR) + sizeof(ASCLIENTPVT));
|
||||
pasclientpvt = (ASCLIENTPVT *)(paddr + 1);
|
||||
status=dbNameToAddr(pname,paddr);
|
||||
if(status) {
|
||||
errMessage(status,"dbNameToAddr error");
|
||||
return(1);
|
||||
}
|
||||
precord = paddr->precord;
|
||||
pflddes = paddr->pfldDes;
|
||||
puser = asCalloc(1,strlen(user)+1);
|
||||
strcpy(puser,user);
|
||||
plocation = asCalloc(1,strlen(location)+1);
|
||||
strcpy(plocation,location);
|
||||
|
||||
status = asAddClient(pasclientpvt,precord->asp,
|
||||
(int)pflddes->as_level,puser,plocation);
|
||||
if(status) {
|
||||
errMessage(status,"asAddClient error");
|
||||
return(1);
|
||||
} else {
|
||||
asPutClientPvt(*pasclientpvt,(void *)precord->name);
|
||||
asRegisterClientCallback(*pasclientpvt,astacCallback);
|
||||
}
|
||||
return(0);
|
||||
}
|
||||
|
||||
static void myMemberCallback(ASMEMBERPVT memPvt,FILE *fp)
|
||||
{
|
||||
dbCommon *precord;
|
||||
|
||||
precord = asGetMemberPvt(memPvt);
|
||||
if(precord) fprintf(fp," Record:%s",precord->name);
|
||||
}
|
||||
|
||||
int asdbdump(void)
|
||||
{
|
||||
asDumpFP(stdout,myMemberCallback,NULL,1);
|
||||
return(0);
|
||||
}
|
||||
|
||||
int asdbdumpFP(FILE *fp)
|
||||
{
|
||||
asDumpFP(fp,myMemberCallback,NULL,1);
|
||||
return(0);
|
||||
}
|
||||
|
||||
int aspuag(const char *uagname)
|
||||
{
|
||||
asDumpUagFP(stdout,uagname);
|
||||
return(0);
|
||||
}
|
||||
|
||||
int aspuagFP(FILE *fp,const char *uagname)
|
||||
{
|
||||
|
||||
asDumpUagFP(fp,uagname);
|
||||
return(0);
|
||||
}
|
||||
|
||||
int asphag(const char *hagname)
|
||||
{
|
||||
asDumpHagFP(stdout,hagname);
|
||||
return(0);
|
||||
}
|
||||
|
||||
int asphagFP(FILE *fp,const char *hagname)
|
||||
{
|
||||
asDumpHagFP(fp,hagname);
|
||||
return(0);
|
||||
}
|
||||
|
||||
int asprules(const char *asgname)
|
||||
{
|
||||
asDumpRulesFP(stdout,asgname);
|
||||
return(0);
|
||||
}
|
||||
|
||||
int asprulesFP(FILE *fp,const char *asgname)
|
||||
{
|
||||
asDumpRulesFP(fp,asgname);
|
||||
return(0);
|
||||
}
|
||||
|
||||
int aspmem(const char *asgname,int clients)
|
||||
{
|
||||
asDumpMemFP(stdout,asgname,myMemberCallback,clients);
|
||||
return(0);
|
||||
}
|
||||
|
||||
int aspmemFP(FILE *fp,const char *asgname,int clients)
|
||||
{
|
||||
asDumpMemFP(fp,asgname,myMemberCallback,clients);
|
||||
return(0);
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
/*************************************************************************\
|
||||
* 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.
|
||||
\*************************************************************************/
|
||||
/* Author: Marty Kraimer Date: 02-23-94*/
|
||||
|
||||
#ifndef INCdbAsLibh
|
||||
#define INCdbAsLibh
|
||||
|
||||
#include "callback.h"
|
||||
#include "shareLib.h"
|
||||
|
||||
typedef struct {
|
||||
CALLBACK callback;
|
||||
long status;
|
||||
} ASDBCALLBACK;
|
||||
|
||||
struct dbChannel;
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
epicsShareFunc int asSetFilename(const char *acf);
|
||||
epicsShareFunc int asSetSubstitutions(const char *substitutions);
|
||||
epicsShareFunc int asInit(void);
|
||||
epicsShareFunc int asInitAsyn(ASDBCALLBACK *pcallback);
|
||||
epicsShareFunc int asShutdown(void);
|
||||
epicsShareFunc int asDbGetAsl(struct dbChannel *chan);
|
||||
epicsShareFunc void * asDbGetMemberPvt(struct dbChannel *chan);
|
||||
epicsShareFunc int asdbdump(void);
|
||||
epicsShareFunc int asdbdumpFP(FILE *fp);
|
||||
epicsShareFunc int aspuag(const char *uagname);
|
||||
epicsShareFunc int aspuagFP(FILE *fp,const char *uagname);
|
||||
epicsShareFunc int asphag(const char *hagname);
|
||||
epicsShareFunc int asphagFP(FILE *fp,const char *hagname);
|
||||
epicsShareFunc int asprules(const char *asgname);
|
||||
epicsShareFunc int asprulesFP(FILE *fp,const char *asgname);
|
||||
epicsShareFunc int aspmem(const char *asgname,int clients);
|
||||
epicsShareFunc int aspmemFP(
|
||||
FILE *fp,const char *asgname,int clients);
|
||||
epicsShareFunc int astac(
|
||||
const char *recordname,const char *user,const char *location);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /*INCdbAsLibh*/
|
||||
@@ -0,0 +1,129 @@
|
||||
/*************************************************************************\
|
||||
* Copyright (c) 2007 UChicago Argonne LLC, as Operator of Argonne
|
||||
* National Laboratory.
|
||||
* Copyright (c) 2002 The Regents of the University of California, as
|
||||
* Operator of Los Alamos National Laboratory.
|
||||
* EPICS BASE is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
\*************************************************************************/
|
||||
|
||||
#include "asLib.h"
|
||||
#include "iocsh.h"
|
||||
|
||||
#define epicsExportSharedSymbols
|
||||
#include "asCa.h"
|
||||
#include "asDbLib.h"
|
||||
#include "asIocRegister.h"
|
||||
|
||||
/* asSetFilename */
|
||||
static const iocshArg asSetFilenameArg0 = { "ascf",iocshArgString};
|
||||
static const iocshArg * const asSetFilenameArgs[] = {&asSetFilenameArg0};
|
||||
static const iocshFuncDef asSetFilenameFuncDef =
|
||||
{"asSetFilename",1,asSetFilenameArgs};
|
||||
static void asSetFilenameCallFunc(const iocshArgBuf *args)
|
||||
{
|
||||
asSetFilename(args[0].sval);
|
||||
}
|
||||
|
||||
/* asSetSubstitutions */
|
||||
static const iocshArg asSetSubstitutionsArg0 = { "substitutions",iocshArgString};
|
||||
static const iocshArg * const asSetSubstitutionsArgs[] = {&asSetSubstitutionsArg0};
|
||||
static const iocshFuncDef asSetSubstitutionsFuncDef =
|
||||
{"asSetSubstitutions",1,asSetSubstitutionsArgs};
|
||||
static void asSetSubstitutionsCallFunc(const iocshArgBuf *args)
|
||||
{
|
||||
asSetSubstitutions(args[0].sval);
|
||||
}
|
||||
|
||||
/* asInit */
|
||||
static const iocshFuncDef asInitFuncDef = {"asInit",0};
|
||||
static void asInitCallFunc(const iocshArgBuf *args)
|
||||
{
|
||||
asInit();
|
||||
}
|
||||
|
||||
/* asdbdump */
|
||||
static const iocshFuncDef asdbdumpFuncDef = {"asdbdump",0};
|
||||
static void asdbdumpCallFunc(const iocshArgBuf *args)
|
||||
{
|
||||
asdbdump();
|
||||
}
|
||||
|
||||
/* aspuag */
|
||||
static const iocshArg aspuagArg0 = { "uagname",iocshArgString};
|
||||
static const iocshArg * const aspuagArgs[] = {&aspuagArg0};
|
||||
static const iocshFuncDef aspuagFuncDef = {"aspuag",1,aspuagArgs};
|
||||
static void aspuagCallFunc(const iocshArgBuf *args)
|
||||
{
|
||||
aspuag(args[0].sval);
|
||||
}
|
||||
|
||||
/* asphag */
|
||||
static const iocshArg asphagArg0 = { "hagname",iocshArgString};
|
||||
static const iocshArg * const asphagArgs[] = {&asphagArg0};
|
||||
static const iocshFuncDef asphagFuncDef = {"asphag",1,asphagArgs};
|
||||
static void asphagCallFunc(const iocshArgBuf *args)
|
||||
{
|
||||
asphag(args[0].sval);
|
||||
}
|
||||
|
||||
/* asprules */
|
||||
static const iocshArg asprulesArg0 = { "asgname",iocshArgString};
|
||||
static const iocshArg * const asprulesArgs[] = {&asprulesArg0};
|
||||
static const iocshFuncDef asprulesFuncDef = {"asprules",1,asprulesArgs};
|
||||
static void asprulesCallFunc(const iocshArgBuf *args)
|
||||
{
|
||||
asprules(args[0].sval);
|
||||
}
|
||||
|
||||
/* aspmem */
|
||||
static const iocshArg aspmemArg0 = { "asgname",iocshArgString};
|
||||
static const iocshArg aspmemArg1 = { "clients",iocshArgInt};
|
||||
static const iocshArg * const aspmemArgs[] = {&aspmemArg0,&aspmemArg1};
|
||||
static const iocshFuncDef aspmemFuncDef = {"aspmem",2,aspmemArgs};
|
||||
static void aspmemCallFunc(const iocshArgBuf *args)
|
||||
{
|
||||
aspmem(args[0].sval,args[1].ival);
|
||||
}
|
||||
|
||||
/* astac */
|
||||
static const iocshArg astacArg0 = { "recordname",iocshArgString};
|
||||
static const iocshArg astacArg1 = { "user",iocshArgString};
|
||||
static const iocshArg astacArg2 = { "location",iocshArgString};
|
||||
static const iocshArg * const astacArgs[] = {&astacArg0,&astacArg1,&astacArg2};
|
||||
static const iocshFuncDef astacFuncDef = {"astac",3,astacArgs};
|
||||
static void astacCallFunc(const iocshArgBuf *args)
|
||||
{
|
||||
astac(args[0].sval,args[1].sval,args[2].sval);
|
||||
}
|
||||
|
||||
/* ascar */
|
||||
static const iocshArg ascarArg0 = { "level",iocshArgInt};
|
||||
static const iocshArg * const ascarArgs[] = {&ascarArg0};
|
||||
static const iocshFuncDef ascarFuncDef = {"ascar",1,ascarArgs};
|
||||
static void ascarCallFunc(const iocshArgBuf *args)
|
||||
{
|
||||
ascar(args[0].ival);
|
||||
}
|
||||
|
||||
/* asDumpHash */
|
||||
static const iocshFuncDef asDumpHashFuncDef = {"asDumpHash",0,0};
|
||||
static void asDumpHashCallFunc(const iocshArgBuf *args)
|
||||
{
|
||||
asDumpHash();
|
||||
}
|
||||
|
||||
void asIocRegister(void)
|
||||
{
|
||||
iocshRegister(&asSetFilenameFuncDef,asSetFilenameCallFunc);
|
||||
iocshRegister(&asSetSubstitutionsFuncDef,asSetSubstitutionsCallFunc);
|
||||
iocshRegister(&asInitFuncDef,asInitCallFunc);
|
||||
iocshRegister(&asdbdumpFuncDef,asdbdumpCallFunc);
|
||||
iocshRegister(&aspuagFuncDef,aspuagCallFunc);
|
||||
iocshRegister(&asphagFuncDef,asphagCallFunc);
|
||||
iocshRegister(&asprulesFuncDef,asprulesCallFunc);
|
||||
iocshRegister(&aspmemFuncDef,aspmemCallFunc);
|
||||
iocshRegister(&astacFuncDef,astacCallFunc);
|
||||
iocshRegister(&ascarFuncDef,ascarCallFunc);
|
||||
iocshRegister(&asDumpHashFuncDef,asDumpHashCallFunc);
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
/*************************************************************************\
|
||||
* Copyright (c) 2007 UChicago Argonne LLC, as Operator of Argonne
|
||||
* National Laboratory.
|
||||
* Copyright (c) 2002 The Regents of the University of California, as
|
||||
* Operator of Los Alamos National Laboratory.
|
||||
* EPICS BASE is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
\*************************************************************************/
|
||||
|
||||
#ifndef INC_asIocRegister_H
|
||||
#define INC_asIocRegister_H
|
||||
|
||||
#include "shareLib.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
epicsShareFunc void asIocRegister(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* INC_asIocRegister_H */
|
||||
@@ -0,0 +1,56 @@
|
||||
/*************************************************************************\
|
||||
* Copyright (c) 2002 The University of Chicago, as Operator of Argonne
|
||||
* National Laboratory.
|
||||
* Copyright (c) 2002 The Regents of the University of California, as
|
||||
* Operator of Los Alamos National Laboratory.
|
||||
* EPICS BASE is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
\*************************************************************************/
|
||||
|
||||
/* Author: Marty Kraimer Date: 03-24-94 */
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "asLib.h"
|
||||
#include "dbStaticLib.h"
|
||||
#include "errlog.h"
|
||||
|
||||
int main(int argc,char **argv)
|
||||
{
|
||||
int argn = 1;
|
||||
char *sub = NULL;
|
||||
int subLength = 0;
|
||||
char **pstr;
|
||||
char *psep;
|
||||
int *len;
|
||||
long status = 0;
|
||||
static char *subSep = ",";
|
||||
|
||||
/* Look for -Smacro=value options */
|
||||
while (argc>argn && (strncmp(argv[argn], "-S", 2)==0)) {
|
||||
pstr = ⊂
|
||||
psep = subSep;
|
||||
len = &subLength;
|
||||
if (strlen(argv[argn])==2) {
|
||||
dbCatString(pstr, len, argv[++argn], psep);
|
||||
} else {
|
||||
dbCatString(pstr, len, argv[argn]+2, psep);
|
||||
}
|
||||
argn++;
|
||||
}
|
||||
if (argc == argn) {
|
||||
status = asInitFP(stdin, sub);
|
||||
if(status) errlogPrintf("ascheck: Access Security File failed.\n");
|
||||
} else if (argc == argn+1) {
|
||||
status = asInitFile(argv[argn], sub);
|
||||
if(status) errlogPrintf("ascheck: Access Security File failed.\n");
|
||||
} else {
|
||||
printf("usage: ascheck [-Smac=sub ...] [<] file\n");
|
||||
status = -1;
|
||||
}
|
||||
errlogFlush();
|
||||
return status;
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
#*************************************************************************
|
||||
# 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.
|
||||
#*************************************************************************
|
||||
|
||||
# This is a Makefile fragment, see src/ioc/Makefile.
|
||||
|
||||
SRC_DIRS += $(IOCDIR)/bpt
|
||||
|
||||
INC += cvtTable.h
|
||||
|
||||
DBDINC += menuConvert
|
||||
|
||||
BPT_DBD += bptTypeJdegC.dbd
|
||||
BPT_DBD += bptTypeJdegF.dbd
|
||||
BPT_DBD += bptTypeKdegC.dbd
|
||||
BPT_DBD += bptTypeKdegF.dbd
|
||||
DBD += $(BPT_DBD)
|
||||
|
||||
PROD_HOST += makeBpt
|
||||
|
||||
makeBpt_SRCS = makeBpt
|
||||
|
||||
HTMLS += menuConvert.html
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
#*************************************************************************
|
||||
# Copyright (c) 2011 UChicago Argonne LLC, as Operator of Argonne
|
||||
# National Laboratory.
|
||||
# Copyright (c) 2010 Brookhaven Science Associates, as Operator of
|
||||
# Brookhaven National Lab.
|
||||
# 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.
|
||||
#*************************************************************************
|
||||
|
||||
# This is a Makefile fragment, see src/ioc/Makefile.
|
||||
|
||||
$(patsubst %,$(COMMON_DIR)/%,$(BPT_DBD)) : \
|
||||
$(COMMON_DIR)/bpt%.dbd : $(MAKEBPT)
|
||||
@@ -0,0 +1,142 @@
|
||||
!header
|
||||
"typeJdegC" 0 0 700 4095 .5 -210 760 1
|
||||
!data
|
||||
|
||||
-8.096 -8.076 -8.057 -8.037 -8.017 -7.996 -7.976 -7.955 -7.934 -7.912
|
||||
|
||||
|
||||
-7.890 -7.868 -7.846 -7.824 -7.801 -7.778 -7.755 -7.731 -7.707 -7.683
|
||||
-7.659 -7.634 -7.609 -7.584 -7.559 -7.533 -7.508 -7.482 -7.455 -7.429
|
||||
-7.402 -7.375 -7.348 -7.321 -7.293 -7.265 -7.237 -7.209 -7.180 -7.151
|
||||
-7.122 -7.093 -7.064 -7.034 -7.004 -6.974 -6.944 -6.914 -6.883 -6.852
|
||||
-6.821 -6.790 -6.758 -6.727 -6.695 -6.663 -6.630 -6.598 -6.565 -6.532
|
||||
|
||||
|
||||
-6.499 -6.466 -6.433 -6.399 -6.365 -6.331 -6.297 -6.263 -6.228 -6.194
|
||||
-6.159 -6.124 -6.089 -6.053 -6.018 -5.982 -5.946 -5.910 -5.874 -5.837
|
||||
-5.801 -5.764 -5.727 -5.690 -5.653 -5.615 -5.578 -5.540 -5.502 -5.464
|
||||
-5.426 -5.388 -5.349 -5.311 -5.272 -5.233 -5.194 -5.155 -5.115 -5.076
|
||||
-5.036 -4.996 -4.956 -4.916 -4.876 -4.836 -4.795 -4.755 -4.714 -4.673
|
||||
|
||||
|
||||
-4.632 -4.591 -4.550 -4.508 -4.467 -4.425 -4.383 -4.341 -4.299 -4.257
|
||||
-4.215 -4.172 -4.130 -4.087 -4.044 -4.001 -3.958 -3.915 -3.872 -3.829
|
||||
-3.785 -3.742 -3.698 -3.654 -3.610 -3.566 -3.522 -3.478 -3.433 -3.389
|
||||
-3.344 -3.299 -3.255 -3.210 -3.165 -3.120 -3.074 -3.029 -2.984 -2.938
|
||||
-2.892 -2.847 -2.801 -2.755 -2.709 -2.663 -2.617 -2.570 -2.524 -2.478
|
||||
|
||||
|
||||
-2.431 -2.384 -2.338 -2.291 -2.244 -2.197 -2.150 -2.102 -2.055 -2.008
|
||||
-1.960 -1.913 -1.865 -1.818 -1.770 -1.722 -1.674 -1.626 -1.578 -1.530
|
||||
-1.481 -1.433 -1.385 -1.336 -1.288 -1.239 -1.190 -1.141 -1.093 -1.044
|
||||
-0.995 -0.945 -0.896 -0.847 -0.798 -0.748 -0.699 -0.650 -0.600 -0.550
|
||||
-0.501 -0.451 -0.401 -0.351 -0.301 -0.251 -0.201 -0.151 -0.101 -0.050
|
||||
|
||||
|
||||
0.0 0.050 0.101 0.151 0.202 0.253 0.303 0.354 0.405 0.456
|
||||
0.507 0.558 0.609 0.660 0.711 0.762 0.813 0.865 0.916 0.967
|
||||
1.019 1.070 1.122 1.174 1.225 1.277 1.329 1.381 1.432 1.484
|
||||
1.536 1.588 1.640 1.693 1.745 1.797 1.849 1.901 1.954 2.006
|
||||
2.058 2.111 2.163 2.216 2.268 2.321 2.374 2.426 2.479 2.532
|
||||
|
||||
|
||||
2.585 2.638 2.691 2.743 2.796 2.849 2.902 2.956 3.009 3.062
|
||||
3.115 3.168 3.221 3.275 3.328 3.381 3.435 3.488 3.542 3.595
|
||||
3.649 3.702 3.756 3.809 3.863 3.917 3.971 4.024 4.078 4.132
|
||||
4.186 4.239 4.293 4.347 4.401 4.455 4.509 4.563 4.617 4.671
|
||||
4.725 4.780 4.834 4.888 4.942 4.996 5.050 5.105 5.159 5.213
|
||||
|
||||
|
||||
5.268 5.322 5.376 5.431 5.485 5.540 5.594 5.649 5.703 5.758
|
||||
5.812 5.867 5.921 5.976 6.031 6.085 6.140 6.195 6.249 6.304
|
||||
6.359 6.414 6.468 6.523 6.578 6.633 6.688 6.742 6.797 6.852
|
||||
6.907 6.962 7.017 7.072 7.127 7.182 7.237 7.292 7.347 7.402
|
||||
7.457 7.512 7.567 7.622 7.677 7.732 7.787 7.843 7.898 7.953
|
||||
|
||||
|
||||
8.008 8.063 8.118 8.174 8.229 8.284 8.339 8.394 8.450 8.505
|
||||
8.560 8.616 8.671 8.726 8.781 8.837 8.892 8.947 9.003 9.058
|
||||
9.113 9.169 9.224 9.279 9.335 9.390 9.446 9.501 9.556 9.612
|
||||
9.667 9.723 9.778 9.834 9.889 9.944 10.000 10.055 10.111 10.166
|
||||
10.222 10.277 10.333 10.388 10.444 10.499 10.555 10.610 10.666 10.721
|
||||
|
||||
|
||||
10.777 10.832 10.888 10.943 10.999 11.054 11.110 11.165 11.221 11.276
|
||||
11.332 11.387 11.443 11.498 11.554 11.609 11.665 11.720 11.776 11.831
|
||||
11.887 11.943 11.998 12.054 12.109 12.165 12.220 12.276 12.331 12.387
|
||||
12.442 12.498 12.553 12.609 12.664 12.720 12.776 12.831 12.887 12.942
|
||||
12.998 13.053 13.109 13.164 13.220 13.275 13.331 13.386 13.442 13.497
|
||||
|
||||
|
||||
13.553 13.608 13.664 13.719 13.775 13.830 13.886 13.941 13.997 14.052
|
||||
14.108 14.163 14.219 14.274 14.330 14.385 14.441 14.496 14.552 14.607
|
||||
14.663 14.718 14.774 14.829 14.885 14.940 14.995 15.051 15.106 15.162
|
||||
15.217 15.273 15.328 15.383 15.439 15.494 15.550 15.605 15.661 15.716
|
||||
15.771 15.827 15.882 15.938 15.993 16.048 16.104 16.159 16.214 16.270
|
||||
|
||||
|
||||
16.325 16.380 16.436 16.491 16.547 16.602 16.657 16.713 16.768 16.823
|
||||
16.879 16.934 16.989 17.044 17.100 17.155 17.210 17.266 17.321 17.376
|
||||
17.432 17.487 17.542 17.597 17.653 17.708 17.763 17.818 17.874 17.929
|
||||
17.984 18.039 18.095 18.150 18.205 18.260 18.316 18.371 18.426 18.481
|
||||
18.537 18.592 18.647 18.702 18.757 18.813 18.868 18.923 18.978 19.033
|
||||
|
||||
|
||||
19.089 19.144 19.199 19.254 19.309 19.364 19.420 19.475 19.530 19.585
|
||||
19.640 19.695 19.751 19.806 19.861 19.916 19.971 20.026 20.081 20.137
|
||||
20.192 20.247 20.302 20.357 20.412 20.467 20.523 20.578 20.633 20.688
|
||||
20.743 20.798 20.853 20.909 20.964 21.019 21.074 21.129 21.184 21.239
|
||||
21.295 21.350 21.405 21.460 21.515 21.570 21.625 21.680 21.736 21.791
|
||||
|
||||
|
||||
21.846 21.901 21.956 22.011 22.066 22.122 22.177 22.232 22.287 22.342
|
||||
22.397 22.453 22.508 22.563 22.618 22.673 22.728 22.784 22.839 22.894
|
||||
22.949 23.004 23.060 23.115 23.170 23.225 23.280 23.336 23.391 23.446
|
||||
23.501 23.556 23.612 23.667 23.722 23.777 23.833 23.888 23.943 23.999
|
||||
24.054 24.109 24.164 24.220 24.275 24.330 24.386 24.441 24.496 24.552
|
||||
|
||||
|
||||
24.607 24.662 24.718 24.773 24.829 24.884 24.939 24.995 25.050 25.106
|
||||
25.161 25.217 25.272 25.327 25.383 25.438 25.494 25.549 25.605 25.661
|
||||
25.716 25.772 25.827 25.883 25.938 25.994 26.050 26.105 26.161 26.216
|
||||
26.272 26.328 26.383 26.439 26.495 26.551 26.606 26.662 26.718 26.774
|
||||
26.829 26.885 26.941 26.997 27.053 27.109 27.165 27.220 27.276 27.332
|
||||
|
||||
|
||||
27.388 27.444 27.500 27.556 27.612 27.668 27.724 27.780 27.836 27.893
|
||||
27.949 28.005 28.061 28.117 28.173 28.230 28.286 28.342 28.398 28.455
|
||||
28.511 28.567 28.624 28.680 28.736 28.793 28.849 28.906 28.962 29.019
|
||||
29.075 29.132 29.188 29.245 29.301 29.358 29.415 29.471 29.528 29.585
|
||||
29.642 29.698 29.755 29.812 29.869 29.926 29.983 30.039 30.096 30.153
|
||||
|
||||
|
||||
30.210 30.267 30.324 30.381 30.439 30.496 30.553 30.610 30.667 30.724
|
||||
30.782 30.839 30.896 30.954 31.011 31.068 31.126 31.183 31.241 31.298
|
||||
31.356 31.413 31.471 31.528 31.586 31.644 31.702 31.759 31.817 31.875
|
||||
31.933 31.991 32.048 32.106 32.164 32.222 32.280 32.338 32.396 32.455
|
||||
32.513 32.571 32.629 32.687 32.746 32.804 32.862 32.921 32.979 33.038
|
||||
|
||||
|
||||
33.096 33.155 33.213 33.272 33.330 33.389 33.448 33.506 33.565 33.624
|
||||
33.683 33.742 33.800 33.859 33.918 33.977 34.036 34.095 34.155 34.214
|
||||
34.273 34.332 34.391 34.451 34.510 34.569 34.629 34.688 34.748 34.807
|
||||
34.867 34.926 34.986 35.046 35.105 35.165 35.225 35.285 35.344 35.404
|
||||
35.464 35.524 35.584 35.644 35.704 35.764 35.825 35.885 35.945 36.005
|
||||
|
||||
|
||||
36.066 36.126 36.186 36.247 36.307 36.368 36.428 36.489 36.549 36.610
|
||||
36.671 36.732 36.792 36.853 36.914 36.975 37.036 37.097 37.158 37.219
|
||||
37.280 37.341 37.402 37.463 37.525 37.586 37.647 37.709 37.770 37.831
|
||||
37.893 37.954 38.016 38.078 38.139 38.201 38.262 38.324 38.386 38.448
|
||||
38.510 38.572 38.633 38.695 38.757 38.819 38.882 38.944 39.006 39.068
|
||||
|
||||
|
||||
39.130 39.192 39.255 39.317 39.379 39.442 39.504 39.567 39.629 39.692
|
||||
39.754 39.817 39.880 39.942 40.005 40.068 40.131 40.193 40.256 40.319
|
||||
40.382 40.445 40.508 40.571 40.634 40.697 40.760 40.823 40.886 40.950
|
||||
41.013 41.076 41.139 41.203 41.266 41.329 41.393 41.456 41.520 41.583
|
||||
41.647 41.710 41.774 41.837 41.901 41.965 42.028 42.092 42.156 42.219
|
||||
|
||||
|
||||
42.283 42.347 42.411 42.475 42.538 42.602 42.666 42.730 42.794 42.858
|
||||
42.922
|
||||
@@ -0,0 +1,213 @@
|
||||
! cvtTypeJdegF.data
|
||||
"typeJdegF" 32 0 1200 4095 1.0 -350 1400 1
|
||||
!
|
||||
-8.137 -8.127 -8.117 -8.106 -8.096 -8.085 -8.074 -8.063 -8.052 -8.041
|
||||
-8.030 -8.019 -8.008 -7.996 -7.985 -7.973 -7.962 -7.950 -7.938 -7.927
|
||||
-7.915 -7.903 -7.890 -7.878 -7.866 -7.854 -7.841 -7.829 -7.816 -7.803
|
||||
-7.791 -7.778 -7.765 -7.752 -7.739 -7.726 -7.712 -7.699 -7.686 -7.672
|
||||
-7.659 -7.645 -7.631 -7.618 -7.604 -7.590 -7.576 -7.562 -7.548 -7.533
|
||||
|
||||
-7.519 -7.505 -7.490 -7.476 -7.461 -7.447 -7.432 -7.417 -7.402 -7.387
|
||||
-7.372 -7.357 -7.342 -7.327 -7.311 -7.296 -7.281 -7.265 -7.250 -7.234
|
||||
-7.218 -7.202 -7.187 -7.171 -7.155 -7.139 -7.122 -7.106 -7.090 -7.074
|
||||
-7.057 -7.041 -7.024 -7.008 -6.991 -6.974 -6.958 -6.941 -6.924 -6.907
|
||||
-6.890 -6.873 -6.856 -6.838 -6.821 -6.804 -6.786 -6.769 -6.751 -6.734
|
||||
|
||||
-6.716 -6.698 -6.680 -6.663 -6.645 -6.627 -6.609 -6.591 -6.572 -6.554
|
||||
-6.536 -6.518 -6.499 -6.481 -6.462 -6.444 -6.425 -6.407 -6.388 -6.369
|
||||
-6.350 -6.331 -6.312 -6.293 -6.274 -6.255 -6.236 -6.217 -6.198 -6.178
|
||||
-6.159 -6.139 -6.120 -6.100 -6.081 -6.061 -6.041 -6.022 -6.002 -5.982
|
||||
-5.962 -5.942 -5.922 -5.902 -5.882 -5.861 -5.841 -5.821 -5.801 -5.780
|
||||
|
||||
-5.760 -5.739 -5.719 -5.698 -5.678 -5.657 -5.636 -5.615 -5.594 -5.574
|
||||
-5.553 -5.532 -5.511 -5.490 -5.468 -5.447 -5.426 -5.405 -5.383 -5.362
|
||||
-5.341 -5.319 -5.298 -5.276 -5.255 -5.233 -5.211 -5.190 -5.168 -5.146
|
||||
-5.124 -5.102 -5.080 -5.058 -5.036 -5.014 -4.992 -4.970 -4.948 -4.925
|
||||
-4.903 -4.881 -4.858 -4.836 -4.813 -4.791 -4.768 -4.746 -4.723 -4.700
|
||||
|
||||
-4.678 -4.655 -4.632 -4.609 -4.586 -4.563 -4.540 -4.517 -4.494 -4.471
|
||||
-4.448 -4.425 -4.402 -4.379 -4.355 -4.332 -4.309 -4.285 -4.262 -4.238
|
||||
-4.215 -4.191 -4.168 -4.144 -4.120 -4.097 -4.073 -4.049 -4.025 -4.001
|
||||
-3.978 -3.954 -3.930 -3.906 -3.882 -3.858 -3.833 -3.809 -3.785 -3.761
|
||||
-3.737 -3.712 -3.688 -3.664 -3.639 -3.615 -3.590 -3.566 -3.541 -3.517
|
||||
|
||||
-3.492 -3.468 -3.443 -3.418 -3.394 -3.369 -3.344 -3.319 -3.294 -3.270
|
||||
-3.245 -3.220 -3.195 -3.170 -3.145 -3.120 -3.094 -3.069 -3.044 -3.019
|
||||
-2.994 -2.968 -2.943 -2.918 -2.892 -2.867 -2.842 -2.816 -2.791 -2.765
|
||||
-2.740 -2.714 -2.689 -2.663 -2.637 -2.612 -2.586 -2.560 -2.534 -2.509
|
||||
-2.483 -2.457 -2.431 -2.405 -2.379 -2.353 -2.327 -2.301 -2.275 -2.249
|
||||
|
||||
-2.223 -2.197 -2.171 -2.144 -2.118 -2.092 -2.066 -2.039 -2.013 -1.987
|
||||
-1.960 -1.934 -1.908 -1.881 -1.855 -1.828 -1.802 -1.775 -1.748 -1.722
|
||||
-1.695 -1.669 -1.642 -1.615 -1.589 -1.562 -1.535 -1.508 -1.481 -1.455
|
||||
-1.428 -1.401 -1.374 -1.347 -1.320 -1.293 -1.266 -1.239 -1.212 -1.185
|
||||
-1.158 -1.131 -1.103 -1.076 -1.049 -1.022 -0.995 -0.967 -0.940 -0.913
|
||||
|
||||
-0.885 -0.858 -0.831 -0.803 -0.776 -0.748 -0.721 -0.694 -0.666 -0.639
|
||||
-0.611 -0.583 -0.556 -0.528 -0.501 -0.473 -0.445 -0.418 -0.390 -0.362
|
||||
-0.334 -0.307 -0.279 -0.251 -0.223 -0.195 -0.168 -0.140 -0.112 -0.084
|
||||
-0.056 -0.028 0.000 0.028 0.056 0.084 0.112 0.140 0.168 0.196
|
||||
0.224 0.253 0.281 0.309 0.337 0.365 0.394 0.422 0.450 0.478
|
||||
|
||||
0.507 0.535 0.563 0.592 0.620 0.648 0.677 0.705 0.734 0.762
|
||||
0.791 0.819 0.848 0.876 0.905 0.933 0.962 0.990 1.019 1.048
|
||||
1.076 1.105 1.134 1.162 1.191 1.220 1.248 1.277 1.306 1.335
|
||||
1.363 1.392 1.421 1.450 1.479 1.507 1.536 1.565 1.594 1.623
|
||||
1.652 1.681 1.710 1.739 1.768 1.797 1.826 1.855 1.884 1.913
|
||||
|
||||
1.942 1.971 2.000 2.029 2.058 2.088 2.117 2.146 2.175 2.204
|
||||
2.233 2.263 2.292 2.321 2.350 2.380 2.409 2.438 2.467 2.497
|
||||
2.526 2.555 2.585 2.614 2.644 2.673 2.702 2.732 2.761 2.791
|
||||
2.820 2.849 2.879 2.908 2.938 2.967 2.997 3.026 3.056 3.085
|
||||
3.115 3.145 3.174 3.204 3.233 3.263 3.293 3.322 3.352 3.381
|
||||
|
||||
3.411 3.441 3.470 3.500 3.530 3.560 3.589 3.619 3.649 3.678
|
||||
3.708 3.738 3.768 3.798 3.827 3.857 3.887 3.917 3.947 3.976
|
||||
4.006 4.036 4.066 4.096 4.126 4.156 4.186 4.216 4.245 4.275
|
||||
4.305 4.335 4.365 4.395 4.425 4.455 4.485 4.515 4.545 4.575
|
||||
4.605 4.635 4.665 4.695 4.725 4.755 4.786 4.816 4.846 4.876
|
||||
|
||||
4.906 4.936 4.966 4.996 5.026 5.057 5.087 5.117 5.147 5.177
|
||||
5.207 5.238 5.268 5.298 5.328 5.358 5.389 5.419 5.449 5.479
|
||||
5.509 5.540 5.570 5.600 5.630 5.661 5.691 5.721 5.752 5.782
|
||||
5.812 5.843 5.873 5.903 5.934 5.964 5.994 6.025 6.055 6.085
|
||||
6.116 6.146 6.176 6.207 6.237 6.268 6.298 6.328 6.359 6.389
|
||||
|
||||
6.420 6.450 6.481 6.511 6.541 6.572 6.602 6.633 6.663 6.694
|
||||
6.724 6.755 6.785 6.816 6.846 6.877 6.907 6.938 6.968 6.999
|
||||
7.029 7.060 7.090 7.121 7.151 7.182 7.212 7.243 7.274 7.304
|
||||
7.335 7.365 7.396 7.426 7.457 7.488 7.518 7.549 7.579 7.610
|
||||
7.641 7.671 7.702 7.732 7.763 7.794 7.824 7.855 7.885 7.916
|
||||
|
||||
7.947 7.977 8.008 8.039 8.069 8.100 8.131 8.161 8.192 8.223
|
||||
8.253 8.284 8.315 8.345 8.376 8.407 8.437 8.468 8.499 8.530
|
||||
8.560 8.591 8.622 8.652 8.683 8.714 8.745 8.775 8.806 8.837
|
||||
8.867 8.898 8.929 8.960 8.990 9.021 9.052 9.083 9.113 9.144
|
||||
9.175 9.206 9.236 9.267 9.298 9.329 9.359 9.390 9.421 9.452
|
||||
|
||||
9.483 9.513 9.544 9.575 9.606 9.636 9.667 9.698 9.729 9.760
|
||||
9.790 9.821 9.852 9.883 9.914 9.944 9.975 10.006 10.037 10.068
|
||||
10.098 10.129 10.160 10.191 10.222 10.252 10.283 10.314 10.345 10.376
|
||||
10.407 10.437 10.468 10.499 10.530 10.561 10.592 10.622 10.653 10.684
|
||||
10.715 10.746 10.777 10.807 10.838 10.869 10.900 10.931 10.962 10.992
|
||||
|
||||
11.023 11.054 11.085 11.116 11.147 11.177 11.208 11.239 11.270 11.301
|
||||
11.332 11.363 11.393 11.424 11.455 11.486 11.517 11.548 11.578 11.609
|
||||
11.640 11.671 11.702 11.733 11.764 11.794 11.825 11.856 11.887 11.918
|
||||
11.949 11.980 12.010 12.041 12.072 12.103 12.134 12.165 12.196 12.226
|
||||
12.257 12.288 12.319 12.350 12.381 12.411 12.442 12.473 12.504 12.535
|
||||
|
||||
12.566 12.597 12.627 12.658 12.689 12.720 12.751 12.782 12.813 12.843
|
||||
12.874 12.905 12.936 12.967 12.998 13.029 13.059 13.090 13.121 13.152
|
||||
13.183 13.214 13.244 13.275 13.306 13.337 13.368 13.399 13.430 13.460
|
||||
13.491 13.522 13.553 13.584 13.615 13.645 13.676 13.707 13.738 13.769
|
||||
13.800 13.830 13.861 13.892 13.923 13.954 13.985 14.015 14.046 14.077
|
||||
|
||||
14.108 14.139 14.170 14.200 14.231 14.262 14.293 14.324 14.355 14.385
|
||||
14.416 14.447 14.478 14.509 14.539 14.570 14.601 14.632 14.663 14.694
|
||||
14.724 14.755 14.786 14.817 14.848 14.878 14.909 14.940 14.971 15.002
|
||||
15.032 15.063 15.094 15.125 15.156 15.186 15.217 15.248 15.279 15.310
|
||||
15.340 15.371 15.402 15.433 15.464 15.494 15.525 15.556 15.587 15.617
|
||||
|
||||
15.648 15.679 15.710 15.741 15.771 15.802 15.833 15.864 15.894 15.925
|
||||
15.956 15.987 16.018 16.048 16.079 16.110 16.141 16.171 16.202 16.233
|
||||
16.264 16.294 16.325 16.356 16.387 16.417 16.448 16.479 16.510 16.540
|
||||
16.571 16.602 16.633 16.663 16.694 16.725 16.756 16.786 16.817 16.848
|
||||
16.879 16.909 16.940 16.971 17.001 17.032 17.063 17.094 17.124 17.155
|
||||
|
||||
17.186 17.217 17.247 17.278 17.309 17.339 17.370 17.401 17.432 17.462
|
||||
17.493 17.524 17.554 17.585 17.616 17.646 17.677 17.708 17.739 17.769
|
||||
17.800 17.831 17.861 17.892 17.923 17.953 17.984 18.015 18.046 18.076
|
||||
18.107 18.138 18.168 18.199 18.230 18.260 18.291 18.322 18.352 18.383
|
||||
18.414 18.444 18.475 18.506 18.537 18.567 18.598 18.629 18.659 18.690
|
||||
|
||||
18.721 18.751 18.782 18.813 18.843 18.874 18.905 18.935 18.966 18.997
|
||||
19.027 19.058 19.089 19.119 19.150 19.180 19.211 19.242 19.272 19.303
|
||||
19.334 19.364 19.395 19.426 19.456 19.487 19.518 19.548 19.579 19.610
|
||||
19.640 19.671 19.702 19.732 19.763 19.793 19.824 19.855 19.885 19.916
|
||||
19.947 19.977 20.008 20.039 20.069 20.100 20.131 20.161 20.192 20.222
|
||||
|
||||
20.253 20.284 20.314 20.345 20.376 20.406 20.437 20.467 20.498 20.529
|
||||
20.559 20.590 20.621 20.651 20.682 20.713 20.743 20.774 20.804 20.835
|
||||
20.866 20.896 20.927 20.958 20.988 21.019 21.049 21.080 21.111 21.141
|
||||
21.172 21.203 21.233 21.264 21.295 21.325 21.356 21.386 21.417 21.448
|
||||
21.478 21.509 21.540 21.570 21.601 21.631 21.662 21.693 21.723 21.754
|
||||
|
||||
21.785 21.815 21.846 21.877 21.907 21.938 21.968 21.999 22.030 22.060
|
||||
22.091 22.122 22.152 22.183 22.214 22.244 22.275 22.305 22.336 22.367
|
||||
22.397 22.428 22.459 22.489 22.520 22.551 22.581 22.612 22.643 22.673
|
||||
22.704 22.735 22.765 22.796 22.826 22.857 22.888 22.918 22.949 22.980
|
||||
23.010 23.041 23.072 23.102 23.133 23.164 23.194 23.225 23.256 23.286
|
||||
|
||||
23.317 23.348 23.378 23.409 23.440 23.471 23.501 23.532 23.563 23.593
|
||||
23.624 23.655 23.685 23.716 23.747 23.777 23.808 23.839 23.870 23.900
|
||||
23.931 23.962 23.992 24.023 24.054 24.085 24.115 24.146 24.177 24.207
|
||||
24.238 24.269 24.300 24.330 24.361 24.392 24.423 24.453 24.484 24.515
|
||||
24.546 24.576 24.607 24.638 24.669 24.699 24.730 24.761 24.792 24.822
|
||||
|
||||
24.853 24.854 24.915 24.946 24.976 25.007 25.038 25.069 25.099 25.130
|
||||
25.161 25.192 25.223 25.254 25.284 25.315 25.346 25.377 25.408 25.438
|
||||
25.469 25.500 25.531 25.562 25.593 25.623 25.654 25.685 25.716 25.747
|
||||
25.778 25.809 25.840 25.870 25.901 25.932 25.963 25.994 26.025 26.056
|
||||
26.087 26.118 26.148 26.179 26.210 26.241 26.272 26.303 26.334 26.365
|
||||
|
||||
26.396 26.427 26.458 26.489 26.520 26.551 26.582 26.613 26.644 26.675
|
||||
26.705 26.736 26.767 26.798 26.829 26.860 26.891 26.922 26.954 26.985
|
||||
27.016 27.047 27.078 27.109 27.140 27.171 27.202 27.233 27.264 27.295
|
||||
27.326 27.357 27.388 27.419 27.450 27.482 27.513 27.544 27.575 27.606
|
||||
27.637 27.668 27.699 27.731 27.762 27.793 27.824 27.855 27.886 27.917
|
||||
|
||||
27.949 27.980 28.011 28.042 28.073 28.105 28.136 28.167 28.198 28.230
|
||||
28.261 28.292 28.323 28.355 28.386 28.417 28.448 28.480 28.511 28.542
|
||||
28.573 28.605 28.636 28.667 28.699 28.730 28.761 28.793 28.824 28.855
|
||||
28.887 28.918 28.950 28.981 29.012 29.044 29.075 29.107 29.138 29.169
|
||||
29.201 29.232 29.264 29.295 29.327 29.358 29.390 29.421 29.452 29.484
|
||||
|
||||
29.515 29.547 29.578 29.610 29.642 29.673 29.705 29.736 29.768 29.799
|
||||
29.831 29.862 29.894 29.926 29.957 29.989 30.020 30.052 30.084 30.115
|
||||
30.147 30.179 30.210 30.242 30.274 30.305 30.337 30.369 30.400 30.432
|
||||
30.464 30.496 30.527 30.559 30.591 30.623 30.654 30.686 30.718 30.750
|
||||
30.782 30.813 30.845 30.877 30.909 30.941 30.973 31.005 31.036 31.068
|
||||
|
||||
31.100 31.132 31.164 31.196 31.228 31.260 31.292 31.324 31.356 31.388
|
||||
31.420 31.452 31.484 31.516 31.548 31.580 31.612 31.644 31.676 31.708
|
||||
31.740 31.772 31.804 31.836 31.868 31.901 31.933 31.965 31.997 32.029
|
||||
32.061 32.094 32.126 32.158 32.190 32.222 32.255 32.287 32.319 32.351
|
||||
32.384 32.416 32.448 32.480 32.513 32.545 32.577 32.610 32.642 32.674
|
||||
|
||||
32.707 32.739 32.772 32.804 32.836 32.869 32.901 32.934 32.966 32.999
|
||||
33.031 33.064 33.096 33.129 33.161 33.194 33.226 33.259 33.291 33.324
|
||||
33.356 33.389 33.422 33.454 33.487 33.519 33.552 33.585 33.617 33.650
|
||||
33.683 33.715 33.748 33.781 33.814 33.846 33.879 33.912 33.945 33.977
|
||||
34.010 34.043 34.076 34.109 34.141 34.174 34.207 34.240 34.273 34.306
|
||||
|
||||
34.339 34.372 34.405 34.437 34.470 34.503 34.536 34.569 34.602 34.635
|
||||
34.668 34.701 34.734 34.767 34.801 34.834 34.867 34.900 34.933 34.966
|
||||
34.999 35.032 35.065 35.099 35.132 35.165 35.198 35.231 35.265 35.298
|
||||
35.331 35.364 35.398 35.431 35.464 35.498 35.531 35.564 35.598 35.631
|
||||
35.664 35.698 35.731 35.764 35.798 35.831 35.865 35.898 35.932 35.965
|
||||
|
||||
35.999 36.032 36.066 36.099 36.133 36.166 36.200 36.233 36.267 36.301
|
||||
36.334 36.368 36.401 36.435 36.469 36.502 36.536 36.570 36.603 36.637
|
||||
36.671 36.705 36.738 36.772 36.806 36.840 36.873 36.907 36.941 36.975
|
||||
37.009 37.043 37.076 37.110 37.144 37.178 37.212 37.246 37.280 37.314
|
||||
37.348 37.382 37.416 37.450 37.484 37.518 37.552 37.586 37.620 37.654
|
||||
|
||||
37.688 37.722 37.756 37.790 37.825 37.859 37.893 37.927 37.961 37.995
|
||||
38.030 38.064 38.098 38.132 38.167 38.201 38.235 38.269 38.304 38.338
|
||||
38.372 38.407 38.441 38.475 38.510 38.544 38.578 38.613 38.647 38.682
|
||||
38.716 38.751 38.785 38.819 38.854 38.888 38.923 38.957 38.992 39.027
|
||||
39.061 39.096 39.130 39.165 39.199 39.234 39.269 39.303 39.338 39.373
|
||||
|
||||
39.407 39.442 39.477 39.511 39.546 39.581 39.615 39.650 39.685 39.720
|
||||
39.754 39.789 39.824 39.859 39.894 39.928 39.963 39.998 40.033 40.068
|
||||
40.103 40.138 40.172 40.207 40.242 40.277 40.312 40.347 40.382 40.417
|
||||
40.452 40.487 40.522 40.557 40.592 40.627 40.662 40.697 40.732 40.767
|
||||
40.802 40.837 40.872 40.908 40.943 40.978 41.013 41.048 41.083 41.118
|
||||
|
||||
41.154 41.189 41.224 41.259 41.294 41.329 41.365 41.400 41.435 41.470
|
||||
41.506 41.541 41.576 41.611 41.647 41.682 41.717 41.753 41.788 41.823
|
||||
41.859 41.894 41.929 41.965 42.000 42.035 42.071 42.106 42.142 42.177
|
||||
42.212 42.248 42.283 42.319 42.354 42.390 42.425 42.460 42.496 42.531
|
||||
42.567 42.602 42.638 42.673 42.709 42.744 42.780 42.815 42.851 42.886
|
||||
42.922
|
||||
@@ -0,0 +1,201 @@
|
||||
! cvtTypeKdegC.data
|
||||
"typeKdegC" 0 0 1000 4095 .5 -270 1372 1
|
||||
!
|
||||
-6.458 -6.457 -6.456 -6.455 -6.453 -6.452 -6.450 -6.448 -6.446 -6.444
|
||||
-6.441 -6.438 -6.435 -6.432 -6.429 -6.425 -6.421 -6.417 -6.413 -6.408
|
||||
|
||||
-6.404 -6.399 -6.394 -6.388 -6.382 -6.377 -6.371 -6.364 -6.358 -6.351
|
||||
-6.344 -6.337 -6.329 -6.322 -6.314 -6.306 -6.297 -6.289 -6.280 -6.271
|
||||
-6.262 -6.253 -6.243 -6.233 -6.223 -6.213 -6.202 -6.192 -6.181 -6.170
|
||||
-6.158 -6.147 -6.135 -6.123 -6.111 -6.099 -6.087 -6.074 -6.061 -6.048
|
||||
-6.035 -6.021 -6.007 -5.994 -5.980 -5.965 -5.951 -5.936 -5.922 -5.907
|
||||
|
||||
-5.891 -5.876 -5.860 -5.845 -5.829 -5.813 -5.796 -5.780 -5.763 -5.747
|
||||
-5.730 -5.712 -5.695 -5.678 -5.660 -5.642 -5.624 -5.606 -5.587 -5.569
|
||||
-5.550 -5.531 -5.512 -5.493 -5.474 -5.454 -5.434 -5.414 -5.394 -5.374
|
||||
-5.354 -5.333 -5.313 -5.292 -5.271 -5.249 -5.228 -5.207 -5.185 -5.163
|
||||
-5.141 -5.119 -5.097 -5.074 -5.051 -5.029 -5.006 -4.983 -4.959 -4.936
|
||||
|
||||
-4.912 -4.889 -4.865 -4.841 -4.817 -4.792 -4.768 -4.743 -4.719 -4.694
|
||||
-4.669 -4.644 -4.618 -4.593 -4.567 -4.541 -4.515 -4.489 -4.463 -4.437
|
||||
-4.410 -4.384 -4.357 -4.330 -4.303 -4.276 -4.248 -4.221 -4.193 -4.166
|
||||
-4.138 -4.110 -4.082 -4.053 -4.025 -3.997 -3.968 -3.939 -3.910 -3.881
|
||||
-3.852 -3.823 -3.793 -3.764 -3.734 -3.704 -3.674 -3.644 -3.614 -3.584
|
||||
|
||||
-3.553 -3.523 -3.492 -3.461 -3.430 -3.399 -3.368 -3.337 -3.305 -3.274
|
||||
-3.242 -3.211 -3.179 -3.147 -3.115 -3.082 -3.050 -3.018 -2.985 -2.953
|
||||
-2.920 -2.887 -2.854 -2.821 -2.788 -2.754 -2.721 -2.687 -2.654 -2.620
|
||||
-2.586 -2.552 -2.518 -2.484 -2.450 -2.416 -2.381 -2.347 -2.312 -2.277
|
||||
-2.243 -2.208 -2.173 -2.137 -2.102 -2.067 -2.032 -1.996 -1.961 -1.925
|
||||
|
||||
-1.889 -1.853 -1.817 -1.781 -1.745 -1.709 -1.673 -1.636 -1.600 -1.563
|
||||
-1.527 -1.490 -1.453 -1.416 -1.379 -1.342 -1.305 -1.268 -1.231 -1.193
|
||||
-1.156 -1.118 -1.081 -1.043 -1.005 -0.968 -0.930 -0.892 -0.854 -0.816
|
||||
-0.777 -0.739 -0.701 -0.662 -0.624 -0.585 -0.547 -0.508 -0.469 -0.431
|
||||
-0.392 -0.353 -0.314 -0.275 -0.236 -0.197 -0.157 -0.118 -0.079 -0.039
|
||||
|
||||
0.000 0.039 0.079 0.119 0.158 0.198 0.238 0.277 0.317 0.357
|
||||
0.397 0.437 0.477 0.517 0.557 0.597 0.637 0.677 0.718 0.758
|
||||
0.798 0.838 0.879 0.919 0.960 1.000 1.041 1.081 1.122 1.162
|
||||
1.203 1.244 1.285 1.325 1.366 1.407 1.448 1.489 1.529 1.570
|
||||
1.611 1.652 1.693 1.734 1.776 1.817 1.858 1.899 1.940 1.981
|
||||
|
||||
2.022 2.064 2.105 2.146 2.188 2.229 2.270 2.312 2.353 2.394
|
||||
2.436 2.477 2.519 2.560 2.601 2.643 2.684 2.726 2.767 2.809
|
||||
2.850 2.892 2.933 2.975 3.016 3.058 3.100 3.141 3.183 3.224
|
||||
3.266 3.307 3.349 3.390 3.432 3.473 3.515 3.556 3.598 3.639
|
||||
3.681 3.722 3.764 3.805 3.847 3.888 3.930 3.971 4.012 4.054
|
||||
|
||||
4.095 4.137 4.178 4.219 4.261 4.302 4.343 4.384 4.426 4.467
|
||||
4.508 4.549 4.590 4.632 4.673 4.714 4.755 4.796 4.837 4.878
|
||||
4.919 4.960 5.001 5.042 5.083 5.124 5.164 5.205 5.246 5.287
|
||||
5.327 5.368 5.409 5.450 5.490 5.531 5.571 5.612 5.652 5.693
|
||||
5.733 5.774 5.814 5.855 5.895 5.936 5.976 6.016 6.057 6.097
|
||||
|
||||
6.137 6.177 6.218 6.258 6.298 6.338 6.378 6.419 6.459 6.499
|
||||
6.539 6.579 6.619 6.659 6.699 6.739 6.779 6.819 6.859 6.899
|
||||
6.939 6.979 7.019 7.059 7.099 7.139 7.179 7.219 7.259 7.299
|
||||
7.338 7.378 7.418 7.458 7.498 7.538 7.578 7.618 7.658 7.697
|
||||
7.737 7.777 7.817 7.857 7.897 7.937 7.977 8.017 8.057 8.097
|
||||
|
||||
8.137 8.177 8.216 8.256 8.296 8.336 8.376 8.416 8.456 8.497
|
||||
8.537 8.577 8.617 8.657 8.697 8.737 8.777 8.817 8.857 8.898
|
||||
8.938 8.978 9.018 9.058 9.099 9.139 9.179 9.220 9.260 9.300
|
||||
9.341 9.381 9.421 9.462 9.502 9.543 9.583 9.624 9.664 9.705
|
||||
9.745 9.786 9.826 9.867 9.907 9.948 9.989 10.029 10.070 10.111
|
||||
|
||||
10.151 10.192 10.233 10.274 10.315 10.355 10.396 10.437 10.478 10.519
|
||||
10.560 10.600 10.641 10.682 10.723 10.764 10.805 10.846 10.887 10.928
|
||||
10.969 11.010 11.051 11.093 11.134 11.175 11.216 11.257 11.298 11.339
|
||||
11.381 11.422 11.463 11.504 11.546 11.587 11.628 11.669 11.711 11.752
|
||||
11.793 11.835 11.876 11.918 11.959 12.000 12.042 12.083 12.125 12.166
|
||||
|
||||
12.207 12.249 12.290 12.332 12.373 12.415 12.456 12.498 12.539 12.581
|
||||
12.623 12.664 12.706 12.747 12.789 12.831 12.872 12.914 12.955 12.997
|
||||
13.039 13.080 13.122 13.164 13.205 13.247 13.289 13.331 13.372 13.414
|
||||
13.456 13.497 13.539 13.581 13.623 13.665 13.706 13.748 13.790 13.832
|
||||
13.874 13.915 13.957 13.999 14.041 14.083 14.125 14.167 14.208 14.250
|
||||
|
||||
14.292 14.334 14.376 14.418 14.460 14.502 14.544 14.586 14.628 14.670
|
||||
14.712 14.754 14.796 14.838 14.880 14.922 14.964 15.006 15.048 15.090
|
||||
15.132 15.174 15.216 15.258 15.300 15.342 15.384 15.426 15.468 15.510
|
||||
15.552 15.594 15.636 15.679 15.721 15.763 15.805 15.847 15.889 15.931
|
||||
15.974 16.016 16.058 16.100 16.142 16.184 16.227 16.269 16.311 16.353
|
||||
|
||||
16.395 16.438 16.480 16.522 16.564 16.607 16.649 16.691 16.733 16.776
|
||||
16.818 16.860 16.902 16.945 16.987 17.029 17.072 17.114 17.156 17.199
|
||||
17.241 17.283 17.326 17.368 17.410 17.453 17.495 17.537 17.580 17.622
|
||||
17.664 17.707 17.749 17.792 17.834 17.876 17.919 17.961 18.004 18.046
|
||||
18.088 18.131 18.173 18.216 18.258 18.301 18.343 18.385 18.428 18.470
|
||||
|
||||
18.513 18.555 18.598 18.640 18.683 18.725 18.768 18.810 18.853 18.895
|
||||
18.938 18.980 19.023 19.065 19.108 19.150 19.193 19.235 19.278 19.320
|
||||
19.363 19.405 19.448 19.490 19.533 19.576 19.618 19.661 19.703 19.746
|
||||
19.788 19.831 19.873 19.916 19.959 20.001 20.044 20.086 20.129 20.172
|
||||
20.214 20.257 20.299 20.342 20.385 20.427 20.470 20.512 20.555 20.598
|
||||
|
||||
20.640 20.683 20.725 20.768 20.811 20.853 20.896 20.938 20.981 21.024
|
||||
21.066 21.109 21.152 21.194 21.237 21.280 21.322 21.365 21.407 21.450
|
||||
21.493 21.535 21.578 21.621 21.663 21.706 21.749 21.791 21.834 21.876
|
||||
21.919 21.962 22.004 22.047 22.090 22.132 22.175 22.218 22.260 22.303
|
||||
22.346 22.388 22.431 22.473 22.516 22.559 22.601 22.644 22.687 22.729
|
||||
|
||||
22.772 22.815 22.857 22.900 22.942 22.985 23.028 23.070 23.113 23.156
|
||||
23.198 23.241 23.284 23.326 23.369 23.411 23.454 23.497 23.539 23.582
|
||||
23.624 23.667 23.710 23.752 23.795 23.837 23.880 23.923 23.965 24.008
|
||||
24.050 24.093 24.136 24.178 24.221 24.263 24.306 24.348 24.391 24.434
|
||||
24.476 24.519 24.561 24.604 24.646 24.689 24.731 24.774 24.817 24.859
|
||||
|
||||
24.902 24.944 24.987 25.029 25.072 25.114 25.157 25.199 25.242 25.284
|
||||
25.327 25.369 25.412 25.454 25.497 25.539 25.582 25.624 25.666 25.709
|
||||
25.751 25.794 25.836 25.879 25.921 25.964 26.006 26.048 26.091 26.133
|
||||
26.176 26.218 26.260 26.303 26.345 26.387 26.430 26.472 26.515 26.557
|
||||
26.599 26.642 26.684 26.726 26.769 26.811 26.853 26.896 26.938 26.980
|
||||
|
||||
27.022 27.065 27.107 27.149 27.192 27.234 27.276 27.318 27.361 27.403
|
||||
27.445 27.487 27.529 27.572 27.614 27.656 27.698 27.740 27.783 27.825
|
||||
27.867 27.909 27.951 27.993 28.035 28.078 28.120 28.162 28.204 28.246
|
||||
28.288 28.330 28.372 28.414 28.456 28.498 28.540 28.583 28.625 28.667
|
||||
28.709 28.751 28.793 28.835 28.877 28.919 28.961 29.002 29.044 29.086
|
||||
|
||||
29.128 29.170 29.212 29.254 29.296 29.338 29.380 29.422 29.464 29.505
|
||||
29.547 29.589 29.631 29.673 29.715 29.756 29.798 29.840 29.882 29.924
|
||||
29.965 30.007 30.049 30.091 30.132 30.174 30.216 30.257 30.299 30.341
|
||||
30.383 30.424 30.466 30.508 30.549 30.591 30.632 30.674 30.716 30.757
|
||||
30.799 30.840 30.882 30.924 30.965 31.007 31.048 31.090 31.131 31.173
|
||||
|
||||
31.214 31.256 31.297 31.339 31.380 31.422 31.463 31.504 31.546 31.587
|
||||
31.629 31.670 31.712 31.753 31.794 31.836 31.877 31.918 31.960 32.001
|
||||
32.042 32.084 32.125 32.166 32.207 32.249 32.290 32.331 32.372 32.414
|
||||
32.455 32.496 32.537 32.578 32.619 32.661 32.702 32.743 32.784 32.825
|
||||
32.866 32.907 32.948 32.990 33.031 33.072 33.113 33.154 33.195 33.236
|
||||
|
||||
33.277 33.318 33.359 33.400 33.441 33.482 33.523 33.564 33.604 33.645
|
||||
33.686 33.727 33.768 33.809 33.850 33.891 33.931 33.972 34.013 34.054
|
||||
34.095 34.136 34.176 34.217 34.258 34.299 34.339 34.380 34.421 34.461
|
||||
34.502 34.543 34.583 34.624 34.665 34.705 34.746 34.787 34.827 34.868
|
||||
34.909 34.949 34.990 35.030 35.071 35.111 35.152 35.192 35.233 35.273
|
||||
|
||||
35.314 35.354 35.395 35.435 35.476 35.516 35.557 35.597 35.637 35.678
|
||||
35.718 35.758 35.799 35.839 35.880 35.920 35.960 36.000 36.041 36.081
|
||||
36.121 36.162 36.202 36.242 36.282 36.323 36.363 36.403 36.443 36.483
|
||||
36.524 36.564 36.604 36.644 36.684 36.724 36.764 36.804 36.844 36.885
|
||||
36.925 36.965 37.005 37.045 37.085 37.125 37.165 37.205 37.245 37.285
|
||||
|
||||
37.325 37.365 37.405 37.445 37.484 37.524 37.564 37.604 37.644 37.684
|
||||
37.724 37.764 37.803 37.843 37.883 37.923 37.963 38.002 38.042 38.082
|
||||
38.122 38.162 38.201 38.241 38.281 38.320 38.360 38.400 38.439 38.479
|
||||
38.519 38.558 38.598 38.638 38.677 38.717 38.756 38.796 38.836 38.875
|
||||
38.915 38.954 38.994 39.033 39.073 39.112 39.152 39.191 39.231 39.270
|
||||
|
||||
39.310 39.349 39.388 39.428 39.467 39.507 39.546 39.585 39.625 39.644
|
||||
39.703 39.743 39.782 39.821 39.861 39.900 39.939 39.979 40.018 40.057
|
||||
40.096 40.136 40.175 40.214 40.253 40.292 40.332 40.371 40.410 40.449
|
||||
40.488 40.527 40.566 40.605 40.645 40.684 40.723 40.762 40.801 40.840
|
||||
40.879 40.918 40.957 40.996 41.035 41.074 41.113 41.152 41.191 41.230
|
||||
|
||||
41.269 41.308 41.347 41.385 41.424 41.463 41.502 41.541 41.580 41.619
|
||||
41.657 41.696 41.735 41.774 41.813 41.851 41.890 41.929 41.968 42.006
|
||||
42.045 42.084 42.123 42.161 42.200 42.239 42.277 42.316 42.355 42.393
|
||||
42.432 42.470 42.509 42.548 42.586 42.625 42.663 42.702 42.740 42.779
|
||||
42.817 42.856 42.894 42.933 42.971 43.010 43.048 43.087 43.125 43.164
|
||||
|
||||
43.202 43.240 43.279 43.317 43.356 43.394 43.432 43.471 43.509 43.547
|
||||
43.585 43.624 43.662 43.700 43.739 43.777 43.815 43.853 43.891 43.930
|
||||
43.968 44.006 44.044 44.082 44.121 44.159 44.197 44.235 44.273 44.311
|
||||
44.349 44.387 44.425 44.463 44.501 44.539 44.577 44.615 44.653 44.691
|
||||
44.729 44.767 44.805 44.843 44.881 44.919 44.957 44.995 45.033 45.070
|
||||
|
||||
45.108 45.146 45.184 45.222 45.260 45.297 45.335 45.373 45.411 45.448
|
||||
45.486 45.524 45.561 45.599 45.637 45.675 45.712 45.750 45.787 45.825
|
||||
45.863 45.900 45.938 45.975 46.013 46.051 46.088 46.126 46.163 46.201
|
||||
46.238 46.275 46.313 46.350 46.388 46.425 46.463 46.500 46.537 46.575
|
||||
46.612 46.649 46.687 46.724 46.761 46.799 46.836 46.873 46.910 46.948
|
||||
|
||||
46.985 47.022 47.059 47.096 47.134 47.171 47.208 47.245 47.282 47.319
|
||||
47.356 47.393 47.430 47.468 47.505 47.542 47.579 47.616 47.653 47.689
|
||||
47.726 47.763 47.800 47.837 47.874 47.911 47.948 47.985 48.021 48.058
|
||||
48.095 48.132 48.169 48.205 48.242 48.279 48.316 48.352 48.389 48.426
|
||||
48.462 48.499 48.536 48.572 48.609 48.645 48.682 48.718 48.755 48.792
|
||||
|
||||
48.828 48.865 48.901 48.937 48.974 49.010 49.047 49.083 49.120 49.156
|
||||
49.192 49.229 49.265 49.301 49.338 49.374 49.410 49.446 49.483 49.519
|
||||
49.555 49.591 49.627 49.663 49.700 49.736 49.772 49.808 49.844 49.880
|
||||
49.916 49.952 49.988 50.024 50.060 50.096 50.132 50.168 50.204 50.240
|
||||
50.276 50.311 50.347 50.383 50.419 50.455 50.491 50.526 50.562 50.598
|
||||
|
||||
50.633 50.669 50.705 50.741 50.776 50.812 50.847 50.883 50.919 50.954
|
||||
50.990 51.025 51.061 51.096 51.132 51.167 51.203 51.238 51.274 51.309
|
||||
51.344 51.380 51.415 51.450 51.486 51.521 51.556 51.592 51.627 51.662
|
||||
51.697 51.733 51.768 51.803 51.838 51.873 51.908 51.943 51.979 52.014
|
||||
52.049 52.084 52.119 52.154 52.189 52.224 52.259 52.294 52.329 52.364
|
||||
|
||||
52.398 52.433 52.468 52.503 52.538 52.573 52.608 52.642 52.677 52.712
|
||||
52.747 52.781 52.816 52.851 52.886 52.920 52.955 52.989 53.024 53.059
|
||||
53.093 53.128 53.162 53.197 53.232 53.266 53.301 53.335 53.370 53.404
|
||||
53.439 53.473 53.507 53.542 53.576 53.611 53.645 53.679 53.714 53.748
|
||||
53.782 53.817 53.851 53.885 53.920 53.954 53.988 54.022 54.057 54.091
|
||||
|
||||
54.125 54.159 54.193 54.228 54.262 54.296 54.330 54.364 54.398 54.432
|
||||
54.466 54.501 54.535 54.569 54.603 54.637 54.671 54.705 54.739 54.773
|
||||
54.807 54.841 54.875
|
||||
@@ -0,0 +1,360 @@
|
||||
! cvtTypeKdegF.data
|
||||
"typeKdegF" 32 0 1832 4095 1.0 -454 2500 1
|
||||
!
|
||||
-6.458 -6.457 -6.457 -6.456
|
||||
|
||||
-6.456 -6.455 -6.454 -6.454 -6.453 -6.452 -6.451 -6.450 -6.449 -6.448
|
||||
-6.447 -6.445 -6.444 -6.443 -6.441 -6.440 -6.438 -6.436 -6.435 -6.433
|
||||
-6.431 -6.429 -6.427 -6.425 -6.423 -6.421 -6.419 -6.416 -6.414 -6.411
|
||||
-6.409 -6.406 -6.404 -6.401 -6.398 -6.395 -6.392 -6.389 -6.386 -6.383
|
||||
-6.380 -6.377 -6.373 -6.370 -6.366 -6.363 -6.359 -6.355 -6.352 -6.348
|
||||
|
||||
-6.344 -6.340 -6.336 -6.332 -6.328 -6.323 -6.319 -6.315 -6.310 -6.306
|
||||
-6.301 -6.296 -6.292 -6.287 -6.282 -6.277 -6.272 -6.267 -6.262 -6.257
|
||||
-6.251 -6.246 -6.241 -6.235 -6.230 -6.224 -6.219 -6.213 -6.207 -6.201
|
||||
-6.195 -6.189 -6.183 -6.177 -6.171 -6.165 -6.158 -6.152 -6.146 -6.139
|
||||
-6.133 -6.126 -6.119 -6.113 -6.106 -6.099 -6.092 -6.085 -6.078 -6.071
|
||||
|
||||
-6.064 -6.057 -6.049 -6.042 -6.035 -6.027 -6.020 -6.012 -6.004 -5.997
|
||||
-5.989 -5.981 -5.973 -5.965 -5.957 -5.949 -5.941 -5.933 -5.925 -5.917
|
||||
-5.908 -5.900 -5.891 -5.883 -5.874 -5.866 -5.857 -5.848 -5.839 -5.831
|
||||
-5.822 -5.813 -5.804 -5.795 -5.786 -5.776 -5.767 -5.758 -5.748 -5.739
|
||||
-5.730 -5.720 -5.711 -5.701 -5.691 -5.682 -5.672 -5.662 -5.652 -5.642
|
||||
|
||||
-5.632 -5.622 -5.612 -5.602 -5.592 -5.581 -5.571 -5.561 -5.550 -5.540
|
||||
-5.529 -5.519 -5.508 -5.497 -5.487 -5.476 -5.465 -5.454 -5.443 -5.432
|
||||
-5.421 -5.410 -5.399 -5.388 -5.376 -5.365 -5.354 -5.342 -5.331 -5.319
|
||||
-5.308 -5.296 -5.285 -5.273 -5.261 -5.249 -5.238 -5.226 -5.214 -5.202
|
||||
-5.190 -5.178 -5.165 -5.153 -5.141 -5.129 -5.116 -5.104 -5.092 -5.079
|
||||
|
||||
-5.067 -5.054 -5.041 -5.029 -5.016 -5.003 -4.990 -4.978 -4.965 -4.952
|
||||
-4.939 -4.926 -4.912 -4.899 -4.886 -4.873 -4.860 -4.846 -4.833 -4.819
|
||||
-4.806 -4.792 -4.779 -4.765 -4.752 -4.738 -4.724 -4.710 -4.697 -4.683
|
||||
-4.669 -4.655 -4.641 -4.627 -4.613 -4.598 -4.584 -4.570 -4.556 -4.541
|
||||
-4.527 -4.512 -4.498 -4.484 -4.469 -4.454 -4.440 -4.425 -4.410 -4.396
|
||||
|
||||
-4.381 -4.366 -4.351 -4.336 -4.321 -4.306 -4.291 -4.276 -4.261 -4.245
|
||||
-4.230 -4.215 -4.200 -4.184 -4.169 -4.153 -4.138 -4.122 -4.107 -4.091
|
||||
-4.075 -4.060 -4.044 -4.028 -4.012 -3.997 -3.981 -3.965 -3.949 -3.933
|
||||
-3.917 -3.901 -3.884 -3.868 -3.852 -3.836 -3.819 -3.803 -3.787 -3.770
|
||||
-3.754 -3.737 -3.721 -3.704 -3.688 -3.671 -3.654 -3.637 -3.621 -3.604
|
||||
|
||||
-3.587 -3.570 -3.553 -3.536 -3.519 -3.502 -3.485 -3.468 -3.451 -3.434
|
||||
-3.417 -3.399 -3.382 -3.365 -3.347 -3.330 -3.312 -3.295 -3.277 -3.260
|
||||
-3.242 -3.225 -3.207 -3.189 -3.172 -3.154 -3.136 -3.118 -3.100 -3.082
|
||||
-3.065 -3.047 -3.029 -3.010 -2.992 -2.974 -2.956 -2.938 -2.920 -2.902
|
||||
-2.883 -2.865 -2.847 -2.828 -2.810 -2.791 -2.773 -2.754 -2.736 -2.717
|
||||
|
||||
-2.699 -2.680 -2.661 -2.643 -2.624 -2.605 -2.586 -2.567 -2.549 -2.530
|
||||
-2.511 -2.492 -2.473 -2.454 -2.435 -2.416 -2.397 -2.377 -2.358 -2.339
|
||||
-2.320 -2.300 -2.281 -2.262 -2.243 -2.223 -2.204 -2.184 -2.165 -2.145
|
||||
-2.126 -2.106 -2.087 -2.067 -2.047 -2.028 -2.008 -1.988 -1.968 -1.949
|
||||
-1.929 -1.909 -1.889 -1.869 -1.849 -1.829 -1.809 -1.789 -1.769 -1.749
|
||||
|
||||
-1.729 -1.709 -1.689 -1.669 -1.648 -1.628 -1.608 -1.588 -1.567 -1.547
|
||||
-1.527 -1.506 -1.486 -1.465 -1.445 -1.424 -1.404 -1.383 -1.363 -1.342
|
||||
-1.322 -1.301 -1.280 -1.260 -1.239 -1.218 -1.197 -1.177 -1.156 -1.135
|
||||
-1.114 -1.093 -1.072 -1.051 -1.031 -1.010 -0.989 -0.968 -0.946 -0.925
|
||||
-0.904 -0.883 -0.862 -0.841 -0.820 -0.799 -0.777 -0.756 -0.735 -0.714
|
||||
|
||||
-0.692 -0.671 -0.650 -0.628 -0.607 -0.585 -0.564 -0.543 -0.521 -0.500
|
||||
-0.478 -0.457 -0.435 -0.413 -0.392 -0.370 -0.349 -0.327 -0.305 -0.284
|
||||
-0.262 -0.240 -0.218 -0.197 -0.175 -0.153 -0.131 -0.109 -0.088 -0.066
|
||||
-0.044 -0.022 0.000 0.022 0.044 0.066 0.088 0.110 0.132 0.154
|
||||
0.176 0.198 0.220 0.242 0.264 0.286 0.308 0.331 0.353 0.375
|
||||
|
||||
0.397 0.419 0.441 0.464 0.486 0.508 0.530 0.553 0.575 0.597
|
||||
0.619 0.642 0.664 0.686 0.709 0.731 0.753 0.776 0.798 0.821
|
||||
0.843 0.865 0.888 0.910 0.933 0.955 0.978 1.000 1.023 1.045
|
||||
1.068 1.090 1.113 1.135 1.158 1.181 1.203 1.226 1.248 1.271
|
||||
1.294 1.316 1.339 1.362 1.384 1.407 1.430 1.452 1.475 1.498
|
||||
|
||||
1.520 1.543 1.566 1.589 1.611 1.634 1.657 1.680 1.703 1.725
|
||||
1.748 1.771 1.794 1.817 1.839 1.862 1.885 1.908 1.931 1.954
|
||||
1.977 2.000 2.022 2.045 2.068 2.091 2.114 2.137 2.160 2.183
|
||||
2.206 2.229 2.252 2.275 2.298 2.321 2.344 2.367 2.390 2.413
|
||||
2.436 2.459 2.482 2.505 2.528 2.551 2.574 2.597 2.620 2.643
|
||||
|
||||
2.666 2.689 2.712 2.735 2.758 2.781 2.804 2.827 2.850 2.873
|
||||
2.896 2.920 2.943 2.966 2.989 3.012 3.035 3.058 3.081 3.104
|
||||
3.127 3.150 3.173 3.196 3.220 3.243 3.266 3.289 3.312 3.335
|
||||
3.358 3.381 3.404 3.427 3.450 3.473 3.496 3.519 3.543 3.566
|
||||
3.589 3.612 3.635 3.658 3.681 3.704 3.727 3.750 3.773 3.796
|
||||
|
||||
3.819 3.842 3.865 3.888 3.911 3.934 3.957 3.980 4.003 4.026
|
||||
4.049 4.072 4.095 4.118 4.141 4.164 4.187 4.210 4.233 4.256
|
||||
4.279 4.302 4.325 4.348 4.371 4.394 4.417 4.439 4.462 4.485
|
||||
4.508 4.531 4.554 4.577 4.600 4.622 4.645 4.668 4.691 4.714
|
||||
4.737 4.759 4.782 4.805 4.828 4.851 4.873 4.896 4.919 4.942
|
||||
|
||||
4.964 4.987 5.010 5.033 5.055 5.078 5.101 5.124 5.146 5.169
|
||||
5.192 5.214 5.237 5.260 5.282 5.305 5.327 5.350 5.373 5.395
|
||||
5.418 5.440 5.463 5.486 5.508 5.531 5.553 5.576 5.598 5.621
|
||||
5.643 5.666 5.688 5.711 5.733 5.756 5.778 5.801 5.823 5.846
|
||||
5.868 5.891 5.913 5.936 5.958 5.980 6.003 6.025 6.048 6.070
|
||||
|
||||
6.092 6.115 6.137 6.160 6.182 6.204 6.227 6.249 6.271 6.294
|
||||
6.316 6.338 6.361 6.383 6.405 6.428 6.450 6.472 6.494 6.517
|
||||
6.539 6.561 6.583 6.606 6.628 6.650 6.672 6.695 6.717 6.739
|
||||
6.761 6.784 6.806 6.828 6.850 6.873 6.895 6.917 6.939 6.961
|
||||
6.984 7.006 7.028 7.050 7.072 7.094 7.117 7.139 7.161 7.183
|
||||
|
||||
7.205 7.228 7.250 7.272 7.294 7.316 7.338 7.361 7.383 7.405
|
||||
7.427 7.449 7.471 7.494 7.516 7.538 7.560 7.582 7.604 7.627
|
||||
7.649 7.671 7.693 7.715 7.737 7.760 7.782 7.804 7.826 7.848
|
||||
7.870 7.893 7.915 7.937 7.959 7.981 8.003 8.026 8.048 8.070
|
||||
8.092 8.114 8.137 8.159 8.181 8.203 8.225 8.248 8.270 8.292
|
||||
|
||||
8.314 8.336 8.359 8.381 8.403 8.425 8.448 8.470 8.492 8.514
|
||||
8.537 8.559 8.581 8.603 8.626 8.648 8.670 8.692 8.715 8.737
|
||||
8.759 8.782 8.804 8.826 8.849 8.871 8.893 8.916 8.938 8.960
|
||||
8.983 9.005 9.027 9.050 9.072 9.094 9.117 9.139 9.161 9.184
|
||||
9.206 9.229 9.251 9.273 9.296 9.318 9.341 9.363 9.385 9.408
|
||||
|
||||
9.430 9.453 9.475 9.498 9.520 9.543 9.565 9.588 9.610 9.633
|
||||
9.655 9.678 9.700 9.723 9.745 9.768 9.790 9.813 9.835 9.858
|
||||
9.880 9.903 9.926 9.948 9.971 9.993 10.016 10.038 10.061 10.084
|
||||
10.106 10.129 10.151 10.174 10.197 10.219 10.242 10.265 10.287 10.310
|
||||
10.333 10.355 10.378 10.401 10.423 10.446 10.469 10.491 10.514 10.537
|
||||
|
||||
10.560 10.582 10.605 10.628 10.650 10.673 10.696 10.719 10.741 10.764
|
||||
10.787 10.810 10.833 10.855 10.878 10.901 10.924 10.947 10.969 10.992
|
||||
11.015 11.038 11.061 11.083 11.106 11.129 11.152 11.175 11.198 11.221
|
||||
11.243 11.266 11.289 11.312 11.335 11.358 11.381 11.404 11.426 11.449
|
||||
11.472 11.495 11.518 11.541 11.564 11.587 11.610 11.633 11.656 11.679
|
||||
|
||||
11.702 11.725 11.748 11.770 11.793 11.816 11.839 11.862 11.885 11.908
|
||||
11.931 11.954 11.977 12.000 12.023 12.046 12.069 12.092 12.115 12.138
|
||||
12.161 12.184 12.207 12.230 12.254 12.277 12.300 12.323 12.346 12.369
|
||||
12.392 12.415 12.438 12.461 12.484 12.507 12.530 12.553 12.576 12.599
|
||||
12.623 12.646 12.669 12.692 12.715 12.738 12.761 12.784 12.807 12.831
|
||||
|
||||
12.854 12.877 12.900 12.923 12.946 12.969 12.992 13.016 13.039 13.062
|
||||
13.085 13.108 13.131 13.154 13.178 13.201 13.224 13.247 13.270 13.293
|
||||
13.317 13.340 13.363 13.386 13.409 13.433 13.456 13.479 13.502 13.525
|
||||
13.549 13.572 13.595 13.618 13.641 13.665 13.688 13.711 13.734 13.757
|
||||
13.781 13.804 13.827 13.850 13.874 13.897 13.920 13.943 13.967 13.990
|
||||
|
||||
14.013 14.036 14.060 14.083 14.106 14.129 14.153 14.176 14.199 14.222
|
||||
14.246 14.269 14.292 14.316 14.339 14.362 14.385 14.409 14.432 14.455
|
||||
14.479 14.502 14.525 14.548 14.572 14.595 14.618 14.642 14.665 14.688
|
||||
14.712 14.735 14.758 14.782 14.805 14.828 14.852 14.875 14.898 14.922
|
||||
14.945 14.968 14.992 15.015 15.038 15.062 15.085 15.108 15.132 15.155
|
||||
|
||||
15.178 15.202 15.225 15.248 15.272 15.295 15.318 15.342 15.365 15.389
|
||||
15.412 15.435 15.459 15.482 15.505 15.529 15.552 15.576 15.599 15.622
|
||||
15.646 15.669 15.693 15.716 15.739 15.763 15.786 15.810 15.833 15.856
|
||||
15.880 15.903 15.927 15.950 15.974 15.997 16.020 16.044 16.067 16.091
|
||||
16.114 16.138 16.161 16.184 16.208 16.231 16.255 16.278 16.302 16.325
|
||||
|
||||
16.349 16.372 16.395 16.419 16.442 16.466 16.489 16.513 16.536 16.560
|
||||
16.583 16.607 16.630 16.654 16.677 16.700 16.724 16.747 16.771 16.794
|
||||
16.818 16.841 16.865 16.888 16.912 16.935 16.959 16.982 17.006 17.029
|
||||
17.053 17.076 17.100 17.123 17.147 17.170 17.194 17.217 17.241 17.264
|
||||
17.288 17.311 17.335 17.358 17.382 17.406 17.429 17.453 17.476 17.500
|
||||
|
||||
17.523 17.547 17.570 17.594 17.617 17.641 17.664 17.688 17.711 17.735
|
||||
17.759 17.782 17.806 17.829 17.853 17.876 17.900 17.923 17.947 17.971
|
||||
17.994 18.018 18.041 18.065 18.088 18.112 18.136 18.159 18.183 18.206
|
||||
18.230 18.253 18.277 18.301 18.324 18.348 18.371 18.395 18.418 18.442
|
||||
18.466 18.489 18.513 18.536 18.560 18.584 18.607 18.631 18.654 18.678
|
||||
|
||||
18.702 18.725 18.749 18.772 18.796 18.820 18.843 18.867 18.890 18.914
|
||||
18.938 18.961 18.985 19.008 19.032 19.056 19.079 19.103 19.127 19.150
|
||||
19.174 19.197 19.221 19.245 19.268 19.292 19.316 19.339 19.363 19.386
|
||||
19.410 19.434 19.457 19.481 19.505 19.528 19.552 19.576 19.599 19.623
|
||||
19.646 19.670 19.694 19.717 19.741 19.765 19.788 19.812 19.836 19.859
|
||||
|
||||
19.883 19.907 19.930 19.954 19.978 20.001 20.025 20.049 20.072 20.096
|
||||
20.120 20.143 20.167 20.190 20.214 20.238 20.261 20.285 20.309 20.332
|
||||
20.356 20.380 20.403 20.427 20.451 20.474 20.498 20.522 20.545 20.569
|
||||
20.593 20.616 20.640 20.664 20.688 20.711 20.735 20.759 20.782 20.806
|
||||
20.830 20.853 20.877 20.901 20.924 20.948 20.972 20.995 21.019 21.043
|
||||
|
||||
21.066 21.090 21.114 21.137 21.161 21.185 21.208 21.232 21.256 21.280
|
||||
21.303 21.327 21.351 21.374 21.398 21.422 21.445 21.469 21.493 21.516
|
||||
21.540 21.564 21.587 21.611 21.635 21.659 21.682 21.706 21.730 21.753
|
||||
21.777 21.801 21.824 21.848 21.872 21.895 21.919 21.943 21.966 21.990
|
||||
22.014 22.038 22.061 22.085 22.109 22.132 22.156 22.180 22.203 22.227
|
||||
|
||||
22.251 22.274 22.298 22.322 22.346 22.369 22.393 22.417 22.440 22.464
|
||||
22.488 22.511 22.535 22.559 22.582 22.606 22.630 22.654 22.677 22.701
|
||||
22.725 22.748 22.772 22.796 22.819 22.843 22.867 22.890 22.914 22.938
|
||||
22.961 22.985 23.009 23.032 23.056 23.080 23.104 23.127 23.151 23.175
|
||||
23.198 23.222 23.246 23.269 23.293 23.317 23.340 23.364 23.388 23.411
|
||||
|
||||
23.435 23.459 23.482 23.506 23.530 23.553 23.577 23.601 23.624 23.648
|
||||
23.672 23.695 23.719 23.743 23.766 23.790 23.814 23.837 23.861 23.885
|
||||
23.908 23.932 23.956 23.979 24.003 24.027 24.050 24.074 24.098 24.121
|
||||
24.145 24.169 24.192 24.216 24.240 24.263 24.287 24.311 24.334 24.358
|
||||
24.382 24.405 24.429 24.453 24.476 24.500 24.523 24.547 24.571 24.594
|
||||
|
||||
24.618 24.642 24.665 24.689 24.713 24.736 24.760 24.783 24.807 24.831
|
||||
24.854 24.878 24.902 24.925 24.949 24.972 24.996 25.020 25.043 25.067
|
||||
25.091 25.114 25.138 25.161 25.185 25.209 25.232 25.256 25.279 25.303
|
||||
25.327 25.350 25.374 25.397 25.421 25.445 25.468 25.492 25.515 25.539
|
||||
25.563 25.586 25.610 25.633 25.657 25.681 25.704 25.728 25.751 25.775
|
||||
|
||||
25.799 25.822 25.846 25.869 25.893 25.916 25.940 25.964 25.987 26.011
|
||||
26.034 26.058 26.081 26.105 26.128 26.152 26.176 26.199 26.223 26.246
|
||||
26.270 26.293 26.317 26.340 26.364 26.387 26.411 26.435 26.458 26.482
|
||||
26.505 26.529 26.552 26.576 26.599 26.623 26.646 26.670 26.693 26.717
|
||||
26.740 26.764 26.787 26.811 26.834 26.858 26.881 26.905 26.928 26.952
|
||||
|
||||
26.975 26.999 27.022 27.046 27.069 27.093 27.116 27.140 27.163 27.187
|
||||
27.210 27.234 27.257 27.281 27.304 27.328 27.351 27.375 27.398 27.422
|
||||
27.445 27.468 27.492 27.515 27.539 27.562 27.586 27.609 27.633 27.656
|
||||
27.679 27.703 27.726 27.750 27.773 27.797 27.820 27.843 27.867 27.890
|
||||
27.914 27.937 27.961 27.984 28.007 28.031 28.054 28.078 28.101 28.124
|
||||
|
||||
28.148 28.171 28.195 28.218 28.241 28.265 28.288 28.311 28.335 28.358
|
||||
28.382 28.405 28.428 28.452 28.475 28.498 28.522 28.545 28.569 28.592
|
||||
28.615 28.639 28.662 28.685 28.709 28.732 28.755 28.779 28.802 28.825
|
||||
28.849 28.872 28.895 28.919 28.942 28.965 28.988 29.012 29.035 29.058
|
||||
29.082 29.105 29.128 29.152 29.175 29.198 29.221 29.245 29.268 29.291
|
||||
|
||||
29.315 29.338 29.361 29.384 29.408 29.431 29.454 29.477 29.501 29.524
|
||||
29.547 29.570 29.594 29.617 29.640 29.663 29.687 29.710 29.733 29.756
|
||||
29.780 29.803 29.826 29.849 29.872 29.896 29.919 29.942 29.965 29.989
|
||||
30.012 30.035 30.058 30.081 30.104 30.128 30.151 30.174 30.197 30.220
|
||||
30.244 30.267 30.290 30.313 30.336 30.359 30.383 30.406 30.429 30.452
|
||||
|
||||
30.475 30.498 30.521 30.545 30.568 30.591 30.614 30.637 30.660 30.683
|
||||
30.706 30.730 30.753 30.776 30.799 30.822 30.845 30.868 30.891 30.914
|
||||
30.937 30.961 30.984 31.007 31.030 31.053 31.076 31.099 31.122 31.145
|
||||
31.168 31.191 31.214 31.237 31.260 31.283 31.306 31.329 31.353 31.376
|
||||
31.399 31.422 31.445 31.468 31.491 31.514 31.537 31.560 31.583 31.606
|
||||
|
||||
31.629 31.652 31.675 31.698 31.721 31.744 31.767 31.790 31.813 31.836
|
||||
31.859 31.882 31.905 31.927 31.950 31.973 31.996 32.019 32.042 32.065
|
||||
32.088 32.111 32.134 32.157 32.180 32.203 32.226 32.249 32.272 32.294
|
||||
32.317 32.340 32.363 32.386 32.409 32.432 32.455 32.478 32.501 32.523
|
||||
32.546 32.569 32.592 32.615 32.638 32.661 32.683 32.706 32.729 32.752
|
||||
|
||||
32.775 32.798 32.821 32.843 32.866 32.889 32.912 32.935 32.958 32.980
|
||||
33.003 33.026 33.049 33.072 33.094 33.117 33.140 33.163 33.186 33.208
|
||||
33.231 33.254 33.277 33.300 33.322 33.345 33.368 33.391 33.413 33.436
|
||||
33.459 33.482 33.504 33.527 33.550 33.573 33.595 33.618 33.641 33.664
|
||||
33.686 33.709 33.732 33.754 33.777 33.800 33.823 33.845 33.868 33.891
|
||||
|
||||
33.913 33.936 33.959 33.981 34.004 34.027 34.049 34.072 34.095 34.117
|
||||
34.140 34.163 34.185 34.208 34.231 34.253 34.276 34.299 34.321 34.344
|
||||
34.366 34.389 34.412 34.434 34.457 34.480 34.502 34.525 34.547 34.570
|
||||
34.593 34.615 34.638 34.660 34.683 34.705 34.728 34.751 34.773 34.796
|
||||
34.818 34.841 34.863 34.886 34.909 34.931 34.954 34.976 34.999 35.021
|
||||
|
||||
35.044 35.066 35.089 35.111 35.134 35.156 35.179 35.201 35.224 35.246
|
||||
35.269 35.291 35.314 35.336 35.359 35.381 35.404 35.426 35.449 35.471
|
||||
35.494 35.516 35.539 35.561 35.583 35.606 35.628 35.651 35.673 35.696
|
||||
35.718 35.741 35.763 35.785 35.808 35.830 35.853 35.875 35.897 35.920
|
||||
35.942 35.965 35.987 36.009 36.032 36.054 36.077 36.099 36.121 36.144
|
||||
|
||||
36.166 36.188 36.211 36.233 36.256 36.278 36.300 36.323 36.345 36.367
|
||||
36.390 36.412 36.434 36.457 36.479 36.501 36.524 36.546 36.568 36.590
|
||||
36.613 36.635 36.657 36.680 36.702 36.724 36.746 36.769 36.791 36.813
|
||||
36.836 36.858 36.880 36.902 36.925 36.947 36.969 36.991 37.014 37.036
|
||||
37.058 37.080 37.103 37.125 37.147 37.169 37.191 37.214 37.236 37.258
|
||||
|
||||
37.280 37.303 37.325 37.347 37.369 37.391 37.413 37.436 37.458 37.480
|
||||
37.502 37.524 37.547 37.569 37.591 37.613 37.635 37.657 37.679 37.702
|
||||
37.724 37.746 37.768 37.790 37.812 37.834 37.857 37.879 37.901 37.923
|
||||
37.945 37.967 37.989 38.011 38.033 38.055 38.078 38.100 38.122 38.144
|
||||
38.166 38.188 38.210 38.232 38.254 38.276 38.298 38.320 38.342 38.364
|
||||
|
||||
38.387 38.409 38.431 38.453 38.475 38.497 38.519 38.541 38.563 38.585
|
||||
38.607 38.629 38.651 38.673 38.695 38.717 38.739 38.761 38.783 38.805
|
||||
38.827 38.849 38.871 38.893 38.915 38.937 38.959 38.981 39.003 39.024
|
||||
39.046 39.068 39.090 39.112 39.134 39.156 39.178 39.200 39.222 39.244
|
||||
39.266 39.288 39.310 39.331 39.353 39.375 39.397 39.419 39.441 39.463
|
||||
|
||||
39.485 39.507 39.529 39.550 39.572 39.594 39.616 39.638 39.660 39.682
|
||||
39.703 39.725 39.747 39.769 39.791 39.813 39.835 39.856 39.878 39.900
|
||||
39.922 39.944 39.965 39.987 40.009 40.031 40.053 40.075 40.096 40.118
|
||||
40.140 40.162 40.183 40.205 40.227 40.249 40.271 40.292 40.314 40.336
|
||||
40.358 40.379 40.401 40.423 40.445 40.466 40.488 40.510 40.532 40.553
|
||||
|
||||
40.575 40.597 40.619 40.640 40.662 40.684 40.705 40.727 40.749 40.770
|
||||
40.792 40.814 40.836 40.857 40.879 40.901 40.922 40.944 40.966 40.987
|
||||
41.009 41.031 41.052 41.074 41.096 41.117 41.139 41.161 41.182 41.204
|
||||
41.225 41.247 41.269 41.290 41.312 41.334 41.355 41.377 41.398 41.420
|
||||
41.442 41.463 41.485 41.506 41.528 41.550 41.571 41.593 41.614 41.636
|
||||
|
||||
41.657 41.679 41.701 41.722 41.744 41.765 41.787 41.808 41.830 41.851
|
||||
41.873 41.895 41.916 41.938 41.959 41.981 42.002 42.024 42.045 42.067
|
||||
42.088 42.110 42.131 42.153 42.174 42.196 42.217 42.239 42.260 42.282
|
||||
42.303 42.325 42.346 42.367 42.389 42.410 42.432 42.453 42.475 42.496
|
||||
42.518 42.539 42.560 42.582 42.603 42.625 42.646 42.668 42.689 42.710
|
||||
|
||||
42.732 42.753 42.775 42.796 42.817 42.839 42.860 42.882 42.903 42.924
|
||||
42.946 42.967 42.989 43.010 43.031 43.053 43.074 43.095 43.117 43.138
|
||||
43.159 43.181 43.202 43.223 43.245 43.266 43.287 43.309 43.330 43.351
|
||||
43.373 43.394 43.415 43.436 43.458 43.479 43.500 43.522 43.543 43.564
|
||||
43.585 43.607 43.628 43.649 43.671 43.692 43.713 43.734 43.756 43.777
|
||||
|
||||
43.798 43.819 43.841 43.862 43.883 43.904 43.925 43.947 43.968 43.989
|
||||
44.010 44.031 44.053 44.074 44.095 44.116 44.137 44.159 44.180 44.201
|
||||
44.222 44.243 44.265 44.286 44.307 44.328 44.349 44.370 44.391 44.413
|
||||
44.434 44.455 44.476 44.497 44.518 44.539 44.560 44.582 44.603 44.624
|
||||
44.645 44.666 44.687 44.708 44.729 44.750 44.771 44.793 44.814 44.835
|
||||
|
||||
44.856 44.877 44.898 44.919 44.940 44.961 44.982 45.003 45.024 45.045
|
||||
45.066 45.087 45.108 45.129 45.150 45.171 45.192 45.213 45.234 45.255
|
||||
45.276 45.297 45.318 45.339 45.360 45.381 45.402 45.423 45.444 45.465
|
||||
45.486 45.507 45.528 45.549 45.570 45.591 45.612 45.633 45.654 45.675
|
||||
45.695 45.716 45.737 45.758 45.779 45.800 45.821 45.842 45.863 45.884
|
||||
|
||||
45.904 45.925 45.946 45.967 45.988 46.009 46.030 46.051 46.071 46.092
|
||||
46.113 46.134 46.155 46.176 46.196 46.217 46.238 46.259 46.280 46.300
|
||||
46.321 46.342 46.363 46.384 46.404 46.425 46.446 46.467 46.488 46.508
|
||||
46.529 46.550 46.571 46.591 46.612 46.633 46.654 46.674 46.695 46.716
|
||||
46.737 46.757 46.778 46.799 46.819 46.840 46.861 46.881 46.902 46.923
|
||||
|
||||
46.944 46.964 46.985 47.006 47.026 47.047 47.068 47.088 47.109 47.130
|
||||
47.150 47.171 47.191 47.212 47.233 47.253 47.274 47.295 47.315 47.336
|
||||
47.356 47.377 47.398 47.418 47.439 47.459 47.480 47.500 47.521 47.542
|
||||
47.562 47.583 47.603 47.624 47.644 47.665 47.685 47.706 47.726 47.747
|
||||
47.767 47.788 47.808 47.829 47.849 47.870 47.890 47.911 47.931 47.952
|
||||
|
||||
47.972 47.993 48.013 48.034 48.054 48.075 48.095 48.116 48.136 48.156
|
||||
48.177 48.197 48.218 48.238 48.258 48.279 48.299 48.320 48.340 48.360
|
||||
48.381 48.401 48.422 48.442 48.462 48.483 48.503 48.523 48.544 48.564
|
||||
48.584 48.605 48.625 48.645 48.666 48.686 48.706 48.727 48.747 48.767
|
||||
48.787 48.808 48.828 48.848 48.869 48.889 48.909 48.929 48.950 48.970
|
||||
|
||||
48.990 49.010 49.031 49.051 49.071 49.091 49.111 49.132 49.152 49.172
|
||||
49.192 49.212 49.233 49.253 49.273 49.293 49.313 49.333 49.354 49.374
|
||||
49.394 49.414 49.434 49.454 49.474 49.495 49.515 49.535 49.555 49.575
|
||||
49.595 49.615 49.635 49.655 49.675 49.696 49.716 49.736 49.756 49.776
|
||||
49.796 49.816 49.836 49.856 49.876 49.896 49.916 49.936 49.956 49.976
|
||||
|
||||
49.996 50.016 50.036 50.056 50.076 50.096 50.116 50.136 50.156 50.176
|
||||
50.196 50.216 50.236 50.256 50.276 50.296 50.315 50.335 50.355 50.375
|
||||
50.395 50.415 50.435 50.455 50.475 50.494 50.514 50.534 50.554 50.574
|
||||
50.594 50.614 50.633 50.653 50.673 50.693 50.713 50.733 50.752 50.772
|
||||
50.792 50.812 50.832 50.851 50.871 50.891 50.911 50.930 50.950 50.970
|
||||
|
||||
50.990 51.009 51.029 51.049 51.069 51.088 51.108 51.128 51.148 51.167
|
||||
51.187 51.207 51.226 51.246 51.266 51.285 51.305 51.325 51.344 51.364
|
||||
51.384 51.403 51.423 51.443 51.462 51.482 51.501 51.521 51.541 51.560
|
||||
51.580 51.599 51.619 51.639 51.658 51.678 51.697 51.717 51.736 51.756
|
||||
51.776 51.795 51.815 51.834 51.854 51.873 51.893 51.912 51.932 51.951
|
||||
|
||||
51.971 51.990 52.010 52.029 52.049 52.068 52.088 52.107 52.127 52.146
|
||||
52.165 52.185 52.204 52.224 52.243 52.263 52.282 52.301 52.321 52.340
|
||||
52.360 52.379 52.398 52.418 52.437 52.457 52.476 52.495 52.515 52.534
|
||||
52.553 52.573 52.592 52.611 52.631 52.650 52.669 52.689 52.708 52.727
|
||||
52.747 52.766 52.785 52.805 52.824 52.843 52.862 52.882 52.901 52.920
|
||||
|
||||
52.939 52.959 52.978 52.997 53.016 53.036 53.055 53.074 53.093 53.113
|
||||
53.132 53.151 53.170 53.189 53.209 53.228 53.247 53.266 53.285 53.304
|
||||
53.324 53.343 53.362 53.381 53.400 53.419 53.439 53.458 53.477 53.496
|
||||
53.515 53.534 53.553 53.572 53.592 53.611 53.630 53.649 53.668 53.687
|
||||
53.706 53.725 53.744 53.763 53.782 53.801 53.821 53.840 53.859 53.878
|
||||
|
||||
53.897 53.916 53.935 53.954 53.973 53.992 54.011 54.030 54.049 54.068
|
||||
54.087 54.106 54.125 54.144 54.163 54.182 54.201 54.220 54.239 54.258
|
||||
54.277 54.296 54.315 54.334 54.353 54.372 54.391 54.410 54.429 54.447
|
||||
54.466 54.485 54.504 54.523 54.542 54.561 54.580 54.599 54.618 54.637
|
||||
54.656 54.675 54.694 54.712 54.731 54.750 54.769 54.788 54.807 54.826
|
||||
|
||||
54.845
|
||||
@@ -0,0 +1,37 @@
|
||||
/*************************************************************************\
|
||||
* 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.
|
||||
\*************************************************************************/
|
||||
/*
|
||||
* Breakpoint Tables
|
||||
*
|
||||
* Author: Marty Kraimer
|
||||
* Date: 11-7-90
|
||||
*/
|
||||
|
||||
#ifndef INCcvtTableh
|
||||
#define INCcvtTableh 1
|
||||
|
||||
#include "shareLib.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Global Routines*/
|
||||
epicsShareFunc long cvtEngToRawBpt(
|
||||
double *pval,short linr,short init,void **ppbrk,short *plbrk);
|
||||
|
||||
epicsShareFunc long cvtRawToEngBpt(
|
||||
double *pval,short linr,short init,void **ppbrk, short *plbrk);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,427 @@
|
||||
/*************************************************************************\
|
||||
* 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.
|
||||
\*************************************************************************/
|
||||
/*
|
||||
* Author: Marty Kraimer
|
||||
* Date: 9/28/95
|
||||
* Replacement for old bldCvtTable
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#include "dbDefs.h"
|
||||
#include "ellLib.h"
|
||||
#include "cvtTable.h"
|
||||
|
||||
#define MAX_LINE_SIZE 160
|
||||
#define MAX_BREAKS 100
|
||||
struct brkCreateInfo {
|
||||
double engLow; /* Lowest value desired: engineering units */
|
||||
double engHigh; /* Highest value desired: engineering units */
|
||||
double rawLow; /* Raw value for EngLow */
|
||||
double rawHigh; /* Raw value for EngHigh */
|
||||
double accuracy; /* accuracy desired in engineering units */
|
||||
double tblEngFirst;/* First table value: engineering units */
|
||||
double tblEngLast; /* Last table value: engineering units */
|
||||
double tblEngDelta;/* Change per table entry: eng units */
|
||||
long nTable; /* number of table entries */
|
||||
/* (last-first)/delta + 1 */
|
||||
double *pTable; /* addr of data table */
|
||||
} brkCreateInfo;
|
||||
|
||||
typedef struct brkInt { /* breakpoint interval */
|
||||
double raw; /* raw value for beginning of interval */
|
||||
double slope; /* slope for interval */
|
||||
double eng; /* converted value for beginning of interval */
|
||||
} brkInt;
|
||||
|
||||
brkInt brkint[MAX_BREAKS];
|
||||
|
||||
static int create_break(struct brkCreateInfo *pbci, brkInt *pabrkInt,
|
||||
int max_breaks, int *n_breaks);
|
||||
static char inbuf[MAX_LINE_SIZE];
|
||||
static int linenum=0;
|
||||
|
||||
typedef struct dataList{
|
||||
struct dataList *next;
|
||||
double value;
|
||||
}dataList;
|
||||
|
||||
static int getNumber(char **pbeg, double *value)
|
||||
{
|
||||
int nchars=0;
|
||||
|
||||
while(isspace((int)**pbeg) && **pbeg!= '\0') (*pbeg)++;
|
||||
if(**pbeg == '!' || **pbeg == '\0') return(-1);
|
||||
if(sscanf(*pbeg,"%lf%n",value,&nchars)!=1) return(-1);
|
||||
*pbeg += nchars;
|
||||
return(0);
|
||||
}
|
||||
|
||||
static void errExit(char *pmessage)
|
||||
{
|
||||
fprintf(stderr, "%s\n", pmessage);
|
||||
fflush(stderr);
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
char *pbeg;
|
||||
char *pend;
|
||||
double value;
|
||||
char *pname = NULL;
|
||||
dataList *phead;
|
||||
dataList *pdataList;
|
||||
dataList *pnext;
|
||||
double *pdata;
|
||||
long ndata;
|
||||
int nBreak,n;
|
||||
size_t len;
|
||||
char *outFilename;
|
||||
char *pext;
|
||||
FILE *outFile;
|
||||
FILE *inFile;
|
||||
char *plastSlash;
|
||||
|
||||
|
||||
if(argc<2) {
|
||||
fprintf(stderr,"usage: makeBpt file.data [outfile]\n");
|
||||
exit(-1);
|
||||
}
|
||||
if (argc==2) {
|
||||
plastSlash = strrchr(argv[1],'/');
|
||||
plastSlash = (plastSlash ? plastSlash+1 : argv[1]);
|
||||
outFilename = calloc(1,strlen(plastSlash)+2);
|
||||
if(!outFilename) {
|
||||
fprintf(stderr,"calloc failed\n");
|
||||
exit(-1);
|
||||
}
|
||||
strcpy(outFilename,plastSlash);
|
||||
pext = strstr(outFilename,".data");
|
||||
if(!pext) {
|
||||
fprintf(stderr,"Input file MUST have .data extension\n");
|
||||
exit(-1);
|
||||
}
|
||||
strcpy(pext,".dbd");
|
||||
} else {
|
||||
outFilename = calloc(1,strlen(argv[2])+1);
|
||||
if(!outFilename) {
|
||||
fprintf(stderr,"calloc failed\n");
|
||||
exit(-1);
|
||||
}
|
||||
strcpy(outFilename,argv[2]);
|
||||
}
|
||||
inFile = fopen(argv[1],"r");
|
||||
if(!inFile) {
|
||||
fprintf(stderr,"Error opening %s\n",argv[1]);
|
||||
exit(-1);
|
||||
}
|
||||
outFile = fopen(outFilename,"w");
|
||||
if(!outFile) {
|
||||
fprintf(stderr,"Error opening %s\n",outFilename);
|
||||
exit(-1);
|
||||
}
|
||||
while(fgets(inbuf,MAX_LINE_SIZE,inFile)) {
|
||||
linenum++;
|
||||
pbeg = inbuf;
|
||||
while(isspace((int)*pbeg) && *pbeg!= '\0') pbeg++;
|
||||
if(*pbeg == '!' || *pbeg == '\0') continue;
|
||||
while(*pbeg!='"' && *pbeg!= '\0') pbeg++;
|
||||
if(*pbeg!='"' ) errExit("Illegal Header");
|
||||
pbeg++; pend = pbeg;
|
||||
while(*pend!='"' && *pend!= '\0') pend++;
|
||||
if(*pend!='"') errExit("Illegal Header");
|
||||
len = pend - pbeg;
|
||||
if(len<=1) errExit("Illegal Header");
|
||||
pname = calloc(len+1,sizeof(char));
|
||||
if(!pname) {
|
||||
fprintf(stderr,"calloc failed while processing line %d\n",linenum);
|
||||
exit(-1);
|
||||
}
|
||||
strncpy(pname,pbeg,len);
|
||||
pname[len]='\0';
|
||||
pbeg = pend + 1;
|
||||
if(getNumber(&pbeg,&value)) errExit("Illegal Header");
|
||||
brkCreateInfo.engLow = value;
|
||||
if(getNumber(&pbeg,&value)) errExit("Illegal Header");
|
||||
brkCreateInfo.rawLow = value;
|
||||
if(getNumber(&pbeg,&value)) errExit("Illegal Header");
|
||||
brkCreateInfo.engHigh = value;
|
||||
if(getNumber(&pbeg,&value)) errExit("Illegal Header");
|
||||
brkCreateInfo.rawHigh = value;
|
||||
if(getNumber(&pbeg,&value)) errExit("Illegal Header");
|
||||
brkCreateInfo.accuracy = value;
|
||||
if(getNumber(&pbeg,&value)) errExit("Illegal Header");
|
||||
brkCreateInfo.tblEngFirst = value;
|
||||
if(getNumber(&pbeg,&value)) errExit("Illegal Header");
|
||||
brkCreateInfo.tblEngLast = value;
|
||||
if(getNumber(&pbeg,&value)) errExit("Illegal Header");
|
||||
brkCreateInfo.tblEngDelta = value;
|
||||
goto got_header;
|
||||
}
|
||||
errExit("Illegal Header");
|
||||
got_header:
|
||||
phead = pnext = 0;
|
||||
ndata = 0;
|
||||
errno = 0;
|
||||
while(fgets(inbuf,MAX_LINE_SIZE,inFile)) {
|
||||
double value;
|
||||
|
||||
linenum++;
|
||||
pbeg = inbuf;
|
||||
while(!getNumber(&pbeg,&value)) {
|
||||
ndata++;
|
||||
pdataList = (dataList *)calloc(1,sizeof(dataList));
|
||||
if(!pdataList) {
|
||||
fprintf(stderr,"calloc failed (after header)"
|
||||
" while processing line %d\n",linenum);
|
||||
exit(-1);
|
||||
}
|
||||
if(!phead)
|
||||
phead = pdataList;
|
||||
else
|
||||
pnext->next = pdataList;
|
||||
pdataList->value = value;
|
||||
pnext = pdataList;
|
||||
}
|
||||
}
|
||||
if(!pname) {
|
||||
errExit("create_break failed: no name specified\n");
|
||||
}
|
||||
brkCreateInfo.nTable = ndata;
|
||||
pdata = (double *)calloc(brkCreateInfo.nTable,sizeof(double));
|
||||
if(!pdata) {
|
||||
fprintf(stderr,"calloc failed for table length %ld\n",brkCreateInfo.nTable);
|
||||
exit(-1);
|
||||
}
|
||||
pnext = phead;
|
||||
for(n=0; n<brkCreateInfo.nTable; n++) {
|
||||
pdata[n] = pnext->value;
|
||||
pdataList = pnext;
|
||||
pnext = pnext->next;
|
||||
free((void *)pdataList);
|
||||
}
|
||||
brkCreateInfo.pTable = pdata;
|
||||
if(create_break(&brkCreateInfo,&brkint[0],MAX_BREAKS,&nBreak))
|
||||
errExit("create_break failed\n");
|
||||
fprintf(outFile,"breaktable(%s) {\n",pname);
|
||||
for(n=0; n<nBreak; n++) {
|
||||
fprintf(outFile,"\t%f %f\n",brkint[n].raw,brkint[n].eng);
|
||||
}
|
||||
fprintf(outFile,"}\n");
|
||||
fclose(inFile);
|
||||
fclose(outFile);
|
||||
return(0);
|
||||
}
|
||||
|
||||
static int create_break( struct brkCreateInfo *pbci, brkInt *pabrkInt,
|
||||
int max_breaks, int *n_breaks)
|
||||
{
|
||||
brkInt *pbrkInt;
|
||||
double *table = pbci->pTable;
|
||||
long ntable = pbci->nTable;
|
||||
double ilow,
|
||||
ihigh,
|
||||
tbllow,
|
||||
tblhigh,
|
||||
slope,
|
||||
offset;
|
||||
int ibeg,
|
||||
iend,
|
||||
i,
|
||||
inc,
|
||||
imax,
|
||||
n;
|
||||
double rawBeg,
|
||||
engBeg,
|
||||
rawEnd,
|
||||
engEnd,
|
||||
engCalc,
|
||||
engActual,
|
||||
error;
|
||||
int valid,
|
||||
all_ok,
|
||||
expanding;
|
||||
/* make checks to ensure that brkCreateInfo makes sense */
|
||||
if (pbci->engLow >= pbci->engHigh) {
|
||||
errExit("create_break: engLow >= engHigh");
|
||||
return (-1);
|
||||
}
|
||||
if ((pbci->engLow < pbci->tblEngFirst)
|
||||
|| (pbci->engHigh > pbci->tblEngLast)) {
|
||||
errExit("create_break: engLow > engHigh");
|
||||
return (-1);
|
||||
}
|
||||
if (pbci->tblEngDelta <= 0.0) {
|
||||
errExit("create_break: tblEngDelta <= 0.0");
|
||||
return (-1);
|
||||
}
|
||||
if (ntable < 3) {
|
||||
errExit("raw data must have at least 3 elements");
|
||||
return (-1);
|
||||
}
|
||||
/***************************************************************************
|
||||
Convert Table to raw values
|
||||
*
|
||||
* raw and table values are assumed to be related by an equation of the form:
|
||||
*
|
||||
* raw = slope*table + offset
|
||||
*
|
||||
* The following algorithm converts each table value to raw units
|
||||
*
|
||||
* 1) Finds the locations in Table corresponding to engLow and engHigh
|
||||
* Note that these locations need not be exact integers
|
||||
* 2) Interpolates to obtain table values corresponding to engLow and enghigh
|
||||
* we now have the equations:
|
||||
* rawLow = slope*tblLow + offset
|
||||
* rawHigh = slope*tblHigh + offset
|
||||
* 4) Solving these equations for slope and offset gives:
|
||||
* slope=(rawHigh-rawLow)/(tblHigh-tblLow)
|
||||
* offset=rawHigh-slope*tblHigh
|
||||
* 5) for each table value set table[i]=table[i]*slope+offset
|
||||
*************************************************************************/
|
||||
/* Find engLow in Table and then compute tblLow */
|
||||
ilow = (pbci->engLow - pbci->tblEngFirst) / (pbci->tblEngDelta);
|
||||
i = (int) ilow;
|
||||
if (i >= ntable - 1)
|
||||
i = ntable - 2;
|
||||
tbllow = table[i] + (table[i + 1] - table[i]) * (ilow - (double) i);
|
||||
/* Find engHigh in Table and then compute tblHigh */
|
||||
ihigh = (pbci->engHigh - pbci->tblEngFirst) / (pbci->tblEngDelta);
|
||||
i = (int) ihigh;
|
||||
if (i >= ntable - 1)
|
||||
i = ntable - 2;
|
||||
tblhigh = table[i] + (table[i + 1] - table[i]) * (ihigh - (double) i);
|
||||
/* compute slope and offset */
|
||||
slope = (pbci->rawHigh - pbci->rawLow) / (tblhigh - tbllow);
|
||||
offset = pbci->rawHigh - slope * tblhigh;
|
||||
/* convert table to raw units */
|
||||
for (i = 0; i < ntable; i++)
|
||||
table[i] = table[i] * slope + offset;
|
||||
|
||||
/*****************************************************************************
|
||||
* Now create break point table
|
||||
*
|
||||
* The algorithm does the following:
|
||||
*
|
||||
* It finds one breakpoint interval at a time. For each it does the following:
|
||||
*
|
||||
* 1) Use a relatively large portion of the remaining table as an interval
|
||||
* 2) It attempts to use the entire interval as a breakpoint interval
|
||||
* Success is determined by the following algorithm:
|
||||
* a) compute the slope using the entire interval
|
||||
* b) for each table entry in the interval determine the eng value
|
||||
* using the slope just determined.
|
||||
* c) compare the computed value with eng value associated with table
|
||||
* d) if all table entries are within the accuracy desired then success.
|
||||
* 3) If successful then attempt to expand the interval and try again.
|
||||
* Note that it is expanded by up to 1/10 of the table size.
|
||||
* 4) If not successful reduce the interval by 1 and try again.
|
||||
* Once the interval is being decreased it will never be increased again.
|
||||
* 5) The algorithm will ultimately fail or will have determined the optimum
|
||||
* breakpoint interval
|
||||
*************************************************************************/
|
||||
|
||||
/* Must start with table entry corresponding to engLow; */
|
||||
i = (int) ilow;
|
||||
if (i >= ntable - 1)
|
||||
i = ntable - 2;
|
||||
rawBeg = table[i] + (table[i + 1] - table[i]) * (ilow - (double) i);
|
||||
engBeg = pbci->engLow;
|
||||
ibeg = (int) (ilow); /* Make sure that ibeg > ilow */
|
||||
if( ibeg < ilow ) ibeg = ibeg + 1;
|
||||
/* start first breakpoint interval */
|
||||
n = 1;
|
||||
pbrkInt = pabrkInt;
|
||||
pbrkInt->raw = rawBeg;
|
||||
pbrkInt->eng = engBeg;
|
||||
/* determine next breakpoint interval */
|
||||
while ((engBeg <= pbci->engHigh) && (ibeg < ntable - 1)) {
|
||||
/* determine next interval to try. Up to 1/10 full range */
|
||||
rawEnd = rawBeg;
|
||||
engEnd = engBeg;
|
||||
iend = ibeg;
|
||||
inc = (int) ((ihigh - ilow) / 10.0);
|
||||
if (inc < 1)
|
||||
inc = 1;
|
||||
valid = TRUE;
|
||||
/* keep trying intervals until cant do better */
|
||||
expanding = TRUE; /* originally we are trying larger and larger
|
||||
* intervals */
|
||||
while (valid) {
|
||||
imax = iend + inc;
|
||||
if (imax >= ntable) {
|
||||
/* don't go past end of table */
|
||||
imax = ntable - 1;
|
||||
inc = ntable - iend - 1;
|
||||
expanding = FALSE;
|
||||
}
|
||||
if (imax > (int) (ihigh + 1.0)) { /* Don't go to far past
|
||||
* engHigh */
|
||||
imax = (int) (ihigh + 1.0);
|
||||
inc = (int) (ihigh + 1.0) - iend;
|
||||
expanding = FALSE;
|
||||
}
|
||||
if (imax <= ibeg)
|
||||
break; /* failure */
|
||||
rawEnd = table[imax];
|
||||
engEnd = pbci->tblEngFirst + (double) imax *(pbci->tblEngDelta);
|
||||
slope = (engEnd - engBeg) / (rawEnd - rawBeg);
|
||||
all_ok = TRUE;
|
||||
for (i = ibeg + 1; i <= imax; i++) {
|
||||
engCalc = engBeg + slope * (table[i] - rawBeg);
|
||||
engActual = pbci->tblEngFirst + ((double) i) * (pbci->tblEngDelta);
|
||||
error = engCalc - engActual;
|
||||
if (error < 0.0)
|
||||
error = -error;
|
||||
if (error >= pbci->accuracy) {
|
||||
/* we will be trying smaller intervals */
|
||||
expanding = FALSE;
|
||||
/* just decrease inc and let while(valid) try again */
|
||||
inc--;
|
||||
all_ok = FALSE;
|
||||
break;
|
||||
}
|
||||
} /* end for */
|
||||
if (all_ok) {
|
||||
iend = imax;
|
||||
/* if not expanding we found interval */
|
||||
if (!expanding)
|
||||
break;
|
||||
/* will automatically try larger interval */
|
||||
}
|
||||
} /* end while(valid) */
|
||||
/* either we failed or optimal interval has been found */
|
||||
if ((iend <= ibeg) && (iend < (int) ihigh)) {
|
||||
errExit("Could not meet accuracy criteria");
|
||||
return (-1);
|
||||
}
|
||||
pbrkInt->slope = slope;
|
||||
/* get ready for next breakpoint interval */
|
||||
if (n++ >= max_breaks) {
|
||||
errExit("Break point table too large");
|
||||
return (-1);
|
||||
}
|
||||
ibeg = iend;
|
||||
pbrkInt++;
|
||||
rawBeg = rawEnd;
|
||||
engBeg = engEnd;
|
||||
pbrkInt->raw = rawBeg;
|
||||
pbrkInt->eng = engBeg + (pbrkInt->raw - rawBeg) * slope;
|
||||
}
|
||||
pbrkInt->slope = 0.0;
|
||||
*n_breaks = n;
|
||||
return (0);
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
#*************************************************************************
|
||||
# Copyright (c) 2013 UChicago Argonne LLC, as Operator of Argonne
|
||||
# National Laboratory.
|
||||
# Copyright (c) 2002 The Regents of the University of California, as
|
||||
# Operator of Los Alamos National Laboratory.
|
||||
# EPICS BASE is distributed subject to a Software License Agreement found
|
||||
# in file LICENSE that is included with this distribution.
|
||||
#*************************************************************************
|
||||
|
||||
=head1 Menu menuConvert
|
||||
|
||||
This menu defines the standard analog conversions which are included with Base.
|
||||
IOC applications may add choices or replace the later choices in this menu,
|
||||
although the first three choices must not be renamed or moved to different
|
||||
positions. The breakpoint table name must exactly match the choice string
|
||||
listed here.
|
||||
|
||||
=menu menuConvert
|
||||
|
||||
=cut
|
||||
|
||||
menu(menuConvert) {
|
||||
choice(menuConvertNO_CONVERSION,"NO CONVERSION")
|
||||
choice(menuConvertSLOPE,"SLOPE")
|
||||
choice(menuConvertLINEAR,"LINEAR")
|
||||
choice(menuConverttypeKdegF,"typeKdegF")
|
||||
choice(menuConverttypeKdegC,"typeKdegC")
|
||||
choice(menuConverttypeJdegF,"typeJdegF")
|
||||
choice(menuConverttypeJdegC,"typeJdegC")
|
||||
choice(menuConverttypeEdegF,"typeEdegF(ixe only)")
|
||||
choice(menuConverttypeEdegC,"typeEdegC(ixe only)")
|
||||
choice(menuConverttypeTdegF,"typeTdegF")
|
||||
choice(menuConverttypeTdegC,"typeTdegC")
|
||||
choice(menuConverttypeRdegF,"typeRdegF")
|
||||
choice(menuConverttypeRdegC,"typeRdegC")
|
||||
choice(menuConverttypeSdegF,"typeSdegF")
|
||||
choice(menuConverttypeSdegC,"typeSdegC")
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
/*************************************************************************\
|
||||
* 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.
|
||||
\*************************************************************************/
|
||||
|
||||
#ifndef DATABASEVERSION_H
|
||||
#define DATABASEVERSION_H
|
||||
|
||||
#include <epicsVersion.h>
|
||||
|
||||
#ifndef VERSION_INT
|
||||
# define VERSION_INT(V,R,M,P) ( ((V)<<24) | ((R)<<16) | ((M)<<8) | (P))
|
||||
#endif
|
||||
|
||||
/* include generated headers with:
|
||||
* EPICS_DATABASE_MAJOR_VERSION
|
||||
* EPICS_DATABASE_MINOR_VERSION
|
||||
* EPICS_DATABASE_MAINTENANCE_VERSION
|
||||
* EPICS_DATABASE_DEVELOPMENT_FLAG
|
||||
*/
|
||||
#include "databaseVersionNum.h"
|
||||
|
||||
#define DATABASE_VERSION_INT VERSION_INT(EPICS_DATABASE_MAJOR_VERSION, EPICS_DATABASE_MINOR_VERSION, EPICS_DATABASE_MAINTENANCE_VERSION, 0)
|
||||
|
||||
#endif // DATABASEVERSION_H
|
||||
@@ -0,0 +1,7 @@
|
||||
#ifndef DATABASEVERSION_H
|
||||
# error include databaseVersion.h, not this header
|
||||
#endif
|
||||
#define EPICS_DATABASE_MAJOR_VERSION @EPICS_DATABASE_MAJOR_VERSION@
|
||||
#define EPICS_DATABASE_MINOR_VERSION @EPICS_DATABASE_MINOR_VERSION@
|
||||
#define EPICS_DATABASE_MAINTENANCE_VERSION @EPICS_DATABASE_MAINTENANCE_VERSION@
|
||||
#define EPICS_DATABASE_DEVELOPMENT_FLAG @EPICS_DATABASE_DEVELOPMENT_FLAG@
|
||||
@@ -0,0 +1,101 @@
|
||||
#*************************************************************************
|
||||
# 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.
|
||||
#*************************************************************************
|
||||
|
||||
# This is a Makefile fragment, see src/ioc/Makefile.
|
||||
|
||||
SRC_DIRS += $(IOCDIR)/db
|
||||
|
||||
INC += callback.h
|
||||
INC += dbAccess.h
|
||||
INC += dbAccessDefs.h
|
||||
INC += dbAddr.h
|
||||
INC += dbBkpt.h
|
||||
INC += dbCa.h
|
||||
INC += dbChannel.h
|
||||
INC += dbConstLink.h
|
||||
INC += dbConvert.h
|
||||
INC += dbConvertFast.h
|
||||
INC += dbConvertJSON.h
|
||||
INC += dbDbLink.h
|
||||
INC += dbExtractArray.h
|
||||
INC += dbEvent.h
|
||||
INC += dbJLink.h
|
||||
INC += dbLink.h
|
||||
INC += dbLock.h
|
||||
INC += dbNotify.h
|
||||
INC += dbScan.h
|
||||
INC += dbServer.h
|
||||
INC += dbTest.h
|
||||
INC += dbCaTest.h
|
||||
INC += db_test.h
|
||||
INC += db_field_log.h
|
||||
INC += initHooks.h
|
||||
INC += recGbl.h
|
||||
INC += dbIocRegister.h
|
||||
INC += chfPlugin.h
|
||||
INC += dbState.h
|
||||
INC += db_access_routines.h
|
||||
INC += db_convert.h
|
||||
INC += dbUnitTest.h
|
||||
|
||||
# Generate menuGlobal.dbd, not really by concatenation, see RULES
|
||||
DBDCAT += menuGlobal.dbd
|
||||
menuGlobal_DBD += menuAlarmSevr.dbd
|
||||
menuGlobal_DBD += menuAlarmStat.dbd
|
||||
menuGlobal_DBD += menuFtype.dbd
|
||||
menuGlobal_DBD += menuIvoa.dbd
|
||||
menuGlobal_DBD += menuOmsl.dbd
|
||||
menuGlobal_DBD += menuPini.dbd
|
||||
menuGlobal_DBD += menuPost.dbd
|
||||
menuGlobal_DBD += menuPriority.dbd
|
||||
menuGlobal_DBD += menuYesNo.dbd
|
||||
menuGlobal_DBD += menuSimm.dbd
|
||||
|
||||
DBDINC += $(basename $(menuGlobal_DBD))
|
||||
DBDINC += menuScan
|
||||
DBDINC += dbCommon
|
||||
|
||||
dbMenusPod = $(notdir $(wildcard ../db/menu*.dbd.pod))
|
||||
HTMLS += $(patsubst %.dbd.pod,%.html,$(menusPod))
|
||||
|
||||
dbCore_SRCS += dbLock.c
|
||||
dbCore_SRCS += dbAccess.c
|
||||
dbCore_SRCS += dbBkpt.c
|
||||
dbCore_SRCS += dbChannel.c
|
||||
dbCore_SRCS += dbConstLink.c
|
||||
dbCore_SRCS += dbConvert.c
|
||||
dbCore_SRCS += dbConvertJSON.c
|
||||
dbCore_SRCS += dbDbLink.c
|
||||
dbCore_SRCS += dbFastLinkConv.c
|
||||
dbCore_SRCS += dbExtractArray.c
|
||||
dbCore_SRCS += dbJLink.c
|
||||
dbCore_SRCS += dbLink.c
|
||||
dbCore_SRCS += dbNotify.c
|
||||
dbCore_SRCS += dbScan.c
|
||||
dbCore_SRCS += dbEvent.c
|
||||
dbCore_SRCS += dbTest.c
|
||||
dbCore_SRCS += db_access.c
|
||||
dbCore_SRCS += db_test.c
|
||||
dbCore_SRCS += recGbl.c
|
||||
dbCore_SRCS += callback.c
|
||||
dbCore_SRCS += dbCa.c
|
||||
dbCore_SRCS += dbCaTest.c
|
||||
dbCore_SRCS += initHooks.c
|
||||
dbCore_SRCS += cvtBpt.c
|
||||
dbCore_SRCS += dbContext.cpp
|
||||
dbCore_SRCS += dbChannelIO.cpp
|
||||
dbCore_SRCS += dbSubscriptionIO.cpp
|
||||
dbCore_SRCS += dbPutNotifyBlocker.cpp
|
||||
dbCore_SRCS += dbContextReadNotifyCache.cpp
|
||||
dbCore_SRCS += dbIocRegister.c
|
||||
dbCore_SRCS += chfPlugin.c
|
||||
dbCore_SRCS += dbState.c
|
||||
dbCore_SRCS += dbUnitTest.c
|
||||
dbCore_SRCS += dbServer.c
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
#*************************************************************************
|
||||
# Copyright (c) 2012 UChicago Argonne LLC, as Operator of Argonne
|
||||
# National Laboratory.
|
||||
# Copyright (c) 2010 Brookhaven Science Associates, as Operator of
|
||||
# Brookhaven National Lab.
|
||||
# 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.
|
||||
#*************************************************************************
|
||||
|
||||
# This is a Makefile fragment, see src/ioc/Makefile.
|
||||
|
||||
dbCommon.h$(DEP): $(IOCDIR)/db/dbCommonRecord.dbd $(IOCDIR)/db/RULES
|
||||
@$(RM) $@
|
||||
@$(DBTORECORDTYPEH) -D -I ../db -o $(COMMONDEP_TARGET) $< > $@
|
||||
|
||||
$(COMMON_DIR)/dbCommon.h: $(IOCDIR)/db/dbCommonRecord.dbd $(IOCDIR)/db/RULES
|
||||
@$(RM) $(notdir $@)
|
||||
$(DBTORECORDTYPEH) -I ../db -o $(notdir $@) $<
|
||||
@$(MV) $(notdir $@) $@
|
||||
|
||||
$(COMMON_DIR)/menuGlobal.dbd: $(IOCDIR)/db/Makefile $(IOCDIR)/db/RULES
|
||||
|
||||
# This is a target-specific variable
|
||||
$(COMMON_DIR)/menuGlobal.dbd: DBDCAT_COMMAND = \
|
||||
$(PERL) $(INSTALL_HOST_BIN)/makeIncludeDbd.pl $(menuGlobal_DBD) $(@F)
|
||||
@@ -0,0 +1,354 @@
|
||||
/*************************************************************************\
|
||||
* Copyright (c) 2008 UChicago Argonne LLC, as Operator of Argonne
|
||||
* National Laboratory.
|
||||
* Copyright (c) 2002 The Regents of the University of California, as
|
||||
* Operator of Los Alamos National Laboratory.
|
||||
* Copyright (c) 2013 ITER Organization.
|
||||
* EPICS BASE is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
\*************************************************************************/
|
||||
/* callback.c */
|
||||
|
||||
/* general purpose callback tasks */
|
||||
/*
|
||||
* Original Author: Marty Kraimer
|
||||
* Date: 07-18-91
|
||||
*/
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "cantProceed.h"
|
||||
#include "dbDefs.h"
|
||||
#include "epicsAtomic.h"
|
||||
#include "epicsEvent.h"
|
||||
#include "epicsInterrupt.h"
|
||||
#include "epicsRingPointer.h"
|
||||
#include "epicsString.h"
|
||||
#include "epicsThread.h"
|
||||
#include "epicsTimer.h"
|
||||
#include "errlog.h"
|
||||
#include "errMdef.h"
|
||||
#include "taskwd.h"
|
||||
|
||||
#define epicsExportSharedSymbols
|
||||
#include "callback.h"
|
||||
#include "dbAccessDefs.h"
|
||||
#include "dbAddr.h"
|
||||
#include "dbBase.h"
|
||||
#include "dbCommon.h"
|
||||
#include "dbFldTypes.h"
|
||||
#include "dbLock.h"
|
||||
#include "dbStaticLib.h"
|
||||
#include "epicsExport.h"
|
||||
#include "link.h"
|
||||
#include "recSup.h"
|
||||
|
||||
|
||||
static int callbackQueueSize = 2000;
|
||||
|
||||
typedef struct cbQueueSet {
|
||||
epicsEventId semWakeUp;
|
||||
epicsRingPointerId queue;
|
||||
int queueOverflow;
|
||||
int shutdown;
|
||||
int threadsConfigured;
|
||||
int threadsRunning;
|
||||
} cbQueueSet;
|
||||
|
||||
static cbQueueSet callbackQueue[NUM_CALLBACK_PRIORITIES];
|
||||
|
||||
int callbackThreadsDefault = 1;
|
||||
/* Don't know what a reasonable default is (yet).
|
||||
* For the time being: parallel means 2 if not explicitly specified */
|
||||
epicsShareDef int callbackParallelThreadsDefault = 2;
|
||||
epicsExportAddress(int,callbackParallelThreadsDefault);
|
||||
|
||||
/* Timer for Delayed Requests */
|
||||
static epicsTimerQueueId timerQueue;
|
||||
|
||||
/* Shutdown handling */
|
||||
enum ctl {ctlInit, ctlRun, ctlPause, ctlExit};
|
||||
static volatile enum ctl cbCtl;
|
||||
static epicsEventId startStopEvent;
|
||||
|
||||
static int callbackIsInit;
|
||||
|
||||
/* Static data */
|
||||
static char *threadNamePrefix[NUM_CALLBACK_PRIORITIES] = {
|
||||
"cbLow", "cbMedium", "cbHigh"
|
||||
};
|
||||
#define FULL_MSG(name) "callbackRequest: " name " ring buffer full\n"
|
||||
static char *fullMessage[NUM_CALLBACK_PRIORITIES] = {
|
||||
FULL_MSG("cbLow"), FULL_MSG("cbMedium"), FULL_MSG("cbHigh")
|
||||
};
|
||||
static unsigned int threadPriority[NUM_CALLBACK_PRIORITIES] = {
|
||||
epicsThreadPriorityScanLow - 1,
|
||||
epicsThreadPriorityScanLow + 4,
|
||||
epicsThreadPriorityScanHigh + 1
|
||||
};
|
||||
static int priorityValue[NUM_CALLBACK_PRIORITIES] = {0, 1, 2};
|
||||
|
||||
|
||||
int callbackSetQueueSize(int size)
|
||||
{
|
||||
if (callbackIsInit) {
|
||||
fprintf(stderr, "Callback system already initialized\n");
|
||||
return -1;
|
||||
}
|
||||
callbackQueueSize = size;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int callbackParallelThreads(int count, const char *prio)
|
||||
{
|
||||
if (callbackIsInit) {
|
||||
fprintf(stderr, "Callback system already initialized\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (count < 0)
|
||||
count = epicsThreadGetCPUs() + count;
|
||||
else if (count == 0)
|
||||
count = callbackParallelThreadsDefault;
|
||||
if (count < 1) count = 1;
|
||||
|
||||
if (!prio || *prio == 0 || strcmp(prio, "*") == 0) {
|
||||
int i;
|
||||
|
||||
for (i = 0; i < NUM_CALLBACK_PRIORITIES; i++) {
|
||||
callbackQueue[i].threadsConfigured = count;
|
||||
}
|
||||
}
|
||||
else {
|
||||
dbMenu *pdbMenu;
|
||||
int i;
|
||||
|
||||
if (!pdbbase) {
|
||||
fprintf(stderr, "callbackParallelThreads: pdbbase not set\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Find prio in menuPriority */
|
||||
pdbMenu = dbFindMenu(pdbbase, "menuPriority");
|
||||
if (!pdbMenu) {
|
||||
fprintf(stderr, "callbackParallelThreads: No Priority menu\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (i = 0; i < pdbMenu->nChoice; i++) {
|
||||
if (epicsStrCaseCmp(prio, pdbMenu->papChoiceValue[i]) == 0)
|
||||
goto found;
|
||||
}
|
||||
fprintf(stderr, "callbackParallelThreads: "
|
||||
"Unknown priority \"%s\"\n", prio);
|
||||
return -1;
|
||||
|
||||
found:
|
||||
callbackQueue[i].threadsConfigured = count;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void callbackTask(void *arg)
|
||||
{
|
||||
int prio = *(int*)arg;
|
||||
cbQueueSet *mySet = &callbackQueue[prio];
|
||||
|
||||
taskwdInsert(0, NULL, NULL);
|
||||
epicsEventSignal(startStopEvent);
|
||||
|
||||
while(!mySet->shutdown) {
|
||||
void *ptr;
|
||||
if (epicsRingPointerIsEmpty(mySet->queue))
|
||||
epicsEventMustWait(mySet->semWakeUp);
|
||||
|
||||
while ((ptr = epicsRingPointerPop(mySet->queue))) {
|
||||
CALLBACK *pcallback = (CALLBACK *)ptr;
|
||||
if(!epicsRingPointerIsEmpty(mySet->queue))
|
||||
epicsEventMustTrigger(mySet->semWakeUp);
|
||||
mySet->queueOverflow = FALSE;
|
||||
(*pcallback->callback)(pcallback);
|
||||
}
|
||||
}
|
||||
|
||||
if(!epicsAtomicDecrIntT(&mySet->threadsRunning))
|
||||
epicsEventSignal(startStopEvent);
|
||||
taskwdRemove(0);
|
||||
}
|
||||
|
||||
void callbackStop(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (cbCtl == ctlExit) return;
|
||||
cbCtl = ctlExit;
|
||||
|
||||
for (i = 0; i < NUM_CALLBACK_PRIORITIES; i++) {
|
||||
callbackQueue[i].shutdown = 1;
|
||||
epicsEventSignal(callbackQueue[i].semWakeUp);
|
||||
}
|
||||
|
||||
for (i = 0; i < NUM_CALLBACK_PRIORITIES; i++) {
|
||||
cbQueueSet *mySet = &callbackQueue[i];
|
||||
|
||||
while (epicsAtomicGetIntT(&mySet->threadsRunning)) {
|
||||
epicsEventSignal(mySet->semWakeUp);
|
||||
epicsEventWaitWithTimeout(startStopEvent, 0.1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void callbackCleanup(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < NUM_CALLBACK_PRIORITIES; i++) {
|
||||
cbQueueSet *mySet = &callbackQueue[i];
|
||||
|
||||
assert(epicsAtomicGetIntT(&mySet->threadsRunning)==0);
|
||||
epicsEventDestroy(mySet->semWakeUp);
|
||||
epicsRingPointerDelete(mySet->queue);
|
||||
}
|
||||
|
||||
epicsTimerQueueRelease(timerQueue);
|
||||
callbackIsInit = 0;
|
||||
memset(callbackQueue, 0, sizeof(callbackQueue));
|
||||
}
|
||||
|
||||
void callbackInit(void)
|
||||
{
|
||||
int i;
|
||||
int j;
|
||||
char threadName[32];
|
||||
|
||||
if (callbackIsInit) {
|
||||
errlogMessage("Warning: callbackInit called again before callbackCleanup\n");
|
||||
return;
|
||||
}
|
||||
callbackIsInit = 1;
|
||||
|
||||
if(!startStopEvent)
|
||||
startStopEvent = epicsEventMustCreate(epicsEventEmpty);
|
||||
cbCtl = ctlRun;
|
||||
timerQueue = epicsTimerQueueAllocate(0, epicsThreadPriorityScanHigh);
|
||||
|
||||
for (i = 0; i < NUM_CALLBACK_PRIORITIES; i++) {
|
||||
epicsThreadId tid;
|
||||
|
||||
callbackQueue[i].semWakeUp = epicsEventMustCreate(epicsEventEmpty);
|
||||
callbackQueue[i].queue = epicsRingPointerLockedCreate(callbackQueueSize);
|
||||
if (callbackQueue[i].queue == 0)
|
||||
cantProceed("epicsRingPointerLockedCreate failed for %s\n",
|
||||
threadNamePrefix[i]);
|
||||
callbackQueue[i].queueOverflow = FALSE;
|
||||
if (callbackQueue[i].threadsConfigured == 0)
|
||||
callbackQueue[i].threadsConfigured = callbackThreadsDefault;
|
||||
|
||||
for (j = 0; j < callbackQueue[i].threadsConfigured; j++) {
|
||||
if (callbackQueue[i].threadsConfigured > 1 )
|
||||
sprintf(threadName, "%s-%d", threadNamePrefix[i], j);
|
||||
else
|
||||
strcpy(threadName, threadNamePrefix[i]);
|
||||
tid = epicsThreadCreate(threadName, threadPriority[i],
|
||||
epicsThreadGetStackSize(epicsThreadStackBig),
|
||||
(EPICSTHREADFUNC)callbackTask, &priorityValue[i]);
|
||||
if (tid == 0) {
|
||||
cantProceed("Failed to spawn callback thread %s\n", threadName);
|
||||
} else {
|
||||
epicsEventWait(startStopEvent);
|
||||
epicsAtomicIncrIntT(&callbackQueue[i].threadsRunning);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* This routine can be called from interrupt context */
|
||||
int callbackRequest(CALLBACK *pcallback)
|
||||
{
|
||||
int priority;
|
||||
int pushOK;
|
||||
cbQueueSet *mySet;
|
||||
|
||||
if (!pcallback) {
|
||||
epicsInterruptContextMessage("callbackRequest: pcallback was NULL\n");
|
||||
return S_db_notInit;
|
||||
}
|
||||
priority = pcallback->priority;
|
||||
if (priority < 0 || priority >= NUM_CALLBACK_PRIORITIES) {
|
||||
epicsInterruptContextMessage("callbackRequest: Bad priority\n");
|
||||
return S_db_badChoice;
|
||||
}
|
||||
mySet = &callbackQueue[priority];
|
||||
if (mySet->queueOverflow) return S_db_bufFull;
|
||||
|
||||
pushOK = epicsRingPointerPush(mySet->queue, pcallback);
|
||||
|
||||
if (!pushOK) {
|
||||
epicsInterruptContextMessage(fullMessage[priority]);
|
||||
mySet->queueOverflow = TRUE;
|
||||
return S_db_bufFull;
|
||||
}
|
||||
epicsEventSignal(mySet->semWakeUp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ProcessCallback(CALLBACK *pcallback)
|
||||
{
|
||||
dbCommon *pRec;
|
||||
|
||||
callbackGetUser(pRec, pcallback);
|
||||
if (!pRec) return;
|
||||
dbScanLock(pRec);
|
||||
(*pRec->rset->process)(pRec);
|
||||
dbScanUnlock(pRec);
|
||||
}
|
||||
|
||||
void callbackSetProcess(CALLBACK *pcallback, int Priority, void *pRec)
|
||||
{
|
||||
callbackSetCallback(ProcessCallback, pcallback);
|
||||
callbackSetPriority(Priority, pcallback);
|
||||
callbackSetUser(pRec, pcallback);
|
||||
}
|
||||
|
||||
int callbackRequestProcessCallback(CALLBACK *pcallback,
|
||||
int Priority, void *pRec)
|
||||
{
|
||||
callbackSetProcess(pcallback, Priority, pRec);
|
||||
return callbackRequest(pcallback);
|
||||
}
|
||||
|
||||
static void notify(void *pPrivate)
|
||||
{
|
||||
CALLBACK *pcallback = (CALLBACK *)pPrivate;
|
||||
callbackRequest(pcallback);
|
||||
}
|
||||
|
||||
void callbackRequestDelayed(CALLBACK *pcallback, double seconds)
|
||||
{
|
||||
epicsTimerId timer = (epicsTimerId)pcallback->timer;
|
||||
|
||||
if (timer == 0) {
|
||||
timer = epicsTimerQueueCreateTimer(timerQueue, notify, pcallback);
|
||||
pcallback->timer = timer;
|
||||
}
|
||||
epicsTimerStartDelay(timer, seconds);
|
||||
}
|
||||
|
||||
void callbackCancelDelayed(CALLBACK *pcallback)
|
||||
{
|
||||
epicsTimerId timer = (epicsTimerId)pcallback->timer;
|
||||
|
||||
if (timer != 0) {
|
||||
epicsTimerCancel(timer);
|
||||
}
|
||||
}
|
||||
|
||||
void callbackRequestProcessCallbackDelayed(CALLBACK *pcallback,
|
||||
int Priority, void *pRec, double seconds)
|
||||
{
|
||||
callbackSetProcess(pcallback, Priority, pRec);
|
||||
callbackRequestDelayed(pcallback, seconds);
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
/*************************************************************************\
|
||||
* Copyright (c) 2007 UChicago Argonne LLC, as Operator of Argonne
|
||||
* National Laboratory.
|
||||
* Copyright (c) 2002 The Regents of the University of California, as
|
||||
* Operator of Los Alamos National Laboratory.
|
||||
* Copyright (c) 2013 ITER Organization.
|
||||
* EPICS BASE is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
\*************************************************************************/
|
||||
|
||||
/* includes for general purpose callback tasks */
|
||||
/*
|
||||
* Original Author: Marty Kraimer
|
||||
* Date: 07-18-91
|
||||
*/
|
||||
|
||||
#ifndef INCcallbackh
|
||||
#define INCcallbackh 1
|
||||
|
||||
#include "shareLib.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*
|
||||
* WINDOWS also has a "CALLBACK" type def
|
||||
*/
|
||||
#ifdef _WIN32
|
||||
# ifdef CALLBACK
|
||||
# undef CALLBACK
|
||||
# endif /*CALLBACK*/
|
||||
#endif /*_WIN32*/
|
||||
|
||||
#define NUM_CALLBACK_PRIORITIES 3
|
||||
#define priorityLow 0
|
||||
#define priorityMedium 1
|
||||
#define priorityHigh 2
|
||||
|
||||
typedef struct callbackPvt {
|
||||
void (*callback)(struct callbackPvt*);
|
||||
int priority;
|
||||
void *user; /*for use by callback user*/
|
||||
void *timer; /*for use by callback itself*/
|
||||
}CALLBACK;
|
||||
|
||||
typedef void (*CALLBACKFUNC)(struct callbackPvt*);
|
||||
|
||||
#define callbackSetCallback(PFUN, PCALLBACK) \
|
||||
( (PCALLBACK)->callback = (PFUN) )
|
||||
#define callbackSetPriority(PRIORITY, PCALLBACK) \
|
||||
( (PCALLBACK)->priority = (PRIORITY) )
|
||||
#define callbackGetPriority(PRIORITY, PCALLBACK) \
|
||||
( (PRIORITY) = (PCALLBACK)->priority )
|
||||
#define callbackSetUser(USER, PCALLBACK) \
|
||||
( (PCALLBACK)->user = (void *) (USER) )
|
||||
#define callbackGetUser(USER, PCALLBACK) \
|
||||
( (USER) = (PCALLBACK)->user )
|
||||
|
||||
epicsShareFunc void callbackInit(void);
|
||||
epicsShareFunc void callbackStop(void);
|
||||
epicsShareFunc void callbackCleanup(void);
|
||||
epicsShareFunc int callbackRequest(CALLBACK *pCallback);
|
||||
epicsShareFunc void callbackSetProcess(
|
||||
CALLBACK *pcallback, int Priority, void *pRec);
|
||||
epicsShareFunc int callbackRequestProcessCallback(
|
||||
CALLBACK *pCallback,int Priority, void *pRec);
|
||||
epicsShareFunc void callbackRequestDelayed(
|
||||
CALLBACK *pCallback,double seconds);
|
||||
epicsShareFunc void callbackCancelDelayed(CALLBACK *pcallback);
|
||||
epicsShareFunc void callbackRequestProcessCallbackDelayed(
|
||||
CALLBACK *pCallback, int Priority, void *pRec, double seconds);
|
||||
epicsShareFunc int callbackSetQueueSize(int size);
|
||||
epicsShareFunc int callbackParallelThreads(int count, const char *prio);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /*INCcallbackh*/
|
||||
@@ -0,0 +1,681 @@
|
||||
/*************************************************************************\
|
||||
* Copyright (c) 2010 Brookhaven National Laboratory.
|
||||
* Copyright (c) 2010 Helmholtz-Zentrum Berlin
|
||||
* für Materialien und Energie GmbH.
|
||||
* Copyright (c) 2014 ITER Organization.
|
||||
* EPICS BASE is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
\*************************************************************************/
|
||||
|
||||
/*
|
||||
* Author: Ralph Lange <Ralph.Lange@gmx.de>
|
||||
*/
|
||||
|
||||
/* Based on the linkoptions utility by Michael Davidsaver (BNL) */
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <limits.h>
|
||||
#include <math.h>
|
||||
|
||||
#include "dbDefs.h"
|
||||
#include "epicsStdio.h"
|
||||
#include "epicsStdlib.h"
|
||||
#include "epicsString.h"
|
||||
#include "epicsTypes.h"
|
||||
#include "errlog.h"
|
||||
|
||||
#define epicsExportSharedSymbols
|
||||
#include "chfPlugin.h"
|
||||
#include "dbStaticLib.h"
|
||||
|
||||
/*
|
||||
* Data for a chfPlugin
|
||||
*/
|
||||
typedef struct chfPlugin {
|
||||
const chfPluginArgDef *opts;
|
||||
size_t nopts;
|
||||
epicsUInt32 *required;
|
||||
const chfPluginIf *pif;
|
||||
} chfPlugin;
|
||||
|
||||
/*
|
||||
* Parser state data for a chfFilter (chfPlugin instance)
|
||||
*/
|
||||
typedef struct chfFilter {
|
||||
const chfPlugin *plugin;
|
||||
epicsUInt32 *found;
|
||||
void *puser;
|
||||
epicsInt16 nextParam;
|
||||
} chfFilter;
|
||||
|
||||
/* Data types we get from the parser */
|
||||
typedef enum chfPluginType {
|
||||
chfPluginTypeBool,
|
||||
chfPluginTypeInt,
|
||||
chfPluginTypeDouble,
|
||||
chfPluginTypeString
|
||||
} chfPluginType;
|
||||
|
||||
/*
|
||||
* Convert the (epicsInt32) integer value 'val' to the type named in
|
||||
* 'opt->optType' and store the result at 'user + opt->offset'.
|
||||
*/
|
||||
static int
|
||||
store_integer_value(const chfPluginArgDef *opt, char *user, epicsInt32 val)
|
||||
{
|
||||
epicsInt32 *ival;
|
||||
int *eval;
|
||||
const chfPluginEnumType *emap;
|
||||
double *dval;
|
||||
char *sval;
|
||||
int ret;
|
||||
char buff[22]; /* 2^64 = 1.8e+19, so 20 digits plus sign max */
|
||||
|
||||
#ifdef DEBUG_CHF
|
||||
printf("Got an integer for %s (type %d): %ld\n",
|
||||
opt->name, opt->optType, (long) val);
|
||||
#endif
|
||||
|
||||
if (!opt->convert && opt->optType != chfPluginArgInt32) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
switch (opt->optType) {
|
||||
case chfPluginArgInt32:
|
||||
ival = (epicsInt32 *) ((char *)user + opt->dataOffset);
|
||||
*ival = val;
|
||||
break;
|
||||
case chfPluginArgBoolean:
|
||||
sval = user + opt->dataOffset;
|
||||
*sval = !!val;
|
||||
break;
|
||||
case chfPluginArgDouble:
|
||||
dval = (double*) (user + opt->dataOffset);
|
||||
*dval = val;
|
||||
break;
|
||||
case chfPluginArgString:
|
||||
sval = user + opt->dataOffset;
|
||||
ret = sprintf(buff, "%ld", (long)val);
|
||||
if (ret < 0 || (unsigned) ret > opt->size - 1) {
|
||||
return -1;
|
||||
}
|
||||
strncpy(sval, buff, opt->size-1);
|
||||
sval[opt->size-1]='\0';
|
||||
break;
|
||||
case chfPluginArgEnum:
|
||||
eval = (int*) (user + opt->dataOffset);
|
||||
for (emap = opt->enums; emap && emap->name; emap++) {
|
||||
if (val == emap->value) {
|
||||
*eval = val;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!emap || !emap->name) {
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
case chfPluginArgInvalid:
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Convert the (int) boolean value 'val' to the type named in 'opt->optType'
|
||||
* and store the result at 'user + opt->offset'.
|
||||
*/
|
||||
static int store_boolean_value(const chfPluginArgDef *opt, char *user, int val)
|
||||
{
|
||||
epicsInt32 *ival;
|
||||
double *dval;
|
||||
char *sval;
|
||||
|
||||
#ifdef DEBUG_CHF
|
||||
printf("Got a boolean for %s (type %d): %d\n",
|
||||
opt->name, opt->optType, val);
|
||||
#endif
|
||||
|
||||
if (!opt->convert && opt->optType != chfPluginArgBoolean) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
switch (opt->optType) {
|
||||
case chfPluginArgInt32:
|
||||
ival = (epicsInt32 *) (user + opt->dataOffset);
|
||||
*ival = val;
|
||||
break;
|
||||
case chfPluginArgBoolean:
|
||||
sval = user + opt->dataOffset;
|
||||
*sval = val;
|
||||
break;
|
||||
case chfPluginArgDouble:
|
||||
dval = (double*) (user + opt->dataOffset);
|
||||
*dval = !!val;
|
||||
break;
|
||||
case chfPluginArgString:
|
||||
sval = user + opt->dataOffset;
|
||||
if ((unsigned) (val ? 4 : 5) > opt->size - 1) {
|
||||
return -1;
|
||||
}
|
||||
strncpy(sval, val ? "true" : "false", opt->size - 1);
|
||||
sval[opt->size - 1] = '\0';
|
||||
break;
|
||||
case chfPluginArgEnum:
|
||||
case chfPluginArgInvalid:
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Convert the double value 'val' to the type named in 'opt->optType'
|
||||
* and store the result at 'user + opt->offset'.
|
||||
*/
|
||||
static int
|
||||
store_double_value(const chfPluginArgDef *opt, void *vuser, double val)
|
||||
{
|
||||
char *user = vuser;
|
||||
epicsInt32 *ival;
|
||||
double *dval;
|
||||
char *sval;
|
||||
int i;
|
||||
|
||||
#ifdef DEBUG_CHF
|
||||
printf("Got a double for %s (type %d, convert: %s): %g\n",
|
||||
opt->name, opt->optType, opt->convert ? "yes" : "no", val);
|
||||
#endif
|
||||
|
||||
if (!opt->convert && opt->optType != chfPluginArgDouble) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
switch (opt->optType) {
|
||||
case chfPluginArgInt32:
|
||||
if (val < INT_MIN || val > INT_MAX) {
|
||||
return -1;
|
||||
}
|
||||
ival = (epicsInt32 *) (user + opt->dataOffset);
|
||||
*ival = (epicsInt32) val;
|
||||
break;
|
||||
case chfPluginArgBoolean:
|
||||
sval = user + opt->dataOffset;
|
||||
*sval = !!val;
|
||||
break;
|
||||
case chfPluginArgDouble:
|
||||
dval = (double*) (user + opt->dataOffset);
|
||||
*dval = val;
|
||||
break;
|
||||
case chfPluginArgString:
|
||||
sval = user + opt->dataOffset;
|
||||
if (opt->size <= 8) { /* Play it safe: 3 exp + 2 sign + 'e' + '.' */
|
||||
return -1;
|
||||
}
|
||||
i = epicsSnprintf(sval, opt->size, "%.*g", (int) opt->size - 7, val);
|
||||
if (i < 0 || (unsigned) i >= opt->size) {
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
case chfPluginArgEnum:
|
||||
case chfPluginArgInvalid:
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Convert the (char*) string value 'val' to the type named in 'opt->optType'
|
||||
* and store the result at 'user + opt->offset'.
|
||||
*/
|
||||
static int
|
||||
store_string_value(const chfPluginArgDef *opt, char *user, const char *val,
|
||||
size_t len)
|
||||
{
|
||||
epicsInt32 *ival;
|
||||
int *eval;
|
||||
const chfPluginEnumType *emap;
|
||||
double *dval;
|
||||
char *sval;
|
||||
char *end;
|
||||
size_t i;
|
||||
|
||||
#ifdef DEBUG_CHF
|
||||
printf("Got a string for %s (type %d): %.*s\n",
|
||||
opt->name, opt->optType, (int) len, val);
|
||||
#endif
|
||||
|
||||
if (!opt->convert && opt->optType != chfPluginArgString &&
|
||||
opt->optType != chfPluginArgEnum) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
switch (opt->optType) {
|
||||
case chfPluginArgInt32:
|
||||
ival = (epicsInt32 *) (user + opt->dataOffset);
|
||||
return epicsParseInt32(val, ival, 0, &end);
|
||||
|
||||
case chfPluginArgBoolean:
|
||||
sval = user + opt->dataOffset;
|
||||
if (epicsStrnCaseCmp(val, "true", len) == 0) {
|
||||
*sval = 1;
|
||||
} else if (epicsStrnCaseCmp(val, "false", len) == 0) {
|
||||
*sval = 0;
|
||||
} else {
|
||||
epicsInt8 i8;
|
||||
|
||||
if (epicsParseInt8(val, &i8, 0, &end))
|
||||
return -1;
|
||||
*sval = !!i8;
|
||||
}
|
||||
break;
|
||||
case chfPluginArgDouble:
|
||||
dval = (double*) (user + opt->dataOffset);
|
||||
return epicsParseDouble(val, dval, &end);
|
||||
|
||||
case chfPluginArgString:
|
||||
i = opt->size-1 < len ? opt->size-1 : (int) len;
|
||||
sval = user + opt->dataOffset;
|
||||
strncpy(sval, val, i);
|
||||
sval[i] = '\0';
|
||||
break;
|
||||
case chfPluginArgEnum:
|
||||
eval = (int*) (user + opt->dataOffset);
|
||||
for (emap = opt->enums; emap && emap->name; emap++) {
|
||||
if (strncmp(emap->name, val, len) == 0) {
|
||||
*eval = emap->value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if( !emap || !emap->name ) {
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
case chfPluginArgInvalid:
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void freeInstanceData(chfFilter *f)
|
||||
{
|
||||
free(f->found);
|
||||
free(f); /* FIXME: Use a free-list */
|
||||
}
|
||||
|
||||
/*
|
||||
* chFilterIf callbacks
|
||||
*/
|
||||
|
||||
/* First entry point when a new filter instance is created.
|
||||
* All per-instance allocations happen here.
|
||||
*/
|
||||
static parse_result parse_start(chFilter *filter)
|
||||
{
|
||||
chfPlugin *p = (chfPlugin*) filter->plug->puser;
|
||||
chfFilter *f;
|
||||
|
||||
/* Filter context */
|
||||
/* FIXME: Use a free-list */
|
||||
f = calloc(1, sizeof(chfFilter));
|
||||
if (!f) {
|
||||
errlogPrintf("chfFilterCtx calloc failed\n");
|
||||
goto errfctx;
|
||||
}
|
||||
f->nextParam = -1;
|
||||
|
||||
/* Bit array to find missing required keys */
|
||||
f->found = calloc( (p->nopts/32)+1, sizeof(epicsUInt32) );
|
||||
if (!f->found) {
|
||||
errlogPrintf("chfConfigParseStart: bit array calloc failed\n");
|
||||
goto errbitarray;
|
||||
}
|
||||
|
||||
/* Call the plugin to allocate its structure, it returns NULL on error */
|
||||
if (p->pif->allocPvt) {
|
||||
if ((f->puser = p->pif->allocPvt()) == NULL) {
|
||||
errlogPrintf("chfConfigParseStart: plugin pvt alloc failed\n");
|
||||
goto errplugin;
|
||||
}
|
||||
}
|
||||
|
||||
filter->puser = (void*) f;
|
||||
|
||||
return parse_continue;
|
||||
|
||||
errplugin:
|
||||
free(f->found);
|
||||
errbitarray:
|
||||
free(f); /* FIXME: Use a free-list */
|
||||
errfctx:
|
||||
return parse_stop;
|
||||
}
|
||||
|
||||
static void parse_abort(chFilter *filter) {
|
||||
chfPlugin *p = (chfPlugin*) filter->plug->puser;
|
||||
chfFilter *f = (chfFilter*) filter->puser;
|
||||
|
||||
/* Call the plugin to tell it we're aborting */
|
||||
if (p->pif->parse_error) p->pif->parse_error(f->puser);
|
||||
if (p->pif->freePvt) p->pif->freePvt(f->puser);
|
||||
freeInstanceData(f);
|
||||
}
|
||||
|
||||
static parse_result parse_end(chFilter *filter)
|
||||
{
|
||||
chfPlugin *p = (chfPlugin*) filter->plug->puser;
|
||||
chfFilter *f = (chfFilter*) filter->puser;
|
||||
int i;
|
||||
|
||||
/* Check if all required arguments were supplied */
|
||||
for(i = 0; i < (p->nopts/32)+1; i++) {
|
||||
if ((f->found[i] & p->required[i]) != p->required[i]) {
|
||||
if (p->pif->parse_error) p->pif->parse_error(f->puser);
|
||||
if (p->pif->freePvt) p->pif->freePvt(f->puser);
|
||||
freeInstanceData(f);
|
||||
return parse_stop;
|
||||
}
|
||||
}
|
||||
|
||||
/* Call the plugin to tell it we're done */
|
||||
if (p->pif->parse_ok) {
|
||||
if (p->pif->parse_ok(f->puser)) {
|
||||
if (p->pif->freePvt) p->pif->freePvt(f->puser);
|
||||
freeInstanceData(f);
|
||||
return parse_stop;
|
||||
}
|
||||
}
|
||||
|
||||
return parse_continue;
|
||||
}
|
||||
|
||||
static parse_result parse_boolean(chFilter *filter, int boolVal)
|
||||
{
|
||||
const chfPluginArgDef *opts = ((chfPlugin*)filter->plug->puser)->opts;
|
||||
chfFilter *f = (chfFilter*)filter->puser;
|
||||
|
||||
if (f->nextParam < 0 ||
|
||||
store_boolean_value(&opts[f->nextParam], f->puser, boolVal)) {
|
||||
return parse_stop;
|
||||
} else {
|
||||
return parse_continue;
|
||||
}
|
||||
}
|
||||
|
||||
static parse_result parse_integer(chFilter *filter, long integerVal)
|
||||
{
|
||||
const chfPluginArgDef *opts = ((chfPlugin*)filter->plug->puser)->opts;
|
||||
chfFilter *f = (chfFilter*)filter->puser;
|
||||
|
||||
if(sizeof(long)>sizeof(epicsInt32)) {
|
||||
epicsInt32 temp=integerVal;
|
||||
if(integerVal !=temp)
|
||||
return parse_stop;
|
||||
}
|
||||
if (f->nextParam < 0 ||
|
||||
store_integer_value(&opts[f->nextParam], f->puser, integerVal)) {
|
||||
return parse_stop;
|
||||
} else {
|
||||
return parse_continue;
|
||||
}
|
||||
}
|
||||
|
||||
static parse_result parse_double(chFilter *filter, double doubleVal)
|
||||
{
|
||||
const chfPluginArgDef *opts = ((chfPlugin*)filter->plug->puser)->opts;
|
||||
chfFilter *f = (chfFilter*)filter->puser;
|
||||
|
||||
if (f->nextParam < 0 ||
|
||||
store_double_value(&opts[f->nextParam], f->puser, doubleVal)) {
|
||||
return parse_stop;
|
||||
} else {
|
||||
return parse_continue;
|
||||
}
|
||||
}
|
||||
|
||||
static parse_result
|
||||
parse_string(chFilter *filter, const char *stringVal, size_t stringLen)
|
||||
{
|
||||
const chfPluginArgDef *opts = ((chfPlugin*)filter->plug->puser)->opts;
|
||||
chfFilter *f = (chfFilter*)filter->puser;
|
||||
|
||||
if (f->nextParam < 0 ||
|
||||
store_string_value(&opts[f->nextParam], f->puser, stringVal, stringLen)) {
|
||||
return parse_stop;
|
||||
} else {
|
||||
return parse_continue;
|
||||
}
|
||||
}
|
||||
|
||||
static parse_result parse_start_map(chFilter *filter)
|
||||
{
|
||||
return parse_continue;
|
||||
}
|
||||
|
||||
static parse_result
|
||||
parse_map_key(chFilter *filter, const char *key, size_t stringLen)
|
||||
{
|
||||
const chfPluginArgDef *cur;
|
||||
const chfPluginArgDef *opts = ((chfPlugin*)filter->plug->puser)->opts;
|
||||
chfFilter *f = (chfFilter*)filter->puser;
|
||||
int *tag;
|
||||
int i;
|
||||
int j;
|
||||
|
||||
f->nextParam = -1;
|
||||
for (cur = opts, i = 0; cur && cur->name; cur++, i++) {
|
||||
if (strncmp(key, cur->name, stringLen) == 0) {
|
||||
f->nextParam = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (f->nextParam == -1) {
|
||||
return parse_stop;
|
||||
}
|
||||
|
||||
if (opts[i].tagged) {
|
||||
tag = (int*) ((char*) f->puser + opts[i].tagOffset);
|
||||
*tag = opts[i].choice;
|
||||
}
|
||||
|
||||
f->found[i/32] |= 1<<(i%32);
|
||||
/* Mark tag and all other options pointing to the same data as found */
|
||||
for (cur = opts, j = 0; cur && cur->name; cur++, j++) {
|
||||
if ((opts[i].tagged && cur->dataOffset == opts[i].tagOffset)
|
||||
|| cur->dataOffset == opts[i].dataOffset)
|
||||
f->found[j/32] |= 1<<(j%32);
|
||||
}
|
||||
|
||||
return parse_continue;
|
||||
}
|
||||
|
||||
static parse_result parse_end_map(chFilter *filter)
|
||||
{
|
||||
return parse_continue;
|
||||
}
|
||||
|
||||
static long channel_open(chFilter *filter)
|
||||
{
|
||||
chfPlugin *p = (chfPlugin*) filter->plug->puser;
|
||||
chfFilter *f = (chfFilter*) filter->puser;
|
||||
|
||||
if (p->pif->channel_open)
|
||||
return p->pif->channel_open(filter->chan, f->puser);
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
channel_register_pre(chFilter *filter, chPostEventFunc **cb_out,
|
||||
void **arg_out, db_field_log *probe)
|
||||
{
|
||||
chfPlugin *p = (chfPlugin*) filter->plug->puser;
|
||||
chfFilter *f = (chfFilter*) filter->puser;
|
||||
|
||||
if (p->pif->channelRegisterPre)
|
||||
p->pif->channelRegisterPre(filter->chan, f->puser, cb_out, arg_out,
|
||||
probe);
|
||||
}
|
||||
|
||||
static void
|
||||
channel_register_post(chFilter *filter, chPostEventFunc **cb_out,
|
||||
void **arg_out, db_field_log *probe)
|
||||
{
|
||||
chfPlugin *p = (chfPlugin*) filter->plug->puser;
|
||||
chfFilter *f = (chfFilter*) filter->puser;
|
||||
|
||||
if (p->pif->channelRegisterPost)
|
||||
p->pif->channelRegisterPost(filter->chan, f->puser, cb_out, arg_out,
|
||||
probe);
|
||||
}
|
||||
|
||||
static void channel_report(chFilter *filter, int level,
|
||||
const unsigned short indent)
|
||||
{
|
||||
chfPlugin *p = (chfPlugin*) filter->plug->puser;
|
||||
chfFilter *f = (chfFilter*) filter->puser;
|
||||
|
||||
if (p->pif->channel_report)
|
||||
p->pif->channel_report(filter->chan, f->puser, level, indent);
|
||||
}
|
||||
|
||||
static void channel_close(chFilter *filter)
|
||||
{
|
||||
chfPlugin *p = (chfPlugin*) filter->plug->puser;
|
||||
chfFilter *f = (chfFilter*) filter->puser;
|
||||
|
||||
if (p->pif->channel_close) p->pif->channel_close(filter->chan, f->puser);
|
||||
if (p->pif->freePvt) p->pif->freePvt(f->puser);
|
||||
free(f->found);
|
||||
free(f); /* FIXME: Use a free-list */
|
||||
}
|
||||
|
||||
static void plugin_free(void* puser)
|
||||
{
|
||||
chfPlugin *p=puser;
|
||||
free(p->required);
|
||||
free(p);
|
||||
}
|
||||
|
||||
/*
|
||||
* chFilterIf for the wrapper
|
||||
* we just support a simple one-level map, and no arrays
|
||||
*/
|
||||
static chFilterIf wrapper_fif = {
|
||||
plugin_free,
|
||||
|
||||
parse_start,
|
||||
parse_abort,
|
||||
parse_end,
|
||||
|
||||
NULL, /* parse_null, */
|
||||
parse_boolean,
|
||||
parse_integer,
|
||||
parse_double,
|
||||
parse_string,
|
||||
|
||||
parse_start_map,
|
||||
parse_map_key,
|
||||
parse_end_map,
|
||||
|
||||
NULL, /* parse_start_array, */
|
||||
NULL, /* parse_end_array, */
|
||||
|
||||
channel_open,
|
||||
channel_register_pre,
|
||||
channel_register_post,
|
||||
channel_report,
|
||||
channel_close
|
||||
};
|
||||
|
||||
const char*
|
||||
chfPluginEnumString(const chfPluginEnumType *emap, int i, const char* def)
|
||||
{
|
||||
for(; emap && emap->name; emap++) {
|
||||
if ( i == emap->value ) {
|
||||
return emap->name;
|
||||
}
|
||||
}
|
||||
return def;
|
||||
}
|
||||
|
||||
int
|
||||
chfPluginRegister(const char* key, const chfPluginIf *pif,
|
||||
const chfPluginArgDef* opts)
|
||||
{
|
||||
chfPlugin *p;
|
||||
size_t i;
|
||||
const chfPluginArgDef *cur;
|
||||
epicsUInt32 *reqd;
|
||||
|
||||
/* Check and count options */
|
||||
for (i = 0, cur = opts; cur && cur->name; i++, cur++) {
|
||||
switch(cur->optType) {
|
||||
case chfPluginArgInt32:
|
||||
if (cur->size < sizeof(epicsInt32)) {
|
||||
errlogPrintf("Plugin %s: %d bytes too small for epicsInt32 %s\n",
|
||||
key, cur->size, cur->name);
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
case chfPluginArgBoolean:
|
||||
if (cur->size < 1) {
|
||||
errlogPrintf("Plugin %s: %d bytes too small for boolean %s\n",
|
||||
key, cur->size, cur->name);
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
case chfPluginArgDouble:
|
||||
if (cur->size < sizeof(double)) {
|
||||
errlogPrintf("Plugin %s: %d bytes too small for double %s\n",
|
||||
key, cur->size, cur->name);
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
case chfPluginArgString:
|
||||
if (cur->size < sizeof(char*)) {
|
||||
/* Catch if someone has given us a char* instead of a char[]
|
||||
* Also means that char buffers must be >=4.
|
||||
*/
|
||||
errlogPrintf("Plugin %s: %d bytes too small for string %s\n",
|
||||
key, cur->size, cur->name);
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
case chfPluginArgEnum:
|
||||
if (cur->size < sizeof(int)) {
|
||||
errlogPrintf("Plugin %s: %d bytes too small for enum %s\n",
|
||||
key, cur->size, cur->name);
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
case chfPluginArgInvalid:
|
||||
errlogPrintf("Plugin %s: storage type for %s is not defined\n",
|
||||
key, cur->name);
|
||||
return -1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Bit array used to find missing required keys */
|
||||
reqd = dbCalloc((i/32)+1, sizeof(epicsUInt32));
|
||||
if (!reqd) {
|
||||
errlogPrintf("Plugin %s: bit array calloc failed\n", key);
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (i = 0, cur = opts; cur && cur->name; i++, cur++) {
|
||||
if (cur->required) reqd[i/32] |= 1 << (i%32);
|
||||
}
|
||||
|
||||
/* Plugin data */
|
||||
p = dbCalloc(1, sizeof(chfPlugin));
|
||||
p->pif = pif;
|
||||
p->opts = opts;
|
||||
p->nopts = i;
|
||||
p->required = reqd;
|
||||
|
||||
dbRegisterFilter(key, &wrapper_fif, p);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,318 @@
|
||||
/*************************************************************************\
|
||||
* Copyright (c) 2010 Brookhaven National Laboratory.
|
||||
* Copyright (c) 2010 Helmholtz-Zentrum Berlin
|
||||
* für Materialien und Energie GmbH.
|
||||
* Copyright (c) 2014 ITER Organization.
|
||||
* EPICS BASE is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
\*************************************************************************/
|
||||
|
||||
/*
|
||||
* Author: Ralph Lange <Ralph.Lange@gmx.de>
|
||||
*/
|
||||
|
||||
/* Based on the linkoptions utility by Michael Davidsaver (BNL) */
|
||||
|
||||
#ifndef CHFPLUGIN_H
|
||||
#define CHFPLUGIN_H
|
||||
|
||||
#include <shareLib.h>
|
||||
#include <dbDefs.h>
|
||||
#include <epicsTypes.h>
|
||||
#include <dbChannel.h>
|
||||
|
||||
struct db_field_log;
|
||||
|
||||
/** @file chfPlugin.h
|
||||
* @brief Channel filter simplified plugins.
|
||||
*
|
||||
* Utility layer to allow an easier (reduced) interface for
|
||||
* channel filter plugins.
|
||||
*
|
||||
* Parsing the configuration arguments of a channel filter plugin
|
||||
* is done according to an argument description table provided by the plugin.
|
||||
* The parser stores the results directly into a user supplied structure
|
||||
* after appropriate type conversion.
|
||||
*
|
||||
* To specify the arguments, a chfPluginArgDef table must be defined
|
||||
* for the user structure. This table has to be specified when the plugin registers.
|
||||
*
|
||||
* The plugin is responsible to register an init function using
|
||||
* epicsExportRegistrar() and the accompanying registrar() directive in the dbd,
|
||||
* and call chfPluginRegister() from within the init function.
|
||||
*
|
||||
* For example:
|
||||
*
|
||||
* typedef struct myStruct {
|
||||
* ... other stuff
|
||||
* char mode;
|
||||
* epicsInt32 ival;
|
||||
* double dval;
|
||||
* epicsInt32 ival2;
|
||||
* int enumval;
|
||||
* char strval[20];
|
||||
* char boolval;
|
||||
* } myStruct;
|
||||
*
|
||||
* static const
|
||||
* chfPluginEnumType colorEnum[] = { {"Red",1}, {"Green",2}, {"Blue",3}, {NULL,0} };
|
||||
*
|
||||
* static const
|
||||
* chfPluginDef myStructDef[] = {
|
||||
* chfTagInt32(myStruct, ival, "Integer" , ival2, 3, 0, 0),
|
||||
* chfInt32 (myStruct, ival2, "Second" , 1, 0),
|
||||
* chfDouble (myStruct, dval, "Double" , 1, 0),
|
||||
* chfString (myStruct, strval , "String" , 1, 0),
|
||||
* chfEnum (myStruct, enumval, "Color" , 1, 0, colorEnum),
|
||||
* chfBoolean (myStruct, boolval, "Bool" , 1, 0),
|
||||
* chfPluginEnd
|
||||
* };
|
||||
*
|
||||
* Note: The 4th argument specifies the parameter to be required (1) or optional (0),
|
||||
* the 5th whether converting to the required type is allowed (1), or
|
||||
* type mismatches are an error (0).
|
||||
* Note: The "Tag" version has two additional arguments. the 4th arg specifies the tag
|
||||
* field (integer type) inside the structure to be set, the 5th arg specifies the
|
||||
* value to set the tag field to. Arguments 6 and 7 specify "required" and
|
||||
* "conversion" as described above.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/** @brief Channel filter simplified plugin interface.
|
||||
*
|
||||
* The routines in this structure must be implemented by each filter plugin.
|
||||
*/
|
||||
typedef struct chfPluginIf {
|
||||
|
||||
/* Memory management */
|
||||
/** @brief Allocate private resources.
|
||||
*
|
||||
* <em>Called before parsing starts.</em>
|
||||
* The plugin should allocate its per-instance structures,
|
||||
* returning a pointer to them or NULL requesting an abort of the operation.
|
||||
*
|
||||
* allocPvt may be set to NULL, if no resource allocation is needed.
|
||||
*
|
||||
* @return Pointer to private structure, NULL if operation is to be aborted.
|
||||
*/
|
||||
void * (* allocPvt) (void);
|
||||
|
||||
/** @brief Free private resources.
|
||||
*
|
||||
* <em>Called as part of abort or shutdown.</em>
|
||||
* The plugin should release any resources allocated for this filter;
|
||||
* no further calls through this interface will be made.
|
||||
*
|
||||
* freePvt may be set to NULL, if no resources need to be released.
|
||||
*
|
||||
* @param pvt Pointer to private structure.
|
||||
*/
|
||||
void (* freePvt) (void *pvt);
|
||||
|
||||
/* Parameter parsing results */
|
||||
/** @brief A parsing error occurred.
|
||||
*
|
||||
* <em>Called after parsing failed with an error.</em>
|
||||
*
|
||||
* @param pvt Pointer to private structure.
|
||||
*/
|
||||
void (* parse_error) (void *pvt);
|
||||
|
||||
/** @brief Configuration has been parsed successfully.
|
||||
*
|
||||
* <em>Called after parsing has finished ok.</em>
|
||||
* The plugin may check the validity of the parsed data,
|
||||
* returning -1 to request an abort of the operation.
|
||||
*
|
||||
* @param pvt Pointer to private structure.
|
||||
* @return 0 for success, -1 if operation is to be aborted.
|
||||
*/
|
||||
int (* parse_ok) (void *pvt);
|
||||
|
||||
/* Channel operations */
|
||||
/** @brief Open channel.
|
||||
*
|
||||
* <em>Called as part of the channel connection setup.</em>
|
||||
*
|
||||
* @param chan dbChannel for which the connection is being made.
|
||||
* @param pvt Pointer to private structure.
|
||||
* @return 0 for success, -1 if operation is to be aborted.
|
||||
*/
|
||||
long (* channel_open) (dbChannel *chan, void *pvt);
|
||||
|
||||
/** @brief Register callbacks for pre-event-queue operation.
|
||||
*
|
||||
* <em>Called as part of the channel connection setup.</em>
|
||||
*
|
||||
* This function is called to establish the stack of plugins that an event
|
||||
* is passed through between the database and the event queue.
|
||||
*
|
||||
* The plugin must set pe_out to point to its own post-event callback in order
|
||||
* to be called when a data update is sent from the database towards the
|
||||
* event queue.
|
||||
*
|
||||
* The plugin may find out the type of data it will receive by looking at 'probe'.
|
||||
* If the plugin will change the data type and/or size, it must update 'probe'
|
||||
* accordingly.
|
||||
*
|
||||
* @param chan dbChannel for which the connection is being made.
|
||||
* @param pvt Pointer to private structure.
|
||||
* @param cb_out Pointer to this plugin's post-event callback (NULL to bypass
|
||||
* this plugin).
|
||||
* @param arg_out Argument that must be supplied when calling
|
||||
* this plugin's post-event callback.
|
||||
*/
|
||||
void (* channelRegisterPre) (dbChannel *chan, void *pvt,
|
||||
chPostEventFunc **cb_out, void **arg_out,
|
||||
db_field_log *probe);
|
||||
|
||||
/** @brief Register callbacks for post-event-queue operation.
|
||||
*
|
||||
* <em>Called as part of the channel connection setup.</em>
|
||||
*
|
||||
* This function is called to establish the stack of plugins that an event
|
||||
* is passed through between the event queue and the final user (CA server or
|
||||
* database access).
|
||||
*
|
||||
* The plugin must set pe_out to point to its own post-event callback in order
|
||||
* to be called when a data update is sent from the event queue towards the
|
||||
* final user.
|
||||
*
|
||||
* The plugin may find out the type of data it will receive by looking at 'probe'.
|
||||
* If the plugin will change the data type and/or size, it must update 'probe'
|
||||
* accordingly.
|
||||
*
|
||||
* @param chan dbChannel for which the connection is being made.
|
||||
* @param pvt Pointer to private structure.
|
||||
* @param cb_out Pointer to this plugin's post-event callback (NULL to bypass
|
||||
* this plugin).
|
||||
* @param arg_out Argument that must be supplied when calling
|
||||
* this plugin's post-event callback.
|
||||
*/
|
||||
void (* channelRegisterPost) (dbChannel *chan, void *pvt,
|
||||
chPostEventFunc **cb_out, void **arg_out,
|
||||
db_field_log *probe);
|
||||
|
||||
/** @brief Channel report request.
|
||||
*
|
||||
* <em>Called as part of show... routines.</em>
|
||||
*
|
||||
* @param chan dbChannel for which the report is requested.
|
||||
* @param pvt Pointer to private structure.
|
||||
* @param level Interest level.
|
||||
* @param indent Number of spaces to print before each output line.
|
||||
*/
|
||||
void (* channel_report) (dbChannel *chan, void *pvt, int level, const unsigned short indent);
|
||||
|
||||
/** @brief Channel close request.
|
||||
*
|
||||
* <em>Called as part of connection shutdown.</em>
|
||||
* @param chan dbChannel for which the connection is being shut down.
|
||||
* @param pvt Pointer to private structure.
|
||||
*/
|
||||
void (* channel_close) (dbChannel *chan, void *pvt);
|
||||
|
||||
} chfPluginIf;
|
||||
|
||||
typedef enum chfPluginArg {
|
||||
chfPluginArgInvalid=0,
|
||||
chfPluginArgBoolean,
|
||||
chfPluginArgInt32,
|
||||
chfPluginArgDouble,
|
||||
chfPluginArgString,
|
||||
chfPluginArgEnum
|
||||
} chfPluginArg;
|
||||
|
||||
typedef struct chfPluginEnumType {
|
||||
const char *name;
|
||||
const int value;
|
||||
} chfPluginEnumType;
|
||||
|
||||
typedef struct chfPluginArgDef {
|
||||
const char * name;
|
||||
chfPluginArg optType;
|
||||
unsigned int required:1;
|
||||
unsigned int convert:1;
|
||||
unsigned int tagged:1;
|
||||
epicsUInt32 tagOffset;
|
||||
epicsUInt32 choice;
|
||||
epicsUInt32 dataOffset;
|
||||
epicsUInt32 size;
|
||||
const chfPluginEnumType *enums;
|
||||
} chfPluginArgDef;
|
||||
|
||||
/* Simple arguments */
|
||||
|
||||
#define chfInt32(Struct, Member, Name, Req, Conv) \
|
||||
{Name, chfPluginArgInt32, Req, Conv, 0, 0, 0, \
|
||||
OFFSET(Struct, Member), sizeof( ((Struct*)0)->Member ), NULL}
|
||||
|
||||
#define chfBoolean(Struct, Member, Name, Req, Conv) \
|
||||
{Name, chfPluginArgBoolean, Req, Conv, 0, 0, 0, \
|
||||
OFFSET(Struct, Member), sizeof( ((Struct*)0)->Member ), NULL}
|
||||
|
||||
#define chfDouble(Struct, Member, Name, Req, Conv) \
|
||||
{Name, chfPluginArgDouble, Req, Conv, 0, 0, 0, \
|
||||
OFFSET(Struct, Member), sizeof( ((Struct*)0)->Member ), NULL}
|
||||
|
||||
#define chfString(Struct, Member, Name, Req, Conv) \
|
||||
{Name, chfPluginArgString, Req, Conv, 0, 0, 0, \
|
||||
OFFSET(Struct, Member), sizeof( ((Struct*)0)->Member ), NULL}
|
||||
|
||||
#define chfEnum(Struct, Member, Name, Req, Conv, Enums) \
|
||||
{Name, chfPluginArgEnum, Req, Conv, 0, 0, 0, \
|
||||
OFFSET(Struct, Member), sizeof( ((Struct*)0)->Member ), Enums}
|
||||
|
||||
/* Tagged arguments */
|
||||
|
||||
#define chfTagInt32(Struct, Member, Name, Tag, Choice, Req, Conv) \
|
||||
{Name, chfPluginArgInt32, Req, Conv, 1, OFFSET(Struct, Tag), Choice, \
|
||||
OFFSET(Struct, Member), sizeof( ((Struct*)0)->Member ), NULL}
|
||||
|
||||
#define chfTagBoolean(Struct, Member, Name, Tag, Choice, Req, Conv) \
|
||||
{Name, chfPluginArgBoolean, Req, Conv, 1, OFFSET(Struct, Tag), Choice, \
|
||||
OFFSET(Struct, Member), sizeof( ((Struct*)0)->Member ), NULL}
|
||||
|
||||
#define chfTagDouble(Struct, Member, Name, Tag, Choice, Req, Conv) \
|
||||
{Name, chfPluginArgDouble, Req, Conv, 1, OFFSET(Struct, Tag), Choice, \
|
||||
OFFSET(Struct, Member), sizeof( ((Struct*)0)->Member ), NULL}
|
||||
|
||||
#define chfTagString(Struct, Member, Name, Tag, Choice, Req, Conv) \
|
||||
{Name, chfPluginArgString, Req, Conv, 1, OFFSET(Struct, Tag), Choice, \
|
||||
OFFSET(Struct, Member), sizeof( ((Struct*)0)->Member ), NULL}
|
||||
|
||||
#define chfTagEnum(Struct, Member, Name, Tag, Choice, Req, Conv, Enums) \
|
||||
{Name, chfPluginArgEnum, Req, Conv, 1, OFFSET(Struct, Tag), Choice, \
|
||||
OFFSET(Struct, Member), sizeof( ((Struct*)0)->Member ), Enums}
|
||||
|
||||
#define chfPluginArgEnd {0}
|
||||
|
||||
/* Extra output when parsing and converting */
|
||||
#define CHFPLUGINDEBUG 1
|
||||
|
||||
/** @brief Return the string associated with Enum index 'i'.
|
||||
*
|
||||
* @param Enums A null-terminated array of string/integer pairs.
|
||||
* @param i An Enum index.
|
||||
* @param def String to be returned when 'i' isn't a valid Enum index.
|
||||
* @return The string associated with 'i'.
|
||||
*/
|
||||
epicsShareFunc const char* chfPluginEnumString(const chfPluginEnumType *Enums, int i, const char* def);
|
||||
|
||||
/** @brief Register a plugin.
|
||||
*
|
||||
* @param key The plugin name key that clients will use.
|
||||
* @param pif Pointer to the plugin's interface.
|
||||
* @param opts Pointer to the configuration argument description table.
|
||||
*/
|
||||
epicsShareFunc int chfPluginRegister(const char* key, const chfPluginIf *pif, const chfPluginArgDef* opts);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // CHFPLUGIN_H
|
||||
@@ -0,0 +1,202 @@
|
||||
/*************************************************************************\
|
||||
* 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.
|
||||
\*************************************************************************/
|
||||
|
||||
/* cvtBpt.c - Convert using breakpoint table
|
||||
*
|
||||
* Author: Marty Kraimer
|
||||
* Date: 04OCT95
|
||||
* This is adaptation of old bldCvtTable
|
||||
*/
|
||||
|
||||
#include "epicsPrint.h"
|
||||
|
||||
#define epicsExportSharedSymbols
|
||||
#include "cvtTable.h"
|
||||
#include "dbAccess.h"
|
||||
#include "dbBase.h"
|
||||
#include "dbStaticLib.h"
|
||||
|
||||
static brkTable *findBrkTable(short linr)
|
||||
{
|
||||
dbMenu *pdbMenu;
|
||||
|
||||
pdbMenu = dbFindMenu(pdbbase,"menuConvert");
|
||||
if (!pdbMenu) {
|
||||
epicsPrintf("findBrkTable: menuConvert not loaded!\n");
|
||||
return NULL;
|
||||
}
|
||||
if (linr < 0 || linr >= pdbMenu->nChoice) {
|
||||
epicsPrintf("findBrkTable: linr=%d but menuConvert only has %d choices\n",
|
||||
linr,pdbMenu->nChoice);
|
||||
return NULL;
|
||||
}
|
||||
return dbFindBrkTable(pdbbase,pdbMenu->papChoiceValue[linr]);
|
||||
}
|
||||
|
||||
/* Used by both ao and ai record types */
|
||||
long cvtRawToEngBpt(double *pval, short linr, short init,
|
||||
void **ppbrk, short *plbrk)
|
||||
{
|
||||
double val = *pval;
|
||||
long status = 0;
|
||||
brkTable *pbrkTable;
|
||||
brkInt *pInt, *nInt;
|
||||
short lbrk;
|
||||
int number;
|
||||
|
||||
if (linr < 2)
|
||||
return -1;
|
||||
|
||||
if (init || *ppbrk == NULL) {
|
||||
pbrkTable = findBrkTable(linr);
|
||||
if (!pbrkTable)
|
||||
return S_dbLib_badField;
|
||||
|
||||
*ppbrk = (void *)pbrkTable;
|
||||
*plbrk = 0;
|
||||
} else
|
||||
pbrkTable = (brkTable *)*ppbrk;
|
||||
|
||||
number = pbrkTable->number;
|
||||
lbrk = *plbrk;
|
||||
|
||||
/* Limit index to the size of the table */
|
||||
if (lbrk < 0)
|
||||
lbrk = 0;
|
||||
else if (lbrk > number-2)
|
||||
lbrk = number-2;
|
||||
|
||||
pInt = & pbrkTable->paBrkInt[lbrk];
|
||||
nInt = pInt + 1;
|
||||
|
||||
if (nInt->raw > pInt->raw) {
|
||||
/* raw values increase down the table */
|
||||
while (val > nInt->raw) {
|
||||
lbrk++;
|
||||
pInt = nInt++;
|
||||
if (lbrk > number-2) {
|
||||
status = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
while (val < pInt->raw) {
|
||||
if (lbrk <= 0) {
|
||||
status = 1;
|
||||
break;
|
||||
}
|
||||
lbrk--;
|
||||
nInt = pInt--;
|
||||
}
|
||||
} else {
|
||||
/* raw values decrease down the table */
|
||||
while (val <= nInt->raw) {
|
||||
lbrk++;
|
||||
pInt = nInt++;
|
||||
if (lbrk > number-2) {
|
||||
status = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
while(val > pInt->raw) {
|
||||
if (lbrk <= 0) {
|
||||
status = 1;
|
||||
break;
|
||||
}
|
||||
lbrk--;
|
||||
nInt = pInt--;
|
||||
}
|
||||
}
|
||||
|
||||
*plbrk = lbrk;
|
||||
*pval = pInt->eng + (val - pInt->raw) * pInt->slope;
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/* Used by the ao record type */
|
||||
long cvtEngToRawBpt(double *pval, short linr, short init,
|
||||
void **ppbrk, short *plbrk)
|
||||
{
|
||||
double val = *pval;
|
||||
long status = 0;
|
||||
brkTable *pbrkTable;
|
||||
brkInt *pInt, *nInt;
|
||||
short lbrk;
|
||||
int number;
|
||||
|
||||
if (linr < 2)
|
||||
return -1;
|
||||
|
||||
if (init || *ppbrk == NULL) { /*must find breakpoint table*/
|
||||
pbrkTable = findBrkTable(linr);
|
||||
if (!pbrkTable)
|
||||
return S_dbLib_badField;
|
||||
|
||||
*ppbrk = (void *)pbrkTable;
|
||||
/* start at the beginning */
|
||||
*plbrk = 0;
|
||||
} else
|
||||
pbrkTable = (brkTable *)*ppbrk;
|
||||
|
||||
number = pbrkTable->number;
|
||||
lbrk = *plbrk;
|
||||
|
||||
/* Limit index to the size of the table */
|
||||
if (lbrk < 0)
|
||||
lbrk = 0;
|
||||
else if (lbrk > number-2)
|
||||
lbrk = number-2;
|
||||
|
||||
pInt = & pbrkTable->paBrkInt[lbrk];
|
||||
nInt = pInt + 1;
|
||||
|
||||
if (nInt->eng > pInt->eng) {
|
||||
/* eng values increase down the table */
|
||||
while (val > nInt->eng) {
|
||||
lbrk++;
|
||||
pInt = nInt++;
|
||||
if (lbrk > number-2) {
|
||||
status = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
while (val < pInt->eng) {
|
||||
if (lbrk <= 0) {
|
||||
status = 1;
|
||||
break;
|
||||
}
|
||||
lbrk--;
|
||||
nInt = pInt--;
|
||||
}
|
||||
} else {
|
||||
/* eng values decrease down the table */
|
||||
while (val <= nInt->eng) {
|
||||
lbrk++;
|
||||
pInt = nInt++;
|
||||
if (lbrk > number-2) {
|
||||
status = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
while (val > pInt->eng) {
|
||||
if (lbrk <= 0) {
|
||||
status = 1;
|
||||
break;
|
||||
}
|
||||
lbrk--;
|
||||
nInt = pInt--;
|
||||
}
|
||||
}
|
||||
|
||||
*plbrk = lbrk;
|
||||
*pval = pInt->raw + (val - pInt->eng) / pInt->slope;
|
||||
|
||||
return status;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,29 @@
|
||||
/*************************************************************************\
|
||||
* Copyright (c) 2002 The University of Chicago, as Operator of Argonne
|
||||
* National Laboratory.
|
||||
* Copyright (c) 2002 The Regents of the University of California, as
|
||||
* Operator of Los Alamos National Laboratory.
|
||||
* EPICS BASE is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
\*************************************************************************/
|
||||
/* dbAccess.h */
|
||||
|
||||
#ifndef INCdbAccessh
|
||||
#define INCdbAccessh
|
||||
|
||||
#include "dbDefs.h"
|
||||
#include "epicsTime.h"
|
||||
#include "caeventmask.h"
|
||||
#include "dbFldTypes.h"
|
||||
#include "link.h"
|
||||
#include "dbBase.h"
|
||||
#include "shareLib.h"
|
||||
#include "dbAddr.h"
|
||||
#include "dbLock.h"
|
||||
#include "dbAccessDefs.h"
|
||||
#include "dbLink.h"
|
||||
#include "dbCa.h"
|
||||
#include "dbCommon.h"
|
||||
#include "db_field_log.h"
|
||||
|
||||
#endif /*INCdbAccessh*/
|
||||
@@ -0,0 +1,252 @@
|
||||
/*************************************************************************\
|
||||
* Copyright (c) 2002 The University of Chicago, as Operator of Argonne
|
||||
* National Laboratory.
|
||||
* Copyright (c) 2002 The Regents of the University of California, as
|
||||
* Operator of Los Alamos National Laboratory.
|
||||
* EPICS BASE is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
\*************************************************************************/
|
||||
/* dbAccessDefs.h */
|
||||
|
||||
#ifndef INCdbAccessDefsh
|
||||
#define INCdbAccessDefsh
|
||||
|
||||
#ifdef epicsExportSharedSymbols
|
||||
# define INCLdb_accessh_epicsExportSharedSymbols
|
||||
# undef epicsExportSharedSymbols
|
||||
#endif
|
||||
|
||||
#include "epicsTypes.h"
|
||||
#include "epicsTime.h"
|
||||
#include "dbBase.h"
|
||||
#include "dbAddr.h"
|
||||
#include "recSup.h"
|
||||
|
||||
#ifdef INCLdb_accessh_epicsExportSharedSymbols
|
||||
# define epicsExportSharedSymbols
|
||||
# include "shareLib.h"
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
epicsShareExtern struct dbBase *pdbbase;
|
||||
epicsShareExtern volatile int interruptAccept;
|
||||
|
||||
/* The database field and request types are defined in dbFldTypes.h*/
|
||||
/* Data Base Request Options */
|
||||
#define DBR_STATUS 0x00000001
|
||||
#define DBR_UNITS 0x00000002
|
||||
#define DBR_PRECISION 0x00000004
|
||||
#define DBR_TIME 0x00000008
|
||||
#define DBR_ENUM_STRS 0x00000010
|
||||
#define DBR_GR_LONG 0x00000020
|
||||
#define DBR_GR_DOUBLE 0x00000040
|
||||
#define DBR_CTRL_LONG 0x00000080
|
||||
#define DBR_CTRL_DOUBLE 0x00000100
|
||||
#define DBR_AL_LONG 0x00000200
|
||||
#define DBR_AL_DOUBLE 0x00000400
|
||||
|
||||
/**********************************************************************
|
||||
* The next page contains macros for defining requests.
|
||||
* As an example the following defines a buffer to accept an array
|
||||
* of 10 float values + DBR_STATUS and DBR_TIME options
|
||||
*
|
||||
* struct {
|
||||
* DBRstatus
|
||||
* DBRtime
|
||||
* epicsFloat32 value[10]
|
||||
* } buffer;
|
||||
*
|
||||
* IMPORTANT!! The DBRoptions must be given in the order that they
|
||||
* appear in the Data Base Request Options #defines
|
||||
*
|
||||
* The associated dbGetField call is:
|
||||
*
|
||||
* long options,number_elements;
|
||||
* ...
|
||||
* options = DBR_STATUS|DBR_TIME;
|
||||
* number_elements = 10;
|
||||
* rtnval=dbGetField(paddr,DBR_FLOAT,&buffer,&options,&number_elements);
|
||||
*
|
||||
* When dbGetField returns:
|
||||
* rtnval is error status (0 means success)
|
||||
* options has a bit set for each option that was accepted
|
||||
* number_elements is actual number of elements obtained
|
||||
*
|
||||
* The individual items can be refered to by the expressions::
|
||||
*
|
||||
* buffer.status
|
||||
* buffer.severity
|
||||
* buffer.err_status
|
||||
* buffer.epoch_seconds
|
||||
* buffer.nano_seconds
|
||||
* buffer.value[i]
|
||||
*
|
||||
* The following is also a valid declaration:
|
||||
*
|
||||
* typedef struct {
|
||||
* DBRstatus
|
||||
* DBRtime
|
||||
* epicsFloat32 value[10]
|
||||
* } MYBUFFER;
|
||||
*
|
||||
* With this definition you can give definitions such as the following:
|
||||
*
|
||||
* MYBUFFER *pbuf1;
|
||||
* MYBUFFER buf;
|
||||
*************************************************************************/
|
||||
|
||||
/* Macros for defining each option */
|
||||
#define DBRstatus \
|
||||
epicsUInt16 status; /* alarm status */\
|
||||
epicsUInt16 severity; /* alarm severity*/\
|
||||
epicsUInt16 acks; /* alarm ack severity*/\
|
||||
epicsUInt16 ackt; /* Acknowledge transient alarms?*/
|
||||
#define DB_UNITS_SIZE 16
|
||||
#define DBRunits \
|
||||
char units[DB_UNITS_SIZE]; /* units */
|
||||
#define DBRprecision union { \
|
||||
long dp; /* number of decimal places*/\
|
||||
double unused; /* for alignment */\
|
||||
} precision;
|
||||
/* precision.dp must be long to match the pointer arguments to
|
||||
* RSET->get_precision() and recGblGetPrec(), which it's
|
||||
* too late to change now. DBRprecision must be padded to
|
||||
* maintain 8-byte alignment. */
|
||||
#define DBRtime \
|
||||
epicsTimeStamp time; /* time stamp*/
|
||||
#define DBRenumStrs \
|
||||
epicsUInt32 no_str; /* number of strings*/\
|
||||
epicsInt32 padenumStrs; /*padding to force 8 byte align*/\
|
||||
char strs[DB_MAX_CHOICES][MAX_STRING_SIZE]; /* string values */
|
||||
#define DBRgrLong \
|
||||
epicsInt32 upper_disp_limit; /*upper limit of graph*/\
|
||||
epicsInt32 lower_disp_limit; /*lower limit of graph*/
|
||||
#define DBRgrDouble \
|
||||
epicsFloat64 upper_disp_limit; /*upper limit of graph*/\
|
||||
epicsFloat64 lower_disp_limit; /*lower limit of graph*/
|
||||
#define DBRctrlLong \
|
||||
epicsInt32 upper_ctrl_limit; /*upper limit of graph*/\
|
||||
epicsInt32 lower_ctrl_limit; /*lower limit of graph*/
|
||||
#define DBRctrlDouble \
|
||||
epicsFloat64 upper_ctrl_limit; /*upper limit of graph*/\
|
||||
epicsFloat64 lower_ctrl_limit; /*lower limit of graph*/
|
||||
#define DBRalLong \
|
||||
epicsInt32 upper_alarm_limit;\
|
||||
epicsInt32 upper_warning_limit;\
|
||||
epicsInt32 lower_warning_limit;\
|
||||
epicsInt32 lower_alarm_limit;
|
||||
#define DBRalDouble \
|
||||
epicsFloat64 upper_alarm_limit;\
|
||||
epicsFloat64 upper_warning_limit;\
|
||||
epicsFloat64 lower_warning_limit;\
|
||||
epicsFloat64 lower_alarm_limit;
|
||||
|
||||
/* structures for each option type */
|
||||
struct dbr_status {DBRstatus};
|
||||
struct dbr_units {DBRunits};
|
||||
struct dbr_precision {DBRprecision};
|
||||
struct dbr_time {DBRtime};
|
||||
struct dbr_enumStrs {DBRenumStrs};
|
||||
struct dbr_grLong {DBRgrLong};
|
||||
struct dbr_grDouble {DBRgrDouble};
|
||||
struct dbr_ctrlLong {DBRctrlLong};
|
||||
struct dbr_ctrlDouble {DBRctrlDouble};
|
||||
struct dbr_alLong {DBRalLong};
|
||||
struct dbr_alDouble {DBRalDouble};
|
||||
/* sizes for each option structure */
|
||||
#define dbr_status_size sizeof(struct dbr_status)
|
||||
#define dbr_units_size sizeof(struct dbr_units)
|
||||
#define dbr_precision_size sizeof(struct dbr_precision)
|
||||
#define dbr_time_size sizeof(struct dbr_time)
|
||||
#define dbr_enumStrs_size sizeof(struct dbr_enumStrs)
|
||||
#define dbr_grLong_size sizeof(struct dbr_grLong)
|
||||
#define dbr_grDouble_size sizeof(struct dbr_grDouble)
|
||||
#define dbr_ctrlLong_size sizeof(struct dbr_ctrlLong)
|
||||
#define dbr_ctrlDouble_size sizeof(struct dbr_ctrlDouble)
|
||||
#define dbr_alLong_size sizeof(struct dbr_alLong)
|
||||
#define dbr_alDouble_size sizeof(struct dbr_alDouble)
|
||||
|
||||
#ifndef INCerrMdefh
|
||||
#include "errMdef.h"
|
||||
#endif
|
||||
#define S_db_notFound (M_dbAccess| 1) /*Process Variable Not Found*/
|
||||
#define S_db_badDbrtype (M_dbAccess| 3) /*Illegal Database Request Type*/
|
||||
#define S_db_noMod (M_dbAccess| 5) /*Attempt to modify noMod field*/
|
||||
#define S_db_badLset (M_dbAccess| 7) /*Illegal Lock Set*/
|
||||
#define S_db_precision (M_dbAccess| 9) /*get precision failed */
|
||||
#define S_db_onlyOne (M_dbAccess|11) /*Only one element allowed*/
|
||||
#define S_db_badChoice (M_dbAccess|13) /*Illegal choice*/
|
||||
#define S_db_badField (M_dbAccess|15) /*Illegal field value*/
|
||||
#define S_db_lsetLogic (M_dbAccess|17) /*Logic error generating lock sets*/
|
||||
#define S_db_noLSET (M_dbAccess|21) /*No link support table or entry*/
|
||||
#define S_db_noRSET (M_dbAccess|31) /*missing record support entry table*/
|
||||
#define S_db_noSupport (M_dbAccess|33) /*RSET or DSXT routine not defined*/
|
||||
#define S_db_BadSub (M_dbAccess|35) /*Subroutine not found*/
|
||||
/*!!!! Do not change next line without changing src/rsrv/server.h!!!!!!!!*/
|
||||
#define S_db_Pending (M_dbAccess|37) /*Request is pending*/
|
||||
|
||||
#define S_db_Blocked (M_dbAccess|39) /*Request is Blocked*/
|
||||
#define S_db_putDisabled (M_dbAccess|41) /*putFields are disabled*/
|
||||
#define S_db_badHWaddr (M_dbAccess|43) /*Hardware link type not on INP/OUT*/
|
||||
#define S_db_bkptSet (M_dbAccess|53) /*Breakpoint already set*/
|
||||
#define S_db_bkptNotSet (M_dbAccess|55) /*No breakpoint set in record*/
|
||||
#define S_db_notStopped (M_dbAccess|57) /*Record not stopped*/
|
||||
#define S_db_errArg (M_dbAccess|59) /*Error in argument*/
|
||||
#define S_db_bkptLogic (M_dbAccess|61) /*Logic error in breakpoint routine*/
|
||||
#define S_db_cntSpwn (M_dbAccess|63) /*Cannot spawn dbContTask*/
|
||||
#define S_db_cntCont (M_dbAccess|65) /*Cannot resume dbContTask*/
|
||||
#define S_db_noMemory (M_dbAccess|66) /*unable to allocate data structure from pool*/
|
||||
#define S_db_notInit (M_dbAccess|67) /*Not initialized*/
|
||||
#define S_db_bufFull (M_dbAccess|68) /*Buffer full*/
|
||||
|
||||
epicsShareFunc long dbPutSpecial(struct dbAddr *paddr,int pass);
|
||||
epicsShareFunc rset * dbGetRset(const struct dbAddr *paddr);
|
||||
epicsShareFunc long dbPutAttribute(
|
||||
const char *recordTypename,const char *name,const char*value);
|
||||
epicsShareFunc int dbIsValueField(const struct dbFldDes *pdbFldDes);
|
||||
epicsShareFunc int dbGetFieldIndex(const struct dbAddr *paddr);
|
||||
epicsShareFunc long dbScanPassive(
|
||||
struct dbCommon *pfrom,struct dbCommon *pto);
|
||||
epicsShareFunc long dbProcess(struct dbCommon *precord);
|
||||
epicsShareFunc long dbNameToAddr(
|
||||
const char *pname,struct dbAddr *);
|
||||
epicsShareFunc devSup* dbDTYPtoDevSup(dbRecordType *prdes, int dtyp);
|
||||
epicsShareFunc devSup* dbDSETtoDevSup(dbRecordType *prdes, struct dset *pdset);
|
||||
epicsShareFunc long dbGetField(
|
||||
struct dbAddr *,short dbrType,void *pbuffer,long *options,
|
||||
long *nRequest,void *pfl);
|
||||
epicsShareFunc long dbGet(
|
||||
struct dbAddr *,short dbrType,void *pbuffer,long *options,
|
||||
long *nRequest,void *pfl);
|
||||
epicsShareFunc long dbPutField(
|
||||
struct dbAddr *,short dbrType,const void *pbuffer,long nRequest);
|
||||
epicsShareFunc long dbPut(
|
||||
struct dbAddr *,short dbrType,const void *pbuffer,long nRequest);
|
||||
|
||||
typedef void(*SPC_ASCALLBACK)(struct dbCommon *);
|
||||
/*dbSpcAsRegisterCallback called by access security */
|
||||
epicsShareFunc void dbSpcAsRegisterCallback(SPC_ASCALLBACK func);
|
||||
epicsShareFunc long dbBufferSize(
|
||||
short dbrType,long options,long nRequest);
|
||||
epicsShareFunc long dbValueSize(short dbrType);
|
||||
|
||||
/* Hook Routine */
|
||||
|
||||
typedef void (*DB_LOAD_RECORDS_HOOK_ROUTINE)(const char* filename,
|
||||
const char* substitutions);
|
||||
epicsShareExtern DB_LOAD_RECORDS_HOOK_ROUTINE dbLoadRecordsHook;
|
||||
|
||||
epicsShareFunc int dbLoadDatabase(
|
||||
const char *filename, const char *path, const char *substitutions);
|
||||
epicsShareFunc int dbLoadRecords(
|
||||
const char* filename, const char* substitutions);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /*INCdbAccessDefsh*/
|
||||
@@ -0,0 +1,30 @@
|
||||
/*************************************************************************\
|
||||
* Copyright (c) 2008 UChicago Argonne LLC, as Operator of Argonne
|
||||
* National Laboratory.
|
||||
* Copyright (c) 2002 The Regents of the University of California, as
|
||||
* Operator of Los Alamos National Laboratory.
|
||||
* EPICS BASE is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
\*************************************************************************/
|
||||
|
||||
#ifndef dbAddrh
|
||||
#define dbAddrh
|
||||
|
||||
struct dbCommon;
|
||||
struct dbFldDes;
|
||||
|
||||
typedef struct dbAddr {
|
||||
struct dbCommon *precord; /* address of record */
|
||||
void *pfield; /* address of field */
|
||||
struct dbFldDes *pfldDes; /* address of struct fldDes */
|
||||
long no_elements; /* number of elements (arrays) */
|
||||
short field_type; /* type of database field */
|
||||
short field_size; /* size of the field being accessed */
|
||||
short special; /* special processing */
|
||||
short dbr_field_type; /* field type as seen by database request*/
|
||||
/* DBR_STRING,...,DBR_ENUM,DBR_NOACCESS */
|
||||
} dbAddr;
|
||||
|
||||
typedef dbAddr DBADDR;
|
||||
|
||||
#endif /* dbAddrh */
|
||||
@@ -0,0 +1,970 @@
|
||||
/*************************************************************************\
|
||||
* 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.
|
||||
\*************************************************************************/
|
||||
/* dbBkpt.c */
|
||||
/*
|
||||
* Author: Matthew Needes
|
||||
* Date: 8-30-93
|
||||
*/
|
||||
|
||||
/*
|
||||
* Database Breakpoint Manipulation and User Interface
|
||||
*
|
||||
* USER COMMANDS
|
||||
* dbb(record_name) Set a breakpoint in a record
|
||||
* dbd(record_name) Delete a record's breakpoint
|
||||
* dbc(record_name) Resume record processing
|
||||
* dbs(record_name) Step through record processing through
|
||||
* IO links, forward process links, etc.
|
||||
* dbstat() Display status of stopped records in lock sets.
|
||||
* dbap(record_name) Toggle automatic print after processing.
|
||||
* dbp(record_name) Print out fields from record currently stopped.
|
||||
* dbprc(record_name) Processes a record once without printing it.
|
||||
* (Unless autoprint is on)
|
||||
*
|
||||
* INTERNAL FUNCTIONS
|
||||
* dbBkpt() Process breakpoints, called by dbProcess().
|
||||
* dbPrint() Prints record if autoprint enabled.
|
||||
* dbBkptCont() The task that continues and steps through
|
||||
* records that are stopped at a breakpoint.
|
||||
*/
|
||||
|
||||
/* #define BKPT_DIAG */
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "alarm.h"
|
||||
#include "dbDefs.h"
|
||||
#include "ellLib.h"
|
||||
#include "epicsEvent.h"
|
||||
#include "epicsMutex.h"
|
||||
#include "epicsThread.h"
|
||||
#include "epicsTime.h"
|
||||
#include "errlog.h"
|
||||
#include "errMdef.h"
|
||||
|
||||
#define epicsExportSharedSymbols
|
||||
#include "dbAccessDefs.h"
|
||||
#include "dbAddr.h"
|
||||
#include "dbBase.h"
|
||||
#include "dbBkpt.h"
|
||||
#include "dbCommon.h"
|
||||
#include "db_field_log.h"
|
||||
#include "dbFldTypes.h"
|
||||
#include "dbFldTypes.h"
|
||||
#include "dbLink.h"
|
||||
#include "dbLock.h"
|
||||
#include "dbScan.h"
|
||||
#include "dbTest.h"
|
||||
#include "link.h"
|
||||
#include "recGbl.h"
|
||||
#include "recSup.h"
|
||||
#include "special.h"
|
||||
|
||||
/* private routines */
|
||||
static void dbBkptCont(dbCommon *precord);
|
||||
static long FIND_CONT_NODE(
|
||||
const char *record_name,
|
||||
struct LS_LIST **ppnode,
|
||||
struct dbCommon **pprecord);
|
||||
|
||||
/*
|
||||
* Breakpoints are used as a debugging instrument to suspend the
|
||||
* processing of database records. Once suspended, record
|
||||
* processing may continue if either a continue (dbc()) or a
|
||||
* step (dbs()) command is then issued. The current record's
|
||||
* contents may be printed either with dbp(), or immediately
|
||||
* after processing (use dbap() to toggle the BKPT_PRINT bit).
|
||||
*
|
||||
* dbb() and dbd() add a breakpoint to a record or delete one
|
||||
* from a record. dbstat() prints out comprehensive breakpoint
|
||||
* status information.
|
||||
*
|
||||
* Breakpoints may be set on a per lockset basis. When a
|
||||
* breakpoint is set in a lockset, a new task is created. A
|
||||
* separate task gets created for _every_ lockset containing
|
||||
* a breakpoint. Thus multiple locksets may be debugged
|
||||
* simultaneously. The breakpoint handler then schedules future
|
||||
* processing in that lockset to this task. The separate task is
|
||||
* used so that locksets that do not have breakpoints are isolated
|
||||
* from locksets that do. This allows the processing of other
|
||||
* locksets to continue uninterupted, even if they exist on the same
|
||||
* scan list as a lockset containing a breakpoint.
|
||||
*
|
||||
* An entrypoint is the first record that gets processed in a lockset.
|
||||
* This type of record is the basis for subsequent recursive executions
|
||||
* of dbProcess(). The breakpoint handler monitors and schedules
|
||||
* these entrypoints to the breakpoint tasks.
|
||||
*
|
||||
* Two hooks have been inserted in dbProcess() to manage breakpoints,
|
||||
* dbBkpt() and dbPrint(). The former does two things:
|
||||
*
|
||||
* 1. Schedule entrypoints with the breakpoint task.
|
||||
* 2. Suspend record processing when a breakpoint is detected.
|
||||
*
|
||||
* 1 occurs only if dbProcess() is called outside of the breakpoint
|
||||
* task. Number 2 only occurs when dbProcess() is called from
|
||||
* _within_ the breakpoint task's context. Number 1 is used for
|
||||
* detection and scheduling, while 2 is used for suspending the task.
|
||||
*
|
||||
* The dbPrint() hook is used to print out a record's contents immediately
|
||||
* _after_ a record has been processed.
|
||||
*
|
||||
* The dbBkptCont, or breakpoint task, pends on a semaphore that gets
|
||||
* released whenever new entrypoints are scheduled for it. When
|
||||
* released, this task then runs down its entrypoint queue and
|
||||
* processes each entrypoint in turn. In this context, dbProcess
|
||||
* will execute the dbBkpt() hook in mode 2, allowing this task to
|
||||
* be suspended whenever a breakpoint is detected.
|
||||
*
|
||||
* NOTE: This is not a very "real-time" implementation (even for those
|
||||
* locksets not containing a breakpoint). I may fix this later.
|
||||
*
|
||||
* Final comment: The scary thing is, I don't think this can be done
|
||||
* more simply...
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* Flag used by dbProcess() to determine if there are
|
||||
* any breakpoints. This is so that there is only
|
||||
* a single comparison in the critical path during
|
||||
* normal record execution, i.e. when there aren't
|
||||
* any breakpoints set.
|
||||
*/
|
||||
long lset_stack_count = 0;
|
||||
|
||||
/*
|
||||
* Stack--in which each entry represents a different
|
||||
* lock set with either breakpoints and/or stopped
|
||||
* execution. (Breakpoints may be disabled even
|
||||
* though execution is stopped). The order of the
|
||||
* list is maintained so that the entry on the top
|
||||
* of stack is used as a default for dbc() and dbs().
|
||||
* The semaphore is used to prevent conflicts while
|
||||
* operating with this stack.
|
||||
*/
|
||||
static ELLLIST lset_stack = ELLLIST_INIT;
|
||||
static epicsMutexId bkpt_stack_sem = 0;
|
||||
|
||||
/*
|
||||
* Stores the last lockset continued or stepped from.
|
||||
* dbs() and dbc() will print a message if the current
|
||||
* lockset to be continued from differs from this
|
||||
* variable.
|
||||
*/
|
||||
static unsigned long last_lset = 0;
|
||||
|
||||
/*
|
||||
* FIND_LOCKSET() finds the stack entry
|
||||
* whose l_num field matches precord's
|
||||
* lset field. The node that is found
|
||||
* is returned in "pnode."
|
||||
*/
|
||||
#define FIND_LOCKSET(precord, pnode) \
|
||||
pnode = (struct LS_LIST *) ellFirst(&lset_stack); \
|
||||
while ((pnode) != NULL) { \
|
||||
if (pnode->l_num == dbLockGetLockId(precord)) break; \
|
||||
pnode = (struct LS_LIST *) ellNext((ELLNODE *)pnode); \
|
||||
} \
|
||||
|
||||
/*
|
||||
* FIND_QUEUE_ENTRY() matches entries in an
|
||||
* entry point queue. pep_queue is the queue
|
||||
* being searched, pqe is the pointer to the
|
||||
* queue entry found, and precord is the record
|
||||
* being searched for in *pep_queue.
|
||||
*/
|
||||
#define FIND_QUEUE_ENTRY(pep_queue, pqe, precord) \
|
||||
pqe = (struct EP_LIST *) ellFirst(pep_queue); \
|
||||
while ((pqe) != NULL) { \
|
||||
if ((pqe)->entrypoint == (precord)) break; \
|
||||
pqe = (struct EP_LIST *) ellNext((ELLNODE *)pqe); \
|
||||
} \
|
||||
|
||||
/*
|
||||
* Fills out pnode and precord structures for dbc() and dbs()
|
||||
* MUST LOCK OUT STACK BEFORE ENTRY
|
||||
*/
|
||||
static long FIND_CONT_NODE(
|
||||
const char *record_name,
|
||||
struct LS_LIST **ppnode,
|
||||
struct dbCommon **pprecord)
|
||||
{
|
||||
struct dbAddr addr;
|
||||
struct LS_LIST *pnode;
|
||||
struct dbCommon *precord = NULL;
|
||||
long status = 0;
|
||||
|
||||
if (record_name == NULL) {
|
||||
/*
|
||||
* Search through stack, taking the first entry that
|
||||
* is currently stopped at a breakpoint.
|
||||
*/
|
||||
pnode = (struct LS_LIST *) ellFirst(&lset_stack);
|
||||
while (pnode != NULL) {
|
||||
if (pnode->precord != NULL) {
|
||||
precord = pnode->precord;
|
||||
break;
|
||||
}
|
||||
pnode = (struct LS_LIST *) ellNext((ELLNODE *)pnode);
|
||||
}
|
||||
|
||||
if (pnode == NULL) {
|
||||
printf(" BKPT> No records are currently stopped\n");
|
||||
return(S_db_notStopped);
|
||||
}
|
||||
}
|
||||
else {
|
||||
/*
|
||||
* Convert name to address
|
||||
*/
|
||||
status = dbNameToAddr(record_name, &addr);
|
||||
if (status == S_db_notFound)
|
||||
printf(" BKPT> Record %s not found\n", record_name);
|
||||
if (status != 0)
|
||||
return(status);
|
||||
|
||||
precord = addr.precord;
|
||||
|
||||
FIND_LOCKSET(precord, pnode);
|
||||
|
||||
if (pnode == NULL || pnode->precord == NULL) {
|
||||
printf(" BKPT> Currently not stopped in this lockset\n");
|
||||
return(S_db_notStopped);
|
||||
}
|
||||
}
|
||||
|
||||
*pprecord = precord;
|
||||
*ppnode = pnode;
|
||||
return(0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialise the breakpoint stack
|
||||
*/
|
||||
void dbBkptInit(void)
|
||||
{
|
||||
if (! bkpt_stack_sem) {
|
||||
bkpt_stack_sem = epicsMutexMustCreate();
|
||||
lset_stack_count = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Add breakpoint to a lock set
|
||||
* 1. Convert name to address and check breakpoint mask.
|
||||
* 2. Lock database.
|
||||
* 3. If empty, initialize lock set stack and its semaphore.
|
||||
* 4. Take that semaphore.
|
||||
* 5. Find lockset in the list. If it doesn't exist, create it.
|
||||
* 6. Turn on breakpoint field in record.
|
||||
* 7. Add breakpoint to list of breakpoints in structure.
|
||||
* 8. Spawn continuation task if it isn't already running.
|
||||
*/
|
||||
long dbb(const char *record_name)
|
||||
{
|
||||
struct dbAddr addr;
|
||||
struct LS_LIST *pnode;
|
||||
struct BP_LIST *pbl;
|
||||
struct dbCommon *precord;
|
||||
long status;
|
||||
|
||||
/*
|
||||
* Convert name to address
|
||||
*/
|
||||
status = dbNameToAddr(record_name, &addr);
|
||||
if (status == S_db_notFound)
|
||||
printf(" BKPT> Record %s not found\n", record_name);
|
||||
if (status != 0) return(status);
|
||||
|
||||
precord = addr.precord;
|
||||
|
||||
if (precord->bkpt & BKPT_ON_MASK) {
|
||||
printf(" BKPT> Breakpoint already set in this record\n");
|
||||
return(S_db_bkptSet);
|
||||
}
|
||||
|
||||
dbScanLock(precord);
|
||||
|
||||
/*
|
||||
* Add lock set to the stack of lock sets that
|
||||
* contain breakpoints and/or stopped records.
|
||||
*/
|
||||
|
||||
epicsMutexMustLock(bkpt_stack_sem);
|
||||
|
||||
FIND_LOCKSET(precord, pnode);
|
||||
|
||||
if (pnode == NULL) {
|
||||
/* lockset not found, create node, add to end of list */
|
||||
pnode = (struct LS_LIST *) malloc(sizeof(struct LS_LIST));
|
||||
if (pnode == NULL) {
|
||||
printf(" BKPT> Out of memory\n");
|
||||
dbScanUnlock(precord);
|
||||
epicsMutexUnlock(bkpt_stack_sem);
|
||||
return(1);
|
||||
}
|
||||
pnode->precord = NULL;
|
||||
|
||||
/* initialize breakpoint list */
|
||||
ellInit(&pnode->bp_list);
|
||||
|
||||
/* initialize entry point queue */
|
||||
ellInit(&pnode->ep_queue);
|
||||
|
||||
/* create execution semaphore */
|
||||
pnode->ex_sem = epicsEventCreate(epicsEventEmpty);
|
||||
if (pnode->ex_sem == NULL) {
|
||||
printf(" BKPT> Out of memory\n");
|
||||
dbScanUnlock(precord);
|
||||
epicsMutexUnlock(bkpt_stack_sem);
|
||||
return(1);
|
||||
}
|
||||
|
||||
pnode->taskid = 0;
|
||||
pnode->step = 0;
|
||||
pnode->l_num = dbLockGetLockId(precord);
|
||||
|
||||
ellAdd(&lset_stack, (ELLNODE *)pnode);
|
||||
++lset_stack_count;
|
||||
}
|
||||
|
||||
/*
|
||||
* Add record to breakpoint list
|
||||
*/
|
||||
pbl = (struct BP_LIST *) malloc(sizeof(struct BP_LIST));
|
||||
if (pbl == NULL) {
|
||||
printf(" BKPT> Out of memory\n");
|
||||
dbScanUnlock(precord);
|
||||
epicsMutexUnlock(bkpt_stack_sem);
|
||||
return(1);
|
||||
}
|
||||
pbl->precord = precord;
|
||||
ellAdd(&pnode->bp_list, (ELLNODE *)pbl);
|
||||
|
||||
/*
|
||||
* Turn on breakpoint field in record
|
||||
*/
|
||||
precord->bkpt |= BKPT_ON_MASK;
|
||||
|
||||
if (! pnode->taskid) {
|
||||
|
||||
#ifdef BKPT_DIAG
|
||||
printf(" BKPT> Spawning task: %s\n", precord->name);
|
||||
#endif
|
||||
/*
|
||||
* Spawn continuation task
|
||||
*/
|
||||
pnode->taskid = epicsThreadCreate("bkptCont",epicsThreadPriorityScanLow-1,
|
||||
epicsThreadGetStackSize(epicsThreadStackBig),
|
||||
(EPICSTHREADFUNC)dbBkptCont,precord);
|
||||
if (pnode->taskid == 0) {
|
||||
printf(" BKPT> Cannot spawn task to process record\n");
|
||||
pnode->taskid = 0;
|
||||
dbScanUnlock(precord);
|
||||
epicsMutexUnlock(bkpt_stack_sem);
|
||||
return(1);
|
||||
}
|
||||
}
|
||||
|
||||
epicsMutexUnlock(bkpt_stack_sem);
|
||||
dbScanUnlock(precord);
|
||||
return(0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Remove breakpoint from a record
|
||||
* 1. Convert name to address and check breakpoint mask.
|
||||
* 2. Lock database and take stack semaphore.
|
||||
* 3. Find structure for record's lockset (in stack).
|
||||
* 4. Find and delete record from breakpoint list.
|
||||
* 5. Turn off break point field.
|
||||
* 6. Give up semaphore to "signal" bkptCont task to quit.
|
||||
*/
|
||||
long dbd(const char *record_name)
|
||||
{
|
||||
struct dbAddr addr;
|
||||
struct LS_LIST *pnode;
|
||||
struct BP_LIST *pbl;
|
||||
struct dbCommon *precord;
|
||||
long status;
|
||||
|
||||
/*
|
||||
* Convert name to address
|
||||
*/
|
||||
status = dbNameToAddr(record_name, &addr);
|
||||
if (status == S_db_notFound)
|
||||
printf(" BKPT> Record %s not found\n", record_name);
|
||||
if (status != 0) return(status);
|
||||
|
||||
precord = addr.precord;
|
||||
|
||||
if (!(precord->bkpt & BKPT_ON_MASK)) {
|
||||
printf(" BKPT> No breakpoint set in this record\n");
|
||||
return(S_db_bkptNotSet);
|
||||
}
|
||||
|
||||
dbScanLock(precord);
|
||||
|
||||
epicsMutexMustLock(bkpt_stack_sem);
|
||||
|
||||
FIND_LOCKSET(precord, pnode);
|
||||
|
||||
if (pnode == NULL) {
|
||||
/* not found, error ! */
|
||||
printf(" BKPT> Logic Error in dbd()\n");
|
||||
precord->bkpt &= BKPT_OFF_MASK;
|
||||
|
||||
epicsMutexUnlock(bkpt_stack_sem);
|
||||
dbScanUnlock(precord);
|
||||
return(S_db_bkptLogic);
|
||||
}
|
||||
|
||||
/*
|
||||
* Remove record from breakpoint list
|
||||
*/
|
||||
|
||||
/* find record in list */
|
||||
pbl = (struct BP_LIST *) ellFirst(&pnode->bp_list);
|
||||
while (pbl != NULL) {
|
||||
if (pbl->precord == precord) {
|
||||
ellDelete(&pnode->bp_list, (ELLNODE *)pbl);
|
||||
free(pbl);
|
||||
break;
|
||||
}
|
||||
pbl = (struct BP_LIST *) ellNext((ELLNODE *)pbl);
|
||||
}
|
||||
|
||||
if (pbl == NULL) {
|
||||
printf(" BKPT> Logic Error in dbd()\n");
|
||||
precord->bkpt &= BKPT_OFF_MASK;
|
||||
epicsMutexUnlock(bkpt_stack_sem);
|
||||
dbScanUnlock(precord);
|
||||
return(S_db_bkptLogic);
|
||||
}
|
||||
|
||||
/*
|
||||
* Turn off breakpoint field in record
|
||||
*/
|
||||
precord->bkpt &= BKPT_OFF_MASK;
|
||||
|
||||
/*
|
||||
* If there are no more breakpoints, give up semaphore
|
||||
* to cause the bkptCont task to quit.
|
||||
*/
|
||||
if (ellCount(&pnode->bp_list) == 0)
|
||||
epicsEventSignal(pnode->ex_sem);
|
||||
|
||||
epicsMutexUnlock(bkpt_stack_sem);
|
||||
|
||||
dbScanUnlock(precord);
|
||||
return(0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Continue processing in a lock set
|
||||
* 1. Find top node in the lockset stack.
|
||||
* 2. Turn off stepping mode.
|
||||
* 2. Resume dbBkptCont.
|
||||
*/
|
||||
long dbc(const char *record_name)
|
||||
{
|
||||
struct LS_LIST *pnode;
|
||||
struct dbCommon *precord = NULL;
|
||||
long status = 0;
|
||||
|
||||
epicsMutexMustLock(bkpt_stack_sem);
|
||||
|
||||
status = FIND_CONT_NODE(record_name, &pnode, &precord);
|
||||
if (status) {
|
||||
epicsMutexUnlock(bkpt_stack_sem);
|
||||
return(status);
|
||||
}
|
||||
|
||||
if (record_name == NULL && last_lset != pnode->l_num)
|
||||
printf(" BKPT> Continuing: %s\n", pnode->precord->name);
|
||||
|
||||
last_lset = pnode->l_num;
|
||||
|
||||
/*
|
||||
* Turn off stepping mode
|
||||
*/
|
||||
pnode->step = 0;
|
||||
|
||||
/*
|
||||
* Resume dbBkptCont() until dbProcess() is executed
|
||||
* for a record with a breakpoint. This occurs
|
||||
* because stepping mode has been switched off.
|
||||
*/
|
||||
epicsThreadResume(pnode->taskid);
|
||||
epicsMutexUnlock(bkpt_stack_sem);
|
||||
return(0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Step through record processing
|
||||
* 1. Find top node in lockset stack.
|
||||
* 2. Resume dbBkptCont.
|
||||
*/
|
||||
long dbs(const char *record_name)
|
||||
{
|
||||
struct LS_LIST *pnode;
|
||||
struct dbCommon *precord = NULL;
|
||||
long status = 0;
|
||||
|
||||
epicsMutexMustLock(bkpt_stack_sem);
|
||||
|
||||
status = FIND_CONT_NODE(record_name, &pnode, &precord);
|
||||
if (status) {
|
||||
epicsMutexUnlock(bkpt_stack_sem);
|
||||
return(status);
|
||||
}
|
||||
|
||||
if (last_lset != pnode->l_num && record_name == NULL)
|
||||
printf(" BKPT> Stepping: %s\n", pnode->precord->name);
|
||||
|
||||
last_lset = pnode->l_num;
|
||||
|
||||
epicsThreadResume(pnode->taskid);
|
||||
epicsMutexUnlock(bkpt_stack_sem);
|
||||
return(0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Task for continuing record processing
|
||||
* 1. Find lockset in stack for precord.
|
||||
* DO 2-3 while breakpoints exist in the lockset.
|
||||
* 2. Wait on execution semaphore ...
|
||||
* 3. Run through every entrypoint in queue, processing
|
||||
* those that are scheduled.
|
||||
* 4. Free resources for lockset, and exit task.
|
||||
*/
|
||||
static void dbBkptCont(dbCommon *precord)
|
||||
{
|
||||
struct LS_LIST *pnode;
|
||||
struct EP_LIST *pqe = NULL;
|
||||
|
||||
/*
|
||||
* Reset breakpoint, process record, and
|
||||
* reset bkpt field in record
|
||||
*/
|
||||
epicsMutexMustLock(bkpt_stack_sem);
|
||||
|
||||
FIND_LOCKSET(precord, pnode);
|
||||
|
||||
if (pnode == NULL) {
|
||||
printf(" BKPT> Logic error in dbBkptCont()\n");
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* For every entrypoint scheduled, process. Run process
|
||||
* until there are no more breakpoints remaining in a
|
||||
* lock set.
|
||||
*/
|
||||
do {
|
||||
/* Give up semaphore before waiting to run ... */
|
||||
epicsMutexUnlock(bkpt_stack_sem);
|
||||
|
||||
/* Wait to run */
|
||||
epicsEventMustWait(pnode->ex_sem);
|
||||
|
||||
/* Bkpt stack must still be stable ! */
|
||||
epicsMutexMustLock(bkpt_stack_sem);
|
||||
|
||||
pqe = (struct EP_LIST *) ellFirst(&pnode->ep_queue);
|
||||
|
||||
/* Run through entrypoint queue */
|
||||
while (pqe != NULL) {
|
||||
/* check if entrypoint is currently scheduled */
|
||||
if (pqe->sched) {
|
||||
/* save current entrypoint */
|
||||
pnode->current_ep = pqe->entrypoint;
|
||||
|
||||
/* lock the lockset, process record, unlock */
|
||||
dbScanLock(precord);
|
||||
dbProcess(pqe->entrypoint);
|
||||
dbScanUnlock(precord);
|
||||
|
||||
/* reset schedule and stepping flag - Do this AFTER processing */
|
||||
pqe->sched = 0;
|
||||
pnode->step = 0;
|
||||
}
|
||||
pqe = (struct EP_LIST *) ellNext((ELLNODE *)pqe);
|
||||
}
|
||||
|
||||
/* Reset precord. (Since no records are at a breakpoint) */
|
||||
pnode->precord = NULL;
|
||||
} while (ellCount(&pnode->bp_list) != 0);
|
||||
|
||||
/* remove node from lockset stack */
|
||||
ellDelete(&lset_stack, (ELLNODE *)pnode);
|
||||
--lset_stack_count;
|
||||
|
||||
/* free entrypoint queue */
|
||||
ellFree(&pnode->ep_queue);
|
||||
|
||||
/* remove execution semaphore */
|
||||
epicsEventDestroy(pnode->ex_sem);
|
||||
|
||||
printf("\n BKPT> End debug of lockset %lu\n-> ", pnode->l_num);
|
||||
|
||||
/* free list node */
|
||||
free(pnode);
|
||||
|
||||
epicsMutexUnlock(bkpt_stack_sem);
|
||||
}
|
||||
|
||||
/*
|
||||
* Process breakpoint
|
||||
* Returns a zero if dbProcess() is to execute
|
||||
* record support, a one if dbProcess() is to
|
||||
* skip over record support. See dbProcess().
|
||||
*
|
||||
* 1. See if there is at least a breakpoint set somewhere
|
||||
* in precord's lockset. If not, return immediately.
|
||||
* 2. Check the disable flag.
|
||||
* 3. Add entry points to the queue for future stepping and
|
||||
* schedule new entrypoints for the continuation task.
|
||||
* 4. Check the pact flag.
|
||||
* 5. Check to see if there is a breakpoint set in a record, and
|
||||
* if so, turn on stepping mode.
|
||||
* 6. If stepping mode is set, stop and report the breakpoint.
|
||||
*/
|
||||
int dbBkpt(dbCommon *precord)
|
||||
{
|
||||
struct LS_LIST *pnode;
|
||||
struct EP_LIST *pqe;
|
||||
|
||||
/*
|
||||
* It is crucial that operations in dbBkpt() execute
|
||||
* in the correct order or certain features in the
|
||||
* breakpoint handler will not work as expected.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Take and give a semaphore to check for breakpoints
|
||||
* every time a record is processed. Slow. Thank
|
||||
* goodness breakpoint checking is turned off during
|
||||
* normal operation.
|
||||
*/
|
||||
epicsMutexMustLock(bkpt_stack_sem);
|
||||
FIND_LOCKSET(precord, pnode);
|
||||
epicsMutexUnlock(bkpt_stack_sem);
|
||||
|
||||
if (pnode == NULL) {
|
||||
/* no breakpoints in precord's lockset */
|
||||
return(0);
|
||||
}
|
||||
|
||||
/* Check disable flag */
|
||||
dbGetLink(&(precord->sdis),DBR_SHORT,&(precord->disa),0,0);
|
||||
if (precord->disa == precord->disv) {
|
||||
/*
|
||||
* Do not process breakpoints if the record is disabled,
|
||||
* but allow disable alarms. Alarms will be raised
|
||||
* in dbProcess() because returning 0 allows dbProcess()
|
||||
* to continue. However processing will be prevented
|
||||
* because disa and disv will be examined again in
|
||||
* dbProcess(). Note that checking for pact will occur
|
||||
* before checking for disa and disv in dbProcess().
|
||||
*/
|
||||
return(0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Queue entry points for future stepping. The taskid comparison
|
||||
* is used to determine if the source of processing is the
|
||||
* continuation task or an external source. If it is an external
|
||||
* source, queue its execution, but dump out of dbProcess without
|
||||
* calling record support.
|
||||
*/
|
||||
if (pnode->taskid && (epicsThreadGetIdSelf() != pnode->taskid)) {
|
||||
/* CONTINUE TASK CANNOT ENTER HERE */
|
||||
|
||||
/*
|
||||
* Add an entry point to queue, if it does
|
||||
* not already exist.
|
||||
*/
|
||||
FIND_QUEUE_ENTRY(&pnode->ep_queue, pqe, precord);
|
||||
|
||||
if (pqe == NULL) {
|
||||
|
||||
pqe = (struct EP_LIST *) malloc(sizeof(struct EP_LIST));
|
||||
if (pqe == NULL)
|
||||
return(1);
|
||||
|
||||
|
||||
pqe->entrypoint = precord;
|
||||
pqe->count = 1;
|
||||
epicsTimeGetCurrent(&pqe->time);
|
||||
pqe->sched = 0;
|
||||
|
||||
#ifdef BKPT_DIAG
|
||||
printf(" BKPT> Adding entrypoint %s to queue\n", precord->name);
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Take semaphore, wait on continuation task
|
||||
*/
|
||||
epicsMutexMustLock(bkpt_stack_sem);
|
||||
|
||||
/* Add entry to queue */
|
||||
ellAdd(&pnode->ep_queue, (ELLNODE *)pqe);
|
||||
|
||||
epicsMutexUnlock(bkpt_stack_sem);
|
||||
}
|
||||
else {
|
||||
if (pqe->count < MAX_EP_COUNT)
|
||||
pqe->count++;
|
||||
}
|
||||
|
||||
/* check pact */
|
||||
if (! precord->pact) {
|
||||
/* schedule if pact not set */
|
||||
pqe->sched = 1;
|
||||
|
||||
/*
|
||||
* Release the semaphore, letting the continuation
|
||||
* task begin execution of the new entrypoint.
|
||||
*/
|
||||
epicsEventSignal(pnode->ex_sem);
|
||||
}
|
||||
return(1);
|
||||
}
|
||||
|
||||
/*
|
||||
* Don't mess with breakpoints if pact set! Skip
|
||||
* over rest of dbProcess() since we don't want
|
||||
* alarms going off. The pact flag is checked
|
||||
* AFTER entry point queuing so that the record
|
||||
* timing feature will work properly.
|
||||
*/
|
||||
if (precord->pact)
|
||||
return(1);
|
||||
|
||||
/* Turn on stepping mode if a breakpoint is found */
|
||||
if (precord->bkpt & BKPT_ON_MASK) {
|
||||
pnode->step = 1;
|
||||
|
||||
#ifdef BKPT_DIAG
|
||||
printf(" BKPT> Bkpt detected: %s\n", precord->name);
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
* If we are currently stepping through the lockset,
|
||||
* suspend task.
|
||||
*/
|
||||
if (pnode->step) {
|
||||
printf("\n BKPT> Stopped at: %s within Entrypoint: %s\n-> ",
|
||||
precord->name, pnode->current_ep->name);
|
||||
|
||||
pnode->precord = precord;
|
||||
|
||||
/* Move current lockset to top of stack */
|
||||
ellDelete(&lset_stack, (ELLNODE *)pnode);
|
||||
ellInsert(&lset_stack, NULL, (ELLNODE *)pnode);
|
||||
/*
|
||||
* Unlock database while the task suspends itself. This
|
||||
* is done so that dbb() dbd() dbc() dbs() may be used
|
||||
* when the task is suspended. Scan tasks that also
|
||||
* use the scan lock feature will not be hung during
|
||||
* a breakpoint, so that records in other locksets will
|
||||
* continue to be processed. Cross your fingers, this
|
||||
* might actually work !
|
||||
*/
|
||||
epicsMutexUnlock(bkpt_stack_sem);
|
||||
dbScanUnlock(precord);
|
||||
epicsThreadSuspendSelf();
|
||||
dbScanLock(precord);
|
||||
epicsMutexMustLock(bkpt_stack_sem);
|
||||
}
|
||||
return(0);
|
||||
}
|
||||
|
||||
/* print record after processing */
|
||||
void dbPrint(dbCommon *precord )
|
||||
{
|
||||
struct LS_LIST *pnode;
|
||||
|
||||
if (! (precord->bkpt & BKPT_PRINT_MASK))
|
||||
return;
|
||||
|
||||
FIND_LOCKSET(precord, pnode);
|
||||
|
||||
/* do not print if lockset does not currently contain breakpoints */
|
||||
if (pnode == NULL)
|
||||
return;
|
||||
|
||||
printf("\n");
|
||||
dbpr(precord->name, 2);
|
||||
printf("-> ");
|
||||
}
|
||||
|
||||
/* print stopped record */
|
||||
long dbp(const char *record_name, int interest_level)
|
||||
{
|
||||
struct LS_LIST *pnode;
|
||||
struct dbCommon *precord = NULL;
|
||||
int status;
|
||||
|
||||
epicsMutexMustLock(bkpt_stack_sem);
|
||||
|
||||
/* find pnode and precord pointers */
|
||||
status = FIND_CONT_NODE(record_name, &pnode, &precord);
|
||||
if (status) {
|
||||
epicsMutexUnlock(bkpt_stack_sem);
|
||||
return(status);
|
||||
}
|
||||
|
||||
/* print out record's fields */
|
||||
dbpr(precord->name, (interest_level == 0) ? 2 : interest_level);
|
||||
|
||||
epicsMutexUnlock(bkpt_stack_sem);
|
||||
return(0);
|
||||
}
|
||||
|
||||
/* toggle printing after processing a certain record */
|
||||
long dbap(const char *record_name)
|
||||
{
|
||||
struct dbAddr addr;
|
||||
struct dbCommon *precord;
|
||||
long status;
|
||||
|
||||
/*
|
||||
* Convert name to address
|
||||
*/
|
||||
status = dbNameToAddr(record_name, &addr);
|
||||
if (status == S_db_notFound)
|
||||
printf(" BKPT> Record %s not found\n", record_name);
|
||||
if (status != 0) return(status);
|
||||
|
||||
precord = addr.precord;
|
||||
|
||||
/*
|
||||
* Toggle print after process field in record
|
||||
*/
|
||||
if (precord->bkpt & BKPT_PRINT_MASK) {
|
||||
printf(" BKPT> Auto print off for record %s\n", precord->name);
|
||||
precord->bkpt &= BKPT_PRINT_OFF_MASK;
|
||||
}
|
||||
else {
|
||||
printf(" BKPT> Auto print on for record %s\n", precord->name);
|
||||
precord->bkpt |= BKPT_PRINT_MASK;
|
||||
}
|
||||
|
||||
return(0);
|
||||
}
|
||||
|
||||
/* print list of stopped records, and breakpoints set in locksets */
|
||||
long dbstat(void)
|
||||
{
|
||||
struct LS_LIST *pnode;
|
||||
struct BP_LIST *pbl;
|
||||
struct EP_LIST *pqe;
|
||||
epicsTimeStamp time;
|
||||
|
||||
epicsMutexMustLock(bkpt_stack_sem);
|
||||
|
||||
epicsTimeGetCurrent(&time);
|
||||
|
||||
/*
|
||||
* Traverse list, reporting stopped records
|
||||
*/
|
||||
pnode = (struct LS_LIST *) ellFirst(&lset_stack);
|
||||
while (pnode != NULL) {
|
||||
if (pnode->precord != NULL) {
|
||||
|
||||
printf("LSet: %lu Stopped at: %-28.28s #B: %5.5d T: %p\n",
|
||||
pnode->l_num, pnode->precord->name, ellCount(&pnode->bp_list), pnode->taskid);
|
||||
|
||||
/* for each entrypoint detected, print out entrypoint statistics */
|
||||
pqe = (struct EP_LIST *) ellFirst(&pnode->ep_queue);
|
||||
while (pqe != NULL) {
|
||||
double diff = epicsTimeDiffInSeconds(&time,&pqe->time);
|
||||
if (diff) {
|
||||
printf(" Entrypoint: %-28.28s #C: %5.5lu C/S: %7.1f\n",
|
||||
pqe->entrypoint->name, pqe->count,diff);
|
||||
}
|
||||
pqe = (struct EP_LIST *) ellNext((ELLNODE *)pqe);
|
||||
}
|
||||
}
|
||||
else {
|
||||
printf("LSet: %lu #B: %5.5d T: %p\n",
|
||||
pnode->l_num, ellCount(&pnode->bp_list), pnode->taskid);
|
||||
}
|
||||
|
||||
/*
|
||||
* Print out breakpoints set in the lock set
|
||||
*/
|
||||
pbl = (struct BP_LIST *) ellFirst(&pnode->bp_list);
|
||||
while (pbl != NULL) {
|
||||
printf(" Breakpoint: %-28.28s", pbl->precord->name);
|
||||
|
||||
/* display auto print flag */
|
||||
if (pbl->precord->bkpt & BKPT_PRINT_MASK)
|
||||
printf(" (ap)\n");
|
||||
else
|
||||
printf("\n");
|
||||
|
||||
pbl = (struct BP_LIST *) ellNext((ELLNODE *)pbl);
|
||||
}
|
||||
|
||||
pnode = (struct LS_LIST *) ellNext((ELLNODE *)pnode);
|
||||
}
|
||||
|
||||
epicsMutexUnlock(bkpt_stack_sem);
|
||||
return(0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Process a record without printing it.
|
||||
*/
|
||||
long dbprc(char *record_name)
|
||||
{
|
||||
struct dbAddr addr;
|
||||
struct dbCommon *precord;
|
||||
long status;
|
||||
|
||||
/*
|
||||
* Convert name to address
|
||||
*/
|
||||
status = dbNameToAddr(record_name, &addr);
|
||||
if (status == S_db_notFound)
|
||||
printf(" BKPT> Record %s not found\n", record_name);
|
||||
if (status != 0) return(status);
|
||||
|
||||
precord = addr.precord;
|
||||
|
||||
/* lock lockset, process record, unlock lockset */
|
||||
dbScanLock(precord);
|
||||
status = dbProcess(precord);
|
||||
dbScanUnlock(precord);
|
||||
|
||||
return(status);
|
||||
}
|
||||
|
||||
#ifdef BKPT_DIAG
|
||||
|
||||
/* Reset breakpoints */
|
||||
int dbreset()
|
||||
{
|
||||
epicsMutexUnlock(bkpt_stack_sem);
|
||||
|
||||
return(0);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,103 @@
|
||||
/*************************************************************************\
|
||||
* 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.
|
||||
\*************************************************************************/
|
||||
/* dbBkpt.h */
|
||||
/*
|
||||
* Author: Matthew Needes
|
||||
* Date: 8-30-93
|
||||
*/
|
||||
|
||||
#ifndef INCdbBkptsh
|
||||
#define INCdbBkptsh 1
|
||||
|
||||
#include "ellLib.h"
|
||||
#include "epicsEvent.h"
|
||||
#include "epicsThread.h"
|
||||
#include "epicsTime.h"
|
||||
#include "shareLib.h"
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Structure containing a list of set breakpoints
|
||||
* in a lockset
|
||||
*/
|
||||
|
||||
struct BP_LIST {
|
||||
ELLNODE *next_list;
|
||||
ELLNODE *prev_list;
|
||||
struct dbCommon *precord;
|
||||
};
|
||||
|
||||
/*
|
||||
* Structure containing queue of entrypoints
|
||||
* detected for a lockset.
|
||||
*/
|
||||
struct EP_LIST {
|
||||
ELLNODE *next_list;
|
||||
ELLNODE *prev_list;
|
||||
struct dbCommon *entrypoint; /* pointer to entry point in lockset */
|
||||
unsigned long count; /* number of times record processed */
|
||||
epicsTimeStamp time; /* time record first logged */
|
||||
char sched; /* schedule record for next dbContTask() pass */
|
||||
};
|
||||
|
||||
/*
|
||||
* Structure for stack of lock sets that
|
||||
* currently contain breakpoints. (uses ellLib)
|
||||
*/
|
||||
struct LS_LIST {
|
||||
ELLNODE *next_list;
|
||||
ELLNODE *prev_list;
|
||||
struct dbCommon *precord;/* points to where execution is currently stopped */
|
||||
struct dbCommon *current_ep; /* current entrypoint */
|
||||
ELLLIST bp_list; /* list of records containing breakpoints in a lockset */
|
||||
ELLLIST ep_queue; /* queue of entrypoints found so far */
|
||||
epicsEventId ex_sem; /* semaphore for execution queue */
|
||||
epicsThreadId taskid; /* saved taskid for the task in stepping mode */
|
||||
int step; /* one if currently "stepping," else zero */
|
||||
unsigned long l_num; /* lockset number */
|
||||
};
|
||||
|
||||
/* Values for BKPT (breakpoint) field in record */
|
||||
|
||||
/* 1st bit = 0 if breakpoint is not set, */
|
||||
/* 1 if breakpoint set */
|
||||
/* 2nd bit = 0 if no printing after processing */
|
||||
/* 1 if print after processing set */
|
||||
|
||||
/* Breakpoint Masks */
|
||||
#define BKPT_ON_MASK 0x001
|
||||
#define BKPT_OFF_MASK 0x0FE
|
||||
#define BKPT_PRINT_MASK 0x002
|
||||
#define BKPT_PRINT_OFF_MASK 0x0FD
|
||||
|
||||
#define MAX_EP_COUNT 99999
|
||||
|
||||
epicsShareFunc void dbBkptInit(void);
|
||||
epicsShareFunc long dbb(const char *recordname);
|
||||
epicsShareFunc long dbd(const char *recordname);
|
||||
epicsShareFunc long dbc(const char *recordname);
|
||||
epicsShareFunc long dbs(const char *recordname);
|
||||
epicsShareFunc long dbstat(void);
|
||||
epicsShareFunc long dbp(
|
||||
const char *record_name, int interest_level);
|
||||
epicsShareFunc long dbap(const char *record_name);
|
||||
epicsShareFunc int dbBkpt(struct dbCommon *precord);
|
||||
epicsShareFunc void dbPrint(struct dbCommon *precord);
|
||||
epicsShareFunc long dbprc(char *record_name);
|
||||
|
||||
extern long lset_stack_count;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,239 @@
|
||||
/*************************************************************************\
|
||||
* 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.
|
||||
\*************************************************************************/
|
||||
/*
|
||||
* Author Jeffrey O. Hill
|
||||
* johill@lanl.gov
|
||||
* 505 665 1831
|
||||
*
|
||||
* NOTES:
|
||||
* 1) This interface is preliminary and will change in the future
|
||||
*/
|
||||
|
||||
#ifndef dbCACh
|
||||
#define dbCACh
|
||||
|
||||
#ifdef epicsExportSharedSymbols
|
||||
# define dbCACh_restore_epicsExportSharedSymbols
|
||||
# undef epicsExportSharedSymbols
|
||||
#endif
|
||||
|
||||
#include "stdlib.h"
|
||||
|
||||
#include <memory> // std::auto_ptr
|
||||
|
||||
#include "tsDLList.h"
|
||||
#include "tsFreeList.h"
|
||||
#include "resourceLib.h"
|
||||
#include "cacIO.h"
|
||||
#include "compilerDependencies.h"
|
||||
|
||||
#ifdef dbCACh_restore_epicsExportSharedSymbols
|
||||
# define epicsExportSharedSymbols
|
||||
# include "shareLib.h"
|
||||
#endif
|
||||
|
||||
#include "db_access.h"
|
||||
#include "dbNotify.h"
|
||||
#include "dbEvent.h"
|
||||
#include "dbChannel.h"
|
||||
#include "dbLock.h"
|
||||
#include "dbCommon.h"
|
||||
#include "db_convert.h"
|
||||
#include "resourceLib.h"
|
||||
|
||||
extern "C" int putNotifyPut ( processNotify *ppn, notifyPutType notifyPutType );
|
||||
extern "C" void putNotifyCompletion ( processNotify *ppn );
|
||||
|
||||
class dbContext;
|
||||
class dbChannelIO;
|
||||
class dbPutNotifyBlocker;
|
||||
class dbSubscriptionIO;
|
||||
|
||||
class dbBaseIO
|
||||
: public chronIntIdRes < dbBaseIO > {
|
||||
public:
|
||||
virtual dbSubscriptionIO * isSubscription () = 0;
|
||||
virtual void show ( epicsGuard < epicsMutex > &, unsigned level ) const = 0;
|
||||
virtual void show ( unsigned level ) const = 0;
|
||||
dbBaseIO ();
|
||||
dbBaseIO ( const dbBaseIO & );
|
||||
dbBaseIO & operator = ( const dbBaseIO & );
|
||||
protected:
|
||||
virtual ~dbBaseIO() {}
|
||||
};
|
||||
|
||||
extern "C" void dbSubscriptionEventCallback ( void *pPrivate, struct dbChannel *dbch,
|
||||
int eventsRemaining, struct db_field_log *pfl );
|
||||
|
||||
class dbSubscriptionIO :
|
||||
public tsDLNode < dbSubscriptionIO >,
|
||||
public dbBaseIO {
|
||||
public:
|
||||
dbSubscriptionIO (
|
||||
epicsGuard < epicsMutex > &, epicsMutex &,
|
||||
dbContext &, dbChannelIO &, struct dbChannel *, cacStateNotify &,
|
||||
unsigned type, unsigned long count, unsigned mask, dbEventCtx );
|
||||
void destructor ( CallbackGuard &, epicsGuard < epicsMutex > & );
|
||||
void unsubscribe ( CallbackGuard &, epicsGuard < epicsMutex > & );
|
||||
void channelDeleteException ( CallbackGuard &, epicsGuard < epicsMutex > & );
|
||||
void show ( epicsGuard < epicsMutex > &, unsigned level ) const;
|
||||
void show ( unsigned level ) const;
|
||||
void * operator new ( size_t size,
|
||||
tsFreeList < dbSubscriptionIO, 256, epicsMutexNOOP > & );
|
||||
epicsPlacementDeleteOperator (( void *,
|
||||
tsFreeList < dbSubscriptionIO, 256, epicsMutexNOOP > & ))
|
||||
private:
|
||||
epicsMutex & mutex;
|
||||
unsigned long count;
|
||||
cacStateNotify & notify;
|
||||
dbChannelIO & chan;
|
||||
dbEventSubscription es;
|
||||
unsigned type;
|
||||
unsigned id;
|
||||
dbSubscriptionIO * isSubscription ();
|
||||
friend void dbSubscriptionEventCallback (
|
||||
void * pPrivate, struct dbChannel * dbch,
|
||||
int eventsRemaining, struct db_field_log * pfl );
|
||||
dbSubscriptionIO ( const dbSubscriptionIO & );
|
||||
dbSubscriptionIO & operator = ( const dbSubscriptionIO & );
|
||||
virtual ~dbSubscriptionIO ();
|
||||
void operator delete ( void * );
|
||||
};
|
||||
|
||||
class dbContext;
|
||||
|
||||
class dbContextPrivateListOfIO {
|
||||
public:
|
||||
dbContextPrivateListOfIO ();
|
||||
~dbContextPrivateListOfIO ();
|
||||
private:
|
||||
tsDLList < dbSubscriptionIO > eventq;
|
||||
dbPutNotifyBlocker * pBlocker;
|
||||
friend class dbContext;
|
||||
dbContextPrivateListOfIO ( const dbContextPrivateListOfIO & );
|
||||
dbContextPrivateListOfIO & operator = ( const dbContextPrivateListOfIO & );
|
||||
};
|
||||
|
||||
class dbContextReadNotifyCacheAllocator {
|
||||
public:
|
||||
dbContextReadNotifyCacheAllocator ();
|
||||
~dbContextReadNotifyCacheAllocator ();
|
||||
char * alloc ( unsigned long size );
|
||||
void free ( char * pFree );
|
||||
void show ( unsigned level ) const;
|
||||
private:
|
||||
struct cacheElem_t {
|
||||
size_t size;
|
||||
struct cacheElem_t * pNext;
|
||||
char buf[1];
|
||||
};
|
||||
unsigned long _readNotifyCacheSize;
|
||||
cacheElem_t * _pReadNotifyCache;
|
||||
void reclaimAllCacheEntries ();
|
||||
dbContextReadNotifyCacheAllocator ( const dbContextReadNotifyCacheAllocator & );
|
||||
dbContextReadNotifyCacheAllocator & operator = ( const dbContextReadNotifyCacheAllocator & );
|
||||
};
|
||||
|
||||
class dbContextReadNotifyCache {
|
||||
public:
|
||||
dbContextReadNotifyCache ( epicsMutex & );
|
||||
void callReadNotify ( epicsGuard < epicsMutex > &,
|
||||
struct dbChannel * dbch, unsigned type, unsigned long count,
|
||||
cacReadNotify & notify );
|
||||
void show ( epicsGuard < epicsMutex > &, unsigned level ) const;
|
||||
private:
|
||||
dbContextReadNotifyCacheAllocator _allocator;
|
||||
epicsMutex & _mutex;
|
||||
dbContextReadNotifyCache ( const dbContextReadNotifyCache & );
|
||||
dbContextReadNotifyCache & operator = ( const dbContextReadNotifyCache & );
|
||||
};
|
||||
|
||||
class dbContext : public cacContext {
|
||||
public:
|
||||
dbContext ( epicsMutex & cbMutex, epicsMutex & mutex,
|
||||
cacContextNotify & notify );
|
||||
virtual ~dbContext ();
|
||||
void destroyChannel ( CallbackGuard &,epicsGuard < epicsMutex > &, dbChannelIO & );
|
||||
void callReadNotify ( epicsGuard < epicsMutex > &,
|
||||
struct dbChannel * dbch, unsigned type, unsigned long count,
|
||||
cacReadNotify & notify );
|
||||
void callStateNotify ( struct dbChannel * dbch, unsigned type, unsigned long count,
|
||||
const struct db_field_log * pfl, cacStateNotify & notify );
|
||||
void subscribe (
|
||||
epicsGuard < epicsMutex > &,
|
||||
struct dbChannel * dbch, dbChannelIO & chan,
|
||||
unsigned type, unsigned long count, unsigned mask,
|
||||
cacStateNotify & notify, cacChannel::ioid * pId );
|
||||
void initiatePutNotify (
|
||||
epicsGuard < epicsMutex > &, dbChannelIO &, struct dbChannel *,
|
||||
unsigned type, unsigned long count, const void * pValue,
|
||||
cacWriteNotify & notify, cacChannel::ioid * pId );
|
||||
void show ( unsigned level ) const;
|
||||
void showAllIO ( const dbChannelIO & chan, unsigned level ) const;
|
||||
void destroyAllIO ( CallbackGuard & cbGuard,
|
||||
epicsGuard < epicsMutex > &, dbChannelIO & chan );
|
||||
void ioCancel ( CallbackGuard &, epicsGuard < epicsMutex > &,
|
||||
dbChannelIO & chan, const cacChannel::ioid &id );
|
||||
void ioShow ( epicsGuard < epicsMutex > &,
|
||||
const cacChannel::ioid & id, unsigned level ) const;
|
||||
private:
|
||||
tsFreeList < dbPutNotifyBlocker, 64, epicsMutexNOOP > dbPutNotifyBlockerFreeList;
|
||||
tsFreeList < dbSubscriptionIO, 256, epicsMutexNOOP > dbSubscriptionIOFreeList;
|
||||
tsFreeList < dbChannelIO, 256, epicsMutexNOOP > dbChannelIOFreeList;
|
||||
chronIntIdResTable < dbBaseIO > ioTable;
|
||||
dbContextReadNotifyCache readNotifyCache;
|
||||
dbEventCtx ctx;
|
||||
unsigned long stateNotifyCacheSize;
|
||||
epicsMutex & mutex;
|
||||
epicsMutex & cbMutex;
|
||||
cacContextNotify & notify;
|
||||
std::auto_ptr < cacContext > pNetContext;
|
||||
char * pStateNotifyCache;
|
||||
bool isolated;
|
||||
|
||||
cacChannel & createChannel (
|
||||
epicsGuard < epicsMutex > &,
|
||||
const char * pChannelName, cacChannelNotify &,
|
||||
cacChannel::priLev );
|
||||
void flush (
|
||||
epicsGuard < epicsMutex > & );
|
||||
unsigned circuitCount (
|
||||
epicsGuard < epicsMutex > & ) const;
|
||||
void selfTest (
|
||||
epicsGuard < epicsMutex > & ) const;
|
||||
unsigned beaconAnomaliesSinceProgramStart (
|
||||
epicsGuard < epicsMutex > & ) const;
|
||||
void show (
|
||||
epicsGuard < epicsMutex > &, unsigned level ) const;
|
||||
|
||||
dbContext ( const dbContext & );
|
||||
dbContext & operator = ( const dbContext & );
|
||||
};
|
||||
|
||||
inline dbContextPrivateListOfIO::dbContextPrivateListOfIO () :
|
||||
pBlocker ( 0 )
|
||||
{
|
||||
}
|
||||
|
||||
inline dbContextPrivateListOfIO::~dbContextPrivateListOfIO ()
|
||||
{
|
||||
assert ( ! this->pBlocker );
|
||||
}
|
||||
|
||||
inline void dbContext::callReadNotify (
|
||||
epicsGuard < epicsMutex > & guard, struct dbChannel * dbch,
|
||||
unsigned type, unsigned long count, cacReadNotify & notifyIn )
|
||||
{
|
||||
guard.assertIdenticalMutex ( this-> mutex );
|
||||
this->readNotifyCache.callReadNotify ( guard, dbch, type, count, notifyIn );
|
||||
}
|
||||
|
||||
#endif // dbCACh
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,87 @@
|
||||
/*************************************************************************\
|
||||
* Copyright (c) 2015 The University of Chicago, as Operator of Argonne
|
||||
* National Laboratory.
|
||||
* Copyright (c) 2002 The Regents of the University of California, as
|
||||
* Operator of Los Alamos National Laboratory.
|
||||
* EPICS BASE is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
\*************************************************************************/
|
||||
/* dbCa.h */
|
||||
|
||||
#ifndef INCdbCah
|
||||
#define INCdbCah
|
||||
|
||||
#include "dbLink.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef void (*dbCaCallback)(void *userPvt);
|
||||
epicsShareFunc void dbCaCallbackProcess(void *usrPvt);
|
||||
|
||||
epicsShareFunc void dbCaLinkInit(void); /* internal initialization for iocBuild() */
|
||||
epicsShareFunc void dbCaLinkInitIsolated(void); /* internal initialization for iocBuildIsolated() */
|
||||
epicsShareFunc void dbCaRun(void);
|
||||
epicsShareFunc void dbCaPause(void);
|
||||
epicsShareFunc void dbCaShutdown(void);
|
||||
|
||||
struct dbLocker;
|
||||
epicsShareFunc void dbCaAddLinkCallback(struct link *plink,
|
||||
dbCaCallback connect, dbCaCallback monitor, void *userPvt);
|
||||
epicsShareFunc long dbCaAddLink(struct dbLocker *locker, struct link *plink, short dbfType);
|
||||
epicsShareFunc void dbCaRemoveLink(struct dbLocker *locker, struct link *plink);
|
||||
|
||||
epicsShareFunc long dbCaGetLink(struct link *plink,
|
||||
short dbrType, void *pbuffer, long *nRequest);
|
||||
|
||||
epicsShareFunc long dbCaGetAttributes(const struct link *plink,
|
||||
dbCaCallback callback, void *userPvt);
|
||||
|
||||
epicsShareFunc long dbCaPutLinkCallback(struct link *plink,
|
||||
short dbrType, const void *pbuffer,long nRequest,
|
||||
dbCaCallback callback, void *userPvt);
|
||||
epicsShareFunc long dbCaPutLink(struct link *plink,short dbrType,
|
||||
const void *pbuffer,long nRequest);
|
||||
|
||||
extern struct ca_client_context * dbCaClientContext;
|
||||
|
||||
#ifdef EPICS_DBCA_PRIVATE_API
|
||||
epicsShareFunc void dbCaSync(void);
|
||||
epicsShareFunc unsigned long dbCaGetUpdateCount(struct link *plink);
|
||||
#endif
|
||||
|
||||
/* These macros are for backwards compatibility */
|
||||
|
||||
#define dbCaIsLinkConnected(link) \
|
||||
dbIsLinkConnected(link)
|
||||
|
||||
#define dbCaGetLinkDBFtype(link) \
|
||||
dbGetLinkDBFtype(link)
|
||||
#define dbCaGetNelements(link, nelements) \
|
||||
dbGetNelements(link, nelements)
|
||||
#define dbCaGetSevr(link, sevr) \
|
||||
dbGetAlarm(link, NULL, sevr)
|
||||
#define dbCaGetAlarm(link, stat, sevr) \
|
||||
dbGetAlarm(link, stat, sevr)
|
||||
#define dbCaGetTimeStamp(link, pstamp) \
|
||||
dbGetTimeStamp(link, pstamp)
|
||||
#define dbCaGetControlLimits(link, low, high) \
|
||||
dbGetControlLimits(link, low, high)
|
||||
#define dbCaGetGraphicLimits(link, low, high) \
|
||||
dbGetGraphicLimits(link, low, high)
|
||||
#define dbCaGetAlarmLimits(link, lolo, low, high, hihi) \
|
||||
dbGetAlarmLimits(link, lolo, low, high, hihi)
|
||||
#define dbCaGetPrecision(link, prec) \
|
||||
dbGetPrecision(link, prec)
|
||||
#define dbCaGetUnits(link, units, unitSize) \
|
||||
dbGetUnits(link, units, unitSize)
|
||||
|
||||
#define dbCaScanFwdLink(link) \
|
||||
dbScanFwdLink(link)
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /*INCdbCah*/
|
||||
@@ -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 is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
\*************************************************************************/
|
||||
/* dbCaPvt.h
|
||||
*
|
||||
* Original Authors: Bob Dalesio, Marty Kraimer
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef INC_dbCaPvt_H
|
||||
#define INC_dbCaPvt_H
|
||||
|
||||
#include "dbCa.h"
|
||||
#include "ellLib.h"
|
||||
#include "epicsMutex.h"
|
||||
#include "epicsTypes.h"
|
||||
#include "link.h"
|
||||
|
||||
/* link_action mask */
|
||||
#define CA_CLEAR_CHANNEL 0x1
|
||||
#define CA_CONNECT 0x2
|
||||
#define CA_WRITE_NATIVE 0x4
|
||||
#define CA_WRITE_STRING 0x8
|
||||
#define CA_MONITOR_NATIVE 0x10
|
||||
#define CA_MONITOR_STRING 0x20
|
||||
#define CA_GET_ATTRIBUTES 0x40
|
||||
#define CA_SYNC 0x1000
|
||||
/* write type */
|
||||
#define CA_PUT 0x1
|
||||
#define CA_PUT_CALLBACK 0x2
|
||||
|
||||
typedef struct caLink
|
||||
{
|
||||
ELLNODE node;
|
||||
int refcount;
|
||||
epicsMutexId lock;
|
||||
struct link *plink;
|
||||
char *pvname;
|
||||
chid chid;
|
||||
short link_action;
|
||||
/* The following have new values after each data event*/
|
||||
epicsEnum16 sevr;
|
||||
epicsEnum16 stat;
|
||||
epicsTimeStamp timeStamp;
|
||||
/* The following have values after connection*/
|
||||
short dbrType;
|
||||
size_t elementSize; /* size of one element in pgetNative */
|
||||
unsigned long nelements; /* PVs max array size */
|
||||
unsigned long usedelements; /* currently used in pgetNative */
|
||||
unsigned long putnelements; /* currently used in pputNative */
|
||||
char hasReadAccess;
|
||||
char hasWriteAccess;
|
||||
char isConnected;
|
||||
char gotFirstConnection;
|
||||
/* The following are for dbCaAddLinkCallback */
|
||||
dbCaCallback connect;
|
||||
dbCaCallback monitor;
|
||||
void *userPvt;
|
||||
/* The following are for write request */
|
||||
short putType;
|
||||
dbCaCallback putCallback;
|
||||
void *putUserPvt;
|
||||
/* The following are for access to additional attributes*/
|
||||
char gotAttributes;
|
||||
dbCaCallback getAttributes;
|
||||
void *getAttributesPvt;
|
||||
/* The following have values after getAttribEventCallback*/
|
||||
double controlLimits[2];
|
||||
double displayLimits[2];
|
||||
double alarmLimits[4];
|
||||
short precision;
|
||||
char units[MAX_UNITS_SIZE]; /* units of value */
|
||||
/* The following are for handling data*/
|
||||
void *pgetNative;
|
||||
char *pgetString;
|
||||
void *pputNative;
|
||||
char *pputString;
|
||||
char gotInNative;
|
||||
char gotInString;
|
||||
char gotOutNative;
|
||||
char gotOutString;
|
||||
char newOutNative;
|
||||
char newOutString;
|
||||
unsigned char scanningOnce;
|
||||
/* The following are for dbcar*/
|
||||
unsigned long nDisconnect;
|
||||
unsigned long nNoWrite; /*only modified by dbCaPutLink*/
|
||||
unsigned long nUpdate;
|
||||
}caLink;
|
||||
|
||||
#endif /* INC_dbCaPvt_H */
|
||||
@@ -0,0 +1,206 @@
|
||||
/*************************************************************************\
|
||||
* Copyright (c) 2009 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.
|
||||
\*************************************************************************/
|
||||
/* dbCaTest.c */
|
||||
|
||||
/****************************************************************
|
||||
*
|
||||
* Author: Marty Kraimer
|
||||
* Date: 10APR96
|
||||
*
|
||||
****************************************************************/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "dbDefs.h"
|
||||
#include "epicsEvent.h"
|
||||
#include "epicsPrint.h"
|
||||
#include "epicsStdio.h"
|
||||
|
||||
#define epicsExportSharedSymbols
|
||||
#include "dbStaticLib.h"
|
||||
#undef epicsExportSharedSymbols
|
||||
/*definitions needed because of old vs new database access*/
|
||||
#undef DBR_SHORT
|
||||
#undef DBR_PUT_ACKT
|
||||
#undef DBR_PUT_ACKS
|
||||
#undef VALID_DB_REQ
|
||||
#undef INVALID_DB_REQ
|
||||
/*end of conflicting definitions*/
|
||||
|
||||
#include "cadef.h"
|
||||
|
||||
/*define DB_CONVERT_GBLSOURCE because db_access.c does not include db_access.h*/
|
||||
#define DB_CONVERT_GBLSOURCE
|
||||
|
||||
#define epicsExportSharedSymbols
|
||||
#include "db_access.h"
|
||||
#include "db_access_routines.h"
|
||||
#include "dbCa.h"
|
||||
#include "dbCaPvt.h"
|
||||
#include "dbCaTest.h"
|
||||
#include "dbCommon.h"
|
||||
#include "db_convert.h"
|
||||
#include "dbLock.h"
|
||||
#include "link.h"
|
||||
|
||||
|
||||
long dbcar(char *precordname, int level)
|
||||
{
|
||||
DBENTRY dbentry;
|
||||
DBENTRY *pdbentry=&dbentry;
|
||||
long status;
|
||||
dbCommon *precord;
|
||||
dbRecordType *pdbRecordType;
|
||||
dbFldDes *pdbFldDes;
|
||||
DBLINK *plink;
|
||||
int ncalinks=0;
|
||||
int nconnected=0;
|
||||
int noReadAccess=0;
|
||||
int noWriteAccess=0;
|
||||
unsigned long nDisconnect=0;
|
||||
unsigned long nNoWrite=0;
|
||||
caLink *pca;
|
||||
int j;
|
||||
|
||||
if (!precordname || precordname[0] == '\0' || !strcmp(precordname, "*")) {
|
||||
precordname = NULL;
|
||||
printf("CA links in all records\n\n");
|
||||
} else {
|
||||
printf("CA links in record named '%s'\n\n", precordname);
|
||||
}
|
||||
dbInitEntry(pdbbase,pdbentry);
|
||||
status = dbFirstRecordType(pdbentry);
|
||||
while (!status) {
|
||||
status = dbFirstRecord(pdbentry);
|
||||
while (!status) {
|
||||
if (precordname ?
|
||||
!strcmp(precordname, dbGetRecordName(pdbentry)) :
|
||||
!dbIsAlias(pdbentry)) {
|
||||
pdbRecordType = pdbentry->precordType;
|
||||
precord = (dbCommon *)pdbentry->precnode->precord;
|
||||
dbScanLock(precord);
|
||||
for (j=0; j<pdbRecordType->no_links; j++) {
|
||||
pdbFldDes = pdbRecordType->papFldDes[pdbRecordType->link_ind[j]];
|
||||
plink = (DBLINK *)((char *)precord + pdbFldDes->offset);
|
||||
if (plink->type == CA_LINK) {
|
||||
ncalinks++;
|
||||
pca = (caLink *)plink->value.pv_link.pvt;
|
||||
if (pca
|
||||
&& pca->chid
|
||||
&& (ca_field_type(pca->chid) != TYPENOTCONN)) {
|
||||
nconnected++;
|
||||
nDisconnect += pca->nDisconnect;
|
||||
nNoWrite += pca->nNoWrite;
|
||||
if (!ca_read_access(pca->chid)) noReadAccess++;
|
||||
if (!ca_write_access(pca->chid)) noWriteAccess++;
|
||||
if (level>1) {
|
||||
int rw = ca_read_access(pca->chid) |
|
||||
ca_write_access(pca->chid) << 1;
|
||||
static const char *rights[4] = {
|
||||
"No Access", "Read Only",
|
||||
"Write Only", "Read/Write"
|
||||
};
|
||||
int mask = plink->value.pv_link.pvlMask;
|
||||
printf("%28s.%-4s ==> %-28s (%lu, %lu)\n",
|
||||
precord->name,
|
||||
pdbFldDes->name,
|
||||
plink->value.pv_link.pvname,
|
||||
pca->nDisconnect,
|
||||
pca->nNoWrite);
|
||||
printf("%21s [%s%s%s%s] host %s, %s\n", "",
|
||||
mask & pvlOptInpNative ? "IN" : " ",
|
||||
mask & pvlOptInpString ? "IS" : " ",
|
||||
mask & pvlOptOutNative ? "ON" : " ",
|
||||
mask & pvlOptOutString ? "OS" : " ",
|
||||
ca_host_name(pca->chid),
|
||||
rights[rw]);
|
||||
}
|
||||
} else {
|
||||
if (level>0) {
|
||||
printf("%28s.%-4s --> %-28s (%lu, %lu)\n",
|
||||
precord->name,
|
||||
pdbFldDes->name,
|
||||
plink->value.pv_link.pvname,
|
||||
pca ? pca->nDisconnect : 0,
|
||||
pca ? pca->nNoWrite : 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
dbScanUnlock(precord);
|
||||
if (precordname) goto done;
|
||||
}
|
||||
status = dbNextRecord(pdbentry);
|
||||
}
|
||||
status = dbNextRecordType(pdbentry);
|
||||
}
|
||||
done:
|
||||
if ((level > 1 && nconnected > 0) ||
|
||||
(level > 0 && ncalinks != nconnected)) printf("\n");
|
||||
printf("Total %d CA link%s; ",
|
||||
ncalinks, (ncalinks != 1) ? "s" : "");
|
||||
printf("%d connected, %d not connected.\n",
|
||||
nconnected, (ncalinks - nconnected));
|
||||
printf(" %d can't read, %d can't write.",
|
||||
noReadAccess, noWriteAccess);
|
||||
printf(" (%lu disconnects, %lu writes prohibited)\n\n",
|
||||
nDisconnect, nNoWrite);
|
||||
dbFinishEntry(pdbentry);
|
||||
|
||||
if ( level > 2 && dbCaClientContext != 0 ) {
|
||||
ca_context_status ( dbCaClientContext, level - 2 );
|
||||
}
|
||||
|
||||
return(0);
|
||||
}
|
||||
|
||||
void dbcaStats(int *pchans, int *pdiscon)
|
||||
{
|
||||
DBENTRY dbentry;
|
||||
DBENTRY *pdbentry = &dbentry;
|
||||
long status;
|
||||
DBLINK *plink;
|
||||
long ncalinks = 0;
|
||||
long nconnected = 0;
|
||||
|
||||
dbInitEntry(pdbbase,pdbentry);
|
||||
status = dbFirstRecordType(pdbentry);
|
||||
while (!status) {
|
||||
dbRecordType *pdbRecordType = pdbentry->precordType;
|
||||
|
||||
status = dbFirstRecord(pdbentry);
|
||||
while (!status) {
|
||||
dbCommon *precord = (dbCommon *)pdbentry->precnode->precord;
|
||||
int j;
|
||||
|
||||
if (!dbIsAlias(pdbentry)) {
|
||||
for (j=0; j<pdbRecordType->no_links; j++) {
|
||||
int i = pdbRecordType->link_ind[j];
|
||||
|
||||
dbFldDes *pdbFldDes = pdbRecordType->papFldDes[i];
|
||||
plink = (DBLINK *)((char *)precord + pdbFldDes->offset);
|
||||
if (plink->type == CA_LINK) {
|
||||
ncalinks++;
|
||||
if (dbCaIsLinkConnected(plink)) {
|
||||
nconnected++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
status = dbNextRecord(pdbentry);
|
||||
}
|
||||
status = dbNextRecordType(pdbentry);
|
||||
}
|
||||
dbFinishEntry(pdbentry);
|
||||
if (pchans) *pchans = ncalinks;
|
||||
if (pdiscon) *pdiscon = ncalinks - nconnected;
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
/*************************************************************************\
|
||||
* Copyright (c) 2008 UChicago Argonne LLC, as Operator of Argonne
|
||||
* National Laboratory.
|
||||
* Copyright (c) 2002 The Regents of the University of California, as
|
||||
* Operator of Los Alamos National Laboratory.
|
||||
* EPICS BASE is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
\*************************************************************************/
|
||||
|
||||
#ifndef INC_dbCaTest_H
|
||||
#define INC_dbCaTest_H
|
||||
|
||||
#include "shareLib.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
epicsShareFunc long dbcar(char *recordname,int level);
|
||||
epicsShareFunc void dbcaStats(int *pchans, int *pdiscon);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* INC_dbCaTest_H */
|
||||
@@ -0,0 +1,826 @@
|
||||
/*************************************************************************\
|
||||
* Copyright (c) 2010 UChicago Argonne LLC, as Operator of Argonne
|
||||
* National Laboratory.
|
||||
* Copyright (c) 2010 Brookhaven National Laboratory.
|
||||
* Copyright (c) 2010 Helmholtz-Zentrum Berlin
|
||||
* fuer Materialien und Energie GmbH.
|
||||
* EPICS BASE is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
\*************************************************************************/
|
||||
|
||||
/*
|
||||
* Author: Andrew Johnson <anj@aps.anl.gov>
|
||||
* Ralph Lange <Ralph.Lange@bessy.de>
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "cantProceed.h"
|
||||
#include "epicsAssert.h"
|
||||
#include "epicsString.h"
|
||||
#include "errlog.h"
|
||||
#include "freeList.h"
|
||||
#include "gpHash.h"
|
||||
#include "yajl_parse.h"
|
||||
|
||||
#define epicsExportSharedSymbols
|
||||
#include "dbAccessDefs.h"
|
||||
#include "dbBase.h"
|
||||
#include "dbChannel.h"
|
||||
#include "dbCommon.h"
|
||||
#include "dbEvent.h"
|
||||
#include "dbLock.h"
|
||||
#include "dbStaticLib.h"
|
||||
#include "link.h"
|
||||
#include "recSup.h"
|
||||
#include "special.h"
|
||||
|
||||
typedef struct parseContext {
|
||||
dbChannel *chan;
|
||||
chFilter *filter;
|
||||
int depth;
|
||||
} parseContext;
|
||||
|
||||
#define CALLIF(rtn) !rtn ? parse_stop : rtn
|
||||
|
||||
static void *dbChannelFreeList;
|
||||
static void *chFilterFreeList;
|
||||
static void *dbchStringFreeList;
|
||||
|
||||
void dbChannelExit(void)
|
||||
{
|
||||
freeListCleanup(dbChannelFreeList);
|
||||
freeListCleanup(chFilterFreeList);
|
||||
freeListCleanup(dbchStringFreeList);
|
||||
dbChannelFreeList = chFilterFreeList = dbchStringFreeList = NULL;
|
||||
}
|
||||
|
||||
void dbChannelInit (void)
|
||||
{
|
||||
if(dbChannelFreeList)
|
||||
return;
|
||||
|
||||
freeListInitPvt(&dbChannelFreeList, sizeof(dbChannel), 128);
|
||||
freeListInitPvt(&chFilterFreeList, sizeof(chFilter), 64);
|
||||
freeListInitPvt(&dbchStringFreeList, sizeof(epicsOldString), 128);
|
||||
}
|
||||
|
||||
static void chf_value(parseContext *parser, parse_result *presult)
|
||||
{
|
||||
chFilter *filter = parser->filter;
|
||||
|
||||
if (*presult == parse_stop || parser->depth > 0)
|
||||
return;
|
||||
|
||||
parser->filter = NULL;
|
||||
if (filter->plug->fif->parse_end(filter) == parse_continue) {
|
||||
ellAdd(&parser->chan->filters, &filter->list_node);
|
||||
} else {
|
||||
freeListFree(chFilterFreeList, filter);
|
||||
*presult = parse_stop;
|
||||
}
|
||||
}
|
||||
|
||||
static int chf_null(void * ctx)
|
||||
{
|
||||
parseContext *parser = (parseContext *) ctx;
|
||||
chFilter *filter = parser->filter;
|
||||
parse_result result;
|
||||
|
||||
assert(filter);
|
||||
result = CALLIF(filter->plug->fif->parse_null)(filter );
|
||||
chf_value(parser, &result);
|
||||
return result;
|
||||
}
|
||||
|
||||
static int chf_boolean(void * ctx, int boolVal)
|
||||
{
|
||||
parseContext *parser = (parseContext *) ctx;
|
||||
chFilter *filter = parser->filter;
|
||||
parse_result result;
|
||||
|
||||
assert(filter);
|
||||
result = CALLIF(filter->plug->fif->parse_boolean)(filter , boolVal);
|
||||
chf_value(parser, &result);
|
||||
return result;
|
||||
}
|
||||
|
||||
static int chf_integer(void * ctx, long long integerVal)
|
||||
{
|
||||
parseContext *parser = (parseContext *) ctx;
|
||||
chFilter *filter = parser->filter;
|
||||
parse_result result;
|
||||
|
||||
assert(filter);
|
||||
result = CALLIF(filter->plug->fif->parse_integer)(filter , integerVal);
|
||||
chf_value(parser, &result);
|
||||
return result;
|
||||
}
|
||||
|
||||
static int chf_double(void * ctx, double doubleVal)
|
||||
{
|
||||
parseContext *parser = (parseContext *) ctx;
|
||||
chFilter *filter = parser->filter;
|
||||
parse_result result;
|
||||
|
||||
assert(filter);
|
||||
result = CALLIF(filter->plug->fif->parse_double)(filter , doubleVal);
|
||||
chf_value(parser, &result);
|
||||
return result;
|
||||
}
|
||||
|
||||
static int chf_string(void * ctx, const unsigned char * stringVal,
|
||||
size_t stringLen)
|
||||
{
|
||||
parseContext *parser = (parseContext *) ctx;
|
||||
chFilter *filter = parser->filter;
|
||||
parse_result result;
|
||||
|
||||
assert(filter);
|
||||
result = CALLIF(filter->plug->fif->parse_string)(filter , (const char *) stringVal, stringLen);
|
||||
chf_value(parser, &result);
|
||||
return result;
|
||||
}
|
||||
|
||||
static int chf_start_map(void * ctx)
|
||||
{
|
||||
parseContext *parser = (parseContext *) ctx;
|
||||
chFilter *filter = parser->filter;
|
||||
|
||||
if (!filter) {
|
||||
assert(parser->depth == 0);
|
||||
return parse_continue; /* Opening '{' */
|
||||
}
|
||||
|
||||
++parser->depth;
|
||||
return CALLIF(filter->plug->fif->parse_start_map)(filter );
|
||||
}
|
||||
|
||||
static int chf_map_key(void * ctx, const unsigned char * key,
|
||||
size_t stringLen)
|
||||
{
|
||||
parseContext *parser = (parseContext *) ctx;
|
||||
chFilter *filter = parser->filter;
|
||||
const chFilterPlugin *plug;
|
||||
parse_result result;
|
||||
|
||||
if (filter) {
|
||||
assert(parser->depth > 0);
|
||||
return CALLIF(filter->plug->fif->parse_map_key)(filter , (const char *) key, stringLen);
|
||||
}
|
||||
|
||||
assert(parser->depth == 0);
|
||||
plug = dbFindFilter((const char *) key, stringLen);
|
||||
if (!plug) {
|
||||
errlogPrintf("dbChannelCreate: Channel filter '%.*s' not found\n",
|
||||
(int) stringLen, key);
|
||||
return parse_stop;
|
||||
}
|
||||
|
||||
filter = freeListCalloc(chFilterFreeList);
|
||||
if (!filter) {
|
||||
errlogPrintf("dbChannelCreate: Out of memory\n");
|
||||
return parse_stop;
|
||||
}
|
||||
filter->chan = parser->chan;
|
||||
filter->plug = plug;
|
||||
filter->puser = NULL;
|
||||
|
||||
result = plug->fif->parse_start(filter);
|
||||
if (result == parse_continue) {
|
||||
parser->filter = filter;
|
||||
} else {
|
||||
freeListFree(chFilterFreeList, filter);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static int chf_end_map(void * ctx)
|
||||
{
|
||||
parseContext *parser = (parseContext *) ctx;
|
||||
chFilter *filter = parser->filter;
|
||||
parse_result result;
|
||||
|
||||
if (!filter) {
|
||||
assert(parser->depth == 0);
|
||||
return parse_continue; /* Final closing '}' */
|
||||
}
|
||||
|
||||
assert(parser->depth > 0);
|
||||
result = CALLIF(filter->plug->fif->parse_end_map)(filter );
|
||||
|
||||
--parser->depth;
|
||||
chf_value(parser, &result);
|
||||
return result;
|
||||
}
|
||||
|
||||
static int chf_start_array(void * ctx)
|
||||
{
|
||||
parseContext *parser = (parseContext *) ctx;
|
||||
chFilter *filter = parser->filter;
|
||||
|
||||
assert(filter);
|
||||
++parser->depth;
|
||||
return CALLIF(filter->plug->fif->parse_start_array)(filter );
|
||||
}
|
||||
|
||||
static int chf_end_array(void * ctx)
|
||||
{
|
||||
parseContext *parser = (parseContext *) ctx;
|
||||
chFilter *filter = parser->filter;
|
||||
parse_result result;
|
||||
|
||||
assert(filter);
|
||||
result = CALLIF(filter->plug->fif->parse_end_array)(filter );
|
||||
--parser->depth;
|
||||
chf_value(parser, &result);
|
||||
return result;
|
||||
}
|
||||
|
||||
static const yajl_callbacks chf_callbacks =
|
||||
{ chf_null, chf_boolean, chf_integer, chf_double, NULL, chf_string,
|
||||
chf_start_map, chf_map_key, chf_end_map, chf_start_array, chf_end_array };
|
||||
|
||||
static void * chf_malloc(void *ctx, size_t sz)
|
||||
{
|
||||
return malloc(sz);
|
||||
}
|
||||
|
||||
static void * chf_realloc(void *ctx, void *ptr, size_t sz)
|
||||
{
|
||||
return realloc(ptr, sz);
|
||||
}
|
||||
|
||||
static void chf_free(void *ctx, void *ptr)
|
||||
{
|
||||
free(ptr);
|
||||
}
|
||||
|
||||
static yajl_alloc_funcs chf_alloc =
|
||||
{ chf_malloc, chf_realloc, chf_free };
|
||||
|
||||
static long chf_parse(dbChannel *chan, const char **pjson)
|
||||
{
|
||||
parseContext parser =
|
||||
{ chan, NULL, 0 };
|
||||
yajl_handle yh = yajl_alloc(&chf_callbacks, &chf_alloc, &parser);
|
||||
const char *json = *pjson;
|
||||
size_t jlen = strlen(json), ylen;
|
||||
yajl_status ys;
|
||||
long status;
|
||||
|
||||
if (!yh)
|
||||
return S_db_noMemory;
|
||||
|
||||
ys = yajl_parse(yh, (const unsigned char *) json, jlen);
|
||||
ylen = yajl_get_bytes_consumed(yh);
|
||||
|
||||
if (ys == yajl_status_ok)
|
||||
ys = yajl_complete_parse(yh);
|
||||
|
||||
switch (ys) {
|
||||
case yajl_status_ok:
|
||||
*pjson += ylen;
|
||||
status = 0;
|
||||
break;
|
||||
|
||||
case yajl_status_error: {
|
||||
unsigned char *err;
|
||||
|
||||
err = yajl_get_error(yh, 1, (const unsigned char *) json, jlen);
|
||||
printf("dbChannelCreate: %s\n", err);
|
||||
yajl_free_error(yh, err);
|
||||
} /* fall through */
|
||||
default:
|
||||
status = S_db_notFound;
|
||||
}
|
||||
|
||||
if (parser.filter) {
|
||||
assert(status);
|
||||
parser.filter->plug->fif->parse_abort(parser.filter);
|
||||
freeListFree(chFilterFreeList, parser.filter);
|
||||
}
|
||||
yajl_free(yh);
|
||||
return status;
|
||||
}
|
||||
|
||||
static long pvNameLookup(DBENTRY *pdbe, const char **ppname)
|
||||
{
|
||||
long status;
|
||||
|
||||
dbInitEntry(pdbbase, pdbe);
|
||||
|
||||
status = dbFindRecordPart(pdbe, ppname);
|
||||
if (status)
|
||||
return status;
|
||||
|
||||
if (**ppname == '.')
|
||||
++*ppname;
|
||||
|
||||
status = dbFindFieldPart(pdbe, ppname);
|
||||
if (status == S_dbLib_fieldNotFound)
|
||||
status = dbGetAttributePart(pdbe, ppname);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
long dbChannelTest(const char *name)
|
||||
{
|
||||
DBENTRY dbEntry;
|
||||
long status;
|
||||
|
||||
if (!name || !*name || !pdbbase)
|
||||
return S_db_notFound;
|
||||
|
||||
status = pvNameLookup(&dbEntry, &name);
|
||||
|
||||
dbFinishEntry(&dbEntry);
|
||||
return status;
|
||||
}
|
||||
|
||||
#define TRY(Func, Arg) \
|
||||
if (Func) { \
|
||||
result = Func Arg; \
|
||||
if (result != parse_continue) goto failure; \
|
||||
}
|
||||
|
||||
static long parseArrayRange(dbChannel* chan, const char *pname, const char **ppnext) {
|
||||
epicsInt32 start = 0;
|
||||
epicsInt32 end = -1;
|
||||
epicsInt32 incr = 1;
|
||||
epicsInt32 l;
|
||||
char *pnext;
|
||||
ptrdiff_t exist;
|
||||
chFilter *filter;
|
||||
const chFilterPlugin *plug;
|
||||
parse_result result;
|
||||
long status = 0;
|
||||
|
||||
/* If no number is present, strtol() returns 0 and sets pnext=pname,
|
||||
else pnext points to the first char after the number */
|
||||
pname++;
|
||||
l = strtol(pname, &pnext, 0);
|
||||
exist = pnext - pname;
|
||||
if (exist) start = l;
|
||||
pname = pnext;
|
||||
if (*pname == ']' && exist) {
|
||||
end = start;
|
||||
goto insertplug;
|
||||
}
|
||||
if (*pname != ':') {
|
||||
status = S_dbLib_fieldNotFound;
|
||||
goto finish;
|
||||
}
|
||||
pname++;
|
||||
l = strtol(pname, &pnext, 0);
|
||||
exist = pnext - pname;
|
||||
pname = pnext;
|
||||
if (*pname == ']') {
|
||||
if (exist) end = l;
|
||||
goto insertplug;
|
||||
}
|
||||
if (exist) incr = l;
|
||||
if (*pname != ':') {
|
||||
status = S_dbLib_fieldNotFound;
|
||||
goto finish;
|
||||
}
|
||||
pname++;
|
||||
l = strtol(pname, &pnext, 0);
|
||||
exist = pnext - pname;
|
||||
if (exist) end = l;
|
||||
pname = pnext;
|
||||
if (*pname != ']') {
|
||||
status = S_dbLib_fieldNotFound;
|
||||
goto finish;
|
||||
}
|
||||
|
||||
insertplug:
|
||||
pname++;
|
||||
*ppnext = pname;
|
||||
|
||||
plug = dbFindFilter("arr", 3);
|
||||
if (!plug) {
|
||||
status = S_dbLib_fieldNotFound;
|
||||
goto finish;
|
||||
}
|
||||
|
||||
filter = freeListCalloc(chFilterFreeList);
|
||||
if (!filter) {
|
||||
status = S_db_noMemory;
|
||||
goto finish;
|
||||
}
|
||||
filter->chan = chan;
|
||||
filter->plug = plug;
|
||||
filter->puser = NULL;
|
||||
|
||||
TRY(filter->plug->fif->parse_start, (filter));
|
||||
TRY(filter->plug->fif->parse_start_map, (filter));
|
||||
if (start != 0) {
|
||||
TRY(filter->plug->fif->parse_map_key, (filter, "s", 1));
|
||||
TRY(filter->plug->fif->parse_integer, (filter, start));
|
||||
}
|
||||
if (incr != 1) {
|
||||
TRY(filter->plug->fif->parse_map_key, (filter, "i", 1));
|
||||
TRY(filter->plug->fif->parse_integer, (filter, incr));
|
||||
}
|
||||
if (end != -1) {
|
||||
TRY(filter->plug->fif->parse_map_key, (filter, "e", 1));
|
||||
TRY(filter->plug->fif->parse_integer, (filter, end));
|
||||
}
|
||||
TRY(filter->plug->fif->parse_end_map, (filter));
|
||||
TRY(filter->plug->fif->parse_end, (filter));
|
||||
|
||||
ellAdd(&chan->filters, &filter->list_node);
|
||||
return 0;
|
||||
|
||||
failure:
|
||||
freeListFree(chFilterFreeList, filter);
|
||||
status = S_dbLib_fieldNotFound;
|
||||
|
||||
finish:
|
||||
return status;
|
||||
}
|
||||
|
||||
/* Stolen from dbAccess.c: */
|
||||
static short mapDBFToDBR[DBF_NTYPES] =
|
||||
{
|
||||
/* DBF_STRING => */DBR_STRING,
|
||||
/* DBF_CHAR => */DBR_CHAR,
|
||||
/* DBF_UCHAR => */DBR_UCHAR,
|
||||
/* DBF_SHORT => */DBR_SHORT,
|
||||
/* DBF_USHORT => */DBR_USHORT,
|
||||
/* DBF_LONG => */DBR_LONG,
|
||||
/* DBF_ULONG => */DBR_ULONG,
|
||||
/* DBF_INT64 => */DBR_INT64,
|
||||
/* DBF_UINT64 => */DBR_UINT64,
|
||||
/* DBF_FLOAT => */DBR_FLOAT,
|
||||
/* DBF_DOUBLE => */DBR_DOUBLE,
|
||||
/* DBF_ENUM, => */DBR_ENUM,
|
||||
/* DBF_MENU, => */DBR_ENUM,
|
||||
/* DBF_DEVICE => */DBR_ENUM,
|
||||
/* DBF_INLINK => */DBR_STRING,
|
||||
/* DBF_OUTLINK => */DBR_STRING,
|
||||
/* DBF_FWDLINK => */DBR_STRING,
|
||||
/* DBF_NOACCESS => */DBR_NOACCESS };
|
||||
|
||||
dbChannel * dbChannelCreate(const char *name)
|
||||
{
|
||||
const char *pname = name;
|
||||
DBENTRY dbEntry;
|
||||
dbChannel *chan = NULL;
|
||||
char *cname;
|
||||
dbAddr *paddr;
|
||||
dbFldDes *pflddes;
|
||||
long status;
|
||||
short dbfType;
|
||||
|
||||
if (!name || !*name || !pdbbase)
|
||||
return NULL;
|
||||
|
||||
status = pvNameLookup(&dbEntry, &pname);
|
||||
if (status)
|
||||
goto finish;
|
||||
|
||||
chan = freeListCalloc(dbChannelFreeList);
|
||||
if (!chan)
|
||||
goto finish;
|
||||
cname = malloc(strlen(name) + 1);
|
||||
if (!cname)
|
||||
goto finish;
|
||||
|
||||
strcpy(cname, name);
|
||||
chan->name = cname;
|
||||
ellInit(&chan->filters);
|
||||
ellInit(&chan->pre_chain);
|
||||
ellInit(&chan->post_chain);
|
||||
|
||||
paddr = &chan->addr;
|
||||
pflddes = dbEntry.pflddes;
|
||||
dbfType = pflddes->field_type;
|
||||
|
||||
paddr->precord = dbEntry.precnode->precord;
|
||||
paddr->pfield = dbEntry.pfield;
|
||||
paddr->pfldDes = pflddes;
|
||||
paddr->no_elements = 1;
|
||||
paddr->field_type = dbfType;
|
||||
paddr->field_size = pflddes->size;
|
||||
paddr->special = pflddes->special;
|
||||
paddr->dbr_field_type = mapDBFToDBR[dbfType];
|
||||
|
||||
if (paddr->special == SPC_DBADDR) {
|
||||
rset *prset = dbGetRset(paddr);
|
||||
|
||||
/* Let record type modify paddr */
|
||||
if (prset && prset->cvt_dbaddr) {
|
||||
status = prset->cvt_dbaddr(paddr);
|
||||
if (status)
|
||||
goto finish;
|
||||
dbfType = paddr->field_type;
|
||||
}
|
||||
}
|
||||
|
||||
/* Handle field modifiers */
|
||||
if (*pname) {
|
||||
if (*pname == '$') {
|
||||
/* Some field types can be accessed as char arrays */
|
||||
if (dbfType == DBF_STRING) {
|
||||
paddr->no_elements = paddr->field_size;
|
||||
paddr->field_type = DBF_CHAR;
|
||||
paddr->field_size = 1;
|
||||
paddr->dbr_field_type = DBR_CHAR;
|
||||
} else if (dbfType >= DBF_INLINK && dbfType <= DBF_FWDLINK) {
|
||||
/* Clients see a char array, but keep original dbfType */
|
||||
paddr->no_elements = PVLINK_STRINGSZ;
|
||||
paddr->field_size = 1;
|
||||
paddr->dbr_field_type = DBR_CHAR;
|
||||
} else {
|
||||
status = S_dbLib_fieldNotFound;
|
||||
goto finish;
|
||||
}
|
||||
pname++;
|
||||
}
|
||||
|
||||
if (*pname == '[') {
|
||||
status = parseArrayRange(chan, pname, &pname);
|
||||
if (status) goto finish;
|
||||
}
|
||||
|
||||
/* JSON may follow */
|
||||
if (*pname == '{') {
|
||||
status = chf_parse(chan, &pname);
|
||||
if (status) goto finish;
|
||||
}
|
||||
|
||||
/* Make sure there's nothing else */
|
||||
if (*pname) {
|
||||
status = S_dbLib_fieldNotFound;
|
||||
goto finish;
|
||||
}
|
||||
}
|
||||
|
||||
finish:
|
||||
if (status && chan) {
|
||||
dbChannelDelete(chan);
|
||||
chan = NULL;
|
||||
}
|
||||
dbFinishEntry(&dbEntry);
|
||||
return chan;
|
||||
}
|
||||
|
||||
db_field_log* dbChannelRunPreChain(dbChannel *chan, db_field_log *pLogIn) {
|
||||
chFilter *filter;
|
||||
ELLNODE *node;
|
||||
db_field_log *pLog = pLogIn;
|
||||
|
||||
for (node = ellFirst(&chan->pre_chain); node && pLog; node = ellNext(node)) {
|
||||
filter = CONTAINER(node, chFilter, pre_node);
|
||||
pLog = filter->pre_func(filter->pre_arg, chan, pLog);
|
||||
}
|
||||
return pLog;
|
||||
}
|
||||
|
||||
db_field_log* dbChannelRunPostChain(dbChannel *chan, db_field_log *pLogIn) {
|
||||
chFilter *filter;
|
||||
ELLNODE *node;
|
||||
db_field_log *pLog = pLogIn;
|
||||
|
||||
for (node = ellFirst(&chan->post_chain); node && pLog; node = ellNext(node)) {
|
||||
filter = CONTAINER(node, chFilter, post_node);
|
||||
pLog = filter->post_func(filter->post_arg, chan, pLog);
|
||||
}
|
||||
return pLog;
|
||||
}
|
||||
|
||||
long dbChannelOpen(dbChannel *chan)
|
||||
{
|
||||
chFilter *filter;
|
||||
chPostEventFunc *func;
|
||||
void *arg;
|
||||
long status;
|
||||
ELLNODE *node;
|
||||
db_field_log probe;
|
||||
db_field_log p;
|
||||
|
||||
for (node = ellFirst(&chan->filters); node; node = ellNext(node)) {
|
||||
filter = CONTAINER(node, chFilter, list_node);
|
||||
/* Call channel_open */
|
||||
status = 0;
|
||||
if (filter->plug->fif->channel_open)
|
||||
status = filter->plug->fif->channel_open(filter);
|
||||
if (status) return status;
|
||||
}
|
||||
|
||||
/* Set up type probe */
|
||||
probe.type = dbfl_type_val;
|
||||
probe.ctx = dbfl_context_read;
|
||||
probe.field_type = dbChannelExportType(chan);
|
||||
probe.no_elements = dbChannelElements(chan);
|
||||
probe.field_size = dbChannelFieldSize(chan);
|
||||
p = probe;
|
||||
|
||||
/*
|
||||
* Build up the pre- and post-event-queue filter chains
|
||||
* Separate loops because the probe must reach the filters in the right order.
|
||||
*/
|
||||
for (node = ellFirst(&chan->filters); node; node = ellNext(node)) {
|
||||
filter = CONTAINER(node, chFilter, list_node);
|
||||
func = NULL;
|
||||
arg = NULL;
|
||||
if (filter->plug->fif->channel_register_pre) {
|
||||
filter->plug->fif->channel_register_pre(filter, &func, &arg, &p);
|
||||
if (func) {
|
||||
ellAdd(&chan->pre_chain, &filter->pre_node);
|
||||
filter->pre_func = func;
|
||||
filter->pre_arg = arg;
|
||||
probe = p;
|
||||
}
|
||||
}
|
||||
}
|
||||
for (node = ellFirst(&chan->filters); node; node = ellNext(node)) {
|
||||
filter = CONTAINER(node, chFilter, list_node);
|
||||
func = NULL;
|
||||
arg = NULL;
|
||||
if (filter->plug->fif->channel_register_post) {
|
||||
filter->plug->fif->channel_register_post(filter, &func, &arg, &p);
|
||||
if (func) {
|
||||
ellAdd(&chan->post_chain, &filter->post_node);
|
||||
filter->post_func = func;
|
||||
filter->post_arg = arg;
|
||||
probe = p;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Save probe results */
|
||||
chan->final_no_elements = probe.no_elements;
|
||||
chan->final_field_size = probe.field_size;
|
||||
chan->final_type = probe.field_type;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Only use dbChannelGet() if the record is already locked. */
|
||||
long dbChannelGet(dbChannel *chan, short type, void *pbuffer,
|
||||
long *options, long *nRequest, void *pfl)
|
||||
{
|
||||
return dbGet(&chan->addr, type, pbuffer, options, nRequest, pfl);
|
||||
}
|
||||
|
||||
long dbChannelGetField(dbChannel *chan, short dbrType, void *pbuffer,
|
||||
long *options, long *nRequest, void *pfl)
|
||||
{
|
||||
dbCommon *precord = chan->addr.precord;
|
||||
long status = 0;
|
||||
|
||||
dbScanLock(precord);
|
||||
status = dbChannelGet(chan, dbrType, pbuffer, options, nRequest, pfl);
|
||||
dbScanUnlock(precord);
|
||||
return status;
|
||||
}
|
||||
|
||||
/* Only use dbChannelPut() if the record is already locked.
|
||||
* This routine doesn't work on link fields, ignores DISP, and
|
||||
* doesn't trigger record processing on PROC or pp(TRUE).
|
||||
*/
|
||||
long dbChannelPut(dbChannel *chan, short type, const void *pbuffer,
|
||||
long nRequest)
|
||||
{
|
||||
return dbPut(&chan->addr, type, pbuffer, nRequest);
|
||||
}
|
||||
|
||||
long dbChannelPutField(dbChannel *chan, short type, const void *pbuffer,
|
||||
long nRequest)
|
||||
{
|
||||
return dbPutField(&chan->addr, type, pbuffer, nRequest);
|
||||
}
|
||||
|
||||
void dbChannelShow(dbChannel *chan, int level, const unsigned short indent)
|
||||
{
|
||||
long elems = chan->addr.no_elements;
|
||||
long felems = chan->final_no_elements;
|
||||
int count = ellCount(&chan->filters);
|
||||
int pre = ellCount(&chan->pre_chain);
|
||||
int post = ellCount(&chan->post_chain);
|
||||
|
||||
printf("%*sChannel: '%s'\n", indent, "", chan->name);
|
||||
if (level > 0) {
|
||||
printf("%*sfield_type=%s (%d bytes), dbr_type=%s, %ld element%s",
|
||||
indent + 4, "",
|
||||
dbGetFieldTypeString(chan->addr.field_type),
|
||||
chan->addr.field_size,
|
||||
dbGetFieldTypeString(chan->addr.dbr_field_type),
|
||||
elems, elems == 1 ? "" : "s");
|
||||
if (count)
|
||||
printf("\n%*s%d filter%s (%d pre eventq, %d post eventq)\n",
|
||||
indent + 4, "", count, count == 1 ? "" : "s", pre, post);
|
||||
else
|
||||
printf(", no filters\n");
|
||||
if (level > 1)
|
||||
dbChannelFilterShow(chan, level - 2, indent + 8);
|
||||
if (count) {
|
||||
printf("%*sfinal field_type=%s (%dB), %ld element%s\n", indent + 4, "",
|
||||
dbGetFieldTypeString(chan->final_type),
|
||||
chan->final_field_size,
|
||||
felems, felems == 1 ? "" : "s");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void dbChannelFilterShow(dbChannel *chan, int level, const unsigned short indent)
|
||||
{
|
||||
chFilter *filter = (chFilter *) ellFirst(&chan->filters);
|
||||
while (filter) {
|
||||
filter->plug->fif->channel_report(filter, level, indent);
|
||||
filter = (chFilter *) ellNext(&filter->list_node);
|
||||
}
|
||||
}
|
||||
|
||||
void dbChannelDelete(dbChannel *chan)
|
||||
{
|
||||
chFilter *filter;
|
||||
|
||||
/* Close filters in reverse order */
|
||||
while ((filter = (chFilter *) ellPop(&chan->filters))) {
|
||||
filter->plug->fif->channel_close(filter);
|
||||
freeListFree(chFilterFreeList, filter);
|
||||
}
|
||||
free((char *) chan->name);
|
||||
freeListFree(dbChannelFreeList, chan);
|
||||
}
|
||||
|
||||
static void freeArray(db_field_log *pfl) {
|
||||
if (pfl->field_type == DBF_STRING && pfl->no_elements == 1) {
|
||||
freeListFree(dbchStringFreeList, pfl->u.r.field);
|
||||
} else {
|
||||
free(pfl->u.r.field);
|
||||
}
|
||||
}
|
||||
|
||||
void dbChannelMakeArrayCopy(void *pvt, db_field_log *pfl, dbChannel *chan)
|
||||
{
|
||||
void *p;
|
||||
struct dbCommon *prec = dbChannelRecord(chan);
|
||||
|
||||
if (pfl->type != dbfl_type_rec) return;
|
||||
|
||||
pfl->type = dbfl_type_ref;
|
||||
pfl->stat = prec->stat;
|
||||
pfl->sevr = prec->sevr;
|
||||
pfl->time = prec->time;
|
||||
pfl->field_type = chan->addr.field_type;
|
||||
pfl->no_elements = chan->addr.no_elements;
|
||||
pfl->field_size = chan->addr.field_size;
|
||||
pfl->u.r.dtor = freeArray;
|
||||
pfl->u.r.pvt = pvt;
|
||||
if (pfl->field_type == DBF_STRING && pfl->no_elements == 1) {
|
||||
p = freeListCalloc(dbchStringFreeList);
|
||||
} else {
|
||||
p = calloc(pfl->no_elements, pfl->field_size);
|
||||
}
|
||||
if (p) dbGet(&chan->addr, mapDBFToDBR[pfl->field_type], p, NULL, &pfl->no_elements, NULL);
|
||||
pfl->u.r.field = p;
|
||||
}
|
||||
|
||||
/* FIXME: Do these belong in a different file? */
|
||||
|
||||
void dbRegisterFilter(const char *name, const chFilterIf *fif, void *puser)
|
||||
{
|
||||
GPHENTRY *pgph;
|
||||
chFilterPlugin *pfilt;
|
||||
|
||||
if (!pdbbase) {
|
||||
printf("dbRegisterFilter: pdbbase not set!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
pgph = gphFind(pdbbase->pgpHash, name, &pdbbase->filterList);
|
||||
if (pgph)
|
||||
return;
|
||||
|
||||
pfilt = dbCalloc(1, sizeof(chFilterPlugin));
|
||||
pfilt->name = epicsStrDup(name);
|
||||
pfilt->fif = fif;
|
||||
pfilt->puser = puser;
|
||||
|
||||
ellAdd(&pdbbase->filterList, &pfilt->node);
|
||||
pgph = gphAdd(pdbbase->pgpHash, pfilt->name, &pdbbase->filterList);
|
||||
if (!pgph) {
|
||||
free((void *) pfilt->name);
|
||||
free(pfilt);
|
||||
printf("dbRegisterFilter: gphAdd failed\n");
|
||||
return;
|
||||
}
|
||||
pgph->userPvt = pfilt;
|
||||
}
|
||||
|
||||
const chFilterPlugin * dbFindFilter(const char *name, size_t len)
|
||||
{
|
||||
GPHENTRY *pgph = gphFindParse(pdbbase->pgpHash, name, len,
|
||||
&pdbbase->filterList);
|
||||
|
||||
if (!pgph)
|
||||
return NULL;
|
||||
return (chFilterPlugin *) pgph->userPvt;
|
||||
}
|
||||
@@ -0,0 +1,233 @@
|
||||
/*************************************************************************\
|
||||
* Copyright (c) 2010 UChicago Argonne LLC, as Operator of Argonne
|
||||
* National Laboratory.
|
||||
* Copyright (c) 2010 Brookhaven National Laboratory.
|
||||
* Copyright (c) 2010 Helmholtz-Zentrum Berlin
|
||||
* fuer Materialien und Energie GmbH.
|
||||
* EPICS BASE is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
\*************************************************************************/
|
||||
|
||||
/*
|
||||
* Author: Andrew Johnson <anj@aps.anl.gov>
|
||||
* Ralph Lange <Ralph.Lange@bessy.de>
|
||||
*/
|
||||
|
||||
#ifndef INC_dbChannel_H
|
||||
#define INC_dbChannel_H
|
||||
|
||||
#include "dbDefs.h"
|
||||
#include "dbAddr.h"
|
||||
#include "ellLib.h"
|
||||
#include "epicsTypes.h"
|
||||
#include "errMdef.h"
|
||||
#include "shareLib.h"
|
||||
#include "db_field_log.h"
|
||||
#include "dbEvent.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*
|
||||
* event subscription
|
||||
*/
|
||||
typedef struct evSubscrip {
|
||||
ELLNODE node;
|
||||
struct dbChannel *chan;
|
||||
EVENTFUNC *user_sub;
|
||||
void *user_arg;
|
||||
struct event_que *ev_que;
|
||||
db_field_log **pLastLog;
|
||||
unsigned long npend; /* n times this event is on the queue */
|
||||
unsigned long nreplace; /* n times replacing event on the queue */
|
||||
unsigned char select;
|
||||
char useValque;
|
||||
char callBackInProgress;
|
||||
char enabled;
|
||||
} evSubscrip;
|
||||
|
||||
typedef struct chFilter chFilter;
|
||||
|
||||
/* A dbChannel points to a record field, and can have multiple filters */
|
||||
typedef struct dbChannel {
|
||||
const char *name;
|
||||
dbAddr addr; /* address structure for record/field */
|
||||
long final_no_elements; /* final number of elements (arrays) */
|
||||
short final_field_size; /* final size of element */
|
||||
short final_type; /* final type of database field */
|
||||
ELLLIST filters; /* list of filters as created from JSON */
|
||||
ELLLIST pre_chain; /* list of filters to be called pre-event-queue */
|
||||
ELLLIST post_chain; /* list of filters to be called post-event-queue */
|
||||
} dbChannel;
|
||||
|
||||
/* Prototype for the channel event function that is called in filter stacks
|
||||
*
|
||||
* When invoked the scan lock for the record associated with 'chan' _may_ be locked.
|
||||
* If pLog->type==dbfl_type_rec then dbScanLock() must be called before copying
|
||||
* data out of the associated record.
|
||||
*
|
||||
* This function has ownership of the field log pLog, if it wishes to discard
|
||||
* this update it should free the field log with db_delete_field_log() and
|
||||
* then return NULL.
|
||||
*/
|
||||
typedef db_field_log* (chPostEventFunc)(void *pvt, dbChannel *chan, db_field_log *pLog);
|
||||
|
||||
/* Return values from chFilterIf->parse_* routines: */
|
||||
typedef enum {
|
||||
parse_stop, parse_continue
|
||||
} parse_result;
|
||||
|
||||
/* These routines must be implemented by each filter plug-in */
|
||||
typedef struct chFilterIf {
|
||||
/* cleanup pointer passed to dbRegisterFilter().
|
||||
* Called during DB shutdown
|
||||
*/
|
||||
void (* priv_free)(void *puser);
|
||||
/* Parsing event handlers: */
|
||||
parse_result (* parse_start)(chFilter *filter);
|
||||
/* If parse_start() returns parse_continue for a filter, one of
|
||||
* parse_abort() or parse_end() will later be called for that same
|
||||
* filter.
|
||||
*/
|
||||
void (* parse_abort)(chFilter *filter);
|
||||
/* If parse_abort() is called it should release any memory allocated
|
||||
* for this filter; no further parse_...() calls will be made;
|
||||
*/
|
||||
parse_result (* parse_end)(chFilter *filter);
|
||||
/* If parse_end() returns parse_stop it should have released any
|
||||
* memory allocated for this filter; no further parse_...() calls will
|
||||
* be made in this case.
|
||||
*/
|
||||
|
||||
parse_result (* parse_null)(chFilter *filter);
|
||||
parse_result (* parse_boolean)(chFilter *filter, int boolVal);
|
||||
parse_result (* parse_integer)(chFilter *filter, long integerVal);
|
||||
parse_result (* parse_double)(chFilter *filter, double doubleVal);
|
||||
parse_result (* parse_string)(chFilter *filter, const char *stringVal,
|
||||
size_t stringLen); /* NB: stringVal is not zero-terminated: */
|
||||
|
||||
parse_result (* parse_start_map)(chFilter *filter);
|
||||
parse_result (* parse_map_key)(chFilter *filter, const char *key,
|
||||
size_t stringLen); /* NB: key is not zero-terminated: */
|
||||
parse_result (* parse_end_map)(chFilter *filter);
|
||||
|
||||
parse_result (* parse_start_array)(chFilter *filter);
|
||||
parse_result (* parse_end_array)(chFilter *filter);
|
||||
|
||||
/* Channel operations: */
|
||||
long (* channel_open)(chFilter *filter);
|
||||
void (* channel_register_pre) (chFilter *filter, chPostEventFunc **cb_out, void **arg_out, db_field_log *probe);
|
||||
void (* channel_register_post)(chFilter *filter, chPostEventFunc **cb_out, void **arg_out, db_field_log *probe);
|
||||
void (* channel_report)(chFilter *filter, int level, const unsigned short indent);
|
||||
void (* channel_close)(chFilter *filter);
|
||||
} chFilterIf;
|
||||
|
||||
/* A chFilterPlugin holds data for a filter plugin */
|
||||
typedef struct chFilterPlugin {
|
||||
ELLNODE node;
|
||||
const char *name;
|
||||
const chFilterIf *fif;
|
||||
void *puser;
|
||||
} chFilterPlugin;
|
||||
|
||||
/* A chFilter holds data for a single filter instance */
|
||||
struct chFilter {
|
||||
ELLNODE list_node;
|
||||
ELLNODE pre_node;
|
||||
ELLNODE post_node;
|
||||
dbChannel *chan;
|
||||
const chFilterPlugin *plug;
|
||||
chPostEventFunc *pre_func;
|
||||
void *pre_arg;
|
||||
chPostEventFunc *post_func;
|
||||
void *post_arg;
|
||||
void *puser;
|
||||
};
|
||||
|
||||
struct dbCommon;
|
||||
struct dbFldDes;
|
||||
|
||||
epicsShareFunc void dbChannelInit (void);
|
||||
epicsShareFunc void dbChannelExit(void);
|
||||
epicsShareFunc long dbChannelTest(const char *name);
|
||||
epicsShareFunc dbChannel * dbChannelCreate(const char *name);
|
||||
epicsShareFunc long dbChannelOpen(dbChannel *chan);
|
||||
|
||||
/*Following is also defined in db_convert.h*/
|
||||
epicsShareExtern unsigned short dbDBRnewToDBRold[];
|
||||
|
||||
/* In the following macros pChan is dbChannel* */
|
||||
|
||||
/* evaluates to const char* */
|
||||
#define dbChannelName(pChan) ((pChan)->name)
|
||||
|
||||
/* evaluates to struct dbCommon* */
|
||||
#define dbChannelRecord(pChan) ((pChan)->addr.precord)
|
||||
|
||||
/* evaluates to struct dbFldDes* */
|
||||
#define dbChannelFldDes(pChan) ((pChan)->addr.pfldDes)
|
||||
|
||||
/* evaluates to long */
|
||||
#define dbChannelElements(pChan) ((pChan)->addr.no_elements)
|
||||
|
||||
/* evaluates to short */
|
||||
#define dbChannelFieldType(pChan) ((pChan)->addr.field_type)
|
||||
|
||||
/* evaluates to short */
|
||||
#define dbChannelExportType(pChan) ((pChan)->addr.dbr_field_type)
|
||||
|
||||
/* evaluates to short */
|
||||
#define dbChannelExportCAType(pChan) (dbDBRnewToDBRold[dbChannelExportType(pChan)])
|
||||
|
||||
/* evaluates to short */
|
||||
#define dbChannelFieldSize(pChan) ((pChan)->addr.field_size)
|
||||
|
||||
/* evaluates to long */
|
||||
#define dbChannelFinalElements(pChan) ((pChan)->final_no_elements)
|
||||
|
||||
/* evaluates to short */
|
||||
#define dbChannelFinalFieldType(pChan) ((pChan)->final_type)
|
||||
|
||||
/* evaluates to short */
|
||||
#define dbChannelFinalCAType(pChan) (dbDBRnewToDBRold[(pChan)->final_type])
|
||||
|
||||
/* evaluates to short */
|
||||
#define dbChannelFinalFieldSize(pChan) ((pChan)->final_field_size)
|
||||
|
||||
/* evaluates to short */
|
||||
#define dbChannelSpecial(pChan) ((pChan)->addr.special)
|
||||
|
||||
/* Channel filters do not get to interpose here since there are many
|
||||
* places where the field pointer is compared with the address of a
|
||||
* specific record field, so they can't modify the pointer value.
|
||||
*/
|
||||
/* evaluates to void* */
|
||||
#define dbChannelField(pChan) ((pChan)->addr.pfield)
|
||||
|
||||
|
||||
epicsShareFunc long dbChannelGet(dbChannel *chan, short type,
|
||||
void *pbuffer, long *options, long *nRequest, void *pfl);
|
||||
epicsShareFunc long dbChannelGetField(dbChannel *chan, short type,
|
||||
void *pbuffer, long *options, long *nRequest, void *pfl);
|
||||
epicsShareFunc long dbChannelPut(dbChannel *chan, short type,
|
||||
const void *pbuffer, long nRequest);
|
||||
epicsShareFunc long dbChannelPutField(dbChannel *chan, short type,
|
||||
const void *pbuffer, long nRequest);
|
||||
epicsShareFunc void dbChannelShow(dbChannel *chan, int level,
|
||||
const unsigned short indent);
|
||||
epicsShareFunc void dbChannelFilterShow(dbChannel *chan, int level,
|
||||
const unsigned short indent);
|
||||
epicsShareFunc void dbChannelDelete(dbChannel *chan);
|
||||
|
||||
epicsShareFunc void dbRegisterFilter(const char *key, const chFilterIf *fif, void *puser);
|
||||
epicsShareFunc db_field_log* dbChannelRunPreChain(dbChannel *chan, db_field_log *pLogIn);
|
||||
epicsShareFunc db_field_log* dbChannelRunPostChain(dbChannel *chan, db_field_log *pLogIn);
|
||||
epicsShareFunc const chFilterPlugin * dbFindFilter(const char *key, size_t len);
|
||||
epicsShareFunc void dbChannelMakeArrayCopy(void *pvt, db_field_log *pfl, dbChannel *chan);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* INC_dbChannel_H */
|
||||
@@ -0,0 +1,237 @@
|
||||
/*************************************************************************\
|
||||
* Copyright (c) 2009 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.
|
||||
\*************************************************************************/
|
||||
|
||||
/*
|
||||
* Author Jeffrey O. Hill
|
||||
* johill@lanl.gov
|
||||
* 505 665 1831
|
||||
*/
|
||||
|
||||
#include <string>
|
||||
#include <stdexcept>
|
||||
|
||||
#include <limits.h>
|
||||
|
||||
#include "tsFreeList.h"
|
||||
#include "epicsMutex.h"
|
||||
#include "epicsEvent.h"
|
||||
#include "db_access.h"
|
||||
#include "errlog.h"
|
||||
|
||||
#define epicsExportSharedSymbols
|
||||
#include "db_access_routines.h"
|
||||
#include "dbCAC.h"
|
||||
#include "dbChannelIO.h"
|
||||
#include "dbPutNotifyBlocker.h"
|
||||
|
||||
dbChannelIO::dbChannelIO (
|
||||
epicsMutex & mutexIn, cacChannelNotify & notify,
|
||||
dbChannel * dbchIn, dbContext & serviceIO ) :
|
||||
cacChannel ( notify ), mutex ( mutexIn ), serviceIO ( serviceIO ),
|
||||
dbch ( dbchIn )
|
||||
{
|
||||
}
|
||||
|
||||
void dbChannelIO::initiateConnect ( epicsGuard < epicsMutex > & guard )
|
||||
{
|
||||
guard.assertIdenticalMutex ( this->mutex );
|
||||
this->notify().connectNotify ( guard );
|
||||
}
|
||||
|
||||
dbChannelIO::~dbChannelIO ()
|
||||
{
|
||||
}
|
||||
|
||||
void dbChannelIO::destructor ( CallbackGuard & cbGuard,
|
||||
epicsGuard < epicsMutex > & guard )
|
||||
{
|
||||
guard.assertIdenticalMutex ( this->mutex );
|
||||
this->serviceIO.destroyAllIO ( cbGuard, guard, *this );
|
||||
dbChannelDelete ( this->dbch );
|
||||
this->~dbChannelIO ();
|
||||
}
|
||||
|
||||
void dbChannelIO::destroy (
|
||||
CallbackGuard & cbGuard,
|
||||
epicsGuard < epicsMutex > & guard )
|
||||
{
|
||||
guard.assertIdenticalMutex ( this->mutex );
|
||||
this->serviceIO.destroyChannel ( cbGuard, guard, *this );
|
||||
// don't access this pointer after above call because
|
||||
// object no longer exists
|
||||
}
|
||||
|
||||
cacChannel::ioStatus dbChannelIO::read (
|
||||
epicsGuard < epicsMutex > & guard, unsigned type,
|
||||
unsigned long count, cacReadNotify & notify, ioid * )
|
||||
{
|
||||
guard.assertIdenticalMutex ( this->mutex );
|
||||
this->serviceIO.callReadNotify ( guard, this->dbch,
|
||||
type, count, notify );
|
||||
return iosSynch;
|
||||
}
|
||||
|
||||
void dbChannelIO::write (
|
||||
epicsGuard < epicsMutex > & guard, unsigned type,
|
||||
unsigned long count, const void *pValue )
|
||||
{
|
||||
epicsGuardRelease < epicsMutex > unguard ( guard );
|
||||
if ( count > LONG_MAX ) {
|
||||
throw outOfBounds();
|
||||
}
|
||||
int status = dbChannel_put ( this->dbch, type, pValue,
|
||||
static_cast <long> (count) );
|
||||
if ( status ) {
|
||||
throw std::logic_error (
|
||||
"db_put_field() completed unsuccessfully" );
|
||||
}
|
||||
}
|
||||
|
||||
cacChannel::ioStatus dbChannelIO::write (
|
||||
epicsGuard < epicsMutex > & guard, unsigned type,
|
||||
unsigned long count, const void * pValue,
|
||||
cacWriteNotify & notify, ioid * pId )
|
||||
{
|
||||
guard.assertIdenticalMutex ( this->mutex );
|
||||
|
||||
if ( count > LONG_MAX ) {
|
||||
throw outOfBounds();
|
||||
}
|
||||
|
||||
this->serviceIO.initiatePutNotify (
|
||||
guard, *this, this->dbch,
|
||||
type, count, pValue, notify, pId );
|
||||
|
||||
return iosAsynch;
|
||||
}
|
||||
|
||||
void dbChannelIO::subscribe (
|
||||
epicsGuard < epicsMutex > & guard, unsigned type, unsigned long count,
|
||||
unsigned mask, cacStateNotify & notify, ioid * pId )
|
||||
{
|
||||
guard.assertIdenticalMutex ( this->mutex );
|
||||
this->serviceIO.subscribe (
|
||||
guard, this->dbch, *this,
|
||||
type, count, mask, notify, pId );
|
||||
}
|
||||
|
||||
void dbChannelIO::ioCancel (
|
||||
CallbackGuard & cbGuard,
|
||||
epicsGuard < epicsMutex > & mutualExclusionGuard,
|
||||
const ioid & id )
|
||||
{
|
||||
mutualExclusionGuard.assertIdenticalMutex ( this->mutex );
|
||||
this->serviceIO.ioCancel ( cbGuard, mutualExclusionGuard, *this, id );
|
||||
}
|
||||
|
||||
void dbChannelIO::ioShow (
|
||||
epicsGuard < epicsMutex > & guard,
|
||||
const ioid & id, unsigned level ) const
|
||||
{
|
||||
guard.assertIdenticalMutex ( this->mutex );
|
||||
this->serviceIO.ioShow ( guard, id, level );
|
||||
}
|
||||
|
||||
void dbChannelIO::show (
|
||||
epicsGuard < epicsMutex > & guard, unsigned level ) const
|
||||
{
|
||||
guard.assertIdenticalMutex ( this->mutex );
|
||||
|
||||
printf ("channel at %p attached to local database record %s\n",
|
||||
static_cast <const void *> ( this ),
|
||||
dbChannelRecord ( this->dbch ) -> name );
|
||||
|
||||
if ( level > 0u ) {
|
||||
printf ( " type %s, element count %li, field at %p\n",
|
||||
dbf_type_to_text ( dbChannelExportCAType ( this->dbch ) ),
|
||||
dbChannelElements ( this->dbch ),
|
||||
dbChannelField ( this->dbch ) );
|
||||
if ( level > 1u ) {
|
||||
dbChannelFilterShow ( this->dbch, level - 2u, 8 );
|
||||
this->serviceIO.show ( level - 2u );
|
||||
this->serviceIO.showAllIO ( *this, level - 2u );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsigned long dbChannelIO::nativeElementCount (
|
||||
epicsGuard < epicsMutex > & guard ) const
|
||||
{
|
||||
guard.assertIdenticalMutex ( this->mutex );
|
||||
long elements = dbChannelElements ( this->dbch );
|
||||
if ( elements >= 0u ) {
|
||||
return static_cast < unsigned long > ( elements );
|
||||
}
|
||||
return 0u;
|
||||
}
|
||||
|
||||
// hopefully to be eventually phased out
|
||||
const char * dbChannelIO::pName (
|
||||
epicsGuard < epicsMutex > & guard ) const throw ()
|
||||
{
|
||||
guard.assertIdenticalMutex ( this->mutex );
|
||||
return dbChannelName ( this->dbch );
|
||||
}
|
||||
|
||||
unsigned dbChannelIO::getName (
|
||||
epicsGuard < epicsMutex > &,
|
||||
char * pBuf, unsigned bufLen ) const throw ()
|
||||
{
|
||||
const char *name = dbChannelName ( this->dbch );
|
||||
size_t len = strlen ( name );
|
||||
strncpy ( pBuf, name, bufLen );
|
||||
if (len < bufLen)
|
||||
return (unsigned) len;
|
||||
pBuf[--bufLen] = '\0';
|
||||
return bufLen;
|
||||
}
|
||||
|
||||
short dbChannelIO::nativeType (
|
||||
epicsGuard < epicsMutex > & guard ) const
|
||||
{
|
||||
guard.assertIdenticalMutex ( this->mutex );
|
||||
return dbChannelExportCAType( this->dbch );
|
||||
}
|
||||
|
||||
void * dbChannelIO::operator new ( size_t size,
|
||||
tsFreeList < dbChannelIO, 256, epicsMutexNOOP > & freeList )
|
||||
{
|
||||
return freeList.allocate ( size );
|
||||
}
|
||||
|
||||
#ifdef CXX_PLACEMENT_DELETE
|
||||
void dbChannelIO::operator delete ( void *pCadaver,
|
||||
tsFreeList < dbChannelIO, 256, epicsMutexNOOP > & freeList )
|
||||
{
|
||||
freeList.release ( pCadaver );
|
||||
}
|
||||
#endif
|
||||
|
||||
void dbChannelIO::operator delete ( void * )
|
||||
{
|
||||
// Visual C++ .net appears to require operator delete if
|
||||
// placement operator delete is defined? I smell a ms rat
|
||||
// because if I declare placement new and delete, but
|
||||
// comment out the placement delete definition there are
|
||||
// no undefined symbols.
|
||||
errlogPrintf ( "%s:%d this compiler is confused about placement delete - memory was probably leaked",
|
||||
__FILE__, __LINE__ );
|
||||
}
|
||||
|
||||
void dbChannelIO::flush (
|
||||
epicsGuard < epicsMutex > & )
|
||||
{
|
||||
}
|
||||
|
||||
unsigned dbChannelIO::requestMessageBytesPending (
|
||||
epicsGuard < epicsMutex > & )
|
||||
{
|
||||
return 0u;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,124 @@
|
||||
/*************************************************************************\
|
||||
* 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.
|
||||
\*************************************************************************/
|
||||
|
||||
/*
|
||||
* Author Jeffrey O. Hill
|
||||
* johill@lanl.gov
|
||||
* 505 665 1831
|
||||
*
|
||||
* NOTES:
|
||||
* 1) This interface is preliminary and will change in the future
|
||||
*/
|
||||
|
||||
#ifndef dbChannelIOh
|
||||
#define dbChannelIOh
|
||||
|
||||
#ifdef epicsExportSharedSymbols
|
||||
# define dbChannelIOh_restore_epicsExportSharedSymbols
|
||||
# undef epicsExportSharedSymbols
|
||||
#endif
|
||||
|
||||
#include "compilerDependencies.h"
|
||||
|
||||
#ifdef dbChannelIOh_restore_epicsExportSharedSymbols
|
||||
# define epicsExportSharedSymbols
|
||||
#endif
|
||||
|
||||
class dbChannelIO : public cacChannel, public dbContextPrivateListOfIO {
|
||||
public:
|
||||
dbChannelIO (
|
||||
epicsMutex &, cacChannelNotify &,
|
||||
dbChannel *, dbContext & );
|
||||
void destructor (
|
||||
CallbackGuard &,
|
||||
epicsGuard < epicsMutex > & );
|
||||
void destroy (
|
||||
CallbackGuard &,
|
||||
epicsGuard < epicsMutex > & mutualExclusionGuard );
|
||||
void callReadNotify (
|
||||
epicsGuard < epicsMutex > &,
|
||||
unsigned type, unsigned long count,
|
||||
cacReadNotify & notify );
|
||||
void callStateNotify (
|
||||
unsigned type, unsigned long count,
|
||||
const struct db_field_log * pfl, cacStateNotify & notify );
|
||||
void show (
|
||||
epicsGuard < epicsMutex > &, unsigned level ) const;
|
||||
unsigned getName (
|
||||
epicsGuard < epicsMutex > &,
|
||||
char * pBuf, unsigned bufLen ) const throw ();
|
||||
const char * pName (
|
||||
epicsGuard < epicsMutex > & ) const throw ();
|
||||
void * operator new ( size_t size,
|
||||
tsFreeList < dbChannelIO, 256, epicsMutexNOOP > & );
|
||||
epicsPlacementDeleteOperator (( void *,
|
||||
tsFreeList < dbChannelIO, 256, epicsMutexNOOP > & ))
|
||||
protected:
|
||||
~dbChannelIO ();
|
||||
private:
|
||||
epicsMutex & mutex;
|
||||
dbContext & serviceIO;
|
||||
dbChannel * dbch;
|
||||
|
||||
void initiateConnect (
|
||||
epicsGuard < epicsMutex > & );
|
||||
unsigned requestMessageBytesPending (
|
||||
epicsGuard < epicsMutex > & );
|
||||
void flush (
|
||||
epicsGuard < epicsMutex > & );
|
||||
ioStatus read (
|
||||
epicsGuard < epicsMutex > &,
|
||||
unsigned type, unsigned long count,
|
||||
cacReadNotify &, ioid * );
|
||||
void write (
|
||||
epicsGuard < epicsMutex > &,
|
||||
unsigned type, unsigned long count,
|
||||
const void * pvalue );
|
||||
ioStatus write (
|
||||
epicsGuard < epicsMutex > &,
|
||||
unsigned type, unsigned long count,
|
||||
const void * pvalue, cacWriteNotify &, ioid * );
|
||||
void subscribe (
|
||||
epicsGuard < epicsMutex > &,
|
||||
unsigned type, unsigned long count,
|
||||
unsigned mask, cacStateNotify ¬ify, ioid * );
|
||||
void ioCancel (
|
||||
CallbackGuard &,
|
||||
epicsGuard < epicsMutex > &,
|
||||
const ioid & );
|
||||
void ioShow (
|
||||
epicsGuard < epicsMutex > &,
|
||||
const ioid &, unsigned level ) const;
|
||||
short nativeType (
|
||||
epicsGuard < epicsMutex > & ) const;
|
||||
unsigned long nativeElementCount (
|
||||
epicsGuard < epicsMutex > & ) const;
|
||||
dbChannelIO ( const dbChannelIO & );
|
||||
dbChannelIO & operator = ( const dbChannelIO & );
|
||||
void operator delete ( void * );
|
||||
};
|
||||
|
||||
inline void dbChannelIO::callReadNotify (
|
||||
epicsGuard < epicsMutex > & guard, unsigned type, unsigned long count,
|
||||
cacReadNotify & notify )
|
||||
{
|
||||
guard.assertIdenticalMutex ( this->mutex );
|
||||
this->serviceIO.callReadNotify ( guard, this->dbch, type, count, notify );
|
||||
}
|
||||
|
||||
inline void dbChannelIO::callStateNotify ( unsigned type, unsigned long count,
|
||||
const struct db_field_log *pfl, cacStateNotify ¬ify )
|
||||
{
|
||||
this->serviceIO.callStateNotify ( this->dbch, type, count, pfl, notify );
|
||||
}
|
||||
|
||||
|
||||
#endif // dbChannelIOh
|
||||
|
||||
@@ -0,0 +1,118 @@
|
||||
#ifndef DBCHANNELNOOP_H
|
||||
#define DBCHANNELNOOP_H
|
||||
|
||||
#include <string.h>
|
||||
#include <string>
|
||||
|
||||
#include "cacIO.h"
|
||||
#include "caerr.h"
|
||||
|
||||
/** @brief A channel which never connects
|
||||
*
|
||||
* Used when dbCa is placed in isolated mode for unittests
|
||||
*/
|
||||
class dbChannelNOOP : public cacChannel
|
||||
{
|
||||
std::string myname;
|
||||
public:
|
||||
dbChannelNOOP(const char *name, cacChannelNotify ¬ify)
|
||||
:cacChannel(notify)
|
||||
,myname(name)
|
||||
{}
|
||||
|
||||
virtual void destroy (
|
||||
CallbackGuard & /*callbackGuard*/,
|
||||
epicsGuard < epicsMutex > & /*mutualExclusionGuard*/ )
|
||||
{
|
||||
delete this; // goodbye cruel world
|
||||
}
|
||||
|
||||
virtual unsigned getName (
|
||||
epicsGuard < epicsMutex > &,
|
||||
char * pBuf, unsigned bufLen ) const throw ()
|
||||
{
|
||||
const char* name = myname.c_str();
|
||||
if(bufLen>myname.size()+1) {
|
||||
bufLen=myname.size()+1;
|
||||
}
|
||||
memcpy(pBuf, name, bufLen);
|
||||
pBuf[--bufLen] = '\0';
|
||||
return bufLen;
|
||||
}
|
||||
|
||||
// !! deprecated, avoid use !!
|
||||
virtual const char * pName (
|
||||
epicsGuard < epicsMutex > & guard ) const throw ()
|
||||
{return myname.c_str();}
|
||||
|
||||
virtual void show (
|
||||
epicsGuard < epicsMutex > &,
|
||||
unsigned level ) const
|
||||
{}
|
||||
|
||||
virtual void initiateConnect (
|
||||
epicsGuard < epicsMutex > & )
|
||||
{}
|
||||
|
||||
virtual unsigned requestMessageBytesPending (
|
||||
epicsGuard < epicsMutex > & /*mutualExclusionGuard*/ )
|
||||
{return 0;}
|
||||
|
||||
virtual void flush (
|
||||
epicsGuard < epicsMutex > & /*mutualExclusionGuard*/ )
|
||||
{}
|
||||
|
||||
virtual ioStatus read (
|
||||
epicsGuard < epicsMutex > &mut,
|
||||
unsigned type, arrayElementCount count,
|
||||
cacReadNotify ¬ify, ioid * = 0 )
|
||||
{
|
||||
notify.exception(mut, ECA_NORDACCESS, "dbChannelNOOP", type, count);
|
||||
return iosSynch;
|
||||
}
|
||||
|
||||
virtual void write (
|
||||
epicsGuard < epicsMutex > &,
|
||||
unsigned type, arrayElementCount count,
|
||||
const void *pValue )
|
||||
{}
|
||||
|
||||
virtual ioStatus write (
|
||||
epicsGuard < epicsMutex > &mut,
|
||||
unsigned type, arrayElementCount count,
|
||||
const void */*pValue*/, cacWriteNotify & notify, ioid * = 0 )
|
||||
{
|
||||
notify.exception(mut, ECA_NOWTACCESS, "dbChannelNOOP", type, count);
|
||||
return iosSynch;
|
||||
}
|
||||
|
||||
virtual void subscribe (
|
||||
epicsGuard < epicsMutex > &mut, unsigned type,
|
||||
arrayElementCount count, unsigned /*mask*/, cacStateNotify & notify,
|
||||
ioid * = 0 )
|
||||
{
|
||||
// should never subscribe
|
||||
notify.exception(mut, ECA_BADMASK, "dbChannelNOOP", type, count);
|
||||
}
|
||||
|
||||
virtual void ioCancel (
|
||||
CallbackGuard & callbackGuard,
|
||||
epicsGuard < epicsMutex > & mutualExclusionGuard,
|
||||
const ioid & )
|
||||
{}
|
||||
|
||||
virtual void ioShow (
|
||||
epicsGuard < epicsMutex > &,
|
||||
const ioid &, unsigned level ) const
|
||||
{}
|
||||
|
||||
virtual short nativeType (
|
||||
epicsGuard < epicsMutex > & ) const
|
||||
{return 0;} // DBR_STRING
|
||||
|
||||
virtual arrayElementCount nativeElementCount (
|
||||
epicsGuard < epicsMutex > & ) const
|
||||
{return 1;}
|
||||
};
|
||||
|
||||
#endif // DBCHANNELNOOP_H
|
||||
@@ -0,0 +1,267 @@
|
||||
#*************************************************************************
|
||||
# Copyright (c) 2007 UChicago Argonne LLC, as Operator of Argonne
|
||||
# National Laboratory.
|
||||
# Copyright (c) 2002 The Regents of the University of California, as
|
||||
# Operator of Los Alamos National Laboratory.
|
||||
# EPICS BASE is distributed subject to a Software License Agreement found
|
||||
# in file LICENSE that is included with this distribution.
|
||||
#*************************************************************************
|
||||
%#include "epicsTypes.h"
|
||||
%#include "link.h"
|
||||
field(NAME,DBF_STRING) {
|
||||
prompt("Record Name")
|
||||
special(SPC_NOMOD)
|
||||
size(61)
|
||||
}
|
||||
field(DESC,DBF_STRING) {
|
||||
prompt("Descriptor")
|
||||
promptgroup("10 - Common")
|
||||
size(41)
|
||||
}
|
||||
field(ASG,DBF_STRING) {
|
||||
prompt("Access Security Group")
|
||||
promptgroup("10 - Common")
|
||||
special(SPC_AS)
|
||||
size(29)
|
||||
}
|
||||
field(SCAN,DBF_MENU) {
|
||||
prompt("Scan Mechanism")
|
||||
promptgroup("20 - Scan")
|
||||
special(SPC_SCAN)
|
||||
interest(1)
|
||||
menu(menuScan)
|
||||
}
|
||||
field(PINI,DBF_MENU) {
|
||||
prompt("Process at iocInit")
|
||||
promptgroup("20 - Scan")
|
||||
interest(1)
|
||||
menu(menuPini)
|
||||
}
|
||||
field(PHAS,DBF_SHORT) {
|
||||
prompt("Scan Phase")
|
||||
promptgroup("20 - Scan")
|
||||
special(SPC_SCAN)
|
||||
interest(1)
|
||||
}
|
||||
field(EVNT,DBF_STRING) {
|
||||
prompt("Event Name")
|
||||
promptgroup("20 - Scan")
|
||||
special(SPC_SCAN)
|
||||
size(40)
|
||||
interest(1)
|
||||
}
|
||||
field(TSE,DBF_SHORT) {
|
||||
prompt("Time Stamp Event")
|
||||
promptgroup("20 - Scan")
|
||||
interest(1)
|
||||
}
|
||||
field(TSEL,DBF_INLINK) {
|
||||
prompt("Time Stamp Link")
|
||||
promptgroup("20 - Scan")
|
||||
interest(1)
|
||||
}
|
||||
field(DTYP,DBF_DEVICE) {
|
||||
prompt("Device Type")
|
||||
promptgroup("10 - Common")
|
||||
interest(1)
|
||||
}
|
||||
field(DISV,DBF_SHORT) {
|
||||
prompt("Disable Value")
|
||||
promptgroup("20 - Scan")
|
||||
initial("1")
|
||||
}
|
||||
field(DISA,DBF_SHORT) {
|
||||
prompt("Disable")
|
||||
}
|
||||
field(SDIS,DBF_INLINK) {
|
||||
prompt("Scanning Disable")
|
||||
promptgroup("20 - Scan")
|
||||
interest(1)
|
||||
}
|
||||
%#include "epicsMutex.h"
|
||||
field(MLOK,DBF_NOACCESS) {
|
||||
prompt("Monitor lock")
|
||||
special(SPC_NOMOD)
|
||||
interest(4)
|
||||
extra("epicsMutexId mlok")
|
||||
}
|
||||
%#include "ellLib.h"
|
||||
field(MLIS,DBF_NOACCESS) {
|
||||
prompt("Monitor List")
|
||||
special(SPC_NOMOD)
|
||||
interest(4)
|
||||
extra("ELLLIST mlis")
|
||||
}
|
||||
field(BKLNK,DBF_NOACCESS) {
|
||||
prompt("Backwards link tracking")
|
||||
special(SPC_NOMOD)
|
||||
interest(4)
|
||||
extra("ELLLIST bklnk")
|
||||
}
|
||||
field(DISP,DBF_UCHAR) {
|
||||
prompt("Disable putField")
|
||||
}
|
||||
field(PROC,DBF_UCHAR) {
|
||||
prompt("Force Processing")
|
||||
pp(TRUE)
|
||||
interest(3)
|
||||
}
|
||||
field(STAT,DBF_MENU) {
|
||||
prompt("Alarm Status")
|
||||
special(SPC_NOMOD)
|
||||
menu(menuAlarmStat)
|
||||
initial("UDF")
|
||||
}
|
||||
field(SEVR,DBF_MENU) {
|
||||
prompt("Alarm Severity")
|
||||
special(SPC_NOMOD)
|
||||
menu(menuAlarmSevr)
|
||||
}
|
||||
field(NSTA,DBF_MENU) {
|
||||
prompt("New Alarm Status")
|
||||
special(SPC_NOMOD)
|
||||
interest(2)
|
||||
menu(menuAlarmStat)
|
||||
}
|
||||
field(NSEV,DBF_MENU) {
|
||||
prompt("New Alarm Severity")
|
||||
special(SPC_NOMOD)
|
||||
interest(2)
|
||||
menu(menuAlarmSevr)
|
||||
}
|
||||
field(ACKS,DBF_MENU) {
|
||||
prompt("Alarm Ack Severity")
|
||||
special(SPC_NOMOD)
|
||||
interest(2)
|
||||
menu(menuAlarmSevr)
|
||||
}
|
||||
field(ACKT,DBF_MENU) {
|
||||
prompt("Alarm Ack Transient")
|
||||
promptgroup("70 - Alarm")
|
||||
special(SPC_NOMOD)
|
||||
interest(2)
|
||||
menu(menuYesNo)
|
||||
initial("YES")
|
||||
}
|
||||
field(DISS,DBF_MENU) {
|
||||
prompt("Disable Alarm Sevrty")
|
||||
promptgroup("70 - Alarm")
|
||||
interest(1)
|
||||
menu(menuAlarmSevr)
|
||||
}
|
||||
field(LCNT,DBF_UCHAR) {
|
||||
prompt("Lock Count")
|
||||
special(SPC_NOMOD)
|
||||
interest(2)
|
||||
}
|
||||
field(PACT,DBF_UCHAR) {
|
||||
prompt("Record active")
|
||||
special(SPC_NOMOD)
|
||||
interest(1)
|
||||
}
|
||||
field(PUTF,DBF_UCHAR) {
|
||||
prompt("dbPutField process")
|
||||
special(SPC_NOMOD)
|
||||
interest(1)
|
||||
}
|
||||
field(RPRO,DBF_UCHAR) {
|
||||
prompt("Reprocess ")
|
||||
special(SPC_NOMOD)
|
||||
interest(1)
|
||||
}
|
||||
field(ASP,DBF_NOACCESS) {
|
||||
prompt("Access Security Pvt")
|
||||
special(SPC_NOMOD)
|
||||
interest(4)
|
||||
extra("struct asgMember *asp")
|
||||
}
|
||||
field(PPN,DBF_NOACCESS) {
|
||||
prompt("pprocessNotify")
|
||||
special(SPC_NOMOD)
|
||||
interest(4)
|
||||
extra("struct processNotify *ppn")
|
||||
}
|
||||
field(PPNR,DBF_NOACCESS) {
|
||||
prompt("pprocessNotifyRecord")
|
||||
special(SPC_NOMOD)
|
||||
interest(4)
|
||||
extra("struct processNotifyRecord *ppnr")
|
||||
}
|
||||
field(SPVT,DBF_NOACCESS) {
|
||||
prompt("Scan Private")
|
||||
special(SPC_NOMOD)
|
||||
interest(4)
|
||||
extra("struct scan_element *spvt")
|
||||
}
|
||||
field(RSET,DBF_NOACCESS) {
|
||||
prompt("Address of RSET")
|
||||
special(SPC_NOMOD)
|
||||
interest(4)
|
||||
extra("struct typed_rset *rset")
|
||||
}
|
||||
field(DSET,DBF_NOACCESS) {
|
||||
prompt("DSET address")
|
||||
special(SPC_NOMOD)
|
||||
interest(4)
|
||||
extra("struct dset *dset")
|
||||
}
|
||||
field(DPVT,DBF_NOACCESS) {
|
||||
prompt("Device Private")
|
||||
special(SPC_NOMOD)
|
||||
interest(4)
|
||||
extra("void *dpvt")
|
||||
}
|
||||
field(RDES,DBF_NOACCESS) {
|
||||
prompt("Address of dbRecordType")
|
||||
special(SPC_NOMOD)
|
||||
interest(4)
|
||||
extra("struct dbRecordType *rdes")
|
||||
}
|
||||
field(LSET,DBF_NOACCESS) {
|
||||
prompt("Lock Set")
|
||||
special(SPC_NOMOD)
|
||||
interest(4)
|
||||
extra("struct lockRecord *lset")
|
||||
}
|
||||
field(PRIO,DBF_MENU) {
|
||||
prompt("Scheduling Priority")
|
||||
promptgroup("20 - Scan")
|
||||
special(SPC_SCAN)
|
||||
interest(1)
|
||||
menu(menuPriority)
|
||||
}
|
||||
field(TPRO,DBF_UCHAR) {
|
||||
prompt("Trace Processing")
|
||||
}
|
||||
field(BKPT,DBF_NOACCESS) {
|
||||
prompt("Break Point")
|
||||
special(SPC_NOMOD)
|
||||
interest(1)
|
||||
extra("char bkpt")
|
||||
}
|
||||
field(UDF,DBF_UCHAR) {
|
||||
prompt("Undefined")
|
||||
promptgroup("10 - Common")
|
||||
pp(TRUE)
|
||||
interest(1)
|
||||
initial("1")
|
||||
}
|
||||
field(UDFS,DBF_MENU) {
|
||||
prompt("Undefined Alarm Sevrty")
|
||||
promptgroup("70 - Alarm")
|
||||
interest(1)
|
||||
menu(menuAlarmSevr)
|
||||
initial("INVALID")
|
||||
}
|
||||
%#include "epicsTime.h"
|
||||
field(TIME,DBF_NOACCESS) {
|
||||
prompt("Time")
|
||||
special(SPC_NOMOD)
|
||||
interest(2)
|
||||
extra("epicsTimeStamp time")
|
||||
}
|
||||
field(FLNK,DBF_FWDLINK) {
|
||||
prompt("Forward Process Link")
|
||||
promptgroup("20 - Scan")
|
||||
interest(1)
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
#ifndef DBCOMMONPVT_H
|
||||
#define DBCOMMONPVT_H
|
||||
|
||||
#include "dbCommon.h"
|
||||
|
||||
/** Base internal additional information for every record
|
||||
*/
|
||||
typedef struct dbCommonPvt {
|
||||
struct dbRecordNode *recnode;
|
||||
|
||||
struct dbCommon common;
|
||||
} dbCommonPvt;
|
||||
|
||||
#endif // DBCOMMONPVT_H
|
||||
@@ -0,0 +1,12 @@
|
||||
#*************************************************************************
|
||||
# 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.
|
||||
#*************************************************************************
|
||||
recordtype(dbCommon) {
|
||||
include "dbCommon.dbd"
|
||||
}
|
||||
@@ -0,0 +1,234 @@
|
||||
/*************************************************************************\
|
||||
* Copyright (c) 2010 UChicago Argonne LLC, as Operator of Argonne
|
||||
* National Laboratory.
|
||||
* Copyright (c) 2002 The Regents of the University of California, as
|
||||
* Operator of Los Alamos National Laboratory.
|
||||
* EPICS BASE is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
\*************************************************************************/
|
||||
/* dbConstLink.c
|
||||
*
|
||||
* Original Authors: Bob Dalesio, Marty Kraimer
|
||||
* Current Author: Andrew Johnson
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "dbDefs.h"
|
||||
#include "epicsStdlib.h"
|
||||
|
||||
#define epicsExportSharedSymbols
|
||||
#include "dbAccessDefs.h"
|
||||
#include "dbAddr.h"
|
||||
#include "dbCommon.h"
|
||||
#include "dbConstLink.h"
|
||||
#include "dbConvertJSON.h"
|
||||
#include "dbFldTypes.h"
|
||||
#include "dbLink.h"
|
||||
#include "link.h"
|
||||
|
||||
/**************************** Convert functions ****************************/
|
||||
|
||||
/* The difference between these and dbFastConvert is that constants
|
||||
* may contain hex numbers, whereas database conversions can't.
|
||||
*/
|
||||
|
||||
/* Copy to STRING */
|
||||
static long cvt_st_st(const char *from, void *pfield, const dbAddr *paddr)
|
||||
{
|
||||
char *to = pfield;
|
||||
size_t size;
|
||||
|
||||
if (paddr && paddr->field_size < MAX_STRING_SIZE) {
|
||||
size = paddr->field_size - 1;
|
||||
} else {
|
||||
size = MAX_STRING_SIZE - 1;
|
||||
}
|
||||
strncpy(to, from, size);
|
||||
to[size] = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Most integer conversions are identical */
|
||||
#define cvt_st_int(TYPE) static long \
|
||||
cvt_st_ ## TYPE(const char *from, void *pfield, const dbAddr *paddr) { \
|
||||
epics##TYPE *to = pfield; \
|
||||
char *end; \
|
||||
\
|
||||
if (*from == 0) { \
|
||||
*to = 0; \
|
||||
return 0; \
|
||||
} \
|
||||
return epicsParse##TYPE(from, to, 0, &end); \
|
||||
}
|
||||
|
||||
/* Instanciate for CHAR, UCHAR, SHORT, USHORT and LONG */
|
||||
cvt_st_int(Int8)
|
||||
cvt_st_int(UInt8)
|
||||
cvt_st_int(Int16)
|
||||
cvt_st_int(UInt16)
|
||||
cvt_st_int(Int32)
|
||||
|
||||
/* Conversion for ULONG is different... */
|
||||
static long cvt_st_UInt32(const char *from, void *pfield, const dbAddr *paddr)
|
||||
{
|
||||
epicsUInt32 *to = pfield;
|
||||
char *end;
|
||||
long status;
|
||||
|
||||
if (*from == 0) {
|
||||
*to = 0;
|
||||
return 0;
|
||||
}
|
||||
status = epicsParseUInt32(from, to, 0, &end);
|
||||
if (status == S_stdlib_noConversion ||
|
||||
(!status && (*end == '.' || *end == 'e' || *end == 'E'))) {
|
||||
/*
|
||||
* Convert via double so numbers like 1.0e3 convert properly.
|
||||
* db_access pretends ULONG fields are DOUBLE.
|
||||
*/
|
||||
double dval;
|
||||
|
||||
status = epicsParseFloat64(from, &dval, &end);
|
||||
if (!status &&
|
||||
dval >=0 &&
|
||||
dval <= ULONG_MAX)
|
||||
*to = dval;
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
/* Instanciate for INT64 and UINT64 */
|
||||
cvt_st_int(Int64)
|
||||
cvt_st_int(UInt64)
|
||||
|
||||
|
||||
/* Float conversions are identical */
|
||||
#define cvt_st_float(TYPE) static long \
|
||||
cvt_st_ ## TYPE(const char *from, void *pfield, const dbAddr *paddr) { \
|
||||
epics##TYPE *to = pfield; \
|
||||
char *end; \
|
||||
\
|
||||
if (*from == 0) { \
|
||||
*to = 0; \
|
||||
return 0; \
|
||||
} \
|
||||
return epicsParse##TYPE(from, to, &end); \
|
||||
}
|
||||
|
||||
/* Instanciate for FLOAT32 and FLOAT64 */
|
||||
cvt_st_float(Float32)
|
||||
cvt_st_float(Float64)
|
||||
|
||||
|
||||
static long (*convert[DBF_DOUBLE+1])(const char *, void *, const dbAddr *) = {
|
||||
cvt_st_st,
|
||||
cvt_st_Int8, cvt_st_UInt8,
|
||||
cvt_st_Int16, cvt_st_UInt16,
|
||||
cvt_st_Int32, cvt_st_UInt32,
|
||||
cvt_st_Int64, cvt_st_UInt64,
|
||||
cvt_st_Float32, cvt_st_Float64
|
||||
};
|
||||
|
||||
/***************************** Constant Links *****************************/
|
||||
|
||||
/* Forward definition */
|
||||
static lset dbConst_lset;
|
||||
|
||||
void dbConstInitLink(struct link *plink)
|
||||
{
|
||||
plink->lset = &dbConst_lset;
|
||||
}
|
||||
|
||||
void dbConstAddLink(struct link *plink)
|
||||
{
|
||||
plink->lset = &dbConst_lset;
|
||||
}
|
||||
|
||||
/**************************** Member functions ****************************/
|
||||
|
||||
static long dbConstLoadScalar(struct link *plink, short dbrType, void *pbuffer)
|
||||
{
|
||||
const char *pstr = plink->value.constantStr;
|
||||
size_t len;
|
||||
|
||||
if (!pstr)
|
||||
return S_db_badField;
|
||||
len = strlen(pstr);
|
||||
|
||||
/* Choice values must be numeric */
|
||||
if (dbrType == DBF_MENU || dbrType == DBF_ENUM || dbrType == DBF_DEVICE)
|
||||
dbrType = DBF_USHORT;
|
||||
|
||||
if (*pstr == '[' && pstr[len-1] == ']') {
|
||||
/* Convert from JSON array */
|
||||
long nReq = 1;
|
||||
|
||||
return dbPutConvertJSON(pstr, dbrType, pbuffer, &nReq);
|
||||
}
|
||||
|
||||
return convert[dbrType](pstr, pbuffer, NULL);
|
||||
}
|
||||
|
||||
static long dbConstLoadLS(struct link *plink, char *pbuffer, epicsUInt32 size,
|
||||
epicsUInt32 *plen)
|
||||
{
|
||||
const char *pstr = plink->value.constantStr;
|
||||
|
||||
if (!pstr)
|
||||
return S_db_badField;
|
||||
|
||||
return dbLSConvertJSON(pstr, pbuffer, size, plen);
|
||||
}
|
||||
|
||||
static long dbConstLoadArray(struct link *plink, short dbrType, void *pbuffer,
|
||||
long *pnReq)
|
||||
{
|
||||
const char *pstr = plink->value.constantStr;
|
||||
|
||||
if (!pstr)
|
||||
return S_db_badField;
|
||||
|
||||
/* Choice values must be numeric */
|
||||
if (dbrType == DBF_MENU || dbrType == DBF_ENUM || dbrType == DBF_DEVICE)
|
||||
dbrType = DBF_USHORT;
|
||||
|
||||
return dbPutConvertJSON(pstr, dbrType, pbuffer, pnReq);
|
||||
}
|
||||
|
||||
static long dbConstGetNelements(const struct link *plink, long *nelements)
|
||||
{
|
||||
*nelements = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long dbConstGetValue(struct link *plink, short dbrType, void *pbuffer,
|
||||
long *pnRequest)
|
||||
{
|
||||
if (pnRequest)
|
||||
*pnRequest = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long dbConstPutValue(struct link *plink, short dbrType,
|
||||
const void *pbuffer, long nRequest)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static lset dbConst_lset = {
|
||||
1, 0, /* Constant, not Volatile */
|
||||
NULL, NULL,
|
||||
dbConstLoadScalar,
|
||||
dbConstLoadLS,
|
||||
dbConstLoadArray,
|
||||
NULL,
|
||||
NULL, dbConstGetNelements,
|
||||
dbConstGetValue,
|
||||
NULL, NULL, NULL,
|
||||
NULL, NULL,
|
||||
NULL, NULL,
|
||||
dbConstPutValue, NULL,
|
||||
NULL, NULL
|
||||
};
|
||||
@@ -0,0 +1,34 @@
|
||||
/*************************************************************************\
|
||||
* Copyright (c) 2016 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.
|
||||
\*************************************************************************/
|
||||
/* dbConstLink.h
|
||||
*
|
||||
* Created on: April 3rd, 2016
|
||||
* Author: Andrew Johnson
|
||||
*/
|
||||
|
||||
#ifndef INC_dbConstLink_H
|
||||
#define INC_dbConstLink_H
|
||||
|
||||
#include "shareLib.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct link;
|
||||
|
||||
epicsShareFunc void dbConstInitLink(struct link *plink);
|
||||
epicsShareFunc void dbConstAddLink(struct link *plink);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* INC_dbConstLink_H */
|
||||
|
||||
@@ -0,0 +1,435 @@
|
||||
/*************************************************************************\
|
||||
* Copyright (c) 2009 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.
|
||||
\*************************************************************************/
|
||||
/*
|
||||
* Author Jeffrey O. Hill
|
||||
* johill@lanl.gov
|
||||
* 505 665 1831
|
||||
*/
|
||||
|
||||
#include <limits.h>
|
||||
|
||||
#include "epicsMutex.h"
|
||||
#include "tsFreeList.h"
|
||||
|
||||
#include "cadef.h" // this can be eliminated when the callbacks use the new interface
|
||||
#include "db_access.h" // should be eliminated here in the future
|
||||
#include "caerr.h" // should be eliminated here in the future
|
||||
#include "epicsEvent.h"
|
||||
#include "epicsThread.h"
|
||||
#include "errlog.h"
|
||||
|
||||
#define epicsExportSharedSymbols
|
||||
#include "db_access_routines.h"
|
||||
#include "dbCAC.h"
|
||||
#include "dbChannel.h"
|
||||
#include "dbChannelIO.h"
|
||||
#include "dbChannelNOOP.h"
|
||||
#include "dbPutNotifyBlocker.h"
|
||||
|
||||
class dbService : public cacService {
|
||||
public:
|
||||
~dbService () {}
|
||||
cacContext & contextCreate (
|
||||
epicsMutex & mutualExclusion,
|
||||
epicsMutex & callbackControl,
|
||||
cacContextNotify & );
|
||||
};
|
||||
|
||||
static dbService dbs;
|
||||
|
||||
cacContext & dbService::contextCreate (
|
||||
epicsMutex & mutualExclusion,
|
||||
epicsMutex & callbackControl,
|
||||
cacContextNotify & notify )
|
||||
{
|
||||
return * new dbContext ( callbackControl,
|
||||
mutualExclusion, notify );
|
||||
}
|
||||
|
||||
extern "C" int dbServiceIsolate;
|
||||
int dbServiceIsolate = 0;
|
||||
|
||||
extern "C" void dbServiceIOInit ()
|
||||
{
|
||||
static int init=0;
|
||||
if(!init) {
|
||||
caInstallDefaultService ( dbs );
|
||||
init=1;
|
||||
}
|
||||
}
|
||||
|
||||
dbBaseIO::dbBaseIO () {}
|
||||
|
||||
dbContext::dbContext ( epicsMutex & cbMutexIn,
|
||||
epicsMutex & mutexIn, cacContextNotify & notifyIn ) :
|
||||
readNotifyCache ( mutexIn ), ctx ( 0 ),
|
||||
stateNotifyCacheSize ( 0 ), mutex ( mutexIn ), cbMutex ( cbMutexIn ),
|
||||
notify ( notifyIn ), pNetContext ( 0 ), pStateNotifyCache ( 0 ),
|
||||
isolated(dbServiceIsolate)
|
||||
{
|
||||
}
|
||||
|
||||
dbContext::~dbContext ()
|
||||
{
|
||||
delete [] this->pStateNotifyCache;
|
||||
if ( this->ctx ) {
|
||||
db_close_events ( this->ctx );
|
||||
}
|
||||
}
|
||||
|
||||
cacChannel & dbContext::createChannel (
|
||||
epicsGuard < epicsMutex > & guard, const char * pName,
|
||||
cacChannelNotify & notifyIn, cacChannel::priLev priority )
|
||||
{
|
||||
guard.assertIdenticalMutex ( this->mutex );
|
||||
|
||||
dbChannel *dbch = dbChannel_create ( pName );
|
||||
if ( ! dbch ) {
|
||||
if ( isolated ) {
|
||||
return *new dbChannelNOOP(pName, notifyIn);
|
||||
|
||||
} else if ( ! this->pNetContext.get() ) {
|
||||
this->pNetContext.reset (
|
||||
& this->notify.createNetworkContext (
|
||||
this->mutex, this->cbMutex ) );
|
||||
}
|
||||
return this->pNetContext->createChannel (
|
||||
guard, pName, notifyIn, priority );
|
||||
}
|
||||
|
||||
if ( ! ca_preemtive_callback_is_enabled () ) {
|
||||
dbChannelDelete ( dbch );
|
||||
errlogPrintf (
|
||||
"dbContext: preemptive callback required for direct in\n"
|
||||
"memory interfacing of CA channels to the DB.\n" );
|
||||
throw cacChannel::unsupportedByService ();
|
||||
}
|
||||
|
||||
try {
|
||||
return * new ( this->dbChannelIOFreeList )
|
||||
dbChannelIO ( this->mutex, notifyIn, dbch, *this );
|
||||
}
|
||||
catch (...) {
|
||||
dbChannelDelete ( dbch );
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
void dbContext::destroyChannel (
|
||||
CallbackGuard & cbGuard,
|
||||
epicsGuard < epicsMutex > & guard,
|
||||
dbChannelIO & chan )
|
||||
{
|
||||
guard.assertIdenticalMutex ( this->mutex );
|
||||
|
||||
if ( chan.dbContextPrivateListOfIO::pBlocker ) {
|
||||
this->ioTable.remove ( *chan.dbContextPrivateListOfIO::pBlocker );
|
||||
chan.dbContextPrivateListOfIO::pBlocker->destructor ( cbGuard, guard );
|
||||
this->dbPutNotifyBlockerFreeList.release ( chan.dbContextPrivateListOfIO::pBlocker );
|
||||
chan.dbContextPrivateListOfIO::pBlocker = 0;
|
||||
}
|
||||
|
||||
chan.destructor ( cbGuard, guard );
|
||||
this->dbChannelIOFreeList.release ( & chan );
|
||||
}
|
||||
|
||||
void dbContext::callStateNotify ( struct dbChannel * dbch,
|
||||
unsigned type, unsigned long count,
|
||||
const struct db_field_log * pfl,
|
||||
cacStateNotify & notifyIn )
|
||||
{
|
||||
long realcount = (count==0)?dbChannelElements(dbch):count;
|
||||
unsigned long size = dbr_size_n ( type, realcount );
|
||||
|
||||
if ( type > INT_MAX ) {
|
||||
epicsGuard < epicsMutex > guard ( this->mutex );
|
||||
notifyIn.exception ( guard, ECA_BADTYPE,
|
||||
"type code out of range (high side)",
|
||||
type, count );
|
||||
return;
|
||||
}
|
||||
|
||||
if ( count > INT_MAX ) {
|
||||
epicsGuard < epicsMutex > guard ( this->mutex );
|
||||
notifyIn.exception ( guard, ECA_BADCOUNT,
|
||||
"element count out of range (high side)",
|
||||
type, count);
|
||||
return;
|
||||
}
|
||||
|
||||
// no need to lock this because state notify is
|
||||
// called from only one event queue consumer thread
|
||||
if ( this->stateNotifyCacheSize < size) {
|
||||
char * pTmp = new char [size];
|
||||
delete [] this->pStateNotifyCache;
|
||||
this->pStateNotifyCache = pTmp;
|
||||
this->stateNotifyCacheSize = size;
|
||||
}
|
||||
void *pvfl = (void *) pfl;
|
||||
int status;
|
||||
if(count==0) /* fetch actual number of elements (dynamic array) */
|
||||
status = dbChannel_get_count( dbch, static_cast <int> ( type ),
|
||||
this->pStateNotifyCache, &realcount, pvfl );
|
||||
else /* fetch requested number of elements, truncated or zero padded */
|
||||
status = dbChannel_get( dbch, static_cast <int> ( type ),
|
||||
this->pStateNotifyCache, realcount, pvfl );
|
||||
if ( status ) {
|
||||
epicsGuard < epicsMutex > guard ( this->mutex );
|
||||
notifyIn.exception ( guard, ECA_GETFAIL,
|
||||
"dbChannel_get() completed unsuccessfully", type, count );
|
||||
}
|
||||
else {
|
||||
epicsGuard < epicsMutex > guard ( this->mutex );
|
||||
notifyIn.current ( guard, type, realcount, this->pStateNotifyCache );
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" void cacAttachClientCtx ( void * pPrivate )
|
||||
{
|
||||
int status = ca_attach_context ( (ca_client_context *) pPrivate );
|
||||
assert ( status == ECA_NORMAL );
|
||||
}
|
||||
|
||||
void dbContext::subscribe (
|
||||
epicsGuard < epicsMutex > & guard,
|
||||
struct dbChannel * dbch, dbChannelIO & chan,
|
||||
unsigned type, unsigned long count, unsigned mask,
|
||||
cacStateNotify & notifyIn, cacChannel::ioid * pId )
|
||||
{
|
||||
guard.assertIdenticalMutex ( this->mutex );
|
||||
|
||||
/*
|
||||
* the database uses type "int" to store these parameters
|
||||
*/
|
||||
if ( type > INT_MAX ) {
|
||||
throw cacChannel::badType();
|
||||
}
|
||||
if ( count > INT_MAX ) {
|
||||
throw cacChannel::outOfBounds();
|
||||
}
|
||||
|
||||
if ( ! this->ctx ) {
|
||||
dbEventCtx tmpctx = 0;
|
||||
{
|
||||
epicsGuardRelease < epicsMutex > unguard ( guard );
|
||||
tmpctx = db_init_events ();
|
||||
if ( ! tmpctx ) {
|
||||
throw std::bad_alloc ();
|
||||
}
|
||||
|
||||
unsigned selfPriority = epicsThreadGetPrioritySelf ();
|
||||
unsigned above;
|
||||
epicsThreadBooleanStatus tbs =
|
||||
epicsThreadLowestPriorityLevelAbove ( selfPriority, &above );
|
||||
if ( tbs != epicsThreadBooleanStatusSuccess ) {
|
||||
above = selfPriority;
|
||||
}
|
||||
int status = db_start_events ( tmpctx, "CAC-event",
|
||||
cacAttachClientCtx, ca_current_context (), above );
|
||||
if ( status ) {
|
||||
db_close_events ( tmpctx );
|
||||
throw std::bad_alloc ();
|
||||
}
|
||||
}
|
||||
if ( this->ctx ) {
|
||||
// another thread tried to simultaneously setup
|
||||
// the event system
|
||||
db_close_events ( tmpctx );
|
||||
}
|
||||
else {
|
||||
this->ctx = tmpctx;
|
||||
}
|
||||
}
|
||||
|
||||
dbSubscriptionIO & subscr =
|
||||
* new ( this->dbSubscriptionIOFreeList )
|
||||
dbSubscriptionIO ( guard, this->mutex, *this, chan,
|
||||
dbch, notifyIn, type, count, mask, this->ctx );
|
||||
chan.dbContextPrivateListOfIO::eventq.add ( subscr );
|
||||
this->ioTable.idAssignAdd ( subscr );
|
||||
|
||||
if ( pId ) {
|
||||
*pId = subscr.getId ();
|
||||
}
|
||||
}
|
||||
|
||||
void dbContext::initiatePutNotify (
|
||||
epicsGuard < epicsMutex > & guard,
|
||||
dbChannelIO & chan, struct dbChannel * dbch,
|
||||
unsigned type, unsigned long count, const void * pValue,
|
||||
cacWriteNotify & notifyIn, cacChannel::ioid * pId )
|
||||
{
|
||||
guard.assertIdenticalMutex ( this->mutex );
|
||||
if ( ! chan.dbContextPrivateListOfIO::pBlocker ) {
|
||||
chan.dbContextPrivateListOfIO::pBlocker =
|
||||
new ( this->dbPutNotifyBlockerFreeList )
|
||||
dbPutNotifyBlocker ( this->mutex );
|
||||
this->ioTable.idAssignAdd ( *chan.dbContextPrivateListOfIO::pBlocker );
|
||||
}
|
||||
chan.dbContextPrivateListOfIO::pBlocker->initiatePutNotify (
|
||||
guard, notifyIn, dbch, type, count, pValue );
|
||||
if ( pId ) {
|
||||
*pId = chan.dbContextPrivateListOfIO::pBlocker->getId ();
|
||||
}
|
||||
}
|
||||
|
||||
void dbContext::destroyAllIO (
|
||||
CallbackGuard & cbGuard,
|
||||
epicsGuard < epicsMutex > & guard,
|
||||
dbChannelIO & chan )
|
||||
{
|
||||
guard.assertIdenticalMutex ( this->mutex );
|
||||
dbSubscriptionIO * pIO;
|
||||
tsDLList < dbSubscriptionIO > tmp;
|
||||
|
||||
while ( ( pIO = chan.dbContextPrivateListOfIO::eventq.get() ) ) {
|
||||
this->ioTable.remove ( *pIO );
|
||||
tmp.add ( *pIO );
|
||||
}
|
||||
if ( chan.dbContextPrivateListOfIO::pBlocker ) {
|
||||
this->ioTable.remove ( *chan.dbContextPrivateListOfIO::pBlocker );
|
||||
}
|
||||
|
||||
while ( ( pIO = tmp.get() ) ) {
|
||||
// This prevents a db event callback from coming
|
||||
// through after the notify IO is deleted
|
||||
pIO->unsubscribe ( cbGuard, guard );
|
||||
// If they call ioCancel() here it will be ignored
|
||||
// because the IO has been unregistered above.
|
||||
pIO->channelDeleteException ( cbGuard, guard );
|
||||
pIO->destructor ( cbGuard, guard );
|
||||
this->dbSubscriptionIOFreeList.release ( pIO );
|
||||
}
|
||||
|
||||
if ( chan.dbContextPrivateListOfIO::pBlocker ) {
|
||||
chan.dbContextPrivateListOfIO::pBlocker->destructor ( cbGuard, guard );
|
||||
this->dbPutNotifyBlockerFreeList.release ( chan.dbContextPrivateListOfIO::pBlocker );
|
||||
chan.dbContextPrivateListOfIO::pBlocker = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void dbContext::ioCancel (
|
||||
CallbackGuard & cbGuard, epicsGuard < epicsMutex > & guard,
|
||||
dbChannelIO & chan, const cacChannel::ioid &id )
|
||||
{
|
||||
guard.assertIdenticalMutex ( this->mutex );
|
||||
dbBaseIO * pIO = this->ioTable.remove ( id );
|
||||
if ( pIO ) {
|
||||
dbSubscriptionIO *pSIO = pIO->isSubscription ();
|
||||
if ( pSIO ) {
|
||||
chan.dbContextPrivateListOfIO::eventq.remove ( *pSIO );
|
||||
pSIO->unsubscribe ( cbGuard, guard );
|
||||
pSIO->channelDeleteException ( cbGuard, guard );
|
||||
pSIO->destructor ( cbGuard, guard );
|
||||
this->dbSubscriptionIOFreeList.release ( pSIO );
|
||||
}
|
||||
else if ( pIO == chan.dbContextPrivateListOfIO::pBlocker ) {
|
||||
chan.dbContextPrivateListOfIO::pBlocker->cancel ( cbGuard, guard );
|
||||
}
|
||||
else {
|
||||
errlogPrintf ( "dbContext::ioCancel() unrecognized IO was probably leaked or not canceled\n" );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void dbContext::ioShow (
|
||||
epicsGuard < epicsMutex > & guard, const cacChannel::ioid &id,
|
||||
unsigned level ) const
|
||||
{
|
||||
guard.assertIdenticalMutex ( this->mutex );
|
||||
const dbBaseIO * pIO = this->ioTable.lookup ( id );
|
||||
if ( pIO ) {
|
||||
pIO->show ( guard, level );
|
||||
}
|
||||
}
|
||||
|
||||
void dbContext::showAllIO ( const dbChannelIO & chan, unsigned level ) const
|
||||
{
|
||||
epicsGuard < epicsMutex > guard ( this->mutex );
|
||||
tsDLIterConst < dbSubscriptionIO > pItem =
|
||||
chan.dbContextPrivateListOfIO::eventq.firstIter ();
|
||||
while ( pItem.valid () ) {
|
||||
pItem->show ( guard, level );
|
||||
pItem++;
|
||||
}
|
||||
if ( chan.dbContextPrivateListOfIO::pBlocker ) {
|
||||
chan.dbContextPrivateListOfIO::pBlocker->show ( guard, level );
|
||||
}
|
||||
}
|
||||
|
||||
void dbContext::show ( unsigned level ) const
|
||||
{
|
||||
epicsGuard < epicsMutex > guard ( this->mutex );
|
||||
this->show ( guard, level );
|
||||
}
|
||||
|
||||
void dbContext::show (
|
||||
epicsGuard < epicsMutex > & guard, unsigned level ) const
|
||||
{
|
||||
guard.assertIdenticalMutex ( this->mutex );
|
||||
printf ( "dbContext at %p\n",
|
||||
static_cast <const void *> ( this ) );
|
||||
if ( level > 0u ) {
|
||||
printf ( "\tevent call back cache location %p, and its size %lu\n",
|
||||
static_cast <void *> ( this->pStateNotifyCache ), this->stateNotifyCacheSize );
|
||||
this->readNotifyCache.show ( guard, level - 1 );
|
||||
}
|
||||
if ( level > 1u ) {
|
||||
this->mutex.show ( level - 2u );
|
||||
}
|
||||
if ( this->pNetContext.get() ) {
|
||||
this->pNetContext.get()->show ( guard, level );
|
||||
}
|
||||
}
|
||||
|
||||
void dbContext::flush (
|
||||
epicsGuard < epicsMutex > & guard )
|
||||
{
|
||||
guard.assertIdenticalMutex ( this->mutex );
|
||||
if ( this->pNetContext.get() ) {
|
||||
this->pNetContext.get()->flush ( guard );
|
||||
}
|
||||
}
|
||||
|
||||
unsigned dbContext::circuitCount (
|
||||
epicsGuard < epicsMutex > & guard ) const
|
||||
{
|
||||
guard.assertIdenticalMutex ( this->mutex );
|
||||
if ( this->pNetContext.get() ) {
|
||||
return this->pNetContext.get()->circuitCount ( guard );
|
||||
}
|
||||
else {
|
||||
return 0u;
|
||||
}
|
||||
}
|
||||
|
||||
void dbContext::selfTest (
|
||||
epicsGuard < epicsMutex > & guard ) const
|
||||
{
|
||||
guard.assertIdenticalMutex ( this->mutex );
|
||||
this->ioTable.verify ();
|
||||
|
||||
if ( this->pNetContext.get() ) {
|
||||
this->pNetContext.get()->selfTest ( guard );
|
||||
}
|
||||
}
|
||||
|
||||
unsigned dbContext::beaconAnomaliesSinceProgramStart (
|
||||
epicsGuard < epicsMutex > & guard ) const
|
||||
{
|
||||
guard.assertIdenticalMutex ( this->mutex );
|
||||
if ( this->pNetContext.get() ) {
|
||||
return this->pNetContext.get()->beaconAnomaliesSinceProgramStart ( guard );
|
||||
}
|
||||
else {
|
||||
return 0u;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,180 @@
|
||||
/*************************************************************************\
|
||||
* 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.
|
||||
\*************************************************************************/
|
||||
|
||||
/*
|
||||
* Auther Jeff Hill
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "epicsMutex.h"
|
||||
#include "dbDefs.h"
|
||||
|
||||
#include "cadef.h" // this can be eliminated when the callbacks use the new interface
|
||||
#include "db_access.h" // should be eliminated here in the future
|
||||
|
||||
#define epicsExportSharedSymbols
|
||||
|
||||
#include "db_access_routines.h"
|
||||
#include "dbCAC.h"
|
||||
|
||||
#include "epicsAssert.h"
|
||||
|
||||
dbContextReadNotifyCache::dbContextReadNotifyCache ( epicsMutex & mutexIn ) :
|
||||
_mutex ( mutexIn )
|
||||
{
|
||||
}
|
||||
|
||||
class privateAutoDestroyPtr {
|
||||
public:
|
||||
privateAutoDestroyPtr (
|
||||
dbContextReadNotifyCacheAllocator & allocator, unsigned long size ) :
|
||||
_allocator ( allocator ), _p ( allocator.alloc ( size ) ) {}
|
||||
~privateAutoDestroyPtr () { _allocator.free ( _p ); }
|
||||
char * get () const { return _p; }
|
||||
private:
|
||||
dbContextReadNotifyCacheAllocator & _allocator;
|
||||
char * _p;
|
||||
privateAutoDestroyPtr ( const privateAutoDestroyPtr & );
|
||||
privateAutoDestroyPtr & operator = ( const privateAutoDestroyPtr & );
|
||||
};
|
||||
|
||||
// extra effort taken here to not hold the lock when calling the callback
|
||||
void dbContextReadNotifyCache::callReadNotify (
|
||||
epicsGuard < epicsMutex > & guard, struct dbChannel * dbch,
|
||||
unsigned type, unsigned long count, cacReadNotify & notify )
|
||||
{
|
||||
guard.assertIdenticalMutex ( _mutex );
|
||||
|
||||
if ( type > INT_MAX ) {
|
||||
notify.exception ( guard, ECA_BADTYPE,
|
||||
"type code out of range (high side)",
|
||||
type, count );
|
||||
return;
|
||||
}
|
||||
|
||||
const long maxcount = dbChannelElements(dbch);
|
||||
|
||||
if ( maxcount < 0 ) {
|
||||
notify.exception ( guard, ECA_BADCOUNT,
|
||||
"database has negetive element count",
|
||||
type, count);
|
||||
return;
|
||||
|
||||
} else if ( count > (unsigned long)maxcount ) {
|
||||
notify.exception ( guard, ECA_BADCOUNT,
|
||||
"element count out of range (high side)",
|
||||
type, count);
|
||||
return;
|
||||
}
|
||||
|
||||
long realcount = (count==0)?maxcount:count;
|
||||
unsigned long size = dbr_size_n ( type, realcount );
|
||||
|
||||
privateAutoDestroyPtr ptr ( _allocator, size );
|
||||
int status;
|
||||
{
|
||||
epicsGuardRelease < epicsMutex > unguard ( guard );
|
||||
if ( count==0 )
|
||||
status = dbChannel_get_count ( dbch, (int)type, ptr.get(), &realcount, 0);
|
||||
else
|
||||
status = dbChannel_get ( dbch, (int)type, ptr.get (), realcount, 0 );
|
||||
}
|
||||
if ( status ) {
|
||||
notify.exception ( guard, ECA_GETFAIL,
|
||||
"db_get_field() completed unsuccessfuly",
|
||||
type, count );
|
||||
}
|
||||
else {
|
||||
notify.completion (
|
||||
guard, type, realcount, ptr.get () );
|
||||
}
|
||||
}
|
||||
|
||||
void dbContextReadNotifyCache::show (
|
||||
epicsGuard < epicsMutex > & guard, unsigned level ) const
|
||||
{
|
||||
guard.assertIdenticalMutex ( _mutex );
|
||||
|
||||
printf ( "dbContextReadNotifyCache\n" );
|
||||
if ( level > 0 ) {
|
||||
this->_allocator.show ( level - 1 );
|
||||
}
|
||||
}
|
||||
|
||||
dbContextReadNotifyCacheAllocator::dbContextReadNotifyCacheAllocator () :
|
||||
_readNotifyCacheSize ( 0 ), _pReadNotifyCache ( 0 )
|
||||
{
|
||||
}
|
||||
|
||||
dbContextReadNotifyCacheAllocator::~dbContextReadNotifyCacheAllocator ()
|
||||
{
|
||||
this->reclaimAllCacheEntries ();
|
||||
}
|
||||
|
||||
void dbContextReadNotifyCacheAllocator::reclaimAllCacheEntries ()
|
||||
{
|
||||
while ( _pReadNotifyCache ) {
|
||||
cacheElem_t * pNext = _pReadNotifyCache->pNext;
|
||||
assert(_pReadNotifyCache->size == _readNotifyCacheSize);
|
||||
::free(_pReadNotifyCache);
|
||||
_pReadNotifyCache = pNext;
|
||||
}
|
||||
}
|
||||
|
||||
char * dbContextReadNotifyCacheAllocator::alloc ( unsigned long size )
|
||||
{
|
||||
if ( size > _readNotifyCacheSize ) {
|
||||
this->reclaimAllCacheEntries ();
|
||||
_readNotifyCacheSize = size;
|
||||
}
|
||||
|
||||
cacheElem_t * pAlloc = _pReadNotifyCache;
|
||||
if ( pAlloc ) {
|
||||
assert(pAlloc->size == _readNotifyCacheSize);
|
||||
_pReadNotifyCache = pAlloc->pNext;
|
||||
}
|
||||
else {
|
||||
pAlloc = (cacheElem_t*)calloc(1, sizeof(cacheElem_t)+_readNotifyCacheSize);
|
||||
if(!pAlloc) throw std::bad_alloc();
|
||||
pAlloc->size = _readNotifyCacheSize;
|
||||
}
|
||||
return pAlloc->buf;
|
||||
}
|
||||
|
||||
void dbContextReadNotifyCacheAllocator::free ( char * pFree )
|
||||
{
|
||||
cacheElem_t * pAlloc = (cacheElem_t*)(pFree - offsetof(cacheElem_t, buf));
|
||||
if (pAlloc->size == _readNotifyCacheSize) {
|
||||
pAlloc->pNext = _pReadNotifyCache;
|
||||
_pReadNotifyCache = pAlloc;
|
||||
} else {
|
||||
::free(pAlloc);
|
||||
}
|
||||
}
|
||||
|
||||
void dbContextReadNotifyCacheAllocator::show ( unsigned level ) const
|
||||
{
|
||||
printf ( "dbContextReadNotifyCacheAlocator\n" );
|
||||
if ( level > 0 ) {
|
||||
size_t count =0;
|
||||
cacheElem_t * pNext = _pReadNotifyCache;
|
||||
while ( pNext ) {
|
||||
assert(pNext->size == _readNotifyCacheSize);
|
||||
pNext = _pReadNotifyCache->pNext;
|
||||
count++;
|
||||
}
|
||||
printf ( "\tcount %lu and size %lu\n",
|
||||
static_cast < unsigned long > ( count ),
|
||||
_readNotifyCacheSize );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,34 @@
|
||||
/*************************************************************************\
|
||||
* Copyright (c) 2009 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.
|
||||
\*************************************************************************/
|
||||
/* dbConvert.h */
|
||||
|
||||
#ifndef INCdbConverth
|
||||
#define INCdbConverth
|
||||
|
||||
#include "dbFldTypes.h"
|
||||
#include "dbAddr.h"
|
||||
#include "shareLib.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef long (*GETCONVERTFUNC)(const DBADDR *paddr, void *pbuffer,
|
||||
long nRequest, long no_elements, long offset);
|
||||
typedef long (*PUTCONVERTFUNC)(DBADDR *paddr, const void *pbuffer,
|
||||
long nRequest, long no_elements, long offset);
|
||||
|
||||
epicsShareExtern GETCONVERTFUNC dbGetConvertRoutine[DBF_DEVICE+1][DBR_ENUM+1];
|
||||
epicsShareExtern PUTCONVERTFUNC dbPutConvertRoutine[DBR_ENUM+1][DBF_DEVICE+1];
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /*INCdbConverth*/
|
||||
@@ -0,0 +1,29 @@
|
||||
/*************************************************************************\
|
||||
* 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.
|
||||
\*************************************************************************/
|
||||
/* dbConvertFast.h */
|
||||
|
||||
#ifndef INCdbConvertFasth
|
||||
#define INCdbConvertFasth
|
||||
|
||||
#include "dbFldTypes.h"
|
||||
#include "shareLib.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
epicsShareExtern long (*dbFastGetConvertRoutine[DBF_DEVICE+1][DBR_ENUM+1])();
|
||||
epicsShareExtern long (*dbFastPutConvertRoutine[DBR_ENUM+1][DBF_DEVICE+1])();
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /*INCdbConvertFasth*/
|
||||
@@ -0,0 +1,252 @@
|
||||
/*************************************************************************\
|
||||
* 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.
|
||||
\*************************************************************************/
|
||||
/* dbConvertJSON.c */
|
||||
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "dbDefs.h"
|
||||
#include "errlog.h"
|
||||
#include "yajl_alloc.h"
|
||||
#include "yajl_parse.h"
|
||||
|
||||
#define epicsExportSharedSymbols
|
||||
#include "dbAccessDefs.h"
|
||||
#include "dbConvertFast.h"
|
||||
#include "dbConvertJSON.h"
|
||||
|
||||
typedef long (*FASTCONVERT)();
|
||||
|
||||
typedef struct parseContext {
|
||||
int depth;
|
||||
short dbrType;
|
||||
short dbrSize;
|
||||
char *pdest;
|
||||
int elems;
|
||||
} parseContext;
|
||||
|
||||
static int dbcj_null(void *ctx) {
|
||||
return 0; /* Illegal */
|
||||
}
|
||||
|
||||
static int dbcj_boolean(void *ctx, int val) {
|
||||
return 0; /* Illegal */
|
||||
}
|
||||
|
||||
static int dbcj_integer(void *ctx, long long num) {
|
||||
parseContext *parser = (parseContext *) ctx;
|
||||
epicsInt64 val64 = num;
|
||||
FASTCONVERT conv = dbFastPutConvertRoutine[DBF_INT64][parser->dbrType];
|
||||
|
||||
if (parser->elems > 0) {
|
||||
conv(&val64, parser->pdest, NULL);
|
||||
parser->pdest += parser->dbrSize;
|
||||
parser->elems--;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int dblsj_integer(void *ctx, long long num) {
|
||||
return 0; /* Illegal */
|
||||
}
|
||||
|
||||
static int dbcj_double(void *ctx, double num) {
|
||||
parseContext *parser = (parseContext *) ctx;
|
||||
FASTCONVERT conv = dbFastPutConvertRoutine[DBF_DOUBLE][parser->dbrType];
|
||||
|
||||
if (parser->elems > 0) {
|
||||
conv(&num, parser->pdest, NULL);
|
||||
parser->pdest += parser->dbrSize;
|
||||
parser->elems--;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int dblsj_double(void *ctx, double num) {
|
||||
return 0; /* Illegal */
|
||||
}
|
||||
|
||||
static int dbcj_string(void *ctx, const unsigned char *val, size_t len) {
|
||||
parseContext *parser = (parseContext *) ctx;
|
||||
char *pdest = parser->pdest;
|
||||
|
||||
/* Not attempting to handle char-array fields here, they need more
|
||||
* metadata about the field than we have available at the moment.
|
||||
*/
|
||||
if (parser->dbrType != DBF_STRING) {
|
||||
errlogPrintf("dbConvertJSON: String provided, numeric value(s) expected\n");
|
||||
return 0; /* Illegal */
|
||||
}
|
||||
|
||||
if (parser->elems > 0) {
|
||||
if (len > parser->dbrSize - 1)
|
||||
len = parser->dbrSize - 1;
|
||||
strncpy(pdest, (const char *) val, len);
|
||||
pdest[len] = 0;
|
||||
parser->pdest += parser->dbrSize;
|
||||
parser->elems--;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int dblsj_string(void *ctx, const unsigned char *val, size_t len) {
|
||||
parseContext *parser = (parseContext *) ctx;
|
||||
char *pdest = parser->pdest;
|
||||
|
||||
if (parser->dbrType != DBF_STRING) {
|
||||
errlogPrintf("dbConvertJSON: dblsj_string dbrType error\n");
|
||||
return 0; /* Illegal */
|
||||
}
|
||||
|
||||
if (parser->elems > 0) {
|
||||
if (len > parser->dbrSize - 1)
|
||||
len = parser->dbrSize - 1;
|
||||
strncpy(pdest, (const char *) val, len);
|
||||
pdest[len] = 0;
|
||||
parser->pdest = pdest + len;
|
||||
parser->elems = 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int dbcj_start_map(void *ctx) {
|
||||
errlogPrintf("dbConvertJSON: Map type not supported\n");
|
||||
return 0; /* Illegal */
|
||||
}
|
||||
|
||||
static int dbcj_map_key(void *ctx, const unsigned char *key, size_t len) {
|
||||
return 0; /* Illegal */
|
||||
}
|
||||
|
||||
static int dbcj_end_map(void *ctx) {
|
||||
return 0; /* Illegal */
|
||||
}
|
||||
|
||||
static int dbcj_start_array(void *ctx) {
|
||||
parseContext *parser = (parseContext *) ctx;
|
||||
|
||||
if (++parser->depth > 1)
|
||||
errlogPrintf("dbConvertJSON: Embedded arrays not supported\n");
|
||||
|
||||
return (parser->depth == 1);
|
||||
}
|
||||
|
||||
static int dbcj_end_array(void *ctx) {
|
||||
parseContext *parser = (parseContext *) ctx;
|
||||
|
||||
parser->depth--;
|
||||
return (parser->depth == 0);
|
||||
}
|
||||
|
||||
|
||||
static yajl_callbacks dbcj_callbacks = {
|
||||
dbcj_null, dbcj_boolean, dbcj_integer, dbcj_double, NULL, dbcj_string,
|
||||
dbcj_start_map, dbcj_map_key, dbcj_end_map,
|
||||
dbcj_start_array, dbcj_end_array
|
||||
};
|
||||
|
||||
long dbPutConvertJSON(const char *json, short dbrType,
|
||||
void *pdest, long *pnRequest)
|
||||
{
|
||||
parseContext context, *parser = &context;
|
||||
yajl_alloc_funcs dbcj_alloc;
|
||||
yajl_handle yh;
|
||||
yajl_status ys;
|
||||
size_t jlen = strlen(json);
|
||||
long status;
|
||||
|
||||
parser->depth = 0;
|
||||
parser->dbrType = dbrType;
|
||||
parser->dbrSize = dbValueSize(dbrType);
|
||||
parser->pdest = pdest;
|
||||
parser->elems = *pnRequest;
|
||||
|
||||
yajl_set_default_alloc_funcs(&dbcj_alloc);
|
||||
yh = yajl_alloc(&dbcj_callbacks, &dbcj_alloc, parser);
|
||||
if (!yh)
|
||||
return S_db_noMemory;
|
||||
|
||||
ys = yajl_parse(yh, (const unsigned char *) json, jlen);
|
||||
if (ys == yajl_status_ok)
|
||||
ys = yajl_complete_parse(yh);
|
||||
|
||||
switch (ys) {
|
||||
case yajl_status_ok:
|
||||
*pnRequest -= parser->elems;
|
||||
status = 0;
|
||||
break;
|
||||
|
||||
case yajl_status_error: {
|
||||
unsigned char *err = yajl_get_error(yh, 1,
|
||||
(const unsigned char *) json, jlen);
|
||||
fprintf(stderr, "dbConvertJSON: %s\n", err);
|
||||
yajl_free_error(yh, err);
|
||||
}
|
||||
/* fall through */
|
||||
default:
|
||||
status = S_db_badField;
|
||||
}
|
||||
|
||||
yajl_free(yh);
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
static yajl_callbacks dblsj_callbacks = {
|
||||
dbcj_null, dbcj_boolean, dblsj_integer, dblsj_double, NULL, dblsj_string,
|
||||
dbcj_start_map, dbcj_map_key, dbcj_end_map,
|
||||
dbcj_start_array, dbcj_end_array
|
||||
};
|
||||
|
||||
long dbLSConvertJSON(const char *json, char *pdest, epicsUInt32 size,
|
||||
epicsUInt32 *plen)
|
||||
{
|
||||
parseContext context, *parser = &context;
|
||||
yajl_alloc_funcs dbcj_alloc;
|
||||
yajl_handle yh;
|
||||
yajl_status ys;
|
||||
size_t jlen = strlen(json);
|
||||
long status;
|
||||
|
||||
if (!size) {
|
||||
*plen = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
parser->depth = 0;
|
||||
parser->dbrType = DBF_STRING;
|
||||
parser->dbrSize = size;
|
||||
parser->pdest = pdest;
|
||||
parser->elems = 1;
|
||||
|
||||
yajl_set_default_alloc_funcs(&dbcj_alloc);
|
||||
yh = yajl_alloc(&dblsj_callbacks, &dbcj_alloc, parser);
|
||||
if (!yh)
|
||||
return S_db_noMemory;
|
||||
|
||||
ys = yajl_parse(yh, (const unsigned char *) json, jlen);
|
||||
|
||||
switch (ys) {
|
||||
case yajl_status_ok:
|
||||
*plen = (char *) parser->pdest - pdest + 1;
|
||||
status = 0;
|
||||
break;
|
||||
|
||||
case yajl_status_error: {
|
||||
unsigned char *err = yajl_get_error(yh, 1,
|
||||
(const unsigned char *) json, jlen);
|
||||
fprintf(stderr, "dbLoadLS_JSON: %s\n", err);
|
||||
yajl_free_error(yh, err);
|
||||
}
|
||||
/* fall through */
|
||||
default:
|
||||
status = S_db_badField;
|
||||
}
|
||||
|
||||
yajl_free(yh);
|
||||
return status;
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
/*************************************************************************\
|
||||
* 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.
|
||||
\*************************************************************************/
|
||||
/* dbConvertJSON.h */
|
||||
|
||||
#ifndef INC_dbConvertJSON_H
|
||||
#define INC_dbConvertJSON_H
|
||||
|
||||
#include <shareLib.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* This name should probably be changed to inclue "array" */
|
||||
epicsShareFunc long dbPutConvertJSON(const char *json, short dbrType,
|
||||
void *pdest, long *psize);
|
||||
epicsShareFunc long dbLSConvertJSON(const char *json, char *pdest,
|
||||
epicsUInt32 size, epicsUInt32 *plen);
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* INC_dbConvertJSON_H */
|
||||
|
||||
@@ -0,0 +1,358 @@
|
||||
/*************************************************************************\
|
||||
* Copyright (c) 2010 UChicago Argonne LLC, as Operator of Argonne
|
||||
* National Laboratory.
|
||||
* Copyright (c) 2002 The Regents of the University of California, as
|
||||
* Operator of Los Alamos National Laboratory.
|
||||
* EPICS BASE is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
\*************************************************************************/
|
||||
/* dbDbLink.c
|
||||
*
|
||||
* Original Authors: Bob Dalesio, Marty Kraimer
|
||||
* Current Author: Andrew Johnson
|
||||
*/
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "alarm.h"
|
||||
#include "cantProceed.h"
|
||||
#include "cvtFast.h"
|
||||
#include "dbDefs.h"
|
||||
#include "ellLib.h"
|
||||
#include "epicsTime.h"
|
||||
#include "errlog.h"
|
||||
|
||||
#include "caeventmask.h"
|
||||
|
||||
#define epicsExportSharedSymbols
|
||||
#include "dbAccessDefs.h"
|
||||
#include "dbAddr.h"
|
||||
#include "dbBase.h"
|
||||
#include "dbBkpt.h"
|
||||
#include "dbCommon.h"
|
||||
#include "dbConvertFast.h"
|
||||
#include "dbConvert.h"
|
||||
#include "db_field_log.h"
|
||||
#include "dbFldTypes.h"
|
||||
#include "dbLink.h"
|
||||
#include "dbLockPvt.h"
|
||||
#include "dbNotify.h"
|
||||
#include "dbScan.h"
|
||||
#include "dbStaticLib.h"
|
||||
#include "devSup.h"
|
||||
#include "link.h"
|
||||
#include "recGbl.h"
|
||||
#include "recSup.h"
|
||||
#include "special.h"
|
||||
|
||||
/***************************** Database Links *****************************/
|
||||
|
||||
/* Forward definition */
|
||||
static lset dbDb_lset;
|
||||
|
||||
long dbDbInitLink(struct link *plink, short dbfType)
|
||||
{
|
||||
DBADDR dbaddr;
|
||||
long status;
|
||||
DBADDR *pdbAddr;
|
||||
|
||||
status = dbNameToAddr(plink->value.pv_link.pvname, &dbaddr);
|
||||
if (status)
|
||||
return status;
|
||||
|
||||
plink->lset = &dbDb_lset;
|
||||
plink->type = DB_LINK;
|
||||
pdbAddr = dbCalloc(1, sizeof(struct dbAddr));
|
||||
*pdbAddr = dbaddr; /* structure copy */
|
||||
plink->value.pv_link.pvt = pdbAddr;
|
||||
ellAdd(&dbaddr.precord->bklnk, &plink->value.pv_link.backlinknode);
|
||||
/* merging into the same lockset is deferred to the caller.
|
||||
* cf. initPVLinks()
|
||||
*/
|
||||
dbLockSetMerge(NULL, plink->precord, dbaddr.precord);
|
||||
assert(plink->precord->lset->plockSet == dbaddr.precord->lset->plockSet);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void dbDbAddLink(struct dbLocker *locker, struct link *plink, short dbfType,
|
||||
DBADDR *ptarget)
|
||||
{
|
||||
plink->lset = &dbDb_lset;
|
||||
plink->type = DB_LINK;
|
||||
plink->value.pv_link.pvt = ptarget;
|
||||
ellAdd(&ptarget->precord->bklnk, &plink->value.pv_link.backlinknode);
|
||||
|
||||
/* target record is already locked in dbPutFieldLink() */
|
||||
dbLockSetMerge(locker, plink->precord, ptarget->precord);
|
||||
}
|
||||
|
||||
static void dbDbRemoveLink(struct dbLocker *locker, struct link *plink)
|
||||
{
|
||||
DBADDR *pdbAddr = (DBADDR *) plink->value.pv_link.pvt;
|
||||
|
||||
plink->type = PV_LINK;
|
||||
|
||||
/* locker is NULL when an isolated IOC is closing its links */
|
||||
if (locker) {
|
||||
plink->value.pv_link.pvt = 0;
|
||||
plink->value.pv_link.getCvt = 0;
|
||||
plink->value.pv_link.pvlMask = 0;
|
||||
plink->value.pv_link.lastGetdbrType = 0;
|
||||
ellDelete(&pdbAddr->precord->bklnk, &plink->value.pv_link.backlinknode);
|
||||
dbLockSetSplit(locker, plink->precord, pdbAddr->precord);
|
||||
}
|
||||
free(pdbAddr);
|
||||
}
|
||||
|
||||
static int dbDbIsConnected(const struct link *plink)
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static int dbDbGetDBFtype(const struct link *plink)
|
||||
{
|
||||
DBADDR *paddr = (DBADDR *) plink->value.pv_link.pvt;
|
||||
|
||||
return paddr->field_type;
|
||||
}
|
||||
|
||||
static long dbDbGetElements(const struct link *plink, long *nelements)
|
||||
{
|
||||
DBADDR *paddr = (DBADDR *) plink->value.pv_link.pvt;
|
||||
|
||||
*nelements = paddr->no_elements;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long dbDbGetValue(struct link *plink, short dbrType, void *pbuffer,
|
||||
long *pnRequest)
|
||||
{
|
||||
struct pv_link *ppv_link = &plink->value.pv_link;
|
||||
DBADDR *paddr = ppv_link->pvt;
|
||||
dbCommon *precord = plink->precord;
|
||||
long status;
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
if (ppv_link->getCvt && ppv_link->lastGetdbrType == dbrType) {
|
||||
status = ppv_link->getCvt(paddr->pfield, pbuffer, paddr);
|
||||
} else {
|
||||
unsigned short dbfType = paddr->field_type;
|
||||
|
||||
if (dbrType < 0 || dbrType > DBR_ENUM || dbfType > DBF_DEVICE)
|
||||
return S_db_badDbrtype;
|
||||
|
||||
if (paddr->no_elements == 1 && (!pnRequest || *pnRequest == 1)
|
||||
&& paddr->special != SPC_DBADDR
|
||||
&& paddr->special != SPC_ATTRIBUTE) {
|
||||
ppv_link->getCvt = dbFastGetConvertRoutine[dbfType][dbrType];
|
||||
status = ppv_link->getCvt(paddr->pfield, pbuffer, paddr);
|
||||
} else {
|
||||
ppv_link->getCvt = NULL;
|
||||
status = dbGet(paddr, dbrType, pbuffer, NULL, pnRequest, NULL);
|
||||
}
|
||||
ppv_link->lastGetdbrType = dbrType;
|
||||
}
|
||||
|
||||
if (!status && precord != paddr->precord)
|
||||
recGblInheritSevr(plink->value.pv_link.pvlMask & pvlOptMsMode,
|
||||
plink->precord, paddr->precord->stat, paddr->precord->sevr);
|
||||
return status;
|
||||
}
|
||||
|
||||
static long dbDbGetControlLimits(const struct link *plink, double *low,
|
||||
double *high)
|
||||
{
|
||||
DBADDR *paddr = (DBADDR *) plink->value.pv_link.pvt;
|
||||
struct buffer {
|
||||
DBRctrlDouble
|
||||
double value;
|
||||
} buffer;
|
||||
long options = DBR_CTRL_DOUBLE;
|
||||
long number_elements = 0;
|
||||
long status = dbGet(paddr, DBR_DOUBLE, &buffer, &options, &number_elements,
|
||||
NULL);
|
||||
|
||||
if (status)
|
||||
return status;
|
||||
|
||||
*low = buffer.lower_ctrl_limit;
|
||||
*high = buffer.upper_ctrl_limit;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long dbDbGetGraphicLimits(const struct link *plink, double *low,
|
||||
double *high)
|
||||
{
|
||||
DBADDR *paddr = (DBADDR *) plink->value.pv_link.pvt;
|
||||
struct buffer {
|
||||
DBRgrDouble
|
||||
double value;
|
||||
} buffer;
|
||||
long options = DBR_GR_DOUBLE;
|
||||
long number_elements = 0;
|
||||
long status = dbGet(paddr, DBR_DOUBLE, &buffer, &options, &number_elements,
|
||||
NULL);
|
||||
|
||||
if (status)
|
||||
return status;
|
||||
|
||||
*low = buffer.lower_disp_limit;
|
||||
*high = buffer.upper_disp_limit;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long dbDbGetAlarmLimits(const struct link *plink, double *lolo,
|
||||
double *low, double *high, double *hihi)
|
||||
{
|
||||
DBADDR *paddr = (DBADDR *) plink->value.pv_link.pvt;
|
||||
struct buffer {
|
||||
DBRalDouble
|
||||
double value;
|
||||
} buffer;
|
||||
long options = DBR_AL_DOUBLE;
|
||||
long number_elements = 0;
|
||||
long status = dbGet(paddr, DBR_DOUBLE, &buffer, &options, &number_elements,
|
||||
0);
|
||||
|
||||
if (status)
|
||||
return status;
|
||||
|
||||
*lolo = buffer.lower_alarm_limit;
|
||||
*low = buffer.lower_warning_limit;
|
||||
*high = buffer.upper_warning_limit;
|
||||
*hihi = buffer.upper_alarm_limit;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long dbDbGetPrecision(const struct link *plink, short *precision)
|
||||
{
|
||||
DBADDR *paddr = (DBADDR *) plink->value.pv_link.pvt;
|
||||
struct buffer {
|
||||
DBRprecision
|
||||
double value;
|
||||
} buffer;
|
||||
long options = DBR_PRECISION;
|
||||
long number_elements = 0;
|
||||
long status = dbGet(paddr, DBR_DOUBLE, &buffer, &options, &number_elements,
|
||||
0);
|
||||
|
||||
if (status)
|
||||
return status;
|
||||
|
||||
*precision = (short) buffer.precision.dp;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long dbDbGetUnits(const struct link *plink, char *units, int unitsSize)
|
||||
{
|
||||
DBADDR *paddr = (DBADDR *) plink->value.pv_link.pvt;
|
||||
struct buffer {
|
||||
DBRunits
|
||||
double value;
|
||||
} buffer;
|
||||
long options = DBR_UNITS;
|
||||
long number_elements = 0;
|
||||
long status = dbGet(paddr, DBR_DOUBLE, &buffer, &options, &number_elements,
|
||||
0);
|
||||
|
||||
if (status)
|
||||
return status;
|
||||
|
||||
strncpy(units, buffer.units, unitsSize);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long dbDbGetAlarm(const struct link *plink, epicsEnum16 *status,
|
||||
epicsEnum16 *severity)
|
||||
{
|
||||
DBADDR *paddr = (DBADDR *) plink->value.pv_link.pvt;
|
||||
|
||||
if (status)
|
||||
*status = paddr->precord->stat;
|
||||
if (severity)
|
||||
*severity = paddr->precord->sevr;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long dbDbGetTimeStamp(const struct link *plink, epicsTimeStamp *pstamp)
|
||||
{
|
||||
DBADDR *paddr = (DBADDR *) plink->value.pv_link.pvt;
|
||||
|
||||
*pstamp = paddr->precord->time;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long dbDbPutValue(struct link *plink, short dbrType,
|
||||
const void *pbuffer, long nRequest)
|
||||
{
|
||||
struct pv_link *ppv_link = &plink->value.pv_link;
|
||||
struct dbCommon *psrce = plink->precord;
|
||||
DBADDR *paddr = (DBADDR *) ppv_link->pvt;
|
||||
dbCommon *pdest = paddr->precord;
|
||||
long status = dbPut(paddr, dbrType, pbuffer, nRequest);
|
||||
|
||||
recGblInheritSevr(ppv_link->pvlMask & pvlOptMsMode, pdest, psrce->nsta,
|
||||
psrce->nsev);
|
||||
if (status)
|
||||
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;
|
||||
}
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
static void dbDbScanFwdLink(struct link *plink)
|
||||
{
|
||||
dbCommon *precord = plink->precord;
|
||||
dbAddr *paddr = (dbAddr *) plink->value.pv_link.pvt;
|
||||
|
||||
dbScanPassive(precord, paddr->precord);
|
||||
}
|
||||
|
||||
static long doLocked(struct link *plink, dbLinkUserCallback rtn, void *priv)
|
||||
{
|
||||
return rtn(plink, priv);
|
||||
}
|
||||
|
||||
static lset dbDb_lset = {
|
||||
0, 0, /* not Constant, not Volatile */
|
||||
NULL, dbDbRemoveLink,
|
||||
NULL, NULL, NULL,
|
||||
dbDbIsConnected,
|
||||
dbDbGetDBFtype, dbDbGetElements,
|
||||
dbDbGetValue,
|
||||
dbDbGetControlLimits, dbDbGetGraphicLimits, dbDbGetAlarmLimits,
|
||||
dbDbGetPrecision, dbDbGetUnits,
|
||||
dbDbGetAlarm, dbDbGetTimeStamp,
|
||||
dbDbPutValue, NULL,
|
||||
dbDbScanFwdLink, doLocked
|
||||
};
|
||||
@@ -0,0 +1,35 @@
|
||||
/*************************************************************************\
|
||||
* Copyright (c) 2016 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.
|
||||
\*************************************************************************/
|
||||
/* dbDbLink.h
|
||||
*
|
||||
* Created on: April 3rd, 2016
|
||||
* Author: Andrew Johnson
|
||||
*/
|
||||
|
||||
#ifndef INC_dbDbLink_H
|
||||
#define INC_dbDbLink_H
|
||||
|
||||
#include "shareLib.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct link;
|
||||
struct dbLocker;
|
||||
|
||||
epicsShareFunc long dbDbInitLink(struct link *plink, short dbfType);
|
||||
epicsShareFunc void dbDbAddLink(struct dbLocker *locker, struct link *plink,
|
||||
short dbfType, DBADDR *ptarget);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* INC_dbDbLink_H */
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,94 @@
|
||||
/*************************************************************************\
|
||||
* Copyright (c) 2010 Brookhaven National Laboratory.
|
||||
* Copyright (c) 2010 Helmholtz-Zentrum Berlin
|
||||
* fuer Materialien und Energie GmbH.
|
||||
* Copyright (c) 2002 The University of Chicago, as Operator of Argonne
|
||||
* National Laboratory.
|
||||
* Copyright (c) 2002 The Regents of the University of California, as
|
||||
* Operator of Los Alamos National Laboratory.
|
||||
* EPICS BASE is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
\*************************************************************************/
|
||||
|
||||
/*
|
||||
* Author: Jeffrey O. Hill <johill@lanl.gov>
|
||||
*
|
||||
* Ralph Lange <Ralph.Lange@bessy.de>
|
||||
*/
|
||||
|
||||
#ifndef INCLdbEventh
|
||||
#define INCLdbEventh
|
||||
|
||||
#ifdef epicsExportSharedSymbols
|
||||
# undef epicsExportSharedSymbols
|
||||
# define INCLdbEventhExporting
|
||||
#endif
|
||||
|
||||
#include "epicsThread.h"
|
||||
|
||||
#ifdef INCLdbEventhExporting
|
||||
# define epicsExportSharedSymbols
|
||||
#endif
|
||||
|
||||
#include "shareLib.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct dbChannel;
|
||||
struct db_field_log;
|
||||
struct evSubscrip;
|
||||
|
||||
epicsShareFunc int db_event_list (
|
||||
const char *name, unsigned level);
|
||||
epicsShareFunc int dbel (
|
||||
const char *name, unsigned level);
|
||||
epicsShareFunc int db_post_events (
|
||||
void *pRecord, void *pField, unsigned caEventMask );
|
||||
|
||||
typedef void * dbEventCtx;
|
||||
|
||||
typedef void EXTRALABORFUNC (void *extralabor_arg);
|
||||
epicsShareFunc dbEventCtx db_init_events (void);
|
||||
epicsShareFunc int db_start_events (
|
||||
dbEventCtx ctx, const char *taskname, void (*init_func)(void *),
|
||||
void *init_func_arg, unsigned osiPriority );
|
||||
epicsShareFunc void db_close_events (dbEventCtx ctx);
|
||||
epicsShareFunc void db_event_flow_ctrl_mode_on (dbEventCtx ctx);
|
||||
epicsShareFunc void db_event_flow_ctrl_mode_off (dbEventCtx ctx);
|
||||
epicsShareFunc int db_add_extra_labor_event (
|
||||
dbEventCtx ctx, EXTRALABORFUNC *func, void *arg);
|
||||
epicsShareFunc void db_flush_extra_labor_event (dbEventCtx);
|
||||
epicsShareFunc int db_post_extra_labor (dbEventCtx ctx);
|
||||
epicsShareFunc void db_event_change_priority ( dbEventCtx ctx, unsigned epicsPriority );
|
||||
|
||||
#ifdef EPICS_PRIVATE_API
|
||||
epicsShareFunc void db_cleanup_events(void);
|
||||
#endif
|
||||
|
||||
typedef void EVENTFUNC (void *user_arg, struct dbChannel *chan,
|
||||
int eventsRemaining, struct db_field_log *pfl);
|
||||
|
||||
typedef void * dbEventSubscription;
|
||||
epicsShareFunc dbEventSubscription db_add_event (
|
||||
dbEventCtx ctx, struct dbChannel *chan,
|
||||
EVENTFUNC *user_sub, void *user_arg, unsigned select);
|
||||
epicsShareFunc void db_cancel_event (dbEventSubscription es);
|
||||
epicsShareFunc void db_post_single_event (dbEventSubscription es);
|
||||
epicsShareFunc void db_event_enable (dbEventSubscription es);
|
||||
epicsShareFunc void db_event_disable (dbEventSubscription es);
|
||||
|
||||
epicsShareFunc struct db_field_log* db_create_event_log (struct evSubscrip *pevent);
|
||||
epicsShareFunc struct db_field_log* db_create_read_log (struct dbChannel *chan);
|
||||
epicsShareFunc void db_delete_field_log (struct db_field_log *pfl);
|
||||
|
||||
#define DB_EVENT_OK 0
|
||||
#define DB_EVENT_ERROR (-1)
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /*INCLdbEventh*/
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
/*************************************************************************\
|
||||
* Copyright (c) 2010 Brookhaven National Laboratory.
|
||||
* Copyright (c) 2010 Helmholtz-Zentrum Berlin
|
||||
* fuer Materialien und Energie GmbH.
|
||||
* Copyright (c) 2002 The University of Chicago, as Operator of Argonne
|
||||
* National Laboratory.
|
||||
* Copyright (c) 2002 The Regents of the University of California, as
|
||||
* Operator of Los Alamos National Laboratory.
|
||||
* EPICS BASE is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
\*************************************************************************/
|
||||
|
||||
/*
|
||||
* Author: Ralph Lange <Ralph.Lange@bessy.de>
|
||||
*
|
||||
* based on dbConvert.c
|
||||
* written by: Bob Dalesio, Marty Kraimer
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "epicsTypes.h"
|
||||
|
||||
#define epicsExportSharedSymbols
|
||||
#include "dbAddr.h"
|
||||
#include "dbExtractArray.h"
|
||||
|
||||
void dbExtractArrayFromRec(const dbAddr *paddr, void *pto,
|
||||
long nRequest, long no_elements, long offset, long increment)
|
||||
{
|
||||
char *pdst = (char *) pto;
|
||||
char *psrc = (char *) paddr->pfield;
|
||||
long nUpperPart;
|
||||
int i;
|
||||
short srcSize = paddr->field_size;
|
||||
short dstSize = srcSize;
|
||||
char isString = (paddr->field_type == DBF_STRING);
|
||||
|
||||
if (nRequest > no_elements) nRequest = no_elements;
|
||||
if (isString && srcSize > MAX_STRING_SIZE) dstSize = MAX_STRING_SIZE;
|
||||
|
||||
if (increment == 1 && dstSize == srcSize) {
|
||||
nUpperPart = nRequest < no_elements - offset ? nRequest : no_elements - offset;
|
||||
memcpy(pdst, &psrc[offset * srcSize], dstSize * nUpperPart);
|
||||
if (nRequest > nUpperPart)
|
||||
memcpy(&pdst[dstSize * nUpperPart], psrc, dstSize * (nRequest - nUpperPart));
|
||||
if (isString)
|
||||
for (i = 1; i <= nRequest; i++)
|
||||
pdst[dstSize*i-1] = '\0';
|
||||
} else {
|
||||
for (; nRequest > 0; nRequest--, pdst += dstSize, offset += increment) {
|
||||
offset %= no_elements;
|
||||
memcpy(pdst, &psrc[offset*srcSize], dstSize);
|
||||
if (isString) pdst[dstSize-1] = '\0';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void dbExtractArrayFromBuf(const void *pfrom, void *pto,
|
||||
short field_size, short field_type,
|
||||
long nRequest, long no_elements, long offset, long increment)
|
||||
{
|
||||
char *pdst = (char *) pto;
|
||||
char *psrc = (char *) pfrom;
|
||||
int i;
|
||||
short srcSize = field_size;
|
||||
short dstSize = srcSize;
|
||||
char isString = (field_type == DBF_STRING);
|
||||
|
||||
if (nRequest > no_elements) nRequest = no_elements;
|
||||
if (offset > no_elements - 1) offset = no_elements - 1;
|
||||
if (isString && dstSize >= MAX_STRING_SIZE) dstSize = MAX_STRING_SIZE - 1;
|
||||
|
||||
if (increment == 1) {
|
||||
memcpy(pdst, &psrc[offset * srcSize], dstSize * nRequest);
|
||||
if (isString)
|
||||
for (i = 1; i <= nRequest; i++)
|
||||
pdst[dstSize*i] = '\0';
|
||||
} else {
|
||||
for (; nRequest > 0; nRequest--, pdst += srcSize, offset += increment) {
|
||||
memcpy(pdst, &psrc[offset*srcSize], dstSize);
|
||||
if (isString) pdst[dstSize] = '\0';
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
/*************************************************************************\
|
||||
* Copyright (c) 2010 Brookhaven National Laboratory.
|
||||
* Copyright (c) 2010 Helmholtz-Zentrum Berlin
|
||||
* fuer Materialien und Energie GmbH.
|
||||
* Copyright (c) 2009 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.
|
||||
\*************************************************************************/
|
||||
|
||||
#ifndef INC_dbExtractArray_H
|
||||
#define INC_dbExtractArray_H
|
||||
|
||||
#include "dbFldTypes.h"
|
||||
#include "dbAddr.h"
|
||||
#include "shareLib.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
epicsShareFunc void dbExtractArrayFromRec(const DBADDR *paddr, void *pto,
|
||||
long nRequest, long no_elements, long offset, long increment);
|
||||
epicsShareFunc void dbExtractArrayFromBuf(const void *pfrom, void *pto,
|
||||
short field_size, short field_type,
|
||||
long nRequest, long no_elements, long offset, long increment);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // INC_dbExtractArray_H
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,453 @@
|
||||
/*************************************************************************\
|
||||
* Copyright (c) 2007 UChicago Argonne LLC, as Operator of Argonne
|
||||
* National Laboratory.
|
||||
* Copyright (c) 2002 The Regents of the University of California, as
|
||||
* Operator of Los Alamos National Laboratory.
|
||||
* EPICS BASE is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
\*************************************************************************/
|
||||
|
||||
#include "iocsh.h"
|
||||
|
||||
#define epicsExportSharedSymbols
|
||||
#include "callback.h"
|
||||
#include "dbAccess.h"
|
||||
#include "dbBkpt.h"
|
||||
#include "dbCaTest.h"
|
||||
#include "dbEvent.h"
|
||||
#include "dbIocRegister.h"
|
||||
#include "dbJLink.h"
|
||||
#include "dbLock.h"
|
||||
#include "dbNotify.h"
|
||||
#include "dbScan.h"
|
||||
#include "dbServer.h"
|
||||
#include "dbState.h"
|
||||
#include "db_test.h"
|
||||
#include "dbTest.h"
|
||||
|
||||
epicsShareExtern int callbackParallelThreadsDefault;
|
||||
|
||||
/* dbLoadDatabase */
|
||||
static const iocshArg dbLoadDatabaseArg0 = { "file name",iocshArgString};
|
||||
static const iocshArg dbLoadDatabaseArg1 = { "path",iocshArgString};
|
||||
static const iocshArg dbLoadDatabaseArg2 = { "substitutions",iocshArgString};
|
||||
static const iocshArg * const dbLoadDatabaseArgs[3] =
|
||||
{
|
||||
&dbLoadDatabaseArg0,&dbLoadDatabaseArg1,&dbLoadDatabaseArg2
|
||||
};
|
||||
static const iocshFuncDef dbLoadDatabaseFuncDef =
|
||||
{"dbLoadDatabase",3,dbLoadDatabaseArgs};
|
||||
static void dbLoadDatabaseCallFunc(const iocshArgBuf *args)
|
||||
{
|
||||
dbLoadDatabase(args[0].sval,args[1].sval,args[2].sval);
|
||||
}
|
||||
|
||||
/* dbLoadRecords */
|
||||
static const iocshArg dbLoadRecordsArg0 = { "file name",iocshArgString};
|
||||
static const iocshArg dbLoadRecordsArg1 = { "substitutions",iocshArgString};
|
||||
static const iocshArg * const dbLoadRecordsArgs[2] = {&dbLoadRecordsArg0,&dbLoadRecordsArg1};
|
||||
static const iocshFuncDef dbLoadRecordsFuncDef = {"dbLoadRecords",2,dbLoadRecordsArgs};
|
||||
static void dbLoadRecordsCallFunc(const iocshArgBuf *args)
|
||||
{
|
||||
dbLoadRecords(args[0].sval,args[1].sval);
|
||||
}
|
||||
|
||||
/* dbb */
|
||||
static const iocshArg dbbArg0 = { "record name",iocshArgString};
|
||||
static const iocshArg * const dbbArgs[1] = {&dbbArg0};
|
||||
static const iocshFuncDef dbbFuncDef = {"dbb",1,dbbArgs};
|
||||
static void dbbCallFunc(const iocshArgBuf *args) { dbb(args[0].sval);}
|
||||
|
||||
/* dbd */
|
||||
static const iocshArg dbdArg0 = { "record name",iocshArgString};
|
||||
static const iocshArg * const dbdArgs[1] = {&dbdArg0};
|
||||
static const iocshFuncDef dbdFuncDef = {"dbd",1,dbdArgs};
|
||||
static void dbdCallFunc(const iocshArgBuf *args) { dbd(args[0].sval);}
|
||||
|
||||
/* dbc */
|
||||
static const iocshArg dbcArg0 = { "record name",iocshArgString};
|
||||
static const iocshArg * const dbcArgs[1] = {&dbcArg0};
|
||||
static const iocshFuncDef dbcFuncDef = {"dbc",1,dbcArgs};
|
||||
static void dbcCallFunc(const iocshArgBuf *args) { dbc(args[0].sval);}
|
||||
|
||||
/* dbs */
|
||||
static const iocshArg dbsArg0 = { "record name",iocshArgString};
|
||||
static const iocshArg * const dbsArgs[1] = {&dbsArg0};
|
||||
static const iocshFuncDef dbsFuncDef = {"dbs",1,dbsArgs};
|
||||
static void dbsCallFunc(const iocshArgBuf *args) { dbs(args[0].sval);}
|
||||
|
||||
/* dbstat */
|
||||
static const iocshFuncDef dbstatFuncDef = {"dbstat",0};
|
||||
static void dbstatCallFunc(const iocshArgBuf *args) { dbstat();}
|
||||
|
||||
/* dbp */
|
||||
static const iocshArg dbpArg0 = { "record name",iocshArgString};
|
||||
static const iocshArg dbpArg1 = { "interest level",iocshArgInt};
|
||||
static const iocshArg * const dbpArgs[2] = {&dbpArg0,&dbpArg1};
|
||||
static const iocshFuncDef dbpFuncDef = {"dbp",2,dbpArgs};
|
||||
static void dbpCallFunc(const iocshArgBuf *args)
|
||||
{ dbp(args[0].sval,args[1].ival);}
|
||||
|
||||
/* dbap */
|
||||
static const iocshArg dbapArg0 = { "record name",iocshArgString};
|
||||
static const iocshArg * const dbapArgs[1] = {&dbapArg0};
|
||||
static const iocshFuncDef dbapFuncDef = {"dbap",1,dbapArgs};
|
||||
static void dbapCallFunc(const iocshArgBuf *args) { dbap(args[0].sval);}
|
||||
|
||||
/* dbsr */
|
||||
static const iocshArg dbsrArg0 = { "interest level",iocshArgInt};
|
||||
static const iocshArg * const dbsrArgs[1] = {&dbsrArg0};
|
||||
static const iocshFuncDef dbsrFuncDef = {"dbsr",1,dbsrArgs};
|
||||
static void dbsrCallFunc(const iocshArgBuf *args) { dbsr(args[0].ival);}
|
||||
|
||||
/* dbcar */
|
||||
static const iocshArg dbcarArg0 = { "record name",iocshArgString};
|
||||
static const iocshArg dbcarArg1 = { "level",iocshArgInt};
|
||||
static const iocshArg * const dbcarArgs[2] = {&dbcarArg0,&dbcarArg1};
|
||||
static const iocshFuncDef dbcarFuncDef = {"dbcar",2,dbcarArgs};
|
||||
static void dbcarCallFunc(const iocshArgBuf *args)
|
||||
{
|
||||
dbcar(args[0].sval,args[1].ival);
|
||||
}
|
||||
|
||||
/* dbjlr */
|
||||
static const iocshArg dbjlrArg0 = { "record name",iocshArgString};
|
||||
static const iocshArg dbjlrArg1 = { "level",iocshArgInt};
|
||||
static const iocshArg * const dbjlrArgs[2] = {&dbjlrArg0,&dbjlrArg1};
|
||||
static const iocshFuncDef dbjlrFuncDef = {"dbjlr",2,dbjlrArgs};
|
||||
static void dbjlrCallFunc(const iocshArgBuf *args)
|
||||
{
|
||||
dbjlr(args[0].sval,args[1].ival);
|
||||
}
|
||||
|
||||
/* dbel */
|
||||
static const iocshArg dbelArg0 = { "record name",iocshArgString};
|
||||
static const iocshArg dbelArg1 = { "level",iocshArgInt};
|
||||
static const iocshArg * const dbelArgs[2] = {&dbelArg0,&dbelArg1};
|
||||
static const iocshFuncDef dbelFuncDef = {"dbel",2,dbelArgs};
|
||||
static void dbelCallFunc(const iocshArgBuf *args)
|
||||
{
|
||||
dbel(args[0].sval, args[1].ival);
|
||||
}
|
||||
|
||||
/* dba */
|
||||
static const iocshArg dbaArg0 = { "record name",iocshArgString};
|
||||
static const iocshArg * const dbaArgs[1] = {&dbaArg0};
|
||||
static const iocshFuncDef dbaFuncDef = {"dba",1,dbaArgs};
|
||||
static void dbaCallFunc(const iocshArgBuf *args) { dba(args[0].sval);}
|
||||
|
||||
/* dbl */
|
||||
static const iocshArg dblArg0 = { "record type",iocshArgString};
|
||||
static const iocshArg dblArg1 = { "fields",iocshArgString};
|
||||
static const iocshArg * const dblArgs[] = {&dblArg0,&dblArg1};
|
||||
static const iocshFuncDef dblFuncDef = {"dbl",2,dblArgs};
|
||||
static void dblCallFunc(const iocshArgBuf *args)
|
||||
{
|
||||
dbl(args[0].sval,args[1].sval);
|
||||
}
|
||||
|
||||
/* dbnr */
|
||||
static const iocshArg dbnrArg0 = { "verbose",iocshArgInt};
|
||||
static const iocshArg * const dbnrArgs[1] = {&dbnrArg0};
|
||||
static const iocshFuncDef dbnrFuncDef = {"dbnr",1,dbnrArgs};
|
||||
static void dbnrCallFunc(const iocshArgBuf *args) { dbnr(args[0].ival);}
|
||||
|
||||
/* dbla */
|
||||
static const iocshArg dblaArg0 = { "pattern",iocshArgString};
|
||||
static const iocshArg * const dblaArgs[1] = {&dblaArg0};
|
||||
static const iocshFuncDef dblaFuncDef = {"dbla",1,dblaArgs};
|
||||
static void dblaCallFunc(const iocshArgBuf *args) { dbla(args[0].sval);}
|
||||
|
||||
/* dbgrep */
|
||||
static const iocshArg dbgrepArg0 = { "pattern",iocshArgString};
|
||||
static const iocshArg * const dbgrepArgs[1] = {&dbgrepArg0};
|
||||
static const iocshFuncDef dbgrepFuncDef = {"dbgrep",1,dbgrepArgs};
|
||||
static void dbgrepCallFunc(const iocshArgBuf *args) { dbgrep(args[0].sval);}
|
||||
|
||||
/* dbgf */
|
||||
static const iocshArg dbgfArg0 = { "record name",iocshArgString};
|
||||
static const iocshArg * const dbgfArgs[1] = {&dbgfArg0};
|
||||
static const iocshFuncDef dbgfFuncDef = {"dbgf",1,dbgfArgs};
|
||||
static void dbgfCallFunc(const iocshArgBuf *args) { dbgf(args[0].sval);}
|
||||
|
||||
/* dbpf */
|
||||
static const iocshArg dbpfArg0 = { "record name",iocshArgString};
|
||||
static const iocshArg dbpfArg1 = { "value",iocshArgString};
|
||||
static const iocshArg * const dbpfArgs[2] = {&dbpfArg0,&dbpfArg1};
|
||||
static const iocshFuncDef dbpfFuncDef = {"dbpf",2,dbpfArgs};
|
||||
static void dbpfCallFunc(const iocshArgBuf *args)
|
||||
{ dbpf(args[0].sval,args[1].sval);}
|
||||
|
||||
/* dbpr */
|
||||
static const iocshArg dbprArg0 = { "record name",iocshArgString};
|
||||
static const iocshArg dbprArg1 = { "interest level",iocshArgInt};
|
||||
static const iocshArg * const dbprArgs[2] = {&dbprArg0,&dbprArg1};
|
||||
static const iocshFuncDef dbprFuncDef = {"dbpr",2,dbprArgs};
|
||||
static void dbprCallFunc(const iocshArgBuf *args)
|
||||
{ dbpr(args[0].sval,args[1].ival);}
|
||||
|
||||
/* dbtr */
|
||||
static const iocshArg dbtrArg0 = { "record name",iocshArgString};
|
||||
static const iocshArg * const dbtrArgs[1] = {&dbtrArg0};
|
||||
static const iocshFuncDef dbtrFuncDef = {"dbtr",1,dbtrArgs};
|
||||
static void dbtrCallFunc(const iocshArgBuf *args) { dbtr(args[0].sval);}
|
||||
|
||||
/* dbtgf */
|
||||
static const iocshArg dbtgfArg0 = { "record name",iocshArgString};
|
||||
static const iocshArg * const dbtgfArgs[1] = {&dbtgfArg0};
|
||||
static const iocshFuncDef dbtgfFuncDef = {"dbtgf",1,dbtgfArgs};
|
||||
static void dbtgfCallFunc(const iocshArgBuf *args) { dbtgf(args[0].sval);}
|
||||
|
||||
/* dbtpf */
|
||||
static const iocshArg dbtpfArg0 = { "record name",iocshArgString};
|
||||
static const iocshArg dbtpfArg1 = { "value",iocshArgString};
|
||||
static const iocshArg * const dbtpfArgs[2] = {&dbtpfArg0,&dbtpfArg1};
|
||||
static const iocshFuncDef dbtpfFuncDef = {"dbtpf",2,dbtpfArgs};
|
||||
static void dbtpfCallFunc(const iocshArgBuf *args)
|
||||
{ dbtpf(args[0].sval,args[1].sval);}
|
||||
|
||||
/* dbior */
|
||||
static const iocshArg dbiorArg0 = { "driver name",iocshArgString};
|
||||
static const iocshArg dbiorArg1 = { "interest level",iocshArgInt};
|
||||
static const iocshArg * const dbiorArgs[] = {&dbiorArg0,&dbiorArg1};
|
||||
static const iocshFuncDef dbiorFuncDef = {"dbior",2,dbiorArgs};
|
||||
static void dbiorCallFunc(const iocshArgBuf *args)
|
||||
{ dbior(args[0].sval,args[1].ival);}
|
||||
|
||||
/* dbhcr */
|
||||
static const iocshFuncDef dbhcrFuncDef = {"dbhcr",0,0};
|
||||
static void dbhcrCallFunc(const iocshArgBuf *args) { dbhcr();}
|
||||
|
||||
/* gft */
|
||||
static const iocshArg gftArg0 = { "record name",iocshArgString};
|
||||
static const iocshArg * const gftArgs[1] = {&gftArg0};
|
||||
static const iocshFuncDef gftFuncDef = {"gft",1,gftArgs};
|
||||
static void gftCallFunc(const iocshArgBuf *args) { gft(args[0].sval);}
|
||||
|
||||
/* pft */
|
||||
static const iocshArg pftArg0 = { "record name",iocshArgString};
|
||||
static const iocshArg pftArg1 = { "value",iocshArgString};
|
||||
static const iocshArg * const pftArgs[2] = {&pftArg0,&pftArg1};
|
||||
static const iocshFuncDef pftFuncDef = {"pft",2,pftArgs};
|
||||
static void pftCallFunc(const iocshArgBuf *args)
|
||||
{ pft(args[0].sval,args[1].sval);}
|
||||
|
||||
/* dbtpn */
|
||||
static const iocshArg dbtpnArg0 = { "record name",iocshArgString};
|
||||
static const iocshArg dbtpnArg1 = { "value",iocshArgString};
|
||||
static const iocshArg * const dbtpnArgs[2] = {&dbtpnArg0,&dbtpnArg1};
|
||||
static const iocshFuncDef dbtpnFuncDef = {"dbtpn",2,dbtpnArgs};
|
||||
static void dbtpnCallFunc(const iocshArgBuf *args)
|
||||
{ dbtpn(args[0].sval,args[1].sval);}
|
||||
|
||||
/* dbNotifyDump */
|
||||
static const iocshFuncDef dbNotifyDumpFuncDef = {"dbNotifyDump",0,0};
|
||||
static void dbNotifyDumpCallFunc(const iocshArgBuf *args) { dbNotifyDump();}
|
||||
|
||||
/* dbPutAttribute */
|
||||
static const iocshArg dbPutAttrArg0 = { "record type",iocshArgString};
|
||||
static const iocshArg dbPutAttrArg1 = { "attribute name",iocshArgString};
|
||||
static const iocshArg dbPutAttrArg2 = { "value",iocshArgString};
|
||||
static const iocshArg * const dbPutAttrArgs[] =
|
||||
{&dbPutAttrArg0, &dbPutAttrArg1, &dbPutAttrArg2};
|
||||
static const iocshFuncDef dbPutAttrFuncDef =
|
||||
{"dbPutAttribute",3,dbPutAttrArgs};
|
||||
static void dbPutAttrCallFunc(const iocshArgBuf *args)
|
||||
{ dbPutAttribute(args[0].sval,args[1].sval,args[2].sval);}
|
||||
|
||||
/* tpn */
|
||||
static const iocshArg tpnArg0 = { "record name",iocshArgString};
|
||||
static const iocshArg tpnArg1 = { "value",iocshArgString};
|
||||
static const iocshArg * const tpnArgs[2] = {&tpnArg0,&tpnArg1};
|
||||
static const iocshFuncDef tpnFuncDef = {"tpn",2,tpnArgs};
|
||||
static void tpnCallFunc(const iocshArgBuf *args)
|
||||
{ tpn(args[0].sval,args[1].sval);}
|
||||
|
||||
/* dblsr */
|
||||
static const iocshArg dblsrArg0 = { "record name",iocshArgString};
|
||||
static const iocshArg dblsrArg1 = { "interest level",iocshArgInt};
|
||||
static const iocshArg * const dblsrArgs[2] = {&dblsrArg0,&dblsrArg1};
|
||||
static const iocshFuncDef dblsrFuncDef = {"dblsr",2,dblsrArgs};
|
||||
static void dblsrCallFunc(const iocshArgBuf *args)
|
||||
{ dblsr(args[0].sval,args[1].ival);}
|
||||
|
||||
/* dbLockShowLocked */
|
||||
static const iocshArg dbLockShowLockedArg0 = { "interest level",iocshArgInt};
|
||||
static const iocshArg * const dbLockShowLockedArgs[1] = {&dbLockShowLockedArg0};
|
||||
static const iocshFuncDef dbLockShowLockedFuncDef =
|
||||
{"dbLockShowLocked",1,dbLockShowLockedArgs};
|
||||
static void dbLockShowLockedCallFunc(const iocshArgBuf *args)
|
||||
{ dbLockShowLocked(args[0].ival);}
|
||||
|
||||
/* scanOnceSetQueueSize */
|
||||
static const iocshArg scanOnceSetQueueSizeArg0 = { "size",iocshArgInt};
|
||||
static const iocshArg * const scanOnceSetQueueSizeArgs[1] =
|
||||
{&scanOnceSetQueueSizeArg0};
|
||||
static const iocshFuncDef scanOnceSetQueueSizeFuncDef =
|
||||
{"scanOnceSetQueueSize",1,scanOnceSetQueueSizeArgs};
|
||||
static void scanOnceSetQueueSizeCallFunc(const iocshArgBuf *args)
|
||||
{
|
||||
scanOnceSetQueueSize(args[0].ival);
|
||||
}
|
||||
|
||||
/* scanppl */
|
||||
static const iocshArg scanpplArg0 = { "rate",iocshArgDouble};
|
||||
static const iocshArg * const scanpplArgs[1] = {&scanpplArg0};
|
||||
static const iocshFuncDef scanpplFuncDef = {"scanppl",1,scanpplArgs};
|
||||
static void scanpplCallFunc(const iocshArgBuf *args)
|
||||
{ scanppl(args[0].dval);}
|
||||
|
||||
/* scanpel */
|
||||
static const iocshArg scanpelArg0 = { "event name",iocshArgString};
|
||||
static const iocshArg * const scanpelArgs[1] = {&scanpelArg0};
|
||||
static const iocshFuncDef scanpelFuncDef = {"scanpel",1,scanpelArgs};
|
||||
static void scanpelCallFunc(const iocshArgBuf *args)
|
||||
{ scanpel(args[0].sval);}
|
||||
|
||||
/* postEvent */
|
||||
static const iocshArg postEventArg0 = { "event name",iocshArgString};
|
||||
static const iocshArg * const postEventArgs[1] = {&postEventArg0};
|
||||
static const iocshFuncDef postEventFuncDef = {"postEvent",1,postEventArgs};
|
||||
static void postEventCallFunc(const iocshArgBuf *args)
|
||||
{
|
||||
EVENTPVT pel = eventNameToHandle(args[0].sval);
|
||||
postEvent(pel);
|
||||
}
|
||||
|
||||
/* scanpiol */
|
||||
static const iocshFuncDef scanpiolFuncDef = {"scanpiol",0};
|
||||
static void scanpiolCallFunc(const iocshArgBuf *args) { scanpiol();}
|
||||
|
||||
/* callbackSetQueueSize */
|
||||
static const iocshArg callbackSetQueueSizeArg0 = { "bufsize",iocshArgInt};
|
||||
static const iocshArg * const callbackSetQueueSizeArgs[1] =
|
||||
{&callbackSetQueueSizeArg0};
|
||||
static const iocshFuncDef callbackSetQueueSizeFuncDef =
|
||||
{"callbackSetQueueSize",1,callbackSetQueueSizeArgs};
|
||||
static void callbackSetQueueSizeCallFunc(const iocshArgBuf *args)
|
||||
{
|
||||
callbackSetQueueSize(args[0].ival);
|
||||
}
|
||||
|
||||
/* callbackParallelThreads */
|
||||
static const iocshArg callbackParallelThreadsArg0 = { "no of threads", iocshArgInt};
|
||||
static const iocshArg callbackParallelThreadsArg1 = { "priority", iocshArgString};
|
||||
static const iocshArg * const callbackParallelThreadsArgs[2] =
|
||||
{&callbackParallelThreadsArg0,&callbackParallelThreadsArg1};
|
||||
static const iocshFuncDef callbackParallelThreadsFuncDef =
|
||||
{"callbackParallelThreads",2,callbackParallelThreadsArgs};
|
||||
static void callbackParallelThreadsCallFunc(const iocshArgBuf *args)
|
||||
{
|
||||
callbackParallelThreads(args[0].ival, args[1].sval);
|
||||
}
|
||||
|
||||
/* dbStateCreate */
|
||||
static const iocshArg dbStateArgName = { "name", iocshArgString };
|
||||
static const iocshArg * const dbStateCreateArgs[] = { &dbStateArgName };
|
||||
static const iocshFuncDef dbStateCreateFuncDef = { "dbStateCreate", 1, dbStateCreateArgs };
|
||||
static void dbStateCreateCallFunc (const iocshArgBuf *args)
|
||||
{
|
||||
dbStateCreate(args[0].sval);
|
||||
}
|
||||
|
||||
/* dbStateSet */
|
||||
static const iocshArg * const dbStateSetArgs[] = { &dbStateArgName };
|
||||
static const iocshFuncDef dbStateSetFuncDef = { "dbStateSet", 1, dbStateSetArgs };
|
||||
static void dbStateSetCallFunc (const iocshArgBuf *args)
|
||||
{
|
||||
dbStateId sid = dbStateFind(args[0].sval);
|
||||
|
||||
if (sid)
|
||||
dbStateSet(sid);
|
||||
}
|
||||
|
||||
/* dbStateClear */
|
||||
static const iocshArg * const dbStateClearArgs[] = { &dbStateArgName };
|
||||
static const iocshFuncDef dbStateClearFuncDef = { "dbStateClear", 1, dbStateClearArgs };
|
||||
static void dbStateClearCallFunc (const iocshArgBuf *args)
|
||||
{
|
||||
dbStateId sid = dbStateFind(args[0].sval);
|
||||
|
||||
if (sid)
|
||||
dbStateClear(sid);
|
||||
}
|
||||
|
||||
/* dbStateShow */
|
||||
static const iocshArg dbStateShowArg1 = { "level", iocshArgInt };
|
||||
static const iocshArg * const dbStateShowArgs[] = { &dbStateArgName, &dbStateShowArg1 };
|
||||
static const iocshFuncDef dbStateShowFuncDef = { "dbStateShow", 2, dbStateShowArgs };
|
||||
static void dbStateShowCallFunc (const iocshArgBuf *args)
|
||||
{
|
||||
dbStateId sid = dbStateFind(args[0].sval);
|
||||
|
||||
if (sid)
|
||||
dbStateShow(sid, args[1].ival);
|
||||
}
|
||||
|
||||
/* dbStateShowAll */
|
||||
static const iocshArg dbStateShowAllArg0 = { "level", iocshArgInt };
|
||||
static const iocshArg * const dbStateShowAllArgs[] = { &dbStateShowAllArg0 };
|
||||
static const iocshFuncDef dbStateShowAllFuncDef = { "dbStateShowAll", 1, dbStateShowAllArgs };
|
||||
static void dbStateShowAllCallFunc (const iocshArgBuf *args)
|
||||
{
|
||||
dbStateShowAll(args[0].ival);
|
||||
}
|
||||
|
||||
void dbIocRegister(void)
|
||||
{
|
||||
iocshRegister(&dbbFuncDef,dbbCallFunc);
|
||||
iocshRegister(&dbdFuncDef,dbdCallFunc);
|
||||
iocshRegister(&dbcFuncDef,dbcCallFunc);
|
||||
iocshRegister(&dbsFuncDef,dbsCallFunc);
|
||||
iocshRegister(&dbstatFuncDef,dbstatCallFunc);
|
||||
iocshRegister(&dbpFuncDef,dbpCallFunc);
|
||||
iocshRegister(&dbapFuncDef,dbapCallFunc);
|
||||
|
||||
iocshRegister(&dbsrFuncDef,dbsrCallFunc);
|
||||
iocshRegister(&dbcarFuncDef,dbcarCallFunc);
|
||||
iocshRegister(&dbelFuncDef,dbelCallFunc);
|
||||
iocshRegister(&dbjlrFuncDef,dbjlrCallFunc);
|
||||
|
||||
iocshRegister(&dbLoadDatabaseFuncDef,dbLoadDatabaseCallFunc);
|
||||
iocshRegister(&dbLoadRecordsFuncDef,dbLoadRecordsCallFunc);
|
||||
|
||||
iocshRegister(&dbaFuncDef,dbaCallFunc);
|
||||
iocshRegister(&dblFuncDef,dblCallFunc);
|
||||
iocshRegister(&dbnrFuncDef,dbnrCallFunc);
|
||||
iocshRegister(&dblaFuncDef,dblaCallFunc);
|
||||
iocshRegister(&dbgrepFuncDef,dbgrepCallFunc);
|
||||
iocshRegister(&dbgfFuncDef,dbgfCallFunc);
|
||||
iocshRegister(&dbpfFuncDef,dbpfCallFunc);
|
||||
iocshRegister(&dbprFuncDef,dbprCallFunc);
|
||||
iocshRegister(&dbtrFuncDef,dbtrCallFunc);
|
||||
iocshRegister(&dbtgfFuncDef,dbtgfCallFunc);
|
||||
iocshRegister(&dbtpfFuncDef,dbtpfCallFunc);
|
||||
iocshRegister(&dbiorFuncDef,dbiorCallFunc);
|
||||
iocshRegister(&dbhcrFuncDef,dbhcrCallFunc);
|
||||
iocshRegister(&gftFuncDef,gftCallFunc);
|
||||
iocshRegister(&pftFuncDef,pftCallFunc);
|
||||
iocshRegister(&dbtpnFuncDef,dbtpnCallFunc);
|
||||
iocshRegister(&dbNotifyDumpFuncDef,dbNotifyDumpCallFunc);
|
||||
iocshRegister(&dbPutAttrFuncDef,dbPutAttrCallFunc);
|
||||
iocshRegister(&tpnFuncDef,tpnCallFunc);
|
||||
iocshRegister(&dblsrFuncDef,dblsrCallFunc);
|
||||
iocshRegister(&dbLockShowLockedFuncDef,dbLockShowLockedCallFunc);
|
||||
|
||||
iocshRegister(&scanOnceSetQueueSizeFuncDef,scanOnceSetQueueSizeCallFunc);
|
||||
iocshRegister(&scanpplFuncDef,scanpplCallFunc);
|
||||
iocshRegister(&scanpelFuncDef,scanpelCallFunc);
|
||||
iocshRegister(&postEventFuncDef,postEventCallFunc);
|
||||
iocshRegister(&scanpiolFuncDef,scanpiolCallFunc);
|
||||
|
||||
iocshRegister(&callbackSetQueueSizeFuncDef,callbackSetQueueSizeCallFunc);
|
||||
iocshRegister(&callbackParallelThreadsFuncDef,callbackParallelThreadsCallFunc);
|
||||
|
||||
/* Needed before callback system is initialized */
|
||||
callbackParallelThreadsDefault = epicsThreadGetCPUs();
|
||||
|
||||
iocshRegister(&dbStateCreateFuncDef, dbStateCreateCallFunc);
|
||||
iocshRegister(&dbStateSetFuncDef, dbStateSetCallFunc);
|
||||
iocshRegister(&dbStateClearFuncDef, dbStateClearCallFunc);
|
||||
iocshRegister(&dbStateShowFuncDef, dbStateShowCallFunc);
|
||||
iocshRegister(&dbStateShowAllFuncDef, dbStateShowAllCallFunc);
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
/*************************************************************************\
|
||||
* Copyright (c) 2007 UChicago Argonne LLC, as Operator of Argonne
|
||||
* National Laboratory.
|
||||
* Copyright (c) 2002 The Regents of the University of California, as
|
||||
* Operator of Los Alamos National Laboratory.
|
||||
* EPICS BASE is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
\*************************************************************************/
|
||||
|
||||
#ifndef INC_dbIocRegister_H
|
||||
#define INC_dbIocRegister_H
|
||||
|
||||
#include "shareLib.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
epicsShareFunc void dbIocRegister(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* INC_dbIocRegister_H */
|
||||
@@ -0,0 +1,538 @@
|
||||
/*************************************************************************\
|
||||
* 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.
|
||||
\*************************************************************************/
|
||||
/* dbJLink.c */
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "epicsAssert.h"
|
||||
#include "dbmf.h"
|
||||
#include "errlog.h"
|
||||
#include "yajl_alloc.h"
|
||||
#include "yajl_parse.h"
|
||||
|
||||
#define epicsExportSharedSymbols
|
||||
#include "dbAccessDefs.h"
|
||||
#include "dbCommon.h"
|
||||
#include "dbStaticLib.h"
|
||||
#include "dbStaticPvt.h"
|
||||
#include "dbLink.h"
|
||||
#include "dbJLink.h"
|
||||
#include "dbLock.h"
|
||||
#include "dbStaticLib.h"
|
||||
#include "link.h"
|
||||
|
||||
#define IFDEBUG(n) if(parser->parse_debug)
|
||||
|
||||
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;
|
||||
|
||||
#define CALL_OR_STOP(routine) !(routine) ? jlif_stop : (routine)
|
||||
|
||||
static int dbjl_return(parseContext *parser, jlif_result result) {
|
||||
jlink *pjlink = parser->pjlink;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
if (result == jlif_stop && pjlink) {
|
||||
jlink *parent;
|
||||
|
||||
while ((parent = pjlink->parent)) {
|
||||
pjlink->pif->free_jlink(pjlink);
|
||||
pjlink = parent;
|
||||
}
|
||||
pjlink->pif->free_jlink(pjlink);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static int dbjl_value(parseContext *parser, jlif_result result) {
|
||||
jlink *pjlink = parser->pjlink;
|
||||
jlink *parent;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
if (result == jlif_stop || pjlink->parseDepth > 0)
|
||||
return dbjl_return(parser, result);
|
||||
|
||||
parent = pjlink->parent;
|
||||
if (!parent) {
|
||||
parser->product = pjlink;
|
||||
} else if (parent->pif->end_child) {
|
||||
parent->pif->end_child(parent, pjlink);
|
||||
}
|
||||
pjlink->debug = 0;
|
||||
|
||||
parser->pjlink = parent;
|
||||
|
||||
IFDEBUG(8)
|
||||
printf("dbjl_value: product = %p\n", pjlink);
|
||||
|
||||
return jlif_continue;
|
||||
}
|
||||
|
||||
static int dbjl_null(void *ctx) {
|
||||
parseContext *parser = (parseContext *) ctx;
|
||||
jlink *pjlink = parser->pjlink;
|
||||
|
||||
IFDEBUG(10)
|
||||
printf("dbjl_null(%s@%p)\n", pjlink ? pjlink->pif->name : "", pjlink);
|
||||
|
||||
assert(pjlink);
|
||||
return dbjl_value(parser,
|
||||
CALL_OR_STOP(pjlink->pif->parse_null)(pjlink));
|
||||
}
|
||||
|
||||
static int dbjl_boolean(void *ctx, int val) {
|
||||
parseContext *parser = (parseContext *) ctx;
|
||||
jlink *pjlink = parser->pjlink;
|
||||
|
||||
assert(pjlink);
|
||||
return dbjl_value(parser,
|
||||
CALL_OR_STOP(pjlink->pif->parse_boolean)(pjlink, val));
|
||||
}
|
||||
|
||||
static int dbjl_integer(void *ctx, long long num) {
|
||||
parseContext *parser = (parseContext *) ctx;
|
||||
jlink *pjlink = parser->pjlink;
|
||||
|
||||
IFDEBUG(10)
|
||||
printf("dbjl_integer(%s@%p, %lld)\n",
|
||||
pjlink->pif->name, pjlink, num);
|
||||
|
||||
assert(pjlink);
|
||||
return dbjl_value(parser,
|
||||
CALL_OR_STOP(pjlink->pif->parse_integer)(pjlink, num));
|
||||
}
|
||||
|
||||
static int dbjl_double(void *ctx, double num) {
|
||||
parseContext *parser = (parseContext *) ctx;
|
||||
jlink *pjlink = parser->pjlink;
|
||||
|
||||
IFDEBUG(10)
|
||||
printf("dbjl_double(%s@%p, %g)\n",
|
||||
pjlink->pif->name, pjlink, num);
|
||||
|
||||
assert(pjlink);
|
||||
return dbjl_value(parser,
|
||||
CALL_OR_STOP(pjlink->pif->parse_double)(pjlink, num));
|
||||
}
|
||||
|
||||
static int dbjl_string(void *ctx, const unsigned char *val, size_t len) {
|
||||
parseContext *parser = (parseContext *) ctx;
|
||||
jlink *pjlink = parser->pjlink;
|
||||
|
||||
IFDEBUG(10)
|
||||
printf("dbjl_string(%s@%p, \"%.*s\")\n",
|
||||
pjlink->pif->name, pjlink, (int) len, val);
|
||||
|
||||
assert(pjlink);
|
||||
return dbjl_value(parser,
|
||||
CALL_OR_STOP(pjlink->pif->parse_string)(pjlink, (const char *) val, len));
|
||||
}
|
||||
|
||||
static int dbjl_start_map(void *ctx) {
|
||||
parseContext *parser = (parseContext *) ctx;
|
||||
jlink *pjlink = parser->pjlink;
|
||||
int result;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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;
|
||||
result = jlif_continue;
|
||||
}
|
||||
|
||||
IFDEBUG(10)
|
||||
printf("dbjl_start_map -> %d\n", result);
|
||||
|
||||
return dbjl_return(parser, result);
|
||||
}
|
||||
|
||||
static int dbjl_map_key(void *ctx, const unsigned char *key, size_t len) {
|
||||
parseContext *parser = (parseContext *) ctx;
|
||||
jlink *pjlink = parser->pjlink;
|
||||
char *link_name;
|
||||
linkSup *linkSup;
|
||||
jlif *pjlif;
|
||||
|
||||
if (!parser->key_is_link) {
|
||||
if (!pjlink) {
|
||||
errlogPrintf("dbJLinkInit: Illegal second link key '%.*s'\n",
|
||||
(int) len, key);
|
||||
return dbjl_return(parser, jlif_stop);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
assert(pjlink->parseDepth > 0);
|
||||
return dbjl_return(parser,
|
||||
CALL_OR_STOP(pjlink->pif->parse_map_key)(pjlink,
|
||||
(const char *) key, 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);
|
||||
}
|
||||
|
||||
link_name = dbmfStrndup((const char *) key, len);
|
||||
|
||||
linkSup = dbFindLinkSup(pdbbase, link_name);
|
||||
if (!linkSup) {
|
||||
errlogPrintf("dbJLinkInit: Link type '%s' not found\n",
|
||||
link_name);
|
||||
dbmfFree(link_name);
|
||||
return dbjl_return(parser, jlif_stop);
|
||||
}
|
||||
|
||||
pjlif = linkSup->pjlif;
|
||||
if (!pjlif) {
|
||||
errlogPrintf("dbJLinkInit: Support for Link type '%s' not loaded\n",
|
||||
link_name);
|
||||
dbmfFree(link_name);
|
||||
return dbjl_return(parser, jlif_stop);
|
||||
}
|
||||
|
||||
dbmfFree(link_name);
|
||||
|
||||
pjlink = pjlif->alloc_jlink(parser->dbfType);
|
||||
if (!pjlink) {
|
||||
errlogPrintf("dbJLinkInit: Out of memory\n");
|
||||
return dbjl_return(parser, jlif_stop);
|
||||
}
|
||||
pjlink->pif = pjlif;
|
||||
pjlink->parent = NULL;
|
||||
pjlink->parseDepth = 0;
|
||||
pjlink->debug = !!parser->lset_debug;
|
||||
|
||||
if (parser->pjlink) {
|
||||
/* We're starting a child link, save its parent */
|
||||
pjlink->parent = parser->pjlink;
|
||||
}
|
||||
parser->pjlink = pjlink;
|
||||
parser->key_is_link = 0;
|
||||
|
||||
IFDEBUG(8)
|
||||
printf("dbjl_map_key: New %s@%p\n", pjlink ? pjlink->pif->name : "", pjlink);
|
||||
|
||||
return jlif_continue;
|
||||
}
|
||||
|
||||
static int dbjl_end_map(void *ctx) {
|
||||
parseContext *parser = (parseContext *) ctx;
|
||||
jlink *pjlink = parser->pjlink;
|
||||
jlif_result result;
|
||||
|
||||
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",
|
||||
parser->jsonDepth, pjlink ? pjlink->parseDepth : 0,
|
||||
parser->key_is_link);
|
||||
}
|
||||
|
||||
parser->jsonDepth--;
|
||||
if (pjlink) {
|
||||
pjlink->parseDepth--;
|
||||
|
||||
result = dbjl_value(parser,
|
||||
CALL_OR_STOP(pjlink->pif->parse_end_map)(pjlink));
|
||||
}
|
||||
else {
|
||||
result = jlif_continue;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static int dbjl_start_array(void *ctx) {
|
||||
parseContext *parser = (parseContext *) ctx;
|
||||
jlink *pjlink = parser->pjlink;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
assert(pjlink);
|
||||
pjlink->parseDepth++;
|
||||
parser->jsonDepth++;
|
||||
|
||||
return dbjl_return(parser,
|
||||
CALL_OR_STOP(pjlink->pif->parse_start_array)(pjlink));
|
||||
}
|
||||
|
||||
static int dbjl_end_array(void *ctx) {
|
||||
parseContext *parser = (parseContext *) ctx;
|
||||
jlink *pjlink = parser->pjlink;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
assert(pjlink);
|
||||
pjlink->parseDepth--;
|
||||
parser->jsonDepth--;
|
||||
|
||||
return dbjl_value(parser,
|
||||
CALL_OR_STOP(pjlink->pif->parse_end_array)(pjlink));
|
||||
}
|
||||
|
||||
|
||||
static yajl_callbacks dbjl_callbacks = {
|
||||
dbjl_null, dbjl_boolean, dbjl_integer, dbjl_double, NULL, dbjl_string,
|
||||
dbjl_start_map, dbjl_map_key, dbjl_end_map, dbjl_start_array, dbjl_end_array
|
||||
};
|
||||
|
||||
long dbJLinkParse(const char *json, size_t jlen, short dbfType,
|
||||
jlink **ppjlink, unsigned opts)
|
||||
{
|
||||
parseContext context, *parser = &context;
|
||||
yajl_alloc_funcs dbjl_allocs;
|
||||
yajl_handle yh;
|
||||
yajl_status ys;
|
||||
long status;
|
||||
|
||||
parser->pjlink = NULL;
|
||||
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);
|
||||
|
||||
yajl_set_default_alloc_funcs(&dbjl_allocs);
|
||||
yh = yajl_alloc(&dbjl_callbacks, &dbjl_allocs, parser);
|
||||
if (!yh)
|
||||
return S_db_noMemory;
|
||||
|
||||
ys = yajl_parse(yh, (const unsigned char *) json, jlen);
|
||||
if (ys == yajl_status_ok)
|
||||
ys = yajl_complete_parse(yh);
|
||||
|
||||
switch (ys) {
|
||||
unsigned char *err;
|
||||
|
||||
case yajl_status_ok:
|
||||
assert(parser->jsonDepth == 0);
|
||||
*ppjlink = parser->product;
|
||||
status = 0;
|
||||
break;
|
||||
|
||||
case yajl_status_error:
|
||||
err = yajl_get_error(yh, 1, (const unsigned char *) json, jlen);
|
||||
errlogPrintf("dbJLinkInit: %s\n", err);
|
||||
yajl_free_error(yh, err);
|
||||
dbJLinkFree(parser->pjlink);
|
||||
dbJLinkFree(parser->product);
|
||||
/* fall through */
|
||||
default:
|
||||
status = S_db_badField;
|
||||
}
|
||||
|
||||
yajl_free(yh);
|
||||
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);
|
||||
|
||||
dbLinkOpen(plink);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void dbJLinkFree(jlink *pjlink)
|
||||
{
|
||||
if (pjlink)
|
||||
pjlink->pif->free_jlink(pjlink);
|
||||
}
|
||||
|
||||
void dbJLinkReport(jlink *pjlink, int level, int indent) {
|
||||
if (pjlink && pjlink->pif->report)
|
||||
pjlink->pif->report(pjlink, level, indent);
|
||||
}
|
||||
|
||||
long dbJLinkMapChildren(struct link *plink, jlink_map_fn rtn, void *ctx)
|
||||
{
|
||||
jlink *pjlink;
|
||||
long status;
|
||||
|
||||
if (!plink || plink->type != JSON_LINK)
|
||||
return 0;
|
||||
|
||||
pjlink = plink->value.json.jlink;
|
||||
if (!pjlink)
|
||||
return 0;
|
||||
|
||||
status = rtn(pjlink, ctx);
|
||||
if (!status && pjlink->pif->map_children)
|
||||
status = pjlink->pif->map_children(pjlink, rtn, ctx);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
long dbjlr(const char *recname, int level)
|
||||
{
|
||||
DBENTRY dbentry;
|
||||
DBENTRY * const pdbentry = &dbentry;
|
||||
long status;
|
||||
|
||||
if (!recname || recname[0] == '\0' || !strcmp(recname, "*")) {
|
||||
recname = NULL;
|
||||
printf("JSON links in all records\n\n");
|
||||
}
|
||||
else
|
||||
printf("JSON links in record '%s'\n\n", recname);
|
||||
|
||||
dbInitEntry(pdbbase, pdbentry);
|
||||
for (status = dbFirstRecordType(pdbentry);
|
||||
status == 0;
|
||||
status = dbNextRecordType(pdbentry)) {
|
||||
for (status = dbFirstRecord(pdbentry);
|
||||
status == 0;
|
||||
status = dbNextRecord(pdbentry)) {
|
||||
dbRecordType *pdbRecordType = pdbentry->precordType;
|
||||
dbCommon *precord = pdbentry->precnode->precord;
|
||||
char *prec = (char *) precord;
|
||||
int i;
|
||||
|
||||
if (recname && strcmp(recname, dbGetRecordName(pdbentry)))
|
||||
continue;
|
||||
if (dbIsAlias(pdbentry))
|
||||
continue;
|
||||
|
||||
printf(" %s record '%s':\n", pdbRecordType->name, precord->name);
|
||||
|
||||
dbScanLock(precord);
|
||||
for (i = 0; i < pdbRecordType->no_links; i++) {
|
||||
int idx = pdbRecordType->link_ind[i];
|
||||
dbFldDes *pdbFldDes = pdbRecordType->papFldDes[idx];
|
||||
DBLINK *plink = (DBLINK *) (prec + pdbFldDes->offset);
|
||||
|
||||
if (plink->type != JSON_LINK)
|
||||
continue;
|
||||
if (!dbLinkIsDefined(plink))
|
||||
continue;
|
||||
|
||||
printf(" Link field '%s':\n", pdbFldDes->name);
|
||||
dbJLinkReport(plink->value.json.jlink, level, 6);
|
||||
}
|
||||
dbScanUnlock(precord);
|
||||
if (recname)
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
done:
|
||||
return 0;
|
||||
}
|
||||
|
||||
long dbJLinkMapAll(char *recname, jlink_map_fn rtn, void *ctx)
|
||||
{
|
||||
DBENTRY dbentry;
|
||||
DBENTRY * const pdbentry = &dbentry;
|
||||
long status;
|
||||
|
||||
if (recname && (recname[0] = '\0' || !strcmp(recname, "*")))
|
||||
recname = NULL;
|
||||
|
||||
dbInitEntry(pdbbase, pdbentry);
|
||||
for (status = dbFirstRecordType(pdbentry);
|
||||
status == 0;
|
||||
status = dbNextRecordType(pdbentry)) {
|
||||
for (status = dbFirstRecord(pdbentry);
|
||||
status == 0;
|
||||
status = dbNextRecord(pdbentry)) {
|
||||
dbRecordType *pdbRecordType = pdbentry->precordType;
|
||||
dbCommon *precord = pdbentry->precnode->precord;
|
||||
char *prec = (char *) precord;
|
||||
int i;
|
||||
|
||||
if (recname && strcmp(recname, dbGetRecordName(pdbentry)))
|
||||
continue;
|
||||
if (dbIsAlias(pdbentry))
|
||||
continue;
|
||||
|
||||
dbScanLock(precord);
|
||||
for (i = 0; i < pdbRecordType->no_links; i++) {
|
||||
int idx = pdbRecordType->link_ind[i];
|
||||
dbFldDes *pdbFldDes = pdbRecordType->papFldDes[idx];
|
||||
DBLINK *plink = (DBLINK *) (prec + pdbFldDes->offset);
|
||||
|
||||
status = dbJLinkMapChildren(plink, rtn, ctx);
|
||||
if (status)
|
||||
goto unlock;
|
||||
}
|
||||
unlock:
|
||||
dbScanUnlock(precord);
|
||||
if (status || recname)
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
done:
|
||||
return status;
|
||||
}
|
||||
@@ -0,0 +1,133 @@
|
||||
/*************************************************************************\
|
||||
* 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.
|
||||
\*************************************************************************/
|
||||
/* dbJLink.h */
|
||||
|
||||
#ifndef INC_dbJLink_H
|
||||
#define INC_dbJLink_H
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <shareLib.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef enum {
|
||||
jlif_stop = 0,
|
||||
jlif_continue = 1
|
||||
} jlif_result;
|
||||
|
||||
typedef enum {
|
||||
jlif_key_stop = jlif_stop,
|
||||
jlif_key_continue = jlif_continue,
|
||||
jlif_key_child_link
|
||||
} jlif_key_result;
|
||||
|
||||
struct link;
|
||||
struct lset;
|
||||
struct jlif;
|
||||
|
||||
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 */
|
||||
/* Link types extend or embed this structure for private storage */
|
||||
} jlink;
|
||||
|
||||
typedef long (*jlink_map_fn)(jlink *, void *ctx);
|
||||
|
||||
typedef struct jlif {
|
||||
/* Optional parser methods below given as NULL are equivalent to
|
||||
* providing a routine that always returns jlif_stop, meaning that
|
||||
* this JSON construct is not allowed at this point in the parse.
|
||||
*/
|
||||
|
||||
const char *name;
|
||||
/* Name for the link type, used in link value */
|
||||
|
||||
jlink* (*alloc_jlink)(short dbfType);
|
||||
/* Required, allocate new link structure */
|
||||
|
||||
void (*free_jlink)(jlink *);
|
||||
/* Required, release all resources allocated for link */
|
||||
|
||||
jlif_result (*parse_null)(jlink *);
|
||||
/* Optional, parser saw a null value */
|
||||
|
||||
jlif_result (*parse_boolean)(jlink *, int val);
|
||||
/* Optional, parser saw a boolean value */
|
||||
|
||||
jlif_result (*parse_integer)(jlink *, long long num);
|
||||
/* Optional, parser saw an integer value */
|
||||
|
||||
jlif_result (*parse_double)(jlink *, double num);
|
||||
/* Optional, parser saw a double value */
|
||||
|
||||
jlif_result (*parse_string)(jlink *, const char *val, size_t len);
|
||||
/* 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).
|
||||
*/
|
||||
|
||||
jlif_result (*parse_map_key)(jlink *, const char *key, size_t len);
|
||||
/* Optional, parser saw a map key */
|
||||
|
||||
jlif_result (*parse_end_map)(jlink *);
|
||||
/* Optional, parser saw a close-brace '}' */
|
||||
|
||||
jlif_result (*parse_start_array)(jlink *);
|
||||
/* Optional, parser saw an open-bracket */
|
||||
|
||||
jlif_result (*parse_end_array)(jlink *);
|
||||
/* Optional, parser saw a close-bracket */
|
||||
|
||||
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 */
|
||||
|
||||
struct lset* (*get_lset)(const jlink *);
|
||||
/* Required, return lset for this link instance */
|
||||
|
||||
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)
|
||||
* for each child.
|
||||
*/
|
||||
|
||||
long (*map_children)(jlink *, jlink_map_fn rtn, void *ctx);
|
||||
/* Optional, call dbJLinkMapChildren() on all embedded links.
|
||||
* Stop immediately and return status if non-zero.
|
||||
*/
|
||||
|
||||
/* 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);
|
||||
epicsShareFunc long dbJLinkInit(struct link *plink);
|
||||
|
||||
epicsShareFunc void dbJLinkFree(jlink *);
|
||||
epicsShareFunc void dbJLinkReport(jlink *, int level, int indent);
|
||||
|
||||
epicsShareFunc long dbJLinkMapChildren(struct link *,
|
||||
jlink_map_fn rtn, void *ctx);
|
||||
|
||||
epicsShareFunc long dbjlr(const char *recname, int level);
|
||||
epicsShareFunc long dbJLinkMapAll(char *recname, jlink_map_fn rtn, void *ctx);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* INC_dbJLink_H */
|
||||
|
||||
@@ -0,0 +1,505 @@
|
||||
/*************************************************************************\
|
||||
* Copyright (c) 2010 UChicago Argonne LLC, as Operator of Argonne
|
||||
* National Laboratory.
|
||||
* Copyright (c) 2002 The Regents of the University of California, as
|
||||
* Operator of Los Alamos National Laboratory.
|
||||
* EPICS BASE is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
\*************************************************************************/
|
||||
/* dbLink.c */
|
||||
/*
|
||||
* Original Authors: Bob Dalesio, Marty Kraimer
|
||||
* Current Author: Andrew Johnson
|
||||
*/
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "alarm.h"
|
||||
#include "cvtFast.h"
|
||||
#include "dbDefs.h"
|
||||
#include "ellLib.h"
|
||||
#include "epicsTime.h"
|
||||
#include "errlog.h"
|
||||
|
||||
#include "caeventmask.h"
|
||||
|
||||
#define epicsExportSharedSymbols
|
||||
#include "dbAccessDefs.h"
|
||||
#include "dbAddr.h"
|
||||
#include "dbBase.h"
|
||||
#include "dbCa.h"
|
||||
#include "dbCommon.h"
|
||||
#include "dbConstLink.h"
|
||||
#include "dbDbLink.h"
|
||||
#include "db_field_log.h"
|
||||
#include "dbFldTypes.h"
|
||||
#include "dbJLink.h"
|
||||
#include "dbLink.h"
|
||||
#include "dbLock.h"
|
||||
#include "dbScan.h"
|
||||
#include "dbStaticLib.h"
|
||||
#include "devSup.h"
|
||||
#include "link.h"
|
||||
#include "recGbl.h"
|
||||
#include "recSup.h"
|
||||
#include "special.h"
|
||||
|
||||
/* How to identify links in error messages */
|
||||
static const char * link_field_name(const struct link *plink)
|
||||
{
|
||||
const struct dbCommon *precord = plink->precord;
|
||||
const dbRecordType *pdbRecordType = precord->rdes;
|
||||
dbFldDes * const *papFldDes = pdbRecordType->papFldDes;
|
||||
const short *link_ind = pdbRecordType->link_ind;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < pdbRecordType->no_links; i++) {
|
||||
const dbFldDes *pdbFldDes = papFldDes[link_ind[i]];
|
||||
|
||||
if (plink == (DBLINK *)((char *)precord + pdbFldDes->offset))
|
||||
return pdbFldDes->name;
|
||||
}
|
||||
return "????";
|
||||
}
|
||||
|
||||
/* Special TSEL handler for PV links */
|
||||
/* FIXME: Generalize for new link types... */
|
||||
static void TSEL_modified(struct link *plink)
|
||||
{
|
||||
struct pv_link *ppv_link;
|
||||
char *pfieldname;
|
||||
|
||||
if (plink->type != PV_LINK) {
|
||||
errlogPrintf("dbLink::TSEL_modified called for non PV_LINK\n");
|
||||
return;
|
||||
}
|
||||
/* If pvname contains .TIME truncate it to point to VAL instead */
|
||||
ppv_link = &plink->value.pv_link;
|
||||
pfieldname = strstr(ppv_link->pvname, ".TIME");
|
||||
if (pfieldname) {
|
||||
*pfieldname = 0;
|
||||
plink->flags |= DBLINK_FLAG_TSELisTIME;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/***************************** Generic Link API *****************************/
|
||||
|
||||
void dbInitLink(struct link *plink, short dbfType)
|
||||
{
|
||||
struct dbCommon *precord = plink->precord;
|
||||
|
||||
/* Only initialize link once */
|
||||
if (plink->flags & DBLINK_FLAG_INITIALIZED)
|
||||
return;
|
||||
else
|
||||
plink->flags |= DBLINK_FLAG_INITIALIZED;
|
||||
|
||||
if (plink->type == CONSTANT) {
|
||||
dbConstInitLink(plink);
|
||||
return;
|
||||
}
|
||||
|
||||
if (plink->type == JSON_LINK) {
|
||||
dbJLinkInit(plink);
|
||||
return;
|
||||
}
|
||||
|
||||
if (plink->type != PV_LINK)
|
||||
return;
|
||||
|
||||
if (plink == &precord->tsel)
|
||||
TSEL_modified(plink);
|
||||
|
||||
if (!(plink->value.pv_link.pvlMask & (pvlOptCA | pvlOptCP | pvlOptCPP))) {
|
||||
/* Make it a DB link if possible */
|
||||
if (!dbDbInitLink(plink, dbfType))
|
||||
return;
|
||||
}
|
||||
|
||||
/* Make it a CA link */
|
||||
if (dbfType == DBF_INLINK)
|
||||
plink->value.pv_link.pvlMask |= pvlOptInpNative;
|
||||
|
||||
dbCaAddLink(NULL, plink, dbfType);
|
||||
if (dbfType == DBF_FWDLINK) {
|
||||
char *pperiod = strrchr(plink->value.pv_link.pvname, '.');
|
||||
|
||||
if (pperiod && strstr(pperiod, "PROC")) {
|
||||
plink->value.pv_link.pvlMask |= pvlOptFWD;
|
||||
}
|
||||
else {
|
||||
errlogPrintf("Forward-link uses Channel Access "
|
||||
"without pointing to PROC field\n"
|
||||
" %s.%s => %s\n",
|
||||
precord->name, link_field_name(plink),
|
||||
plink->value.pv_link.pvname);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void dbAddLink(struct dbLocker *locker, struct link *plink, short dbfType,
|
||||
DBADDR *ptarget)
|
||||
{
|
||||
struct dbCommon *precord = plink->precord;
|
||||
|
||||
/* Clear old TSELisTIME flag */
|
||||
plink->flags &= ~DBLINK_FLAG_TSELisTIME;
|
||||
|
||||
if (plink->type == CONSTANT) {
|
||||
dbConstAddLink(plink);
|
||||
return;
|
||||
}
|
||||
|
||||
if (plink->type == JSON_LINK) {
|
||||
/*
|
||||
* FIXME: Can't create DB links as dbJLink types yet,
|
||||
* dbLock.c doesn't have any way to find/track them.
|
||||
*/
|
||||
dbJLinkInit(plink);
|
||||
return;
|
||||
}
|
||||
|
||||
if (plink->type != PV_LINK)
|
||||
return;
|
||||
|
||||
if (plink == &precord->tsel)
|
||||
TSEL_modified(plink);
|
||||
|
||||
if (ptarget) {
|
||||
/* It's a DB link */
|
||||
dbDbAddLink(locker, plink, dbfType, ptarget);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Make it a CA link */
|
||||
if (dbfType == DBF_INLINK)
|
||||
plink->value.pv_link.pvlMask |= pvlOptInpNative;
|
||||
|
||||
dbCaAddLink(locker, plink, dbfType);
|
||||
if (dbfType == DBF_FWDLINK) {
|
||||
char *pperiod = strrchr(plink->value.pv_link.pvname, '.');
|
||||
|
||||
if (pperiod && strstr(pperiod, "PROC"))
|
||||
plink->value.pv_link.pvlMask |= pvlOptFWD;
|
||||
}
|
||||
}
|
||||
|
||||
void dbLinkOpen(struct link *plink)
|
||||
{
|
||||
lset *plset = plink->lset;
|
||||
|
||||
if (plset && plset->openLink)
|
||||
plset->openLink(plink);
|
||||
}
|
||||
|
||||
void dbRemoveLink(struct dbLocker *locker, struct link *plink)
|
||||
{
|
||||
lset *plset = plink->lset;
|
||||
|
||||
if (plset) {
|
||||
if (plset->removeLink)
|
||||
plset->removeLink(locker, plink);
|
||||
plink->lset = NULL;
|
||||
}
|
||||
if (plink->type == JSON_LINK)
|
||||
plink->value.json.jlink = NULL;
|
||||
}
|
||||
|
||||
int dbLinkIsDefined(const struct link *plink)
|
||||
{
|
||||
return (plink->lset != 0);
|
||||
}
|
||||
|
||||
int dbLinkIsConstant(const struct link *plink)
|
||||
{
|
||||
lset *plset = plink->lset;
|
||||
|
||||
return !plset || plset->isConstant;
|
||||
}
|
||||
|
||||
int dbLinkIsVolatile(const struct link *plink)
|
||||
{
|
||||
lset *plset = plink->lset;
|
||||
|
||||
return plset && plset->isVolatile;
|
||||
}
|
||||
|
||||
long dbLoadLink(struct link *plink, short dbrType, void *pbuffer)
|
||||
{
|
||||
lset *plset = plink->lset;
|
||||
|
||||
if (plset && plset->loadScalar)
|
||||
return plset->loadScalar(plink, dbrType, pbuffer);
|
||||
|
||||
return S_db_noLSET;
|
||||
}
|
||||
|
||||
long dbLoadLinkLS(struct link *plink, char *pbuffer, epicsUInt32 size,
|
||||
epicsUInt32 *plen)
|
||||
{
|
||||
lset *plset = plink->lset;
|
||||
|
||||
if (plset && plset->loadLS)
|
||||
return plset->loadLS(plink, pbuffer, size, plen);
|
||||
|
||||
return S_db_noLSET;
|
||||
}
|
||||
|
||||
long dbLoadLinkArray(struct link *plink, short dbrType, void *pbuffer,
|
||||
long *pnRequest)
|
||||
{
|
||||
lset *plset = plink->lset;
|
||||
|
||||
if (plset && plset->loadArray)
|
||||
return plset->loadArray(plink, dbrType, pbuffer, pnRequest);
|
||||
|
||||
return S_db_noLSET;
|
||||
}
|
||||
|
||||
int dbIsLinkConnected(const struct link *plink)
|
||||
{
|
||||
lset *plset = plink->lset;
|
||||
|
||||
if (!plset || !plset->isConnected)
|
||||
return FALSE;
|
||||
|
||||
return plset->isConnected(plink);
|
||||
}
|
||||
|
||||
int dbGetLinkDBFtype(const struct link *plink)
|
||||
{
|
||||
lset *plset = plink->lset;
|
||||
|
||||
if (!plset || !plset->getDBFtype)
|
||||
return -1;
|
||||
|
||||
return plset->getDBFtype(plink);
|
||||
}
|
||||
|
||||
long dbGetNelements(const struct link *plink, long *nelements)
|
||||
{
|
||||
lset *plset = plink->lset;
|
||||
|
||||
if (!plset || !plset->getElements)
|
||||
return S_db_noLSET;
|
||||
|
||||
return plset->getElements(plink, nelements);
|
||||
}
|
||||
|
||||
long dbTryGetLink(struct link *plink, short dbrType, void *pbuffer,
|
||||
long *pnRequest)
|
||||
{
|
||||
lset *plset = plink->lset;
|
||||
|
||||
if (!plset || !plset->getValue)
|
||||
return S_db_noLSET;
|
||||
|
||||
return plset->getValue(plink, dbrType, pbuffer, pnRequest);
|
||||
}
|
||||
|
||||
long dbGetLink(struct link *plink, short dbrType, void *pbuffer,
|
||||
long *poptions, long *pnRequest)
|
||||
{
|
||||
struct dbCommon *precord = plink->precord;
|
||||
long status;
|
||||
|
||||
if (poptions && *poptions) {
|
||||
printf("dbGetLink: Use of poptions no longer supported\n");
|
||||
*poptions = 0;
|
||||
}
|
||||
|
||||
status = dbTryGetLink(plink, dbrType, pbuffer, pnRequest);
|
||||
if (status == S_db_noLSET)
|
||||
return -1;
|
||||
if (status)
|
||||
recGblSetSevr(precord, LINK_ALARM, INVALID_ALARM);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
long dbGetControlLimits(const struct link *plink, double *low, double *high)
|
||||
{
|
||||
lset *plset = plink->lset;
|
||||
|
||||
if (!plset || !plset->getControlLimits)
|
||||
return S_db_noLSET;
|
||||
|
||||
return plset->getControlLimits(plink, low, high);
|
||||
}
|
||||
|
||||
long dbGetGraphicLimits(const struct link *plink, double *low, double *high)
|
||||
{
|
||||
lset *plset = plink->lset;
|
||||
|
||||
if (!plset || !plset->getGraphicLimits)
|
||||
return S_db_noLSET;
|
||||
|
||||
return plset->getGraphicLimits(plink, low, high);
|
||||
}
|
||||
|
||||
long dbGetAlarmLimits(const struct link *plink, double *lolo, double *low,
|
||||
double *high, double *hihi)
|
||||
{
|
||||
lset *plset = plink->lset;
|
||||
|
||||
if (!plset || !plset->getAlarmLimits)
|
||||
return S_db_noLSET;
|
||||
|
||||
return plset->getAlarmLimits(plink, lolo, low, high, hihi);
|
||||
}
|
||||
|
||||
long dbGetPrecision(const struct link *plink, short *precision)
|
||||
{
|
||||
lset *plset = plink->lset;
|
||||
|
||||
if (!plset || !plset->getPrecision)
|
||||
return S_db_noLSET;
|
||||
|
||||
return plset->getPrecision(plink, precision);
|
||||
}
|
||||
|
||||
long dbGetUnits(const struct link *plink, char *units, int unitsSize)
|
||||
{
|
||||
lset *plset = plink->lset;
|
||||
|
||||
if (!plset || !plset->getUnits)
|
||||
return S_db_noLSET;
|
||||
|
||||
return plset->getUnits(plink, units, unitsSize);
|
||||
}
|
||||
|
||||
long dbGetAlarm(const struct link *plink, epicsEnum16 *status,
|
||||
epicsEnum16 *severity)
|
||||
{
|
||||
lset *plset = plink->lset;
|
||||
|
||||
if (!plset || !plset->getAlarm)
|
||||
return S_db_noLSET;
|
||||
|
||||
return plset->getAlarm(plink, status, severity);
|
||||
}
|
||||
|
||||
long dbGetTimeStamp(const struct link *plink, epicsTimeStamp *pstamp)
|
||||
{
|
||||
lset *plset = plink->lset;
|
||||
|
||||
if (!plset || !plset->getTimeStamp)
|
||||
return S_db_noLSET;
|
||||
|
||||
return plset->getTimeStamp(plink, pstamp);
|
||||
}
|
||||
|
||||
long dbPutLink(struct link *plink, short dbrType, const void *pbuffer,
|
||||
long nRequest)
|
||||
{
|
||||
lset *plset = plink->lset;
|
||||
long status;
|
||||
|
||||
if (!plset || !plset->putValue)
|
||||
return S_db_noLSET;
|
||||
|
||||
status = plset->putValue(plink, dbrType, pbuffer, nRequest);
|
||||
if (status) {
|
||||
struct dbCommon *precord = plink->precord;
|
||||
|
||||
recGblSetSevr(precord, LINK_ALARM, INVALID_ALARM);
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
void dbLinkAsyncComplete(struct link *plink)
|
||||
{
|
||||
dbCommon *pdbCommon = plink->precord;
|
||||
|
||||
dbScanLock(pdbCommon);
|
||||
pdbCommon->rset->process(pdbCommon);
|
||||
dbScanUnlock(pdbCommon);
|
||||
}
|
||||
|
||||
long dbPutLinkAsync(struct link *plink, short dbrType, const void *pbuffer,
|
||||
long nRequest)
|
||||
{
|
||||
lset *plset = plink->lset;
|
||||
long status;
|
||||
|
||||
if (!plset || !plset->putAsync)
|
||||
return S_db_noLSET;
|
||||
|
||||
status = plset->putAsync(plink, dbrType, pbuffer, nRequest);
|
||||
if (status) {
|
||||
struct dbCommon *precord = plink->precord;
|
||||
|
||||
recGblSetSevr(precord, LINK_ALARM, INVALID_ALARM);
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
void dbScanFwdLink(struct link *plink)
|
||||
{
|
||||
lset *plset = plink->lset;
|
||||
|
||||
if (plset && plset->scanForward)
|
||||
plset->scanForward(plink);
|
||||
}
|
||||
|
||||
long dbLinkDoLocked(struct link *plink, dbLinkUserCallback rtn,
|
||||
void *priv)
|
||||
{
|
||||
lset *plset = plink->lset;
|
||||
|
||||
if (!rtn || !plset || !plset->doLocked)
|
||||
return S_db_noLSET;
|
||||
|
||||
return plset->doLocked(plink, rtn, priv);
|
||||
}
|
||||
|
||||
|
||||
/* Helper functions for long string support */
|
||||
|
||||
long dbGetLinkLS(struct link *plink, char *pbuffer, epicsUInt32 size,
|
||||
epicsUInt32 *plen)
|
||||
{
|
||||
int dtyp = dbGetLinkDBFtype(plink);
|
||||
long len = size;
|
||||
long status;
|
||||
|
||||
if (dtyp < 0) /* Not connected */
|
||||
return 0;
|
||||
|
||||
if (dtyp == DBR_CHAR || dtyp == DBF_UCHAR) {
|
||||
status = dbGetLink(plink, dtyp, pbuffer, 0, &len);
|
||||
}
|
||||
else if (size >= MAX_STRING_SIZE)
|
||||
status = dbGetLink(plink, DBR_STRING, pbuffer, 0, 0);
|
||||
else {
|
||||
/* pbuffer is too small to fetch using DBR_STRING */
|
||||
char tmp[MAX_STRING_SIZE];
|
||||
|
||||
status = dbGetLink(plink, DBR_STRING, tmp, 0, 0);
|
||||
if (!status)
|
||||
strncpy(pbuffer, tmp, len - 1);
|
||||
}
|
||||
if (!status) {
|
||||
pbuffer[--len] = 0;
|
||||
*plen = (epicsUInt32) strlen(pbuffer) + 1;
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
long dbPutLinkLS(struct link *plink, char *pbuffer, epicsUInt32 len)
|
||||
{
|
||||
int dtyp = dbGetLinkDBFtype(plink);
|
||||
|
||||
if (dtyp < 0)
|
||||
return 0; /* Not connected */
|
||||
|
||||
if (dtyp == DBR_CHAR || dtyp == DBF_UCHAR)
|
||||
return dbPutLink(plink, dtyp, pbuffer, len);
|
||||
|
||||
return dbPutLink(plink, DBR_STRING, pbuffer, 1);
|
||||
}
|
||||
@@ -0,0 +1,141 @@
|
||||
/*************************************************************************\
|
||||
* Copyright (c) 2010 The UChicago Argonne LLC, as Operator of Argonne
|
||||
* National Laboratory.
|
||||
* Copyright (c) 2002 The Regents of the University of California, as
|
||||
* Operator of Los Alamos National Laboratory.
|
||||
* EPICS BASE is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
\*************************************************************************/
|
||||
/* dbLink.h
|
||||
*
|
||||
* Created on: Mar 21, 2010
|
||||
* Author: Andrew Johnson
|
||||
*/
|
||||
|
||||
#ifndef INC_dbLink_H
|
||||
#define INC_dbLink_H
|
||||
|
||||
#include "link.h"
|
||||
#include "shareLib.h"
|
||||
#include "epicsTypes.h"
|
||||
#include "epicsTime.h"
|
||||
#include "dbAddr.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct dbLocker;
|
||||
|
||||
typedef long (*dbLinkUserCallback)(struct link *plink, void *priv);
|
||||
|
||||
typedef struct lset {
|
||||
/* Characteristics of the link type */
|
||||
const unsigned isConstant:1;
|
||||
const unsigned isVolatile:1;
|
||||
|
||||
/* Activation */
|
||||
void (*openLink)(struct link *plink);
|
||||
|
||||
/* Destructor */
|
||||
void (*removeLink)(struct dbLocker *locker, struct link *plink);
|
||||
|
||||
/* Const init, data type hinting */
|
||||
long (*loadScalar)(struct link *plink, short dbrType, void *pbuffer);
|
||||
long (*loadLS)(struct link *plink, char *pbuffer, epicsUInt32 size,
|
||||
epicsUInt32 *plen);
|
||||
long (*loadArray)(struct link *plink, short dbrType, void *pbuffer,
|
||||
long *pnRequest);
|
||||
|
||||
/* Metadata */
|
||||
int (*isConnected)(const struct link *plink);
|
||||
int (*getDBFtype)(const struct link *plink);
|
||||
long (*getElements)(const struct link *plink, long *nelements);
|
||||
|
||||
/* Get data */
|
||||
long (*getValue)(struct link *plink, short dbrType, void *pbuffer,
|
||||
long *pnRequest);
|
||||
long (*getControlLimits)(const struct link *plink, double *lo, double *hi);
|
||||
long (*getGraphicLimits)(const struct link *plink, double *lo, double *hi);
|
||||
long (*getAlarmLimits)(const struct link *plink, double *lolo, double *lo,
|
||||
double *hi, double *hihi);
|
||||
long (*getPrecision)(const struct link *plink, short *precision);
|
||||
long (*getUnits)(const struct link *plink, char *units, int unitsSize);
|
||||
long (*getAlarm)(const struct link *plink, epicsEnum16 *status,
|
||||
epicsEnum16 *severity);
|
||||
long (*getTimeStamp)(const struct link *plink, epicsTimeStamp *pstamp);
|
||||
|
||||
/* Put data */
|
||||
long (*putValue)(struct link *plink, short dbrType,
|
||||
const void *pbuffer, long nRequest);
|
||||
long (*putAsync)(struct link *plink, short dbrType,
|
||||
const void *pbuffer, long nRequest);
|
||||
|
||||
/* Process */
|
||||
void (*scanForward)(struct link *plink);
|
||||
|
||||
/* Atomicity */
|
||||
long (*doLocked)(struct link *plink, dbLinkUserCallback rtn, void *priv);
|
||||
} lset;
|
||||
|
||||
#define dbGetSevr(link, sevr) \
|
||||
dbGetAlarm(link, NULL, sevr)
|
||||
|
||||
epicsShareFunc void dbInitLink(struct link *plink, short dbfType);
|
||||
epicsShareFunc void dbAddLink(struct dbLocker *locker, struct link *plink,
|
||||
short dbfType, DBADDR *ptarget);
|
||||
|
||||
epicsShareFunc void dbLinkOpen(struct link *plink);
|
||||
epicsShareFunc void dbRemoveLink(struct dbLocker *locker, struct link *plink);
|
||||
|
||||
epicsShareFunc int dbLinkIsDefined(const struct link *plink); /* 0 or 1 */
|
||||
epicsShareFunc int dbLinkIsConstant(const struct link *plink); /* 0 or 1 */
|
||||
epicsShareFunc int dbLinkIsVolatile(const struct link *plink); /* 0 or 1 */
|
||||
|
||||
epicsShareFunc long dbLoadLink(struct link *plink, short dbrType,
|
||||
void *pbuffer);
|
||||
epicsShareFunc long dbLoadLinkArray(struct link *, short dbrType, void *pbuffer,
|
||||
long *pnRequest);
|
||||
|
||||
epicsShareFunc long dbGetNelements(const struct link *plink, long *nelements);
|
||||
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,
|
||||
long *nRequest);
|
||||
epicsShareFunc long dbGetLink(struct link *, short dbrType, void *pbuffer,
|
||||
long *options, long *nRequest);
|
||||
epicsShareFunc long dbGetControlLimits(const struct link *plink, double *low,
|
||||
double *high);
|
||||
epicsShareFunc long dbGetGraphicLimits(const struct link *plink, double *low,
|
||||
double *high);
|
||||
epicsShareFunc long dbGetAlarmLimits(const struct link *plink, double *lolo,
|
||||
double *low, double *high, double *hihi);
|
||||
epicsShareFunc long dbGetPrecision(const struct link *plink, short *precision);
|
||||
epicsShareFunc long dbGetUnits(const struct link *plink, char *units,
|
||||
int unitsSize);
|
||||
epicsShareFunc long dbGetAlarm(const struct link *plink, epicsEnum16 *status,
|
||||
epicsEnum16 *severity);
|
||||
epicsShareFunc long dbGetTimeStamp(const struct link *plink,
|
||||
epicsTimeStamp *pstamp);
|
||||
epicsShareFunc long dbPutLink(struct link *plink, short dbrType,
|
||||
const void *pbuffer, long nRequest);
|
||||
epicsShareFunc void dbLinkAsyncComplete(struct link *plink);
|
||||
epicsShareFunc long dbPutLinkAsync(struct link *plink, short dbrType,
|
||||
const void *pbuffer, long nRequest);
|
||||
epicsShareFunc void dbScanFwdLink(struct link *plink);
|
||||
|
||||
epicsShareFunc long dbLinkDoLocked(struct link *plink, dbLinkUserCallback rtn,
|
||||
void *priv);
|
||||
|
||||
epicsShareFunc long dbLoadLinkLS(struct link *plink, char *pbuffer,
|
||||
epicsUInt32 size, epicsUInt32 *plen);
|
||||
epicsShareFunc long dbGetLinkLS(struct link *plink, char *pbuffer,
|
||||
epicsUInt32 buffer_size, epicsUInt32 *plen);
|
||||
epicsShareFunc long dbPutLinkLS(struct link *plink, char *pbuffer,
|
||||
epicsUInt32 len);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* INC_dbLink_H */
|
||||
@@ -0,0 +1,990 @@
|
||||
/*************************************************************************\
|
||||
* 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.
|
||||
\*************************************************************************/
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "cantProceed.h"
|
||||
#include "dbDefs.h"
|
||||
#include "ellLib.h"
|
||||
#include "epicsAssert.h"
|
||||
#include "epicsAtomic.h"
|
||||
#include "epicsMutex.h"
|
||||
#include "epicsPrint.h"
|
||||
#include "epicsSpin.h"
|
||||
#include "epicsStdio.h"
|
||||
#include "epicsThread.h"
|
||||
#include "errMdef.h"
|
||||
|
||||
#define epicsExportSharedSymbols
|
||||
#include "dbAccessDefs.h"
|
||||
#include "dbAddr.h"
|
||||
#include "dbBase.h"
|
||||
#include "dbLink.h"
|
||||
#include "dbCommon.h"
|
||||
#include "dbFldTypes.h"
|
||||
#include "dbLockPvt.h"
|
||||
#include "dbStaticLib.h"
|
||||
#include "link.h"
|
||||
|
||||
typedef struct dbScanLockNode dbScanLockNode;
|
||||
|
||||
static epicsThreadOnceId dbLockOnceInit = EPICS_THREAD_ONCE_INIT;
|
||||
|
||||
static ELLLIST lockSetsActive; /* in use */
|
||||
#ifndef LOCKSET_NOFREE
|
||||
static ELLLIST lockSetsFree; /* free list */
|
||||
#endif
|
||||
|
||||
/* Guard the global list */
|
||||
static epicsMutexId lockSetsGuard;
|
||||
|
||||
#ifndef LOCKSET_NOCNT
|
||||
/* Counter which we increment whenever
|
||||
* any lockRecord::plockSet is changed.
|
||||
* An optimization to avoid checking lockSet
|
||||
* associations when no links have changed.
|
||||
*/
|
||||
static size_t recomputeCnt;
|
||||
#endif
|
||||
|
||||
/*private routines */
|
||||
static void dbLockOnce(void* ignore)
|
||||
{
|
||||
lockSetsGuard = epicsMutexMustCreate();
|
||||
}
|
||||
|
||||
/* global ID number assigned to each lockSet on creation.
|
||||
* When the free-list is in use will never exceed
|
||||
* the number of records +1.
|
||||
* Without the free-list it can roll over, potentially
|
||||
* leading to duplicate IDs.
|
||||
*/
|
||||
static size_t next_id = 1;
|
||||
|
||||
static lockSet* makeSet(void)
|
||||
{
|
||||
lockSet *ls;
|
||||
int iref;
|
||||
epicsMutexMustLock(lockSetsGuard);
|
||||
#ifndef LOCKSET_NOFREE
|
||||
ls = (lockSet*)ellGet(&lockSetsFree);
|
||||
if(!ls) {
|
||||
epicsMutexUnlock(lockSetsGuard);
|
||||
#endif
|
||||
|
||||
ls=dbCalloc(1,sizeof(*ls));
|
||||
ellInit(&ls->lockRecordList);
|
||||
ls->lock = epicsMutexMustCreate();
|
||||
ls->id = epicsAtomicIncrSizeT(&next_id);
|
||||
|
||||
#ifndef LOCKSET_NOFREE
|
||||
epicsMutexMustLock(lockSetsGuard);
|
||||
}
|
||||
#endif
|
||||
/* the initial reference for the first lockRecord */
|
||||
iref = epicsAtomicIncrIntT(&ls->refcount);
|
||||
ellAdd(&lockSetsActive, &ls->node);
|
||||
epicsMutexUnlock(lockSetsGuard);
|
||||
|
||||
assert(ls->id>0);
|
||||
assert(iref>0);
|
||||
assert(ellCount(&ls->lockRecordList)==0);
|
||||
|
||||
return ls;
|
||||
}
|
||||
|
||||
unsigned long dbLockGetRefs(struct dbCommon* prec)
|
||||
{
|
||||
return (unsigned long)epicsAtomicGetIntT(&prec->lset->plockSet->refcount);
|
||||
}
|
||||
|
||||
unsigned long dbLockCountSets(void)
|
||||
{
|
||||
unsigned long count;
|
||||
epicsMutexMustLock(lockSetsGuard);
|
||||
count = (unsigned long)ellCount(&lockSetsActive);
|
||||
epicsMutexUnlock(lockSetsGuard);
|
||||
return count;
|
||||
}
|
||||
|
||||
/* caller must lock accessLock.*/
|
||||
void dbLockIncRef(lockSet* ls)
|
||||
{
|
||||
int cnt = epicsAtomicIncrIntT(&ls->refcount);
|
||||
if(cnt<=1) {
|
||||
errlogPrintf("dbLockIncRef(%p) on dead lockSet. refs: %d\n", ls, cnt);
|
||||
cantProceed(NULL);
|
||||
}
|
||||
}
|
||||
|
||||
/* caller must lock accessLock.
|
||||
* lockSet must *not* be locked
|
||||
*/
|
||||
void dbLockDecRef(lockSet *ls)
|
||||
{
|
||||
int cnt = epicsAtomicDecrIntT(&ls->refcount);
|
||||
assert(cnt>=0);
|
||||
|
||||
if(cnt)
|
||||
return;
|
||||
|
||||
/* lockSet is unused and will be free'd */
|
||||
|
||||
/* not necessary as no one else (should) hold a reference,
|
||||
* but lock anyway to quiet valgrind
|
||||
*/
|
||||
epicsMutexMustLock(ls->lock);
|
||||
|
||||
if(ellCount(&ls->lockRecordList)!=0) {
|
||||
errlogPrintf("dbLockDecRef(%p) would free lockSet with %d records\n",
|
||||
ls, ellCount(&ls->lockRecordList));
|
||||
cantProceed(NULL);
|
||||
}
|
||||
|
||||
epicsMutexUnlock(ls->lock);
|
||||
|
||||
epicsMutexMustLock(lockSetsGuard);
|
||||
ellDelete(&lockSetsActive, &ls->node);
|
||||
#ifndef LOCKSET_NOFREE
|
||||
ellAdd(&lockSetsFree, &ls->node);
|
||||
#else
|
||||
epicsMutexDestroy(ls->lock);
|
||||
memset(ls, 0, sizeof(*ls)); /* paranoia */
|
||||
free(ls);
|
||||
#endif
|
||||
epicsMutexUnlock(lockSetsGuard);
|
||||
}
|
||||
|
||||
lockSet* dbLockGetRef(lockRecord *lr)
|
||||
{
|
||||
lockSet *ls;
|
||||
epicsSpinLock(lr->spin);
|
||||
ls = lr->plockSet;
|
||||
dbLockIncRef(ls);
|
||||
epicsSpinUnlock(lr->spin);
|
||||
return ls;
|
||||
}
|
||||
|
||||
unsigned long dbLockGetLockId(dbCommon *precord)
|
||||
{
|
||||
unsigned long id=0;
|
||||
epicsSpinLock(precord->lset->spin);
|
||||
id = precord->lset->plockSet->id;
|
||||
epicsSpinUnlock(precord->lset->spin);
|
||||
return id;
|
||||
}
|
||||
|
||||
void dbScanLock(dbCommon *precord)
|
||||
{
|
||||
int cnt;
|
||||
lockRecord * const lr = precord->lset;
|
||||
lockSet *ls;
|
||||
|
||||
assert(lr);
|
||||
|
||||
ls = dbLockGetRef(lr);
|
||||
assert(epicsAtomicGetIntT(&ls->refcount)>0);
|
||||
|
||||
retry:
|
||||
epicsMutexMustLock(ls->lock);
|
||||
|
||||
epicsSpinLock(lr->spin);
|
||||
if(ls!=lr->plockSet) {
|
||||
/* oops, collided with recompute.
|
||||
* take a reference to the new lockSet.
|
||||
*/
|
||||
lockSet *ls2 = lr->plockSet;
|
||||
int newcnt = epicsAtomicIncrIntT(&ls2->refcount);
|
||||
assert(newcnt>=2); /* at least lockRecord and us */
|
||||
epicsSpinUnlock(lr->spin);
|
||||
|
||||
epicsMutexUnlock(ls->lock);
|
||||
dbLockDecRef(ls);
|
||||
|
||||
ls = ls2;
|
||||
goto retry;
|
||||
}
|
||||
epicsSpinUnlock(lr->spin);
|
||||
|
||||
/* Release reference taken within this
|
||||
* function. The count will *never* fall to zero
|
||||
* as the lockRecords can't be changed while
|
||||
* we hold the lock.
|
||||
*/
|
||||
cnt = epicsAtomicDecrIntT(&ls->refcount);
|
||||
assert(cnt>0);
|
||||
|
||||
#ifdef LOCKSET_DEBUG
|
||||
if(ls->owner) {
|
||||
assert(ls->owner==epicsThreadGetIdSelf());
|
||||
assert(ls->ownercount>=1);
|
||||
ls->ownercount++;
|
||||
} else {
|
||||
assert(ls->ownercount==0);
|
||||
ls->owner = epicsThreadGetIdSelf();
|
||||
ls->ownercount = 1;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void dbScanUnlock(dbCommon *precord)
|
||||
{
|
||||
lockSet *ls = precord->lset->plockSet;
|
||||
dbLockIncRef(ls);
|
||||
#ifdef LOCKSET_DEBUG
|
||||
assert(ls->owner==epicsThreadGetIdSelf());
|
||||
assert(ls->ownercount>=1);
|
||||
ls->ownercount--;
|
||||
if(ls->ownercount==0)
|
||||
ls->owner = NULL;
|
||||
#endif
|
||||
epicsMutexUnlock(ls->lock);
|
||||
dbLockDecRef(ls);
|
||||
}
|
||||
|
||||
static
|
||||
int lrrcompare(const void *rawA, const void *rawB)
|
||||
{
|
||||
const lockRecordRef *refA=rawA, *refB=rawB;
|
||||
const lockSet *A=refA->plockSet, *B=refB->plockSet;
|
||||
if(!A && !B)
|
||||
return 0; /* NULL == NULL */
|
||||
else if(!A)
|
||||
return 1; /* NULL > !NULL */
|
||||
else if(!B)
|
||||
return -1; /* !NULL < NULL */
|
||||
else if(A < B)
|
||||
return -1;
|
||||
else if(A > B)
|
||||
return 1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Call w/ update=1 before locking to update cached lockSet entries.
|
||||
* Call w/ update=0 after locking to verify that lockRecord weren't updated
|
||||
*/
|
||||
static
|
||||
int dbLockUpdateRefs(dbLocker *locker, int update)
|
||||
{
|
||||
int changed = 0;
|
||||
size_t i, nlock = locker->maxrefs;
|
||||
|
||||
#ifndef LOCKSET_NOCNT
|
||||
const size_t recomp = epicsAtomicGetSizeT(&recomputeCnt);
|
||||
if(locker->recomp!=recomp) {
|
||||
#endif
|
||||
/* some lockset recompute happened.
|
||||
* must re-check our references.
|
||||
*/
|
||||
|
||||
for(i=0; i<nlock; i++) {
|
||||
lockRecordRef *ref = &locker->refs[i];
|
||||
lockSet *oldref = NULL;
|
||||
if(!ref->plr) { /* this lockRecord slot not used */
|
||||
assert(!ref->plockSet);
|
||||
continue;
|
||||
}
|
||||
|
||||
epicsSpinLock(ref->plr->spin);
|
||||
if(ref->plockSet!=ref->plr->plockSet) {
|
||||
changed = 1;
|
||||
if(update) {
|
||||
/* exchange saved lockSet reference */
|
||||
oldref = ref->plockSet; /* will be NULL on first iteration */
|
||||
ref->plockSet = ref->plr->plockSet;
|
||||
dbLockIncRef(ref->plockSet);
|
||||
}
|
||||
}
|
||||
epicsSpinUnlock(ref->plr->spin);
|
||||
if(oldref)
|
||||
dbLockDecRef(oldref);
|
||||
if(!update && changed)
|
||||
return changed;
|
||||
}
|
||||
#ifndef LOCKSET_NOCNT
|
||||
/* Use the value captured before we started.
|
||||
* If it has changed in the intrim we will catch this later
|
||||
* during the update==0 pass (which triggers a re-try)
|
||||
*/
|
||||
if(update)
|
||||
locker->recomp = recomp;
|
||||
}
|
||||
#endif
|
||||
|
||||
if(changed && update) {
|
||||
qsort(locker->refs, nlock, sizeof(lockRecordRef),
|
||||
&lrrcompare);
|
||||
}
|
||||
return changed;
|
||||
}
|
||||
|
||||
void dbLockerPrepare(struct dbLocker *locker,
|
||||
struct dbCommon * const *precs,
|
||||
size_t nrecs)
|
||||
{
|
||||
size_t i;
|
||||
locker->maxrefs = nrecs;
|
||||
/* intentionally spoil the recomp count to ensure that
|
||||
* references will be updated this first time
|
||||
*/
|
||||
#ifndef LOCKSET_NOCNT
|
||||
locker->recomp = epicsAtomicGetSizeT(&recomputeCnt)-1;
|
||||
#endif
|
||||
|
||||
for(i=0; i<nrecs; i++) {
|
||||
locker->refs[i].plr = precs[i] ? precs[i]->lset : NULL;
|
||||
}
|
||||
|
||||
/* acquire a reference to all lockRecords */
|
||||
dbLockUpdateRefs(locker, 1);
|
||||
}
|
||||
|
||||
dbLocker *dbLockerAlloc(dbCommon * const *precs,
|
||||
size_t nrecs,
|
||||
unsigned int flags)
|
||||
{
|
||||
size_t Nextra = nrecs>DBLOCKER_NALLOC ? nrecs-DBLOCKER_NALLOC : 0;
|
||||
dbLocker *locker = calloc(1, sizeof(*locker)+Nextra*sizeof(lockRecordRef));
|
||||
|
||||
if(locker)
|
||||
dbLockerPrepare(locker, precs, nrecs);
|
||||
|
||||
return locker;
|
||||
}
|
||||
|
||||
void dbLockerFinalize(dbLocker *locker)
|
||||
{
|
||||
size_t i;
|
||||
assert(ellCount(&locker->locked)==0);
|
||||
|
||||
/* release references taken in dbLockUpdateRefs() */
|
||||
for(i=0; i<locker->maxrefs; i++) {
|
||||
if(locker->refs[i].plockSet)
|
||||
dbLockDecRef(locker->refs[i].plockSet);
|
||||
}
|
||||
}
|
||||
|
||||
void dbLockerFree(dbLocker *locker)
|
||||
{
|
||||
dbLockerFinalize(locker);
|
||||
free(locker);
|
||||
}
|
||||
|
||||
/* Lock the given list of records.
|
||||
* This function modifies its arguments.
|
||||
*/
|
||||
void dbScanLockMany(dbLocker* locker)
|
||||
{
|
||||
size_t i, nlock = locker->maxrefs;
|
||||
lockSet *plock;
|
||||
#ifdef LOCKSET_DEBUG
|
||||
const epicsThreadId myself = epicsThreadGetIdSelf();
|
||||
#endif
|
||||
|
||||
if(ellCount(&locker->locked)!=0) {
|
||||
cantProceed("dbScanLockMany(%p) already locked. Recursive locking not allowed", locker);
|
||||
return;
|
||||
}
|
||||
|
||||
retry:
|
||||
assert(ellCount(&locker->locked)==0);
|
||||
dbLockUpdateRefs(locker, 1);
|
||||
|
||||
for(i=0, plock=NULL; i<nlock; i++) {
|
||||
lockRecordRef *ref = &locker->refs[i];
|
||||
|
||||
/* skip duplicates (same lockSet
|
||||
* referenced by more than one lockRecord).
|
||||
* Sorting will group these together.
|
||||
*/
|
||||
if(!ref->plr || (plock && plock==ref->plockSet))
|
||||
continue;
|
||||
plock = ref->plockSet;
|
||||
|
||||
epicsMutexMustLock(plock->lock);
|
||||
assert(plock->ownerlocker==NULL);
|
||||
plock->ownerlocker = locker;
|
||||
ellAdd(&locker->locked, &plock->lockernode);
|
||||
/* An extra ref for the locked list */
|
||||
dbLockIncRef(plock);
|
||||
|
||||
#ifdef LOCKSET_DEBUG
|
||||
if(plock->owner) {
|
||||
if(plock->owner!=myself || plock->ownercount<1) {
|
||||
errlogPrintf("dbScanLockMany(%p) ownership violation %p (%p) %u\n",
|
||||
locker, plock->owner, myself, plock->ownercount);
|
||||
cantProceed(NULL);
|
||||
}
|
||||
plock->ownercount++;
|
||||
} else {
|
||||
assert(plock->ownercount==0);
|
||||
plock->owner = myself;
|
||||
plock->ownercount = 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
if(dbLockUpdateRefs(locker, 0)) {
|
||||
/* oops, collided with recompute */
|
||||
dbScanUnlockMany(locker);
|
||||
goto retry;
|
||||
}
|
||||
if(nlock!=0 && ellCount(&locker->locked)<=0) {
|
||||
/* if we have at least one lockRecord, then we will always lock
|
||||
* at least its present lockSet
|
||||
*/
|
||||
errlogPrintf("dbScanLockMany(%p) didn't lock anything\n", locker);
|
||||
cantProceed(NULL);
|
||||
}
|
||||
}
|
||||
|
||||
void dbScanUnlockMany(dbLocker* locker)
|
||||
{
|
||||
ELLNODE *cur;
|
||||
#ifdef LOCKSET_DEBUG
|
||||
const epicsThreadId myself = epicsThreadGetIdSelf();
|
||||
#endif
|
||||
|
||||
while((cur=ellGet(&locker->locked))!=NULL) {
|
||||
lockSet *plock = CONTAINER(cur, lockSet, lockernode);
|
||||
|
||||
assert(plock->ownerlocker==locker);
|
||||
plock->ownerlocker = NULL;
|
||||
#ifdef LOCKSET_DEBUG
|
||||
assert(plock->owner==myself);
|
||||
assert(plock->ownercount>=1);
|
||||
plock->ownercount--;
|
||||
if(plock->ownercount==0)
|
||||
plock->owner = NULL;
|
||||
#endif
|
||||
|
||||
epicsMutexUnlock(plock->lock);
|
||||
/* release ref for locked list */
|
||||
dbLockDecRef(plock);
|
||||
}
|
||||
}
|
||||
|
||||
typedef int (*reciter)(void*, DBENTRY*);
|
||||
static int forEachRecord(void *priv, dbBase *pdbbase, reciter fn)
|
||||
{
|
||||
long status;
|
||||
int ret = 0;
|
||||
DBENTRY dbentry;
|
||||
dbInitEntry(pdbbase,&dbentry);
|
||||
status = dbFirstRecordType(&dbentry);
|
||||
while(!status)
|
||||
{
|
||||
status = dbFirstRecord(&dbentry);
|
||||
while(!status)
|
||||
{
|
||||
/* skip alias names */
|
||||
if(!dbentry.precnode->recordname[0] || dbentry.precnode->flags & DBRN_FLAGS_ISALIAS) {
|
||||
/* skip */
|
||||
} else {
|
||||
ret = fn(priv, &dbentry);
|
||||
if(ret)
|
||||
goto done;
|
||||
}
|
||||
|
||||
status = dbNextRecord(&dbentry);
|
||||
}
|
||||
|
||||
status = dbNextRecordType(&dbentry);
|
||||
}
|
||||
done:
|
||||
dbFinishEntry(&dbentry);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int createLockRecord(void* junk, DBENTRY* pdbentry)
|
||||
{
|
||||
dbCommon *prec = pdbentry->precnode->precord;
|
||||
lockRecord *lrec;
|
||||
assert(!prec->lset);
|
||||
|
||||
/* TODO: one allocation for all records? */
|
||||
lrec = callocMustSucceed(1, sizeof(*lrec), "lockRecord");
|
||||
lrec->spin = epicsSpinCreate();
|
||||
if(!lrec->spin)
|
||||
cantProceed("no memory for spinlock in lockRecord");
|
||||
|
||||
lrec->precord = prec;
|
||||
|
||||
prec->lset = lrec;
|
||||
|
||||
prec->lset->plockSet = makeSet();
|
||||
ellAdd(&prec->lset->plockSet->lockRecordList, &prec->lset->node);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void dbLockInitRecords(dbBase *pdbbase)
|
||||
{
|
||||
epicsThreadOnce(&dbLockOnceInit, &dbLockOnce, NULL);
|
||||
|
||||
/* create all lockRecords and lockSets */
|
||||
forEachRecord(NULL, pdbbase, &createLockRecord);
|
||||
}
|
||||
|
||||
static int freeLockRecord(void* junk, DBENTRY* pdbentry)
|
||||
{
|
||||
dbCommon *prec = pdbentry->precnode->precord;
|
||||
lockRecord *lr = prec->lset;
|
||||
lockSet *ls = lr->plockSet;
|
||||
|
||||
prec->lset = NULL;
|
||||
lr->precord = NULL;
|
||||
|
||||
assert(ls->refcount>0);
|
||||
assert(ellCount(&ls->lockRecordList)>0);
|
||||
ellDelete(&ls->lockRecordList, &lr->node);
|
||||
dbLockDecRef(ls);
|
||||
|
||||
epicsSpinDestroy(lr->spin);
|
||||
free(lr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void dbLockCleanupRecords(dbBase *pdbbase)
|
||||
{
|
||||
#ifndef LOCKSET_NOFREE
|
||||
ELLNODE *cur;
|
||||
#endif
|
||||
epicsThreadOnce(&dbLockOnceInit, &dbLockOnce, NULL);
|
||||
|
||||
forEachRecord(NULL, pdbbase, &freeLockRecord);
|
||||
if(ellCount(&lockSetsActive)) {
|
||||
printf("Warning: dbLockCleanupRecords() leaking lockSets\n");
|
||||
dblsr(NULL,2);
|
||||
}
|
||||
|
||||
#ifndef LOCKSET_NOFREE
|
||||
while((cur=ellGet(&lockSetsFree))!=NULL) {
|
||||
lockSet *ls = (lockSet*)cur;
|
||||
|
||||
assert(ls->refcount==0);
|
||||
assert(ellCount(&ls->lockRecordList)==0);
|
||||
epicsMutexDestroy(ls->lock);
|
||||
free(ls);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Called in two modes.
|
||||
* During dbLockInitRecords w/ locker==NULL, then no mutex are locked.
|
||||
* After dbLockInitRecords w/ locker!=NULL, then
|
||||
* the caller must lock both pfirst and psecond.
|
||||
*
|
||||
* Assumes that pfirst has been modified
|
||||
* to link to psecond.
|
||||
*/
|
||||
void dbLockSetMerge(dbLocker *locker, dbCommon *pfirst, dbCommon *psecond)
|
||||
{
|
||||
ELLNODE *cur;
|
||||
lockSet *A=pfirst->lset->plockSet,
|
||||
*B=psecond->lset->plockSet;
|
||||
int Nb;
|
||||
#ifdef LOCKSET_DEBUG
|
||||
const epicsThreadId myself = epicsThreadGetIdSelf();
|
||||
#endif
|
||||
|
||||
assert(A && B);
|
||||
|
||||
#ifdef LOCKSET_DEBUG
|
||||
if(locker && (A->owner!=myself || B->owner!=myself)) {
|
||||
errlogPrintf("dbLockSetMerge(%p,\"%s\",\"%s\") ownership violation %p %p (%p)\n",
|
||||
locker, pfirst->name, psecond->name,
|
||||
A->owner, B->owner, myself);
|
||||
cantProceed(NULL);
|
||||
}
|
||||
#endif
|
||||
if(locker && (A->ownerlocker!=locker || B->ownerlocker!=locker)) {
|
||||
errlogPrintf("dbLockSetMerge(%p,\"%s\",\"%s\") locker ownership violation %p %p (%p)\n",
|
||||
locker, pfirst->name, psecond->name,
|
||||
A->ownerlocker, B->ownerlocker, locker);
|
||||
cantProceed(NULL);
|
||||
}
|
||||
|
||||
if(A==B)
|
||||
return; /* already in the same lockSet */
|
||||
|
||||
Nb = ellCount(&B->lockRecordList);
|
||||
assert(Nb>0);
|
||||
|
||||
/* move all records from B to A */
|
||||
while((cur=ellGet(&B->lockRecordList))!=NULL)
|
||||
{
|
||||
lockRecord *lr = CONTAINER(cur, lockRecord, node);
|
||||
assert(lr->plockSet==B);
|
||||
ellAdd(&A->lockRecordList, cur);
|
||||
|
||||
epicsSpinLock(lr->spin);
|
||||
lr->plockSet = A;
|
||||
#ifndef LOCKSET_NOCNT
|
||||
epicsAtomicIncrSizeT(&recomputeCnt);
|
||||
#endif
|
||||
epicsSpinUnlock(lr->spin);
|
||||
}
|
||||
|
||||
/* there are at minimum, 1 ref for each lockRecord,
|
||||
* and one for the locker's locked list
|
||||
* (and perhaps another for its refs cache)
|
||||
*/
|
||||
assert(epicsAtomicGetIntT(&B->refcount)>=Nb+(locker?1:0));
|
||||
|
||||
/* update ref counters. for lockRecords */
|
||||
epicsAtomicAddIntT(&A->refcount, Nb);
|
||||
epicsAtomicAddIntT(&B->refcount, -Nb+1); /* drop all but one ref, see below */
|
||||
|
||||
if(locker) {
|
||||
/* at least two refs, possibly three, remain.
|
||||
* # One ref from above
|
||||
* # locker->locked list, which is released now.
|
||||
* # locker->refs array, assuming it is directly referenced,
|
||||
* and not added as the result of a dbLockSetSplit,
|
||||
* which will be cleaned when the locker is free'd (not here).
|
||||
*/
|
||||
#ifdef LOCKSET_DEBUG
|
||||
B->owner = NULL;
|
||||
B->ownercount = 0;
|
||||
#endif
|
||||
assert(B->ownerlocker==locker);
|
||||
ellDelete(&locker->locked, &B->lockernode);
|
||||
B->ownerlocker = NULL;
|
||||
epicsAtomicDecrIntT(&B->refcount);
|
||||
|
||||
epicsMutexUnlock(B->lock);
|
||||
}
|
||||
|
||||
dbLockDecRef(B); /* last ref we hold */
|
||||
|
||||
assert(A==psecond->lset->plockSet);
|
||||
}
|
||||
|
||||
/* recompute assuming a link from pfirst to psecond
|
||||
* may have been removed.
|
||||
* pfirst and psecond must currently be in the same lockset,
|
||||
* which the caller must lock before calling this function.
|
||||
* If a new lockset is created, then it is locked
|
||||
* when this function returns.
|
||||
*/
|
||||
void dbLockSetSplit(dbLocker *locker, dbCommon *pfirst, dbCommon *psecond)
|
||||
{
|
||||
lockSet *ls = pfirst->lset->plockSet;
|
||||
ELLLIST toInspect, newLS;
|
||||
#ifdef LOCKSET_DEBUG
|
||||
const epicsThreadId myself = epicsThreadGetIdSelf();
|
||||
#endif
|
||||
|
||||
#ifdef LOCKSET_DEBUG
|
||||
if(ls->owner!=myself || psecond->lset->plockSet->owner!=myself) {
|
||||
errlogPrintf("dbLockSetSplit(%p,\"%s\",\"%s\") ownership violation %p %p (%p)\n",
|
||||
locker, pfirst->name, psecond->name,
|
||||
ls->owner, psecond->lset->plockSet->owner, myself);
|
||||
cantProceed(NULL);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* lockset consistency violation */
|
||||
if(ls!=psecond->lset->plockSet) {
|
||||
errlogPrintf("dbLockSetSplit(%p,\"%s\",\"%s\") consistency violation %p %p\n",
|
||||
locker, pfirst->name, psecond->name,
|
||||
pfirst->lset->plockSet, psecond->lset->plockSet);
|
||||
cantProceed(NULL);
|
||||
}
|
||||
|
||||
|
||||
if(pfirst==psecond)
|
||||
return;
|
||||
|
||||
/* at least 1 ref for each lockRecord,
|
||||
* and one for the locker
|
||||
*/
|
||||
assert(epicsAtomicGetIntT(&ls->refcount)>=ellCount(&ls->lockRecordList)+1);
|
||||
|
||||
ellInit(&toInspect);
|
||||
ellInit(&newLS);
|
||||
|
||||
/* strategy is to start with psecond and do
|
||||
* a breadth first traversal until all records are
|
||||
* visited. If we encounter pfirst, then there
|
||||
* is no need to create a new lockset so we abort
|
||||
* early.
|
||||
*/
|
||||
ellAdd(&toInspect, &psecond->lset->compnode);
|
||||
psecond->lset->compflag = 1;
|
||||
|
||||
{
|
||||
lockSet *splitset;
|
||||
ELLNODE *cur;
|
||||
while((cur=ellGet(&toInspect))!=NULL)
|
||||
{
|
||||
lockRecord *lr=CONTAINER(cur,lockRecord,compnode);
|
||||
dbCommon *prec=lr->precord;
|
||||
dbRecordType *rtype = prec->rdes;
|
||||
size_t i;
|
||||
ELLNODE *bcur;
|
||||
|
||||
ellAdd(&newLS, cur);
|
||||
prec->lset->compflag = 2;
|
||||
|
||||
/* Visit all the links originating from prec */
|
||||
for(i=0; i<rtype->no_links; i++) {
|
||||
dbFldDes *pdesc = rtype->papFldDes[rtype->link_ind[i]];
|
||||
DBLINK *plink = (DBLINK*)((char*)prec + pdesc->offset);
|
||||
DBADDR *ptarget;
|
||||
lockRecord *lr;
|
||||
|
||||
if(plink->type!=DB_LINK)
|
||||
continue;
|
||||
|
||||
ptarget = plink->value.pv_link.pvt;
|
||||
lr = ptarget->precord->lset;
|
||||
assert(lr);
|
||||
|
||||
if(lr->precord==pfirst) {
|
||||
/* so pfirst is still reachable from psecond,
|
||||
* no new lock set should be created.
|
||||
*/
|
||||
goto nosplit;
|
||||
}
|
||||
|
||||
/* have we already visited this record? */
|
||||
if(lr->compflag)
|
||||
continue;
|
||||
|
||||
ellAdd(&toInspect, &lr->compnode);
|
||||
lr->compflag = 1;
|
||||
}
|
||||
|
||||
/* Visit all links terminating at prec */
|
||||
for(bcur=ellFirst(&prec->bklnk); bcur; bcur=ellNext(bcur))
|
||||
{
|
||||
struct pv_link *plink1 = CONTAINER(bcur, struct pv_link, backlinknode);
|
||||
union value *plink2 = CONTAINER(plink1, union value, pv_link);
|
||||
DBLINK *plink = CONTAINER(plink2, DBLINK, value);
|
||||
lockRecord *lr = plink->precord->lset;
|
||||
|
||||
/* plink->type==DB_LINK is implied. Only DB_LINKs are tracked from BKLNK */
|
||||
|
||||
if(lr->precord==pfirst) {
|
||||
goto nosplit;
|
||||
}
|
||||
|
||||
if(lr->compflag)
|
||||
continue;
|
||||
|
||||
ellAdd(&toInspect, &lr->compnode);
|
||||
lr->compflag = 1;
|
||||
}
|
||||
}
|
||||
/* All links involving psecond were traversed without finding
|
||||
* pfirst. So we must create a new lockset.
|
||||
* newLS contains the nodes which will
|
||||
* make up this new lockset.
|
||||
*/
|
||||
/* newLS will have at least psecond in it */
|
||||
assert(ellCount(&newLS) > 0);
|
||||
/* If we didn't find pfirst, then it must be in the
|
||||
* original lockset, and not the new one
|
||||
*/
|
||||
assert(ellCount(&newLS) < ellCount(&ls->lockRecordList));
|
||||
assert(ellCount(&newLS) < ls->refcount);
|
||||
|
||||
splitset = makeSet(); /* reference for locker->locked */
|
||||
|
||||
epicsMutexMustLock(splitset->lock);
|
||||
|
||||
assert(splitset->ownerlocker==NULL);
|
||||
ellAdd(&locker->locked, &splitset->lockernode);
|
||||
splitset->ownerlocker = locker;
|
||||
|
||||
assert(splitset->refcount==1);
|
||||
|
||||
#ifdef LOCKSET_DEBUG
|
||||
splitset->owner = ls->owner;
|
||||
splitset->ownercount = 1;
|
||||
assert(ls->ownercount==1);
|
||||
#endif
|
||||
|
||||
while((cur=ellGet(&newLS))!=NULL)
|
||||
{
|
||||
lockRecord *lr=CONTAINER(cur,lockRecord,compnode);
|
||||
|
||||
lr->compflag = 0; /* reset for next time */
|
||||
|
||||
assert(lr->plockSet == ls);
|
||||
ellDelete(&ls->lockRecordList, &lr->node);
|
||||
ellAdd(&splitset->lockRecordList, &lr->node);
|
||||
|
||||
epicsSpinLock(lr->spin);
|
||||
lr->plockSet = splitset;
|
||||
#ifndef LOCKSET_NOCNT
|
||||
epicsAtomicIncrSizeT(&recomputeCnt);
|
||||
#endif
|
||||
epicsSpinUnlock(lr->spin);
|
||||
/* new lockSet is "live" at this point
|
||||
* as other threads may find it.
|
||||
*/
|
||||
}
|
||||
|
||||
/* refcount of ls can't go to zero as the locker
|
||||
* holds at least one reference (its locked list)
|
||||
*/
|
||||
epicsAtomicAddIntT(&ls->refcount, -ellCount(&splitset->lockRecordList));
|
||||
assert(ls->refcount>0);
|
||||
epicsAtomicAddIntT(&splitset->refcount, ellCount(&splitset->lockRecordList));
|
||||
|
||||
assert(splitset->refcount>=ellCount(&splitset->lockRecordList)+1);
|
||||
|
||||
assert(psecond->lset->plockSet==splitset);
|
||||
|
||||
/* must have refs from pfirst lockRecord,
|
||||
* and the locked list.
|
||||
*/
|
||||
assert(epicsAtomicGetIntT(&ls->refcount)>=2);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
nosplit:
|
||||
{
|
||||
/* reset compflag for all nodes visited
|
||||
* during the aborted search
|
||||
*/
|
||||
ELLNODE *cur;
|
||||
while((cur=ellGet(&toInspect))!=NULL)
|
||||
{
|
||||
lockRecord *lr=CONTAINER(cur,lockRecord,compnode);
|
||||
lr->compflag = 0;
|
||||
}
|
||||
while((cur=ellGet(&newLS))!=NULL)
|
||||
{
|
||||
lockRecord *lr=CONTAINER(cur,lockRecord,compnode);
|
||||
lr->compflag = 0;
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static char *msstring[4]={"NMS","MS","MSI","MSS"};
|
||||
|
||||
long dblsr(char *recordname,int level)
|
||||
{
|
||||
int link;
|
||||
DBENTRY dbentry;
|
||||
DBENTRY *pdbentry=&dbentry;
|
||||
long status;
|
||||
dbCommon *precord;
|
||||
lockSet *plockSet;
|
||||
lockRecord *plockRecord;
|
||||
dbRecordType *pdbRecordType;
|
||||
dbFldDes *pdbFldDes;
|
||||
DBLINK *plink;
|
||||
|
||||
if (recordname && ((*recordname == '\0') || !strcmp(recordname,"*")))
|
||||
recordname = NULL;
|
||||
if(recordname) {
|
||||
dbInitEntry(pdbbase,pdbentry);
|
||||
status = dbFindRecord(pdbentry,recordname);
|
||||
if(status) {
|
||||
printf("Record not found\n");
|
||||
dbFinishEntry(pdbentry);
|
||||
return 0;
|
||||
}
|
||||
precord = pdbentry->precnode->precord;
|
||||
dbFinishEntry(pdbentry);
|
||||
plockRecord = precord->lset;
|
||||
if (!plockRecord) return 0; /* before iocInit */
|
||||
plockSet = plockRecord->plockSet;
|
||||
} else {
|
||||
plockSet = (lockSet *)ellFirst(&lockSetsActive);
|
||||
}
|
||||
for( ; plockSet; plockSet = (lockSet *)ellNext(&plockSet->node)) {
|
||||
printf("Lock Set %lu %d members %d refs epicsMutexId %p\n",
|
||||
plockSet->id,ellCount(&plockSet->lockRecordList),plockSet->refcount,plockSet->lock);
|
||||
|
||||
if(level==0) { if(recordname) break; continue; }
|
||||
for(plockRecord = (lockRecord *)ellFirst(&plockSet->lockRecordList);
|
||||
plockRecord; plockRecord = (lockRecord *)ellNext(&plockRecord->node)) {
|
||||
precord = plockRecord->precord;
|
||||
pdbRecordType = precord->rdes;
|
||||
printf("%s\n",precord->name);
|
||||
if(level<=1) continue;
|
||||
for(link=0; (link<pdbRecordType->no_links) ; link++) {
|
||||
DBADDR *pdbAddr;
|
||||
pdbFldDes = pdbRecordType->papFldDes[pdbRecordType->link_ind[link]];
|
||||
plink = (DBLINK *)((char *)precord + pdbFldDes->offset);
|
||||
if(plink->type != DB_LINK) continue;
|
||||
pdbAddr = (DBADDR *)(plink->value.pv_link.pvt);
|
||||
printf("\t%s",pdbFldDes->name);
|
||||
if(pdbFldDes->field_type==DBF_INLINK) {
|
||||
printf("\t INLINK");
|
||||
} else if(pdbFldDes->field_type==DBF_OUTLINK) {
|
||||
printf("\tOUTLINK");
|
||||
} else if(pdbFldDes->field_type==DBF_FWDLINK) {
|
||||
printf("\tFWDLINK");
|
||||
}
|
||||
printf(" %s %s",
|
||||
((plink->value.pv_link.pvlMask&pvlOptPP)?" PP":"NPP"),
|
||||
msstring[plink->value.pv_link.pvlMask&pvlOptMsMode]);
|
||||
printf(" %s\n",pdbAddr->precord->name);
|
||||
}
|
||||
}
|
||||
if(recordname) break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
long dbLockShowLocked(int level)
|
||||
{
|
||||
int indListType;
|
||||
lockSet *plockSet;
|
||||
|
||||
printf("Active lockSets: %d\n", ellCount(&lockSetsActive));
|
||||
#ifndef LOCKSET_NOFREE
|
||||
printf("Free lockSets: %d\n", ellCount(&lockSetsFree));
|
||||
#endif
|
||||
|
||||
/*Even if failure on lockSetModifyLock will continue */
|
||||
for(indListType=0; indListType <= 1; ++indListType) {
|
||||
plockSet = (lockSet *)ellFirst(&lockSetsActive);
|
||||
if(plockSet) {
|
||||
if (indListType==0)
|
||||
printf("listTypeScanLock\n");
|
||||
else
|
||||
printf("listTypeRecordLock\n");
|
||||
}
|
||||
while(plockSet) {
|
||||
epicsMutexLockStatus status;
|
||||
|
||||
status = epicsMutexTryLock(plockSet->lock);
|
||||
if(status==epicsMutexLockOK) epicsMutexUnlock(plockSet->lock);
|
||||
if(status!=epicsMutexLockOK || indListType==1) {
|
||||
|
||||
epicsMutexShow(plockSet->lock,level);
|
||||
}
|
||||
plockSet = (lockSet *)ellNext(&plockSet->node);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int * dbLockSetAddrTrace(dbCommon *precord)
|
||||
{
|
||||
lockRecord *plockRecord = precord->lset;
|
||||
lockSet *plockSet = plockRecord->plockSet;
|
||||
|
||||
return(&plockSet->trace);
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
/*************************************************************************\
|
||||
* 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.
|
||||
\*************************************************************************/
|
||||
/* dbLock.h */
|
||||
/* Author: Marty Kraimer Date: 12MAR96 */
|
||||
|
||||
#ifndef INCdbLockh
|
||||
#define INCdbLockh
|
||||
|
||||
#include "ellLib.h"
|
||||
#include "shareLib.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct dbCommon;
|
||||
struct dbBase;
|
||||
typedef struct dbLocker dbLocker;
|
||||
|
||||
epicsShareFunc void dbScanLock(struct dbCommon *precord);
|
||||
epicsShareFunc void dbScanUnlock(struct dbCommon *precord);
|
||||
|
||||
epicsShareFunc dbLocker *dbLockerAlloc(struct dbCommon * const *precs,
|
||||
size_t nrecs,
|
||||
unsigned int flags);
|
||||
|
||||
epicsShareFunc void dbLockerFree(dbLocker *);
|
||||
|
||||
epicsShareFunc void dbScanLockMany(dbLocker*);
|
||||
epicsShareFunc void dbScanUnlockMany(dbLocker*);
|
||||
|
||||
epicsShareFunc unsigned long dbLockGetLockId(
|
||||
struct dbCommon *precord);
|
||||
|
||||
epicsShareFunc void dbLockInitRecords(struct dbBase *pdbbase);
|
||||
epicsShareFunc void dbLockCleanupRecords(struct dbBase *pdbbase);
|
||||
|
||||
|
||||
/* Lock Set Report */
|
||||
epicsShareFunc long dblsr(char *recordname,int level);
|
||||
/* If recordname NULL then all records*/
|
||||
/* level = (0,1,2) (lock set state, + recordname, +DB links) */
|
||||
|
||||
epicsShareFunc long dbLockShowLocked(int level);
|
||||
|
||||
/*KLUDGE to support field TPRO*/
|
||||
epicsShareFunc int * dbLockSetAddrTrace(struct dbCommon *precord);
|
||||
|
||||
/* debugging */
|
||||
epicsShareFunc unsigned long dbLockGetRefs(struct dbCommon*);
|
||||
epicsShareFunc unsigned long dbLockCountSets(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /*INCdbLockh*/
|
||||
@@ -0,0 +1,110 @@
|
||||
/*************************************************************************\
|
||||
* Copyright (c) 2014 Brookhaven Science Assoc., as Operator of Brookhaven
|
||||
* National Laboratory.
|
||||
* EPICS BASE is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
\*************************************************************************/
|
||||
|
||||
#ifndef DBLOCKPVT_H
|
||||
#define DBLOCKPVT_H
|
||||
|
||||
#include "dbLock.h"
|
||||
#include "epicsSpin.h"
|
||||
|
||||
/* Define to enable additional error checking */
|
||||
#undef LOCKSET_DEBUG
|
||||
|
||||
/* Define to disable the free list for lockSets */
|
||||
#undef LOCKSET_NOFREE
|
||||
|
||||
/* Define to disable use of recomputeCnt optimization */
|
||||
#undef LOCKSET_NOCNT
|
||||
|
||||
/* except for refcount (and lock), all members of dbLockSet
|
||||
* are guarded by its lock.
|
||||
*/
|
||||
typedef struct dbLockSet {
|
||||
ELLNODE node;
|
||||
ELLLIST lockRecordList; /* holds lockRecord::node */
|
||||
epicsMutexId lock;
|
||||
unsigned long id;
|
||||
|
||||
int refcount;
|
||||
#ifdef LOCKSET_DEBUG
|
||||
int ownercount;
|
||||
epicsThreadId owner;
|
||||
#endif
|
||||
dbLocker *ownerlocker;
|
||||
ELLNODE lockernode;
|
||||
|
||||
int trace; /*For field TPRO*/
|
||||
} lockSet;
|
||||
|
||||
struct lockRecord;
|
||||
|
||||
/* dbCommon.LSET is a plockRecord.
|
||||
* Except for spin and plockSet, all members of lockRecord are guarded
|
||||
* by the present lockset lock.
|
||||
* plockSet is guarded by spin.
|
||||
*/
|
||||
typedef struct lockRecord {
|
||||
ELLNODE node; /* in lockSet::lockRecordList */
|
||||
/* The association between lockRecord and lockSet
|
||||
* can only be changed while the lockSet is held,
|
||||
* and the lockRecord's spinlock is held.
|
||||
* It may be read which either lock is held.
|
||||
*/
|
||||
lockSet *plockSet;
|
||||
/* the association between lockRecord and dbCommon never changes */
|
||||
dbCommon *precord;
|
||||
epicsSpinId spin;
|
||||
|
||||
/* temp used during lockset split.
|
||||
* lockSet must be locked for access
|
||||
*/
|
||||
ELLNODE compnode;
|
||||
unsigned int compflag;
|
||||
} lockRecord;
|
||||
|
||||
typedef struct {
|
||||
lockRecord *plr;
|
||||
/* the last lock found associated with the ref.
|
||||
* not stable unless lock is locked, or ref spin
|
||||
* is locked.
|
||||
*/
|
||||
lockSet *plockSet;
|
||||
} lockRecordRef;
|
||||
|
||||
#define DBLOCKER_NALLOC 2
|
||||
/* a dbLocker can only be used by a single thread. */
|
||||
struct dbLocker {
|
||||
ELLLIST locked;
|
||||
#ifndef LOCKSET_NOCNT
|
||||
size_t recomp; /* snapshot of recomputeCnt when refs[] cache updated */
|
||||
#endif
|
||||
size_t maxrefs;
|
||||
lockRecordRef refs[DBLOCKER_NALLOC]; /* actual length is maxrefs */
|
||||
};
|
||||
|
||||
/* These are exported for testing only */
|
||||
epicsShareFunc lockSet* dbLockGetRef(lockRecord *lr); /* lookup lockset and increment ref count */
|
||||
epicsShareFunc void dbLockIncRef(lockSet* ls);
|
||||
epicsShareFunc void dbLockDecRef(lockSet *ls);
|
||||
|
||||
/* Calling dbLockerPrepare directly is an internal
|
||||
* optimization used when dbLocker on the stack.
|
||||
* nrecs must be <=DBLOCKER_NALLOC.
|
||||
*/
|
||||
void dbLockerPrepare(struct dbLocker *locker,
|
||||
struct dbCommon * const *precs,
|
||||
size_t nrecs);
|
||||
void dbLockerFinalize(dbLocker *);
|
||||
|
||||
void dbLockSetMerge(struct dbLocker *locker,
|
||||
struct dbCommon *pfirst,
|
||||
struct dbCommon *psecond);
|
||||
void dbLockSetSplit(struct dbLocker *locker,
|
||||
struct dbCommon *psource,
|
||||
struct dbCommon *psecond);
|
||||
|
||||
#endif /* DBLOCKPVT_H */
|
||||
@@ -0,0 +1,686 @@
|
||||
/*************************************************************************\
|
||||
* Copyright (c) 2012 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.
|
||||
\*************************************************************************/
|
||||
/* dbNotify.c */
|
||||
/*
|
||||
* Author: Marty Kraimer
|
||||
* Andrew Johnson <anj@aps.anl.gov>
|
||||
*
|
||||
* Extracted from dbLink.c
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <limits.h>
|
||||
|
||||
#include "cantProceed.h"
|
||||
#include "dbDefs.h"
|
||||
#include "ellLib.h"
|
||||
#include "epicsAssert.h"
|
||||
#include "epicsEvent.h"
|
||||
#include "epicsMutex.h"
|
||||
#include "epicsString.h"
|
||||
#include "epicsThread.h"
|
||||
#include "epicsTime.h"
|
||||
#include "errlog.h"
|
||||
#include "errMdef.h"
|
||||
|
||||
#define epicsExportSharedSymbols
|
||||
#include "callback.h"
|
||||
#include "dbAccessDefs.h"
|
||||
#include "dbBase.h"
|
||||
#include "dbChannel.h"
|
||||
#include "dbCommon.h"
|
||||
#include "dbFldTypes.h"
|
||||
#include "dbLock.h"
|
||||
#include "dbNotify.h"
|
||||
#include "dbScan.h"
|
||||
#include "dbStaticLib.h"
|
||||
#include "link.h"
|
||||
#include "recGbl.h"
|
||||
|
||||
/*notify state values */
|
||||
typedef enum {
|
||||
notifyNotActive,
|
||||
notifyWaitForRestart,
|
||||
notifyRestartCallbackRequested,
|
||||
notifyRestartInProgress,
|
||||
notifyProcessInProgress,
|
||||
notifyUserCallbackRequested,
|
||||
notifyUserCallbackActive
|
||||
} notifyState;
|
||||
|
||||
/*structure attached to ppnr field of each record*/
|
||||
typedef struct processNotifyRecord {
|
||||
ellCheckNode waitNode;
|
||||
ELLLIST restartList; /*list of processNotifys to restart*/
|
||||
dbCommon *precord;
|
||||
} processNotifyRecord;
|
||||
|
||||
#define MAGIC 0xfedc0123
|
||||
typedef struct notifyPvt {
|
||||
ELLNODE node; /*For free list*/
|
||||
long magic;
|
||||
short state;
|
||||
CALLBACK callback;
|
||||
ELLLIST waitList; /*list of records for current processNotify*/
|
||||
short cancelWait;
|
||||
short userCallbackWait;
|
||||
epicsEventId cancelEvent;
|
||||
epicsEventId userCallbackEvent;
|
||||
} notifyPvt;
|
||||
|
||||
/* processNotify groups can span locksets if links are dynamically modified*/
|
||||
/* Thus a global lock is taken while processNotify fields are accessed */
|
||||
typedef struct notifyGlobal {
|
||||
epicsMutexId lock;
|
||||
ELLLIST freeList;
|
||||
} notifyGlobal;
|
||||
|
||||
static notifyGlobal *pnotifyGlobal = 0;
|
||||
|
||||
/*Local routines*/
|
||||
static void notifyInit(processNotify *ppn);
|
||||
static void notifyCleanup(processNotify *ppn);
|
||||
static void restartCheck(processNotifyRecord *ppnr);
|
||||
static void callDone(dbCommon *precord,processNotify *ppn);
|
||||
static void processNotifyCommon(processNotify *ppn,dbCommon *precord);
|
||||
static void notifyCallback(CALLBACK *pcallback);
|
||||
|
||||
#define ellSafeAdd(list,listnode) \
|
||||
{ \
|
||||
assert((listnode)->isOnList==0); \
|
||||
ellAdd((list),&((listnode)->node)); \
|
||||
(listnode)->isOnList=1; \
|
||||
}
|
||||
|
||||
#define ellSafeDelete(list,listnode) \
|
||||
{ \
|
||||
assert((listnode)->isOnList); \
|
||||
ellDelete((list),&((listnode)->node)); \
|
||||
(listnode)->isOnList=0; \
|
||||
}
|
||||
|
||||
static void notifyFree(void *raw)
|
||||
{
|
||||
notifyPvt *pnotifyPvt = raw;
|
||||
assert(pnotifyPvt->magic==MAGIC);
|
||||
epicsEventDestroy(pnotifyPvt->cancelEvent);
|
||||
epicsEventDestroy(pnotifyPvt->userCallbackEvent);
|
||||
free(pnotifyPvt);
|
||||
}
|
||||
|
||||
static void notifyInit(processNotify *ppn)
|
||||
{
|
||||
notifyPvt *pnotifyPvt;
|
||||
|
||||
pnotifyPvt = (notifyPvt *) ellFirst(&pnotifyGlobal->freeList);
|
||||
if (pnotifyPvt) {
|
||||
ellDelete(&pnotifyGlobal->freeList, &pnotifyPvt->node);
|
||||
} else {
|
||||
pnotifyPvt = dbCalloc(1,sizeof(notifyPvt));
|
||||
pnotifyPvt->cancelEvent = epicsEventCreate(epicsEventEmpty);
|
||||
pnotifyPvt->userCallbackEvent = epicsEventCreate(epicsEventEmpty);
|
||||
pnotifyPvt->magic = MAGIC;
|
||||
pnotifyPvt->state = notifyNotActive;
|
||||
}
|
||||
pnotifyPvt->state = notifyNotActive;
|
||||
callbackSetCallback(notifyCallback,&pnotifyPvt->callback);
|
||||
callbackSetUser(ppn,&pnotifyPvt->callback);
|
||||
callbackSetPriority(priorityLow,&pnotifyPvt->callback);
|
||||
ellInit(&pnotifyPvt->waitList);
|
||||
ppn->status = notifyOK;
|
||||
ppn->wasProcessed = 0;
|
||||
pnotifyPvt->state = notifyNotActive;
|
||||
pnotifyPvt->cancelWait = pnotifyPvt->userCallbackWait = 0;
|
||||
ppn->pnotifyPvt = pnotifyPvt;
|
||||
}
|
||||
|
||||
static void notifyCleanup(processNotify *ppn)
|
||||
{
|
||||
notifyPvt *pnotifyPvt = (notifyPvt *) ppn->pnotifyPvt;
|
||||
|
||||
pnotifyPvt->state = notifyNotActive;
|
||||
ellAdd(&pnotifyGlobal->freeList, &pnotifyPvt->node);
|
||||
ppn->pnotifyPvt = 0;
|
||||
}
|
||||
|
||||
static void restartCheck(processNotifyRecord *ppnr)
|
||||
{
|
||||
dbCommon *precord = ppnr->precord;
|
||||
processNotify *pfirst;
|
||||
notifyPvt *pnotifyPvt;
|
||||
|
||||
assert(precord->ppn);
|
||||
pfirst = (processNotify *) ellFirst(&ppnr->restartList);
|
||||
if (!pfirst) {
|
||||
precord->ppn = 0;
|
||||
return;
|
||||
}
|
||||
pnotifyPvt = (notifyPvt *) pfirst->pnotifyPvt;
|
||||
assert(pnotifyPvt->state == notifyWaitForRestart);
|
||||
/* remove pfirst from restartList */
|
||||
ellSafeDelete(&ppnr->restartList, &pfirst->restartNode);
|
||||
/*make pfirst owner of the record*/
|
||||
precord->ppn = pfirst;
|
||||
/* request callback for pfirst */
|
||||
pnotifyPvt->state = notifyRestartCallbackRequested;
|
||||
callbackRequest(&pnotifyPvt->callback);
|
||||
}
|
||||
|
||||
static void callDone(dbCommon *precord, processNotify *ppn)
|
||||
{
|
||||
notifyPvt *pnotifyPvt = (notifyPvt *) ppn->pnotifyPvt;
|
||||
|
||||
epicsMutexUnlock(pnotifyGlobal->lock);
|
||||
if (ppn->requestType == processGetRequest ||
|
||||
ppn->requestType == putProcessGetRequest) {
|
||||
ppn->getCallback(ppn, getFieldType);
|
||||
}
|
||||
dbScanUnlock(precord);
|
||||
ppn->doneCallback(ppn);
|
||||
epicsMutexMustLock(pnotifyGlobal->lock);
|
||||
if (pnotifyPvt->cancelWait && pnotifyPvt->userCallbackWait) {
|
||||
errlogPrintf("%s processNotify: both cancelWait and userCallbackWait true."
|
||||
"This is illegal\n", precord->name);
|
||||
pnotifyPvt->cancelWait = pnotifyPvt->userCallbackWait = 0;
|
||||
}
|
||||
if (!pnotifyPvt->cancelWait && !pnotifyPvt->userCallbackWait) {
|
||||
notifyCleanup(ppn);
|
||||
epicsMutexUnlock(pnotifyGlobal->lock);
|
||||
return;
|
||||
}
|
||||
if (pnotifyPvt->cancelWait) {
|
||||
pnotifyPvt->cancelWait = 0;
|
||||
epicsEventSignal(pnotifyPvt->cancelEvent);
|
||||
epicsMutexUnlock(pnotifyGlobal->lock);
|
||||
return;
|
||||
}
|
||||
assert(pnotifyPvt->userCallbackWait);
|
||||
pnotifyPvt->userCallbackWait = 0;
|
||||
epicsEventSignal(pnotifyPvt->userCallbackEvent);
|
||||
epicsMutexUnlock(pnotifyGlobal->lock);
|
||||
return;
|
||||
}
|
||||
|
||||
static void processNotifyCommon(processNotify *ppn,dbCommon *precord)
|
||||
{
|
||||
notifyPvt *pnotifyPvt = (notifyPvt *) ppn->pnotifyPvt;
|
||||
int didPut = 0;
|
||||
int doProcess = 0;
|
||||
|
||||
if (precord->ppn &&
|
||||
pnotifyPvt->state != notifyRestartCallbackRequested) {
|
||||
/* Another processNotify owns the record */
|
||||
pnotifyPvt->state = notifyWaitForRestart;
|
||||
ellSafeAdd(&precord->ppnr->restartList, &ppn->restartNode);
|
||||
epicsMutexUnlock(pnotifyGlobal->lock);
|
||||
dbScanUnlock(precord);
|
||||
return;
|
||||
} else if (precord->ppn) {
|
||||
assert(precord->ppn == ppn);
|
||||
assert(pnotifyPvt->state == notifyRestartCallbackRequested);
|
||||
}
|
||||
if (precord->pact) {
|
||||
precord->ppn = ppn;
|
||||
ellSafeAdd(&pnotifyPvt->waitList, &precord->ppnr->waitNode);
|
||||
pnotifyPvt->state = notifyRestartInProgress;
|
||||
epicsMutexUnlock(pnotifyGlobal->lock);
|
||||
dbScanUnlock(precord);
|
||||
return;
|
||||
}
|
||||
if (ppn->requestType == putProcessRequest ||
|
||||
ppn->requestType == putProcessGetRequest) {
|
||||
/* Check if puts disabled */
|
||||
if (precord->disp && (dbChannelField(ppn->chan) != (void *) &precord->disp)) {
|
||||
ppn->putCallback(ppn, putDisabledType);
|
||||
} else {
|
||||
didPut = ppn->putCallback(ppn, putType);
|
||||
}
|
||||
}
|
||||
/* Check if dbProcess should be called */
|
||||
if (didPut &&
|
||||
((dbChannelField(ppn->chan) == (void *) &precord->proc) ||
|
||||
(dbChannelFldDes(ppn->chan)->process_passive && precord->scan == 0)))
|
||||
doProcess = 1;
|
||||
else
|
||||
if (ppn->requestType == processGetRequest &&
|
||||
precord->scan == 0)
|
||||
doProcess = 1;
|
||||
|
||||
if (doProcess) {
|
||||
ppn->wasProcessed = 1;
|
||||
precord->ppn = ppn;
|
||||
ellSafeAdd(&pnotifyPvt->waitList, &precord->ppnr->waitNode);
|
||||
pnotifyPvt->state = notifyProcessInProgress;
|
||||
epicsMutexUnlock(pnotifyGlobal->lock);
|
||||
dbProcess(precord);
|
||||
dbScanUnlock(precord);
|
||||
return;
|
||||
}
|
||||
if (pnotifyPvt->state == notifyRestartCallbackRequested) {
|
||||
restartCheck(precord->ppnr);
|
||||
}
|
||||
pnotifyPvt->state = notifyUserCallbackActive;
|
||||
assert(precord->ppn!=ppn);
|
||||
callDone(precord, ppn);
|
||||
}
|
||||
|
||||
static void notifyCallback(CALLBACK *pcallback)
|
||||
{
|
||||
processNotify *ppn = NULL;
|
||||
dbCommon *precord;
|
||||
notifyPvt *pnotifyPvt;
|
||||
|
||||
callbackGetUser(ppn,pcallback);
|
||||
pnotifyPvt = (notifyPvt *) ppn->pnotifyPvt;
|
||||
precord = dbChannelRecord(ppn->chan);
|
||||
dbScanLock(precord);
|
||||
epicsMutexMustLock(pnotifyGlobal->lock);
|
||||
assert(precord->ppnr);
|
||||
assert(pnotifyPvt->state == notifyRestartCallbackRequested ||
|
||||
pnotifyPvt->state == notifyUserCallbackRequested);
|
||||
assert(ellCount(&pnotifyPvt->waitList) == 0);
|
||||
if (pnotifyPvt->cancelWait) {
|
||||
if (pnotifyPvt->state == notifyRestartCallbackRequested) {
|
||||
restartCheck(precord->ppnr);
|
||||
}
|
||||
epicsEventSignal(pnotifyPvt->cancelEvent);
|
||||
epicsMutexUnlock(pnotifyGlobal->lock);
|
||||
dbScanUnlock(precord);
|
||||
return;
|
||||
}
|
||||
if(pnotifyPvt->state == notifyRestartCallbackRequested) {
|
||||
processNotifyCommon(ppn, precord);
|
||||
return;
|
||||
}
|
||||
/* All done. Clean up and call userCallback */
|
||||
pnotifyPvt->state = notifyUserCallbackActive;
|
||||
assert(precord->ppn!=ppn);
|
||||
callDone(precord, ppn);
|
||||
}
|
||||
|
||||
void dbProcessNotifyExit(void)
|
||||
{
|
||||
ellFree2(&pnotifyGlobal->freeList, ¬ifyFree);
|
||||
epicsMutexDestroy(pnotifyGlobal->lock);
|
||||
free(pnotifyGlobal);
|
||||
pnotifyGlobal = NULL;
|
||||
}
|
||||
|
||||
void dbProcessNotifyInit(void)
|
||||
{
|
||||
if (pnotifyGlobal)
|
||||
return;
|
||||
pnotifyGlobal = dbCalloc(1,sizeof(notifyGlobal));
|
||||
pnotifyGlobal->lock = epicsMutexMustCreate();
|
||||
ellInit(&pnotifyGlobal->freeList);
|
||||
}
|
||||
|
||||
void dbProcessNotify(processNotify *ppn)
|
||||
{
|
||||
struct dbChannel *chan = ppn->chan;
|
||||
dbCommon *precord = dbChannelRecord(chan);
|
||||
short dbfType = dbChannelFieldType(chan);
|
||||
notifyPvt *pnotifyPvt;
|
||||
|
||||
/* Must handle DBF_XXXLINKs as special case.
|
||||
* Only dbPutField will change link fields.
|
||||
* Also the record is not processed as a result
|
||||
*/
|
||||
ppn->status = notifyOK;
|
||||
ppn->wasProcessed = 0;
|
||||
if (dbfType>=DBF_INLINK && dbfType<=DBF_FWDLINK) {
|
||||
if (ppn->requestType == putProcessRequest ||
|
||||
ppn->requestType == putProcessGetRequest) {
|
||||
/* Check if puts disabled */
|
||||
if (precord->disp && (dbChannelField(ppn->chan) != (void *) &precord->disp)) {
|
||||
ppn->putCallback(ppn, putDisabledType);
|
||||
} else {
|
||||
ppn->putCallback(ppn, putFieldType);
|
||||
}
|
||||
}
|
||||
if (ppn->requestType == processGetRequest ||
|
||||
ppn->requestType == putProcessGetRequest) {
|
||||
ppn->getCallback(ppn, getFieldType);
|
||||
|
||||
}
|
||||
ppn->doneCallback(ppn);
|
||||
return;
|
||||
}
|
||||
dbScanLock(precord);
|
||||
epicsMutexMustLock(pnotifyGlobal->lock);
|
||||
pnotifyPvt = (notifyPvt *) ppn->pnotifyPvt;
|
||||
if (pnotifyPvt && (pnotifyPvt->magic != MAGIC)) {
|
||||
printf("dbPutNotify:pnotifyPvt was not initialized\n");
|
||||
pnotifyPvt = 0;
|
||||
}
|
||||
if (pnotifyPvt) {
|
||||
assert(pnotifyPvt->state == notifyUserCallbackActive);
|
||||
pnotifyPvt->userCallbackWait = 1;
|
||||
epicsMutexUnlock(pnotifyGlobal->lock);
|
||||
dbScanUnlock(precord);
|
||||
epicsEventWait(pnotifyPvt->userCallbackEvent);
|
||||
dbScanLock(precord);
|
||||
epicsMutexMustLock(pnotifyGlobal->lock);
|
||||
notifyCleanup(ppn);
|
||||
}
|
||||
pnotifyPvt = (notifyPvt *) ppn->pnotifyPvt;
|
||||
assert(!pnotifyPvt);
|
||||
notifyInit(ppn);
|
||||
pnotifyPvt = (notifyPvt *) ppn->pnotifyPvt;
|
||||
if (!precord->ppnr) {
|
||||
/* make sure record has a processNotifyRecord*/
|
||||
precord->ppnr = dbCalloc(1, sizeof(processNotifyRecord));
|
||||
precord->ppnr->precord = precord;
|
||||
ellInit(&precord->ppnr->restartList);
|
||||
}
|
||||
processNotifyCommon(ppn, precord);
|
||||
}
|
||||
|
||||
void dbNotifyCancel(processNotify *ppn)
|
||||
{
|
||||
dbCommon *precord = dbChannelRecord(ppn->chan);
|
||||
notifyState state;
|
||||
notifyPvt *pnotifyPvt;
|
||||
|
||||
dbScanLock(precord);
|
||||
epicsMutexMustLock(pnotifyGlobal->lock);
|
||||
ppn->status = notifyCanceled;
|
||||
pnotifyPvt = (notifyPvt *) ppn->pnotifyPvt;
|
||||
if (!pnotifyPvt || pnotifyPvt->state == notifyNotActive) {
|
||||
epicsMutexUnlock(pnotifyGlobal->lock);
|
||||
dbScanUnlock(precord);
|
||||
return;
|
||||
}
|
||||
|
||||
state = pnotifyPvt->state;
|
||||
switch (state) {
|
||||
case notifyUserCallbackRequested:
|
||||
case notifyRestartCallbackRequested:
|
||||
case notifyUserCallbackActive:
|
||||
/* Callback is scheduled or active, wait for it to complete */
|
||||
pnotifyPvt->cancelWait = 1;
|
||||
epicsMutexUnlock(pnotifyGlobal->lock);
|
||||
dbScanUnlock(precord);
|
||||
epicsEventWait(pnotifyPvt->cancelEvent);
|
||||
epicsMutexMustLock(pnotifyGlobal->lock);
|
||||
notifyCleanup(ppn);
|
||||
epicsMutexUnlock(pnotifyGlobal->lock);
|
||||
return;
|
||||
case notifyNotActive:
|
||||
break;
|
||||
case notifyWaitForRestart:
|
||||
assert(precord->ppn);
|
||||
assert(precord->ppn!=ppn);
|
||||
ellSafeDelete(&precord->ppnr->restartList,&ppn->restartNode);
|
||||
break;
|
||||
case notifyRestartInProgress:
|
||||
case notifyProcessInProgress:
|
||||
{ /*Take all records out of wait list */
|
||||
processNotifyRecord *ppnrWait;
|
||||
|
||||
while ((ppnrWait = (processNotifyRecord *)
|
||||
ellFirst(&pnotifyPvt->waitList))) {
|
||||
ellSafeDelete(&pnotifyPvt->waitList, &ppnrWait->waitNode);
|
||||
restartCheck(ppnrWait);
|
||||
}
|
||||
}
|
||||
if (precord->ppn == ppn)
|
||||
restartCheck(precord->ppnr);
|
||||
break;
|
||||
default:
|
||||
printf("dbNotify: illegal state for notifyCallback\n");
|
||||
}
|
||||
pnotifyPvt->state = notifyNotActive;
|
||||
notifyCleanup(ppn);
|
||||
epicsMutexUnlock(pnotifyGlobal->lock);
|
||||
dbScanUnlock(precord);
|
||||
}
|
||||
|
||||
void dbNotifyCompletion(dbCommon *precord)
|
||||
{
|
||||
processNotify *ppn = precord->ppn;
|
||||
notifyPvt *pnotifyPvt;
|
||||
|
||||
epicsMutexMustLock(pnotifyGlobal->lock);
|
||||
assert(ppn);
|
||||
assert(precord->ppnr);
|
||||
pnotifyPvt = (notifyPvt *) ppn->pnotifyPvt;
|
||||
if (pnotifyPvt->state != notifyRestartInProgress &&
|
||||
pnotifyPvt->state != notifyProcessInProgress) {
|
||||
epicsMutexUnlock(pnotifyGlobal->lock);
|
||||
return;
|
||||
}
|
||||
ellSafeDelete(&pnotifyPvt->waitList, &precord->ppnr->waitNode);
|
||||
if ((ellCount(&pnotifyPvt->waitList) != 0)) {
|
||||
restartCheck(precord->ppnr);
|
||||
}
|
||||
else if (pnotifyPvt->state == notifyProcessInProgress) {
|
||||
pnotifyPvt->state = notifyUserCallbackRequested;
|
||||
restartCheck(precord->ppnr);
|
||||
callbackRequest(&pnotifyPvt->callback);
|
||||
}
|
||||
else if(pnotifyPvt->state == notifyRestartInProgress) {
|
||||
pnotifyPvt->state = notifyRestartCallbackRequested;
|
||||
callbackRequest(&pnotifyPvt->callback);
|
||||
} else {
|
||||
cantProceed("dbNotifyCompletion illegal state");
|
||||
}
|
||||
epicsMutexUnlock(pnotifyGlobal->lock);
|
||||
}
|
||||
|
||||
void dbNotifyAdd(dbCommon *pfrom, dbCommon *pto)
|
||||
{
|
||||
processNotify *ppn = pfrom->ppn;
|
||||
notifyPvt *pnotifyPvt;
|
||||
|
||||
if (pto->pact)
|
||||
return; /*if active it will not be processed*/
|
||||
epicsMutexMustLock(pnotifyGlobal->lock);
|
||||
if (!pto->ppnr) {/* make sure record has a processNotifyRecord*/
|
||||
pto->ppnr = dbCalloc(1, sizeof(processNotifyRecord));
|
||||
pto->ppnr->precord = pto;
|
||||
ellInit(&pto->ppnr->restartList);
|
||||
}
|
||||
assert(ppn);
|
||||
pnotifyPvt = (notifyPvt *) ppn->pnotifyPvt;
|
||||
if (!pto->ppn &&
|
||||
(pnotifyPvt->state == notifyProcessInProgress) &&
|
||||
(pto != dbChannelRecord(ppn->chan))) {
|
||||
notifyPvt *pnotifyPvt;
|
||||
pto->ppn = pfrom->ppn;
|
||||
pnotifyPvt = (notifyPvt *) pfrom->ppn->pnotifyPvt;
|
||||
ellSafeAdd(&pnotifyPvt->waitList, &pto->ppnr->waitNode);
|
||||
}
|
||||
epicsMutexUnlock(pnotifyGlobal->lock);
|
||||
}
|
||||
|
||||
typedef struct tpnInfo {
|
||||
epicsEventId callbackDone;
|
||||
processNotify *ppn;
|
||||
char buffer[80];
|
||||
} tpnInfo;
|
||||
|
||||
static int putCallback(processNotify *ppn, notifyPutType type)
|
||||
{
|
||||
tpnInfo *ptpnInfo = (tpnInfo *) ppn->usrPvt;
|
||||
int status = 0;
|
||||
|
||||
if (ppn->status == notifyCanceled)
|
||||
return 0;
|
||||
ppn->status = notifyOK;
|
||||
switch (type) {
|
||||
case putDisabledType:
|
||||
ppn->status = notifyError;
|
||||
return 0;
|
||||
case putFieldType:
|
||||
status = dbChannelPutField(ppn->chan, DBR_STRING, ptpnInfo->buffer, 1);
|
||||
break;
|
||||
case putType:
|
||||
status = dbChannelPut(ppn->chan, DBR_STRING, ptpnInfo->buffer, 1);
|
||||
break;
|
||||
}
|
||||
if (status)
|
||||
ppn->status = notifyError;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void getCallback(processNotify *ppn,notifyGetType type)
|
||||
{
|
||||
tpnInfo *ptpnInfo = (tpnInfo *)ppn->usrPvt;
|
||||
int status = 0;
|
||||
long no_elements = 1;
|
||||
long options = 0;
|
||||
|
||||
if(ppn->status==notifyCanceled) {
|
||||
printf("dbtpn:getCallback notifyCanceled\n");
|
||||
return;
|
||||
}
|
||||
switch(type) {
|
||||
case getFieldType:
|
||||
status = dbChannelGetField(ppn->chan, DBR_STRING, ptpnInfo->buffer,
|
||||
&options, &no_elements, 0);
|
||||
break;
|
||||
case getType:
|
||||
status = dbChannelGet(ppn->chan, DBR_STRING, ptpnInfo->buffer,
|
||||
&options, &no_elements, 0);
|
||||
break;
|
||||
}
|
||||
if (status) {
|
||||
ppn->status = notifyError;
|
||||
printf("dbtpn:getCallback error\n");
|
||||
} else {
|
||||
printf("dbtpn:getCallback value %s\n", ptpnInfo->buffer);
|
||||
}
|
||||
}
|
||||
|
||||
static void doneCallback(processNotify *ppn)
|
||||
{
|
||||
notifyStatus status = ppn->status;
|
||||
tpnInfo *ptpnInfo = (tpnInfo *) ppn->usrPvt;
|
||||
const char *pname = dbChannelRecord(ppn->chan)->name;
|
||||
|
||||
if (status == 0)
|
||||
printf("dbtpnCallback: success record=%s\n", pname);
|
||||
else
|
||||
printf("%s dbtpnCallback processNotify.status %d\n",
|
||||
pname, (int) status);
|
||||
epicsEventSignal(ptpnInfo->callbackDone);
|
||||
}
|
||||
|
||||
static void tpnThread(void *pvt)
|
||||
{
|
||||
tpnInfo *ptpnInfo = (tpnInfo *) pvt;
|
||||
processNotify *ppn = (processNotify *) ptpnInfo->ppn;
|
||||
|
||||
dbProcessNotify(ppn);
|
||||
epicsEventWait(ptpnInfo->callbackDone);
|
||||
dbNotifyCancel(ppn);
|
||||
epicsEventDestroy(ptpnInfo->callbackDone);
|
||||
dbChannelDelete(ppn->chan);
|
||||
free(ppn);
|
||||
free(ptpnInfo);
|
||||
}
|
||||
|
||||
long dbtpn(char *pname, char *pvalue)
|
||||
{
|
||||
struct dbChannel *chan;
|
||||
tpnInfo *ptpnInfo;
|
||||
processNotify *ppn=NULL;
|
||||
|
||||
chan = dbChannelCreate(pname);
|
||||
if (!chan) {
|
||||
printf("dbtpn: No such channel");
|
||||
return -1;
|
||||
}
|
||||
|
||||
ppn = dbCalloc(1, sizeof(processNotify));
|
||||
ppn->requestType = pvalue ? putProcessRequest : processGetRequest;
|
||||
ppn->chan = chan;
|
||||
ppn->putCallback = putCallback;
|
||||
ppn->getCallback = getCallback;
|
||||
ppn->doneCallback = doneCallback;
|
||||
|
||||
ptpnInfo = dbCalloc(1, sizeof(tpnInfo));
|
||||
ptpnInfo->ppn = ppn;
|
||||
ptpnInfo->callbackDone = epicsEventCreate(epicsEventEmpty);
|
||||
strncpy(ptpnInfo->buffer, pvalue, 80);
|
||||
ptpnInfo->buffer[79] = 0;
|
||||
|
||||
ppn->usrPvt = ptpnInfo;
|
||||
epicsThreadCreate("dbtpn", epicsThreadPriorityHigh,
|
||||
epicsThreadGetStackSize(epicsThreadStackMedium), tpnThread, ptpnInfo);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dbNotifyDump(void)
|
||||
{
|
||||
epicsMutexLockStatus lockStatus;
|
||||
dbRecordType *pdbRecordType;
|
||||
processNotify *ppnRestart;
|
||||
processNotifyRecord *ppnr;
|
||||
int itry;
|
||||
|
||||
for (itry = 0; itry < 100; itry++) {
|
||||
lockStatus = epicsMutexTryLock(pnotifyGlobal->lock);
|
||||
if (lockStatus == epicsMutexLockOK)
|
||||
break;
|
||||
epicsThreadSleep(.05);
|
||||
}
|
||||
|
||||
for (pdbRecordType = (dbRecordType *) ellFirst(&pdbbase->recordTypeList);
|
||||
pdbRecordType;
|
||||
pdbRecordType = (dbRecordType *) ellNext(&pdbRecordType->node)) {
|
||||
dbRecordNode *pdbRecordNode;
|
||||
|
||||
for (pdbRecordNode = (dbRecordNode *) ellFirst(&pdbRecordType->recList);
|
||||
pdbRecordNode;
|
||||
pdbRecordNode = (dbRecordNode *) ellNext(&pdbRecordNode->node)) {
|
||||
dbCommon *precord = pdbRecordNode->precord;
|
||||
processNotify *ppn;
|
||||
notifyPvt *pnotifyPvt;
|
||||
|
||||
if (!precord->name[0] || pdbRecordNode->flags & DBRN_FLAGS_ISALIAS)
|
||||
continue;
|
||||
ppn = precord->ppn;
|
||||
if (!ppn || !precord->ppnr)
|
||||
continue;
|
||||
if (dbChannelRecord(precord->ppn->chan) != precord)
|
||||
continue;
|
||||
|
||||
pnotifyPvt = (notifyPvt *) ppn->pnotifyPvt;
|
||||
printf("%s state %d ppn %p\n waitList\n",
|
||||
precord->name, pnotifyPvt->state, (void*) ppn);
|
||||
ppnr = (processNotifyRecord *) ellFirst(&pnotifyPvt->waitList);
|
||||
while (ppnr) {
|
||||
printf(" %s pact %d\n",
|
||||
ppnr->precord->name, ppnr->precord->pact);
|
||||
ppnr = (processNotifyRecord *) ellNext(&ppnr->waitNode.node);
|
||||
}
|
||||
ppnr = precord->ppnr;
|
||||
if (ppnr) {
|
||||
ppnRestart = (processNotify *)ellFirst(
|
||||
&precord->ppnr->restartList);
|
||||
if (ppnRestart)
|
||||
printf("%s restartList\n", precord->name);
|
||||
while (ppnRestart) {
|
||||
printf(" %s\n", dbChannelRecord(ppnRestart->chan)->name);
|
||||
ppnRestart = (processNotify *) ellNext(
|
||||
&ppnRestart->restartNode.node);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (lockStatus == epicsMutexLockOK)
|
||||
epicsMutexUnlock(pnotifyGlobal->lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,168 @@
|
||||
/*************************************************************************\
|
||||
* Copyright (c) 2002 The University of Chicago, as Operator of Argonne
|
||||
* National Laboratory.
|
||||
* Copyright (c) 2002 The Regents of the University of California, as
|
||||
* Operator of Los Alamos National Laboratory.
|
||||
* EPICS BASE is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
\*************************************************************************/
|
||||
/* dbNotify.h */
|
||||
|
||||
#ifndef INCdbNotifyh
|
||||
#define INCdbNotifyh
|
||||
|
||||
#include "shareLib.h"
|
||||
#include "ellLib.h"
|
||||
#include "epicsEvent.h"
|
||||
#include "callback.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct dbCommon;
|
||||
struct processNotify;
|
||||
|
||||
typedef struct ellCheckNode{
|
||||
ELLNODE node;
|
||||
int isOnList;
|
||||
} ellCheckNode;
|
||||
|
||||
typedef enum {
|
||||
processRequest,
|
||||
putProcessRequest,
|
||||
processGetRequest,
|
||||
putProcessGetRequest
|
||||
} notifyRequestType;
|
||||
|
||||
typedef enum {
|
||||
putDisabledType,
|
||||
putFieldType,
|
||||
putType
|
||||
} notifyPutType;
|
||||
|
||||
typedef enum {
|
||||
getFieldType,
|
||||
getType /* FIXME: Never used? */
|
||||
} notifyGetType;
|
||||
|
||||
typedef enum {
|
||||
notifyOK,
|
||||
notifyCanceled,
|
||||
notifyError,
|
||||
notifyPutDisabled
|
||||
} notifyStatus;
|
||||
|
||||
typedef struct processNotify {
|
||||
/* following fields are for private use by dbNotify implementation */
|
||||
ellCheckNode restartNode;
|
||||
void *pnotifyPvt;
|
||||
/* The following fields are set by dbNotify. */
|
||||
notifyStatus status;
|
||||
int wasProcessed; /* (0,1) => (no,yes) */
|
||||
/*The following members are set by user*/
|
||||
notifyRequestType requestType;
|
||||
struct dbChannel *chan; /*dbChannel*/
|
||||
int (*putCallback)(struct processNotify *,notifyPutType type);
|
||||
void (*getCallback)(struct processNotify *,notifyGetType type);
|
||||
void (*doneCallback)(struct processNotify *);
|
||||
void *usrPvt; /*for private use of user*/
|
||||
} processNotify;
|
||||
|
||||
|
||||
/* dbProcessNotify and dbNotifyCancel are called by user*/
|
||||
epicsShareFunc void dbProcessNotify(processNotify *pprocessNotify);
|
||||
epicsShareFunc void dbNotifyCancel(processNotify *pprocessNotify);
|
||||
|
||||
/* dbProcessNotifyInit called by iocInit */
|
||||
epicsShareFunc void dbProcessNotifyInit(void);
|
||||
epicsShareFunc void dbProcessNotifyExit(void);
|
||||
|
||||
/*dbNotifyAdd called by dbScanPassive and dbScanLink*/
|
||||
epicsShareFunc void dbNotifyAdd(
|
||||
struct dbCommon *pfrom,struct dbCommon *pto);
|
||||
/*dbNotifyCompletion called by recGblFwdLink or dbAccess*/
|
||||
epicsShareFunc void dbNotifyCompletion(struct dbCommon *precord);
|
||||
|
||||
/* db_put_process defined here since it requires dbNotify.
|
||||
* src_type is the old DBR type
|
||||
* This is called by a dbNotify putCallback that uses oldDbr types
|
||||
*/
|
||||
epicsShareFunc int db_put_process(
|
||||
processNotify *processNotify,notifyPutType type,
|
||||
int src_type,const void *psrc, int no_elements);
|
||||
|
||||
/* dbtpn is test routine for dbNotify putProcessRequest */
|
||||
epicsShareFunc long dbtpn(char *recordname,char *value);
|
||||
|
||||
/* dbNotifyDump is an INVASIVE debug utility. Don't use this needlessly*/
|
||||
epicsShareFunc int dbNotifyDump(void);
|
||||
|
||||
/* This module provides code to handle process notify.
|
||||
* client code semantics are:
|
||||
* 1) The client code allocates storage for a processNotify structure.
|
||||
* This structure can be used for multiple calls to dbProcessNotify.
|
||||
* The client is responsible for setting the following fields :
|
||||
* requestType - The type of request.
|
||||
* chan - This is typically set via a call to dbChannelCreate.
|
||||
* putCallback - If requestType is putProcessRequest or putProcessGetRequest
|
||||
* getCallback - If request is processGetRequest or putProcessGetRequest
|
||||
* doneCallback - Must be set
|
||||
* usrPvt - For exclusive use of client. dbNotify does not access this field
|
||||
* 2) The client calls dbProcessNotify.
|
||||
* 3) putCallback is called after dbNotify has claimed the record instance
|
||||
* but before a potential process is requested.
|
||||
* The putCallback MUST issue the correct put request
|
||||
* specified by notifyPutType
|
||||
* 4) getCallback is called after a possible process is complete
|
||||
* (including asynchronous completion) but before dbNotify has
|
||||
* released the record.
|
||||
* The getCallback MUST issue the correct get request
|
||||
* specified by notifyGetType
|
||||
* 5) doneCallback is called when dbNotify has released the record.
|
||||
* The client can issue a new dbProcessNotify request from
|
||||
* doneCallback or anytime after doneCallback returns.
|
||||
* 6) The client can call dbNotifyCancel at any time.
|
||||
* If a dbProcessNotify is active, dbNotifyCancel will not return until
|
||||
* the dbNotifyRequest is actually canceled. The client must be prepared
|
||||
* for a callback to be called while dbNotifyCancel is active.
|
||||
*
|
||||
* dbProcessNotify handles the semantics of record locking and deciding
|
||||
* if a process request is issued and also calls the client callbacks.
|
||||
*
|
||||
* A process request is issued if any of the following is true.
|
||||
* 1) The requester has issued a processs request and record is passive.
|
||||
* 2) The requester is doing a put, the record is passive, and either
|
||||
* a) The field description is process passive.
|
||||
* b) The field is PROC.
|
||||
* 3) The requester has requested processGet and the record is passive.
|
||||
*
|
||||
* iocInit calls processNotifyInit.
|
||||
*
|
||||
* The other global routines (dbNotifyAdd and dbNotifyCompletion) are called by:
|
||||
*
|
||||
* dbAccess.c
|
||||
* dbScanPassive and dbScanLink
|
||||
* call dbNotifyAdd just before calling dbProcess
|
||||
* dbProcess
|
||||
* Calls dbNotifyCompletion if dbProcess does not call process
|
||||
* Unless pact is already true.
|
||||
* recGbl
|
||||
* recGblFwdLink calls dbNotifyCompletion
|
||||
*
|
||||
* Two fields in dbCommon are used for put notify.
|
||||
* ppn pointer to processNotify
|
||||
* If a record is part of a put notify group,
|
||||
* This field is the address of the associated processNotify.
|
||||
* As soon as a record completes processing the field is set NULL
|
||||
* ppnr pointer to processNotifyRecord, which is a private structure
|
||||
* owned by dbNotify.
|
||||
* dbNotify is reponsible for this structure.
|
||||
*
|
||||
*/
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /*INCdbNotifyh*/
|
||||
|
||||
@@ -0,0 +1,231 @@
|
||||
/*************************************************************************\
|
||||
* Copyright (c) 2012 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.
|
||||
\*************************************************************************/
|
||||
|
||||
/*
|
||||
* Author:
|
||||
* Jeffrey O. Hill
|
||||
* johill@lanl.gov
|
||||
* 505 665 1831
|
||||
*/
|
||||
|
||||
#include <string>
|
||||
#include <stdexcept>
|
||||
|
||||
#include <limits.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "epicsMutex.h"
|
||||
#include "epicsEvent.h"
|
||||
#include "epicsTime.h"
|
||||
#include "tsFreeList.h"
|
||||
#include "errMdef.h"
|
||||
#include "errlog.h"
|
||||
|
||||
#include "caerr.h" // this needs to be eliminated
|
||||
#include "db_access.h" // this needs to be eliminated
|
||||
|
||||
#define epicsExportSharedSymbols
|
||||
#include "dbCAC.h"
|
||||
#include "dbChannelIO.h"
|
||||
#include "dbPutNotifyBlocker.h"
|
||||
|
||||
dbPutNotifyBlocker::dbPutNotifyBlocker ( epicsMutex & mutexIn ) :
|
||||
mutex ( mutexIn ), pNotify ( 0 ),
|
||||
maxValueSize ( sizeof ( this->dbrScalarValue ) )
|
||||
{
|
||||
memset ( & this->pn, '\0', sizeof ( this->pn ) );
|
||||
memset ( & this->dbrScalarValue, '\0', sizeof ( this->dbrScalarValue ) );
|
||||
this->pbuffer = & this->dbrScalarValue;
|
||||
}
|
||||
|
||||
dbPutNotifyBlocker::~dbPutNotifyBlocker ()
|
||||
{
|
||||
}
|
||||
|
||||
void dbPutNotifyBlocker::destructor ( CallbackGuard & cbGuard,
|
||||
epicsGuard < epicsMutex > & guard )
|
||||
{
|
||||
guard.assertIdenticalMutex ( this->mutex );
|
||||
this->cancel ( cbGuard, guard );
|
||||
if ( this->maxValueSize > sizeof ( this->dbrScalarValue ) ) {
|
||||
char * pBuf = static_cast < char * > ( this->pbuffer );
|
||||
delete [] pBuf;
|
||||
}
|
||||
this->~dbPutNotifyBlocker ();
|
||||
}
|
||||
|
||||
void dbPutNotifyBlocker::cancel (
|
||||
CallbackGuard & cbGuard,
|
||||
epicsGuard < epicsMutex > & guard )
|
||||
{
|
||||
guard.assertIdenticalMutex ( this->mutex );
|
||||
if ( this->pNotify ) {
|
||||
epicsGuardRelease < epicsMutex > unguard ( guard );
|
||||
dbNotifyCancel ( &this->pn );
|
||||
}
|
||||
this->pNotify = 0;
|
||||
this->block.signal ();
|
||||
}
|
||||
|
||||
void dbPutNotifyBlocker::expandValueBuf (
|
||||
epicsGuard < epicsMutex > & guard, unsigned long newSize )
|
||||
{
|
||||
guard.assertIdenticalMutex ( this->mutex );
|
||||
if ( this->maxValueSize < newSize ) {
|
||||
if ( this->maxValueSize > sizeof ( this->dbrScalarValue ) ) {
|
||||
char * pBuf = static_cast < char * > ( this->pbuffer );
|
||||
delete [] pBuf;
|
||||
this->maxValueSize = sizeof ( this->dbrScalarValue );
|
||||
this->pbuffer = & this->dbrScalarValue;
|
||||
}
|
||||
this->pbuffer = new char [newSize];
|
||||
this->maxValueSize = newSize;
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" int putNotifyPut ( processNotify *ppn, notifyPutType type )
|
||||
{
|
||||
if(ppn->status==notifyCanceled) return 0;
|
||||
/*
|
||||
* No locking in this method because only a dbNotifyCancel could interrupt
|
||||
* and it does not return until cancel is done.
|
||||
*/
|
||||
dbPutNotifyBlocker * pBlocker = static_cast < dbPutNotifyBlocker * > ( ppn->usrPvt );
|
||||
return db_put_process(ppn,type,
|
||||
pBlocker->dbrType,pBlocker->pbuffer,pBlocker->nRequest);
|
||||
}
|
||||
|
||||
extern "C" void putNotifyCompletion ( processNotify *ppn )
|
||||
{
|
||||
dbPutNotifyBlocker * const pBlocker =
|
||||
static_cast < dbPutNotifyBlocker * > ( ppn->usrPvt );
|
||||
epicsGuard < epicsMutex > guard ( pBlocker->mutex );
|
||||
cacWriteNotify * const pNtfy = pBlocker->pNotify;
|
||||
if ( pNtfy ) {
|
||||
pBlocker->pNotify = 0;
|
||||
// Its necessary to signal the initiators now before we call
|
||||
// the user callback. This is less efficent, and potentially
|
||||
// causes more thread context switching, but its probably
|
||||
// unavoidable because its possible that the use callback
|
||||
// might destroy this object.
|
||||
pBlocker->block.signal ();
|
||||
if ( pBlocker->pn.status != notifyOK ) {
|
||||
pNtfy->exception (
|
||||
guard, ECA_PUTFAIL, "put notify unsuccessful",
|
||||
static_cast < unsigned > (pBlocker->dbrType),
|
||||
static_cast < unsigned > (pBlocker->nRequest) );
|
||||
}
|
||||
else {
|
||||
pNtfy->completion ( guard );
|
||||
}
|
||||
}
|
||||
else {
|
||||
errlogPrintf ( "put notify completion with nill pNotify?\n" );
|
||||
}
|
||||
}
|
||||
|
||||
void dbPutNotifyBlocker::initiatePutNotify (
|
||||
epicsGuard < epicsMutex > & guard, cacWriteNotify & notify,
|
||||
struct dbChannel * dbch, unsigned type, unsigned long count,
|
||||
const void * pValue )
|
||||
{
|
||||
guard. assertIdenticalMutex ( this->mutex );
|
||||
epicsTime begin;
|
||||
bool beginTimeInit = false;
|
||||
while ( true ) {
|
||||
if ( this->pNotify == 0 ) {
|
||||
this->pNotify = & notify;
|
||||
break;
|
||||
}
|
||||
if ( beginTimeInit ) {
|
||||
if ( epicsTime::getCurrent () - begin > 30.0 ) {
|
||||
throw cacChannel::requestTimedOut ();
|
||||
}
|
||||
}
|
||||
else {
|
||||
begin = epicsTime::getCurrent ();
|
||||
beginTimeInit = true;
|
||||
}
|
||||
{
|
||||
epicsGuardRelease < epicsMutex > autoRelease ( guard );
|
||||
this->block.wait ( 1.0 );
|
||||
}
|
||||
}
|
||||
|
||||
if ( count > LONG_MAX ) {
|
||||
throw cacChannel::outOfBounds();
|
||||
}
|
||||
|
||||
if ( type > SHRT_MAX ) {
|
||||
throw cacChannel::badType();
|
||||
}
|
||||
|
||||
this->dbrType = type;
|
||||
this->nRequest = static_cast < unsigned > ( count );
|
||||
this->pn.requestType = putProcessRequest;
|
||||
this->pn.chan = dbch;
|
||||
this->pn.putCallback = putNotifyPut;
|
||||
this->pn.doneCallback = putNotifyCompletion;
|
||||
this->pn.usrPvt = this;
|
||||
|
||||
unsigned long size = dbr_size_n ( type, count );
|
||||
this->expandValueBuf ( guard, size );
|
||||
memcpy ( this->pbuffer, pValue, size );
|
||||
|
||||
{
|
||||
epicsGuardRelease < epicsMutex > autoRelease ( guard );
|
||||
::dbProcessNotify ( &this->pn );
|
||||
}
|
||||
}
|
||||
|
||||
void dbPutNotifyBlocker::show ( unsigned level ) const
|
||||
{
|
||||
epicsGuard < epicsMutex > guard ( this->mutex );
|
||||
this->show ( guard, level );
|
||||
}
|
||||
|
||||
void dbPutNotifyBlocker::show (
|
||||
epicsGuard < epicsMutex > &, unsigned level ) const
|
||||
{
|
||||
printf ( "put notify blocker at %p\n",
|
||||
static_cast <const void *> ( this ) );
|
||||
if ( level > 0u ) {
|
||||
this->block.show ( level - 1u );
|
||||
}
|
||||
}
|
||||
|
||||
dbSubscriptionIO * dbPutNotifyBlocker::isSubscription ()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
void * dbPutNotifyBlocker::operator new ( size_t size,
|
||||
tsFreeList < dbPutNotifyBlocker, 64, epicsMutexNOOP > & freeList )
|
||||
{
|
||||
return freeList.allocate ( size );
|
||||
}
|
||||
|
||||
#ifdef CXX_PLACEMENT_DELETE
|
||||
void dbPutNotifyBlocker::operator delete ( void *pCadaver,
|
||||
tsFreeList < dbPutNotifyBlocker, 64, epicsMutexNOOP > & freeList )
|
||||
{
|
||||
freeList.release ( pCadaver );
|
||||
}
|
||||
#endif
|
||||
|
||||
void dbPutNotifyBlocker::operator delete ( void * )
|
||||
{
|
||||
// Visual C++ .net appears to require operator delete if
|
||||
// placement operator delete is defined? I smell a ms rat
|
||||
// because if I declare placement new and delete, but
|
||||
// comment out the placement delete definition there are
|
||||
// no undefined symbols.
|
||||
errlogPrintf ( "%s:%d this compiler is confused about placement delete - memory was probably leaked",
|
||||
__FILE__, __LINE__ );
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
/*************************************************************************\
|
||||
* 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.
|
||||
\*************************************************************************/
|
||||
|
||||
/*
|
||||
* Author Jeffrey O. Hill
|
||||
* johill@lanl.gov
|
||||
* 505 665 1831
|
||||
*/
|
||||
|
||||
#ifndef dbPutNotifyBlockerh
|
||||
#define dbPutNotifyBlockerh
|
||||
|
||||
#ifdef epicsExportSharedSymbols
|
||||
#define dbPutNotifyBlockerh_restore_epicsExportSharedSymbols
|
||||
#undef epicsExportSharedSymbols
|
||||
#endif
|
||||
|
||||
#include "tsFreeList.h"
|
||||
#include "compilerDependencies.h"
|
||||
|
||||
#ifdef dbPutNotifyBlockerh_restore_epicsExportSharedSymbols
|
||||
#define epicsExportSharedSymbols
|
||||
#endif
|
||||
|
||||
class dbPutNotifyBlocker : public dbBaseIO {
|
||||
public:
|
||||
dbPutNotifyBlocker ( epicsMutex & );
|
||||
void destructor ( CallbackGuard &, epicsGuard < epicsMutex > & );
|
||||
void initiatePutNotify ( epicsGuard < epicsMutex > &,
|
||||
cacWriteNotify &, struct dbChannel *,
|
||||
unsigned type, unsigned long count, const void * pValue );
|
||||
void cancel ( CallbackGuard &, epicsGuard < epicsMutex > & );
|
||||
void show ( epicsGuard < epicsMutex > &, unsigned level ) const;
|
||||
void show ( unsigned level ) const;
|
||||
void * operator new ( size_t size,
|
||||
tsFreeList < dbPutNotifyBlocker, 64, epicsMutexNOOP > & );
|
||||
epicsPlacementDeleteOperator (( void *,
|
||||
tsFreeList < dbPutNotifyBlocker, 64, epicsMutexNOOP > & ))
|
||||
private:
|
||||
processNotify pn;
|
||||
//
|
||||
// Include a union of all scalar types
|
||||
// including fixed length strings so
|
||||
// that in many cases we can avoid
|
||||
// allocating another buffer
|
||||
//
|
||||
union {
|
||||
dbr_string_t strval;
|
||||
dbr_short_t shrtval;
|
||||
dbr_short_t intval;
|
||||
dbr_float_t fltval;
|
||||
dbr_enum_t enmval;
|
||||
dbr_char_t charval;
|
||||
dbr_long_t longval;
|
||||
dbr_double_t doubleval;
|
||||
} dbrScalarValue;
|
||||
epicsEvent block;
|
||||
epicsMutex & mutex;
|
||||
cacWriteNotify * pNotify;
|
||||
unsigned long maxValueSize;
|
||||
// arguments for db_put_field
|
||||
void *pbuffer;
|
||||
long nRequest;
|
||||
short dbrType;
|
||||
// end arguments for db_put_field
|
||||
dbSubscriptionIO * isSubscription ();
|
||||
void expandValueBuf (
|
||||
epicsGuard < epicsMutex > &, unsigned long newSize );
|
||||
friend void putNotifyCompletion ( processNotify * ppn );
|
||||
friend int putNotifyPut ( processNotify *ppn, notifyPutType type );
|
||||
dbPutNotifyBlocker ( const dbPutNotifyBlocker & );
|
||||
dbPutNotifyBlocker & operator = ( const dbPutNotifyBlocker & );
|
||||
virtual ~dbPutNotifyBlocker ();
|
||||
void operator delete ( void * );
|
||||
};
|
||||
|
||||
#endif // ifndef dbPutNotifyBlockerh
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,80 @@
|
||||
/*************************************************************************\
|
||||
* Copyright (c) 2008 UChicago Argonne LLC, as Operator of Argonne
|
||||
* National Laboratory.
|
||||
* Copyright (c) 2002 The Regents of the University of California, as
|
||||
* Operator of Los Alamos National Laboratory.
|
||||
* EPICS BASE is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
\*************************************************************************/
|
||||
/*
|
||||
* Author: Marty Kraimer
|
||||
* Date: 07-17-91
|
||||
*/
|
||||
|
||||
#ifndef INCdbScanH
|
||||
#define INCdbScanH
|
||||
|
||||
#include <limits.h>
|
||||
|
||||
#include "menuScan.h"
|
||||
#include "shareLib.h"
|
||||
#include "compilerDependencies.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define SCAN_PASSIVE menuScanPassive
|
||||
#define SCAN_EVENT menuScanEvent
|
||||
#define SCAN_IO_EVENT menuScanI_O_Intr
|
||||
#define SCAN_1ST_PERIODIC (menuScanI_O_Intr + 1)
|
||||
|
||||
#define MAX_PHASE SHRT_MAX
|
||||
#define MIN_PHASE SHRT_MIN
|
||||
|
||||
/*definitions for I/O Interrupt Scanning */
|
||||
struct ioscan_head;
|
||||
|
||||
typedef struct ioscan_head *IOSCANPVT;
|
||||
typedef struct event_list *EVENTPVT;
|
||||
|
||||
struct dbCommon;
|
||||
|
||||
typedef void (*io_scan_complete)(void *usr, IOSCANPVT, int prio);
|
||||
typedef void (*once_complete)(void *usr, struct dbCommon*);
|
||||
|
||||
epicsShareFunc long scanInit(void);
|
||||
epicsShareFunc void scanRun(void);
|
||||
epicsShareFunc void scanPause(void);
|
||||
epicsShareFunc void scanStop(void);
|
||||
epicsShareFunc void scanCleanup(void);
|
||||
|
||||
epicsShareFunc EVENTPVT eventNameToHandle(const char* event);
|
||||
epicsShareFunc void postEvent(EVENTPVT epvt);
|
||||
epicsShareFunc void post_event(int event) EPICS_DEPRECATED;
|
||||
epicsShareFunc void scanAdd(struct dbCommon *);
|
||||
epicsShareFunc void scanDelete(struct dbCommon *);
|
||||
epicsShareFunc double scanPeriod(int scan);
|
||||
epicsShareFunc int scanOnce(struct dbCommon *);
|
||||
epicsShareFunc int scanOnceCallback(struct dbCommon *, once_complete cb, void *usr);
|
||||
epicsShareFunc int scanOnceSetQueueSize(int size);
|
||||
|
||||
/*print periodic lists*/
|
||||
epicsShareFunc int scanppl(double rate);
|
||||
|
||||
/*print event lists*/
|
||||
epicsShareFunc int scanpel(const char *event_name);
|
||||
|
||||
/*print io_event list*/
|
||||
epicsShareFunc int scanpiol(void);
|
||||
|
||||
epicsShareFunc void scanIoInit(IOSCANPVT *ppios);
|
||||
epicsShareFunc unsigned int scanIoRequest(IOSCANPVT pios);
|
||||
epicsShareFunc unsigned int scanIoImmediate(IOSCANPVT pios, int prio);
|
||||
epicsShareFunc void scanIoSetComplete(IOSCANPVT, io_scan_complete, void *usr);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,140 @@
|
||||
/*************************************************************************\
|
||||
* Copyright (c) 2014 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.
|
||||
\*************************************************************************/
|
||||
|
||||
/*
|
||||
* Author: Andrew Johnson <anj@aps.anl.gov>
|
||||
*/
|
||||
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "ellLib.h"
|
||||
#include "envDefs.h"
|
||||
#include "epicsStdio.h"
|
||||
|
||||
#define epicsExportSharedSymbols
|
||||
#include "dbServer.h"
|
||||
|
||||
static ELLLIST serverList = ELLLIST_INIT;
|
||||
static enum { registering, initialized, running, paused, stopped }
|
||||
state = registering;
|
||||
static char *stateNames[] = {
|
||||
"registering", "initialized", "running", "paused", "stopped"
|
||||
};
|
||||
|
||||
int dbRegisterServer(dbServer *psrv)
|
||||
{
|
||||
const char * ignore = envGetConfigParamPtr(&EPICS_IOC_IGNORE_SERVERS);
|
||||
|
||||
if (!psrv || !psrv->name || state != registering)
|
||||
return -1;
|
||||
|
||||
if (strchr(psrv->name, ' ')) {
|
||||
fprintf(stderr, "dbRegisterServer: Bad server name '%s'\n",
|
||||
psrv->name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (ignore) {
|
||||
size_t len = strlen(psrv->name);
|
||||
const char *found;
|
||||
while ((found = strstr(ignore, psrv->name))) {
|
||||
/* Make sure the name isn't just a substring */
|
||||
if ((found == ignore || (found > ignore && found[-1] == ' ')) &&
|
||||
(found[len] == 0 || found[len] == ' ')) {
|
||||
fprintf(stderr, "dbRegisterServer: Ignoring '%s', per environment\n",
|
||||
psrv->name);
|
||||
return 0;
|
||||
}
|
||||
/* It was, try again further down */
|
||||
ignore = found + len;
|
||||
}
|
||||
}
|
||||
|
||||
if (ellNext(&psrv->node) || ellLast(&serverList) == &psrv->node) {
|
||||
fprintf(stderr, "dbRegisterServer: '%s' registered twice?\n",
|
||||
psrv->name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
ellAdd(&serverList, &psrv->node);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dbUnregisterServer(dbServer *psrv)
|
||||
{
|
||||
if (state != registering && state != stopped) {
|
||||
fprintf(stderr, "dbUnregisterServer: Servers still active!\n");
|
||||
return -1;
|
||||
}
|
||||
if (ellFind(&serverList, &psrv->node) < 0) {
|
||||
fprintf(stderr, "dbUnregisterServer: '%s' not registered.\n",
|
||||
psrv->name);
|
||||
return -1;
|
||||
}
|
||||
if (state == stopped && psrv->stop == NULL) {
|
||||
fprintf(stderr, "dbUnregisterServer: '%s' has no stop() method.\n",
|
||||
psrv->name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
ellDelete(&serverList, &psrv->node);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void dbsr(unsigned level)
|
||||
{
|
||||
dbServer *psrv = (dbServer *)ellFirst(&serverList);
|
||||
|
||||
if (!psrv) {
|
||||
printf("No server layers registered with IOC\n");
|
||||
return;
|
||||
}
|
||||
|
||||
printf("Server state: %s\n", stateNames[state]);
|
||||
|
||||
while (psrv) {
|
||||
printf("Server '%s'\n", psrv->name);
|
||||
if (state == running && psrv->report)
|
||||
psrv->report(level);
|
||||
psrv = (dbServer *)ellNext(&psrv->node);
|
||||
}
|
||||
}
|
||||
|
||||
int dbServerClient(char *pBuf, size_t bufSize)
|
||||
{
|
||||
dbServer *psrv = (dbServer *)ellFirst(&serverList);
|
||||
|
||||
if (state != running)
|
||||
return -1;
|
||||
|
||||
while (psrv) {
|
||||
if (psrv->client &&
|
||||
psrv->client(pBuf, bufSize) == 0)
|
||||
return 0;
|
||||
psrv = (dbServer *)ellNext(&psrv->node);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
#define STARTSTOP(routine, method, newState) \
|
||||
void routine(void) \
|
||||
{ \
|
||||
dbServer *psrv = (dbServer *)ellFirst(&serverList); \
|
||||
\
|
||||
while (psrv) { \
|
||||
if (psrv->method) \
|
||||
psrv->method(); \
|
||||
psrv = (dbServer *)ellNext(&psrv->node); \
|
||||
} \
|
||||
state = newState; \
|
||||
}
|
||||
|
||||
STARTSTOP(dbInitServers, init, initialized)
|
||||
STARTSTOP(dbRunServers, run, running)
|
||||
STARTSTOP(dbPauseServers, pause, paused)
|
||||
STARTSTOP(dbStopServers, stop, stopped)
|
||||
@@ -0,0 +1,175 @@
|
||||
/*************************************************************************\
|
||||
* Copyright (c) 2014 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.
|
||||
\*************************************************************************/
|
||||
|
||||
/**
|
||||
* @file dbServer.h
|
||||
* @author Andrew Johnson <anj@aps.anl.gov>
|
||||
*
|
||||
* @brief The IOC's interface to the server layers that publish its PVs.
|
||||
*
|
||||
* All server layers which publish IOC record data should initialize a
|
||||
* dbServer structure and register it with the IOC. The methods that
|
||||
* the dbServer interface provides allow the IOC to start, pause and stop
|
||||
* the servers together, and to provide status and debugging information
|
||||
* to the IOC user/developer through a common set of commands.
|
||||
*
|
||||
* @todo No API is provided yet for calling stats() methods.
|
||||
* Nothing in the IOC calls dbStopServers(), not sure where it should go.
|
||||
*/
|
||||
|
||||
#ifndef INC_dbServer_H
|
||||
#define INC_dbServer_H
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include "ellLib.h"
|
||||
#include "shareLib.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/** @brief Server information structure.
|
||||
*
|
||||
* Every server layer should initialize and register an instance of this
|
||||
* structure with the IOC by passing it to the dbRegisterServer() routine.
|
||||
*
|
||||
* All methods in this struct are optional; use @c NULL if a server is
|
||||
* unable to support a particular operation (or if it hasn't been
|
||||
* implemented yet).
|
||||
*/
|
||||
|
||||
typedef struct dbServer {
|
||||
/** @brief Linked list node; initialize to @c ELLNODE_INIT */
|
||||
ELLNODE node;
|
||||
|
||||
/** @brief A short server identifier; printable, with no spaces */
|
||||
const char *name;
|
||||
|
||||
/** @brief Print level-dependent status report to stdout.
|
||||
*
|
||||
* @param level Interest level, specifies how much detail to print.
|
||||
*/
|
||||
void (* report) (unsigned level);
|
||||
|
||||
/** @brief Get number of channels and clients currently connected.
|
||||
*
|
||||
* @param channels NULL or pointer for returning channel count.
|
||||
* @param clients NULL or pointer for returning client count.
|
||||
*/
|
||||
void (* stats) (unsigned *channels, unsigned *clients);
|
||||
|
||||
/** @brief Get identity of client initiating the calling thread.
|
||||
*
|
||||
* Must fill in the buffer with the client's identity when called from a
|
||||
* thread that belongs to this server layer. For other threads, the
|
||||
* method should do nothing, just return -1.
|
||||
* @param pBuf Buffer for client identity string.
|
||||
* @param bufSize Number of chars available in pBuf.
|
||||
* @return -1 means calling thread is not owned by this server.
|
||||
* 0 means the thread was recognized and pBuf has been filled in.
|
||||
*/
|
||||
int (* client) (char *pBuf, size_t bufSize);
|
||||
|
||||
/** @name Control Methods
|
||||
* These control methods for the server will be called by routines
|
||||
* related to iocInit for all registered servers in turn when the IOC
|
||||
* is being initialized, run, paused and stopped respectively.
|
||||
*
|
||||
* @{
|
||||
*/
|
||||
|
||||
/** @brief Server init method.
|
||||
*
|
||||
* Called for all registered servers by dbInitServers().
|
||||
*/
|
||||
void (* init) (void);
|
||||
|
||||
/** @brief Server run method.
|
||||
*
|
||||
* Called for all registered servers by dbRunServers().
|
||||
*/
|
||||
void (* run) (void);
|
||||
|
||||
/** @brief Server pause method.
|
||||
*
|
||||
* Called for all registered servers by dbPauseServers().
|
||||
*/
|
||||
void (* pause) (void);
|
||||
|
||||
/** @brief Server stop method.
|
||||
*
|
||||
* Called for all registered servers by dbStopServers().
|
||||
*/
|
||||
void (* stop) (void);
|
||||
|
||||
/** @}
|
||||
*/
|
||||
} dbServer;
|
||||
|
||||
|
||||
/** @brief Register a server layer with the IOC
|
||||
*
|
||||
* This should only be called once for each server layer.
|
||||
* @param psrv Server information structure for the server
|
||||
*/
|
||||
epicsShareFunc int dbRegisterServer(dbServer *psrv);
|
||||
|
||||
/** @brief Unregister a server layer
|
||||
*
|
||||
* This should only be called when the servers are inactive.
|
||||
* @param psrv Server information structure for the server
|
||||
*/
|
||||
epicsShareFunc int dbUnregisterServer(dbServer *psrv);
|
||||
|
||||
/** @brief Print dbServer Reports.
|
||||
*
|
||||
* Calls the report methods of all registered servers.
|
||||
* This routine is provided as an IOC Shell command.
|
||||
* @param level Interest level, specifies how much detail to print.
|
||||
*/
|
||||
epicsShareFunc void dbsr(unsigned level);
|
||||
|
||||
/** @brief Query servers for client's identity.
|
||||
*
|
||||
* This routine is called by code that wants to identify who (or what)
|
||||
* is responsible for the thread which is currently running. Setting
|
||||
* the @c TPRO field of a record is one way to trigger this; the identity
|
||||
* of the calling thread is printed along with the record name whenever
|
||||
* the record is subsequently processed.
|
||||
*/
|
||||
epicsShareFunc int dbServerClient(char *pBuf, size_t bufSize);
|
||||
|
||||
/** @brief Initialize all registered servers.
|
||||
*
|
||||
* Calls all dbServer::init() methods.
|
||||
*/
|
||||
epicsShareFunc void dbInitServers(void);
|
||||
|
||||
/** @brief Run all registered servers.
|
||||
*
|
||||
* Calls all dbServer::run() methods.
|
||||
*/
|
||||
epicsShareFunc void dbRunServers(void);
|
||||
|
||||
/** @brief Pause all registered servers.
|
||||
*
|
||||
* Calls all dbServer::pause() methods.
|
||||
*/
|
||||
epicsShareFunc void dbPauseServers(void);
|
||||
|
||||
/** @brief Stop all registered servers.
|
||||
*
|
||||
* Calls all dbServer::stop() methods.
|
||||
*/
|
||||
epicsShareFunc void dbStopServers(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* INC_dbServer_H */
|
||||
@@ -0,0 +1,109 @@
|
||||
/*************************************************************************\
|
||||
* Copyright (c) 2010 Brookhaven National Laboratory.
|
||||
* Copyright (c) 2010 Helmholtz-Zentrum Berlin
|
||||
* fuer Materialien und Energie GmbH.
|
||||
* EPICS BASE is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
\*************************************************************************/
|
||||
|
||||
/*
|
||||
* Author: Ralph Lange <Ralph.Lange@bessy.de>
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "ellLib.h"
|
||||
#include "epicsMutex.h"
|
||||
#include "epicsString.h"
|
||||
#include "iocsh.h"
|
||||
|
||||
#define epicsExportSharedSymbols
|
||||
#include "dbDefs.h"
|
||||
#include "dbState.h"
|
||||
#include "dbStaticLib.h"
|
||||
|
||||
static ELLLIST states = ELLLIST_INIT;
|
||||
|
||||
typedef struct dbState {
|
||||
ELLNODE node;
|
||||
int status;
|
||||
char *name;
|
||||
epicsMutexId lock; /* FIXME: Use atomic operations instead */
|
||||
} dbState;
|
||||
|
||||
dbStateId dbStateFind(const char *name)
|
||||
{
|
||||
ELLNODE *node;
|
||||
dbStateId id;
|
||||
|
||||
for (node = ellFirst(&states); node; node = ellNext(node)) {
|
||||
id = CONTAINER(node, dbState, node);
|
||||
if (strcmp(id->name, name) == 0)
|
||||
return id;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
dbStateId dbStateCreate(const char *name)
|
||||
{
|
||||
dbStateId id;
|
||||
|
||||
if ((id = dbStateFind(name)))
|
||||
return id;
|
||||
|
||||
id = callocMustSucceed(1, sizeof(dbState), "createDbState");
|
||||
id->name = epicsStrDup(name);
|
||||
id->lock = epicsMutexMustCreate();
|
||||
ellAdd(&states, &id->node);
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
void dbStateSet(dbStateId id)
|
||||
{
|
||||
if (!id)
|
||||
return;
|
||||
epicsMutexMustLock(id->lock);
|
||||
id->status = 1;
|
||||
epicsMutexUnlock(id->lock);
|
||||
}
|
||||
|
||||
void dbStateClear(dbStateId id)
|
||||
{
|
||||
if (!id)
|
||||
return;
|
||||
epicsMutexMustLock(id->lock);
|
||||
id->status = 0;
|
||||
epicsMutexUnlock(id->lock);
|
||||
}
|
||||
|
||||
int dbStateGet(dbStateId id)
|
||||
{
|
||||
int status;
|
||||
|
||||
if (!id)
|
||||
return 0;
|
||||
epicsMutexMustLock(id->lock);
|
||||
status = id->status;
|
||||
epicsMutexUnlock(id->lock);
|
||||
return status;
|
||||
}
|
||||
|
||||
void dbStateShow(dbStateId id, unsigned int level)
|
||||
{
|
||||
if (level >=1)
|
||||
printf("id %p '%s' : ", id, id->name);
|
||||
printf("%s\n", dbStateGet(id) ? "TRUE" : "FALSE");
|
||||
}
|
||||
|
||||
void dbStateShowAll(unsigned int level)
|
||||
{
|
||||
ELLNODE *node;
|
||||
dbStateId id;
|
||||
|
||||
for (node = ellFirst(&states); node; node = ellNext(node)) {
|
||||
id = CONTAINER(node, dbState, node);
|
||||
dbStateShow(id, level+1);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
/*************************************************************************\
|
||||
* Copyright (c) 2010 Brookhaven National Laboratory.
|
||||
* Copyright (c) 2010 Helmholtz-Zentrum Berlin
|
||||
* fuer Materialien und Energie GmbH.
|
||||
* EPICS BASE is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
\*************************************************************************/
|
||||
|
||||
/*
|
||||
* Author: Ralph Lange <Ralph.Lange@bessy.de>
|
||||
*/
|
||||
|
||||
#ifndef INCdbStateH
|
||||
#define INCdbStateH
|
||||
|
||||
#include "shareLib.h"
|
||||
|
||||
/** @file dbState.h
|
||||
* @brief Generic IOC state facility
|
||||
*
|
||||
* This library provides a simple global flag facility that can be used to
|
||||
* synchronize e.g. plugins with IOC-wide states, that may be derived from
|
||||
* events (either soft events or hard events coming from specialized timing
|
||||
* and event hardware).
|
||||
*
|
||||
* A subset of this API is provided as IOC Shell commands to allow
|
||||
* command line debugging.
|
||||
*
|
||||
*/
|
||||
|
||||
typedef struct dbState *dbStateId;
|
||||
|
||||
/** @brief Create db state.
|
||||
*
|
||||
* Creates a new db state with the specified 'name', returning the new id.
|
||||
* If state with that name already exists, the existing state's id is returned.
|
||||
*
|
||||
* <em>Also provided as an IOC Shell command.</em>
|
||||
*
|
||||
* @param name Db state name.
|
||||
* @return Id of db state, NULL for failure.
|
||||
*/
|
||||
epicsShareFunc dbStateId dbStateCreate(const char *name);
|
||||
|
||||
/** @brief Find db state.
|
||||
*
|
||||
* @param name Db state name.
|
||||
* @return Id of db state, NULL if not found.
|
||||
*/
|
||||
epicsShareFunc dbStateId dbStateFind(const char *name);
|
||||
|
||||
/** @brief Set db state to TRUE.
|
||||
*
|
||||
* <em>Also provided as an IOC Shell command.</em>
|
||||
*
|
||||
* @param id Db state id.
|
||||
*/
|
||||
epicsShareFunc void dbStateSet(dbStateId id);
|
||||
|
||||
/** @brief Set db state to FALSE.
|
||||
*
|
||||
* <em>Also provided as an IOC Shell command.</em>
|
||||
*
|
||||
* @param id Db state id.
|
||||
*/
|
||||
epicsShareFunc void dbStateClear(dbStateId id);
|
||||
|
||||
/** @brief Get db state.
|
||||
*
|
||||
* @param id Db state id.
|
||||
* @return Current db state (0|1).
|
||||
*/
|
||||
epicsShareFunc int dbStateGet(dbStateId id);
|
||||
|
||||
/** @brief Print info about db state.
|
||||
*
|
||||
* <em>Also provided as an IOC Shell command.</em>
|
||||
*
|
||||
* @param id Db state id.
|
||||
* @param level Interest level.
|
||||
*/
|
||||
epicsShareFunc void dbStateShow(dbStateId id, unsigned int level);
|
||||
|
||||
/** @brief Print info about all db states.
|
||||
*
|
||||
* <em>Also provided as an IOC Shell command.</em>
|
||||
*
|
||||
* @param level Interest level.
|
||||
*/
|
||||
epicsShareFunc void dbStateShowAll(unsigned int level);
|
||||
|
||||
#endif // INCdbStateH
|
||||
@@ -0,0 +1,154 @@
|
||||
/*************************************************************************\
|
||||
* Copyright (c) 2009 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.
|
||||
\*************************************************************************/
|
||||
|
||||
/*
|
||||
* Author Jeffrey O. Hill
|
||||
* johill@lanl.gov
|
||||
* 505 665 1831
|
||||
*/
|
||||
|
||||
#include <string>
|
||||
#include <stdexcept>
|
||||
|
||||
#include <limits.h>
|
||||
|
||||
#include "epicsMutex.h"
|
||||
#include "epicsEvent.h"
|
||||
#include "tsFreeList.h"
|
||||
|
||||
#include "db_access.h" // need to eliminate this
|
||||
#include "cadef.h" // this can be eliminated when the callbacks use the new interface
|
||||
#include "errlog.h"
|
||||
|
||||
#define epicsExportSharedSymbols
|
||||
#include "dbCAC.h"
|
||||
#include "dbChannelIO.h"
|
||||
#include "db_access_routines.h"
|
||||
|
||||
dbSubscriptionIO::dbSubscriptionIO (
|
||||
epicsGuard < epicsMutex > & guard, epicsMutex & mutexIn,
|
||||
dbContext &, dbChannelIO & chanIO,
|
||||
dbChannel * dbch, cacStateNotify & notifyIn, unsigned typeIn,
|
||||
unsigned long countIn, unsigned maskIn, dbEventCtx ctx ) :
|
||||
mutex ( mutexIn ), count ( countIn ), notify ( notifyIn ),
|
||||
chan ( chanIO ), es ( 0 ), type ( typeIn ), id ( 0u )
|
||||
{
|
||||
guard.assertIdenticalMutex ( this->mutex );
|
||||
{
|
||||
epicsGuardRelease < epicsMutex > unguard ( guard );
|
||||
this->es = db_add_event ( ctx, dbch,
|
||||
dbSubscriptionEventCallback, (void *) this, maskIn );
|
||||
if ( this->es == 0 ) {
|
||||
throw std::bad_alloc();
|
||||
}
|
||||
db_post_single_event ( this->es );
|
||||
db_event_enable ( this->es );
|
||||
}
|
||||
}
|
||||
|
||||
dbSubscriptionIO::~dbSubscriptionIO ()
|
||||
{
|
||||
}
|
||||
|
||||
void dbSubscriptionIO::destructor ( CallbackGuard & cbGuard,
|
||||
epicsGuard < epicsMutex > & guard )
|
||||
{
|
||||
guard.assertIdenticalMutex ( this->mutex );
|
||||
this->~dbSubscriptionIO ();
|
||||
}
|
||||
|
||||
void dbSubscriptionIO::unsubscribe ( CallbackGuard & cbGuard,
|
||||
epicsGuard < epicsMutex > & guard )
|
||||
{
|
||||
guard.assertIdenticalMutex ( this->mutex );
|
||||
if ( this->es ) {
|
||||
dbEventSubscription tmp = this->es;
|
||||
this->es = 0;
|
||||
{
|
||||
epicsGuardRelease < epicsMutex > unguard ( guard );
|
||||
db_cancel_event ( tmp );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void dbSubscriptionIO::channelDeleteException (
|
||||
CallbackGuard &,
|
||||
epicsGuard < epicsMutex > & guard )
|
||||
{
|
||||
guard.assertIdenticalMutex ( this->mutex );
|
||||
this->notify.exception ( guard, ECA_CHANDESTROY,
|
||||
this->chan.pName(guard), this->type, this->count );
|
||||
}
|
||||
|
||||
void dbSubscriptionIO::operator delete ( void * )
|
||||
{
|
||||
// Visual C++ .net appears to require operator delete if
|
||||
// placement operator delete is defined? I smell a ms rat
|
||||
// because if I declare placement new and delete, but
|
||||
// comment out the placement delete definition there are
|
||||
// no undefined symbols.
|
||||
errlogPrintf ( "%s:%d this compiler is confused about placement delete - memory was probably leaked",
|
||||
__FILE__, __LINE__ );
|
||||
}
|
||||
|
||||
void * dbSubscriptionIO::operator new ( size_t size,
|
||||
tsFreeList < dbSubscriptionIO, 256, epicsMutexNOOP > & freeList )
|
||||
{
|
||||
return freeList.allocate ( size );
|
||||
}
|
||||
|
||||
#ifdef CXX_PLACEMENT_DELETE
|
||||
void dbSubscriptionIO::operator delete ( void * pCadaver,
|
||||
tsFreeList < dbSubscriptionIO, 256, epicsMutexNOOP > & freeList )
|
||||
{
|
||||
freeList.release ( pCadaver );
|
||||
}
|
||||
#endif
|
||||
|
||||
extern "C" void dbSubscriptionEventCallback ( void *pPrivate, struct dbChannel * /* dbch */,
|
||||
int /* eventsRemaining */, struct db_field_log *pfl )
|
||||
{
|
||||
dbSubscriptionIO * pIO = static_cast < dbSubscriptionIO * > ( pPrivate );
|
||||
pIO->chan.callStateNotify ( pIO->type, pIO->count, pfl, pIO->notify );
|
||||
}
|
||||
|
||||
void dbSubscriptionIO::show ( unsigned level ) const
|
||||
{
|
||||
epicsGuard < epicsMutex > guard ( this->mutex );
|
||||
this->show ( guard, level );
|
||||
}
|
||||
|
||||
void dbSubscriptionIO::show (
|
||||
epicsGuard < epicsMutex > & guard, unsigned level ) const
|
||||
{
|
||||
guard.assertIdenticalMutex ( this->mutex );
|
||||
|
||||
printf ( "Data base subscription IO at %p\n",
|
||||
static_cast <const void *> ( this ) );
|
||||
if ( level > 0u ) {
|
||||
short tmpType;
|
||||
if ( this->type < SHRT_MAX ) {
|
||||
tmpType = static_cast < short > ( this->type );
|
||||
printf ( "\ttype %s, count %lu, channel at %p\n",
|
||||
dbf_type_to_text ( tmpType ), this->count,
|
||||
static_cast <void *> ( &this->chan ) );
|
||||
}
|
||||
else {
|
||||
printf ( "strange type !, count %lu, channel at %p\n",
|
||||
this->count, static_cast <void *> ( &this->chan ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dbSubscriptionIO * dbSubscriptionIO::isSubscription ()
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,52 @@
|
||||
/*************************************************************************\
|
||||
* Copyright (c) 2007 UChicago Argonne LLC, as Operator of Argonne
|
||||
* National Laboratory.
|
||||
* Copyright (c) 2002 The Regents of the University of California, as
|
||||
* Operator of Los Alamos National Laboratory.
|
||||
* EPICS BASE is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
\*************************************************************************/
|
||||
|
||||
#ifndef INC_dbTest_H
|
||||
#define INC_dbTest_H
|
||||
|
||||
#include "shareLib.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*dbAddr info */
|
||||
epicsShareFunc long dba(const char *pname);
|
||||
/*list records*/
|
||||
epicsShareFunc long dbl(
|
||||
const char *precordTypename,const char *fields);
|
||||
/*list number of records of each type*/
|
||||
epicsShareFunc long dbnr(int verbose);
|
||||
/* list aliases */
|
||||
epicsShareFunc long dbla(const char *pmask);
|
||||
/*list records with mask*/
|
||||
epicsShareFunc long dbgrep(const char *pmask);
|
||||
/*get field value*/
|
||||
epicsShareFunc long dbgf(const char *pname);
|
||||
/*put field value*/
|
||||
epicsShareFunc long dbpf(const char *pname,const char *pvalue);
|
||||
/*print record*/
|
||||
epicsShareFunc long dbpr(const char *pname,int interest_level);
|
||||
/*test record*/
|
||||
epicsShareFunc long dbtr(const char *pname);
|
||||
/*test get field*/
|
||||
epicsShareFunc long dbtgf(const char *pname);
|
||||
/*test put field*/
|
||||
epicsShareFunc long dbtpf(const char *pname,const char *pvalue);
|
||||
/*I/O report */
|
||||
epicsShareFunc long dbior(
|
||||
const char *pdrvName,int interest_level);
|
||||
/*Hardware Configuration Report*/
|
||||
epicsShareFunc int dbhcr(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* INC_dbTest_H */
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user