Merged changes from 3.15 branch, to revno 12807
This commit is contained in:
@ -4,7 +4,7 @@
|
||||
# Copyright (c) 2002 The Regents of the University of California, as
|
||||
# Operator of Los Alamos National Laboratory.
|
||||
# EPICS BASE is distributed subject to a Software License Agreement found
|
||||
# in the file LICENSE that is included with this distribution.
|
||||
# in the file LICENSE that is included with this distribution.
|
||||
#*************************************************************************
|
||||
#
|
||||
# EPICS Version information
|
||||
@ -34,10 +34,10 @@ BASE_3_16 = YES
|
||||
# EPICS_VERSION must be a number >0 and <256
|
||||
EPICS_VERSION = 3
|
||||
|
||||
# EPICS_REVISION must be a number >=0 and <256
|
||||
# EPICS_REVISION must be a number >=0 and <256
|
||||
EPICS_REVISION = 16
|
||||
|
||||
# EPICS_MODIFICATION must be a number >=0 and <256
|
||||
# EPICS_MODIFICATION must be a number >=0 and <256
|
||||
EPICS_MODIFICATION = 0
|
||||
|
||||
# EPICS_PATCH_LEVEL must be a number (win32 resource file requirement)
|
||||
|
@ -14,7 +14,7 @@ build$(DIVIDER)$(ARCH) build: buildInstall
|
||||
install$(DIVIDER)$(ARCH) install: buildInstall
|
||||
$(ARCH): buildInstall
|
||||
|
||||
ifeq ($(filter $(ARCH),$(BUILD_ARCHS)),$(ARCH))
|
||||
ifeq ($(filter $(ARCH),$(BUILD_ARCHS)),$(strip $(ARCH)))
|
||||
buildInstall$(DIVIDER)$(ARCH) buildInstall: $(TARGETS)
|
||||
|
||||
clean$(DIVIDER)$(ARCH) clean:
|
||||
@ -35,4 +35,3 @@ envPaths: $(wildcard $(TOP)/configure/RELEASE*) \
|
||||
|
||||
realclean:
|
||||
$(RM) cdCommands envPaths dllPath.bat relPaths.sh
|
||||
|
||||
|
@ -105,13 +105,17 @@ endif
|
||||
PRODTARGETS += $(PRODNAME) $(MUNCHNAME) $(CTDT_SRCS) $(CTDT_OBJS) $(NMS)
|
||||
|
||||
#---------------------------------------------------------------
|
||||
# Generate a test specification if any tests are defined.
|
||||
# Test specifications and test result files
|
||||
#
|
||||
ifneq (,$(strip $(TESTS)))
|
||||
TARGETS += testspec
|
||||
endif
|
||||
|
||||
# Enable testing if this host can run tests on the current target
|
||||
ifneq (,$(findstring $(T_A),$(EPICS_HOST_ARCH) $(CROSS_COMPILER_RUNTEST_ARCHS)))
|
||||
RUNTESTS_ENABLED = YES
|
||||
TAPFILES += $(TESTSCRIPTS:.t=.tap)
|
||||
endif
|
||||
|
||||
#---------------------------------------------------------------
|
||||
# Libraries
|
||||
@ -324,7 +328,7 @@ $(MODNAME): %$(MODEXT): %$(EXE)
|
||||
# Automated testing
|
||||
|
||||
runtests: $(TESTSCRIPTS)
|
||||
ifneq (,$(findstring $(T_A),$(EPICS_HOST_ARCH) $(CROSS_COMPILER_RUNTEST_ARCHS)))
|
||||
ifdef RUNTESTS_ENABLED
|
||||
-$(PERL) -MTest::Harness -e 'runtests @ARGV if @ARGV;' $^
|
||||
endif
|
||||
|
||||
@ -340,7 +344,7 @@ tapfiles: $(TESTSCRIPTS) $(TAPFILES)
|
||||
|
||||
# A .tap file is the output from running the associated test script
|
||||
%.tap: %.t
|
||||
ifneq (,$(findstring $(T_A),$(EPICS_HOST_ARCH) $(CROSS_COMPILER_RUNTEST_ARCHS)))
|
||||
ifdef RUNTESTS_ENABLED
|
||||
-$(PERL) $< -tap > $@
|
||||
endif
|
||||
|
||||
|
@ -22,7 +22,3 @@ GNU_LDLIBS_YES =
|
||||
|
||||
# Link with winsock2
|
||||
OP_SYS_LDLIBS = -lws2_32
|
||||
|
||||
# Use static compiler-support libraries
|
||||
OP_SYS_LDFLAGS += -static-libgcc -static-libstdc++
|
||||
# There is no compiler flag for static libwinpthread
|
||||
|
@ -10,6 +10,8 @@ VALID_BUILDS = Host Ioc
|
||||
|
||||
CMPLR_CLASS = msvc
|
||||
|
||||
OPT_WHOLE_PROGRAM = YES
|
||||
|
||||
#-------------------------------------------------------
|
||||
|
||||
WINLINK = link
|
||||
@ -42,11 +44,13 @@ WARN_CFLAGS_NO = -W1
|
||||
# -Ox maximum optimizations
|
||||
# -GL whole program optimization
|
||||
# -Oy- re-enable creation of frame pointers
|
||||
OPT_CFLAGS_YES = -Ox -GL -Oy-
|
||||
OPT_CFLAGS_YES_YES = -Ox -GL -Oy-
|
||||
OPT_CFLAGS_YES_NO = -Ox -Oy-
|
||||
OPT_CFLAGS_YES = $(OPT_CFLAGS_YES_$(OPT_WHOLE_PROGRAM))
|
||||
|
||||
#
|
||||
# -Zi generate program database for debugging information
|
||||
# -RTCsu catch bugs occuring only inoptimized code
|
||||
# -RTCsu enable run-time error checks
|
||||
OPT_CFLAGS_NO = -Zi -RTCsu
|
||||
|
||||
# specify object file name and location
|
||||
@ -96,17 +100,19 @@ CODE_CPPFLAGS += -D_CRT_SECURE_NO_DEPRECATE -D_CRT_NONSTDC_NO_DEPRECATE
|
||||
# -w44355 set "'this' used in the base initializer list" to be level 4
|
||||
# -w44344 "behavior change: use of explicit template arguments results in ..."
|
||||
WARN_CXXFLAGS_YES = -W3 -w44355 -w44344
|
||||
WARN_CXXFLAGS_NO = -W1
|
||||
WARN_CXXFLAGS_NO = -W1
|
||||
|
||||
#
|
||||
# -Ox maximum optimizations
|
||||
# -GL whole program optimization
|
||||
# -Oy- re-enable creation of frame pointers
|
||||
OPT_CXXFLAGS_YES = -Ox -GL -Oy-
|
||||
OPT_CXXFLAGS_YES_YES = -Ox -GL -Oy-
|
||||
OPT_CXXFLAGS_YES_NO = -Ox -Oy-
|
||||
OPT_CXXFLAGS_YES = $(OPT_CXXFLAGS_YES_$(OPT_WHOLE_PROGRAM))
|
||||
|
||||
#
|
||||
# -Zi generate program database for debugging information
|
||||
# -RTCsu catch bugs occurring only in optimized code
|
||||
# -RTCsu enable run-time error checks
|
||||
OPT_CXXFLAGS_NO = -RTCsu -Zi
|
||||
|
||||
# specify object file name and location
|
||||
@ -136,11 +142,14 @@ RANLIB=
|
||||
# -fixed:no generate relocatable code
|
||||
# -version:<major>.<minor> - only 2 components allowed, 0-65535 each
|
||||
# -debug generate debugging info
|
||||
LINK_OPT_FLAGS_YES = -LTCG -incremental:no -opt:ref \
|
||||
-release $(PROD_VERSION:%=-version:%)
|
||||
LINK_OPT_FLAGS_WHOLE_YES = -LTCG
|
||||
LINK_OPT_FLAGS_YES = $(LINK_OPT_FLAGS_WHOLE_$(OPT_WHOLE_PROGRAM))
|
||||
LINK_OPT_FLAGS_YES += -incremental:no -opt:ref
|
||||
LINK_OPT_FLAGS_YES += -release $(PROD_VERSION:%=-version:%)
|
||||
LINK_OPT_FLAGS_NO = -debug -incremental:no -fixed:no
|
||||
OPT_LDFLAGS = $(LINK_OPT_FLAGS_$(HOST_OPT))
|
||||
LIB_OPT_FLAGS_YES = -LTCG
|
||||
|
||||
LIB_OPT_FLAGS_YES = $(LINK_OPT_FLAGS_WHOLE_$(OPT_WHOLE_PROGRAM))
|
||||
LIB_OPT_LDFLAGS = $(LIB_OPT_FLAGS_$(HOST_OPT))
|
||||
|
||||
ARCH_DEP_CFLAGS=
|
||||
@ -156,48 +165,38 @@ EXE=.exe
|
||||
OBJ=.obj
|
||||
RES=.res
|
||||
|
||||
# Problem: MS Visual C++ does not recognize *.cc as C++ source,
|
||||
# so we do C++ compiles using the global flag -TP
|
||||
# MS Visual C++ doesn't recognize *.cc as a C++ source file,
|
||||
# so C++ compiles get the flag -TP
|
||||
COMPILER_CXXFLAGS = -TP
|
||||
|
||||
# Operating system flags
|
||||
# Operating system flags
|
||||
OP_SYS_CFLAGS =
|
||||
OP_SYS_CXXFLAGS = $(COMPILER_CXXFLAGS)
|
||||
|
||||
#
|
||||
# Files and flags needed to link DLLs (used in RULES_BUILD)
|
||||
#
|
||||
WIN32_DLLFLAGS = /subsystem:windows /dll $(OPT_LDFLAGS) \
|
||||
# Files and flags needed to link DLLs (used in RULES_BUILD)
|
||||
WIN32_DLLFLAGS = -subsystem:windows -dll $(OPT_LDFLAGS) \
|
||||
$(USR_LDFLAGS) $(CMD_LDFLAGS) $(TARGET_LDFLAGS) $(LIB_LDFLAGS)
|
||||
|
||||
#
|
||||
# specify dll .def file only if it exists
|
||||
#
|
||||
# Specify dll .def file only if it exists
|
||||
DLL_DEF_FLAG = $(addprefix -def:,$(wildcard ../$(addsuffix .def,$*)))
|
||||
|
||||
#
|
||||
# A WIN32 dll has three parts:
|
||||
# A WIN32 dll has three parts:
|
||||
# x.dll: the real dll (SHRLIBNAME)
|
||||
# x.lib: what you link to progs that use the dll (DLLSTUB_LIBNAME)
|
||||
# x.exp: what you need to build the dll (in no variable)
|
||||
#
|
||||
LINK.shrlib = $(WINLINK) -nologo $(WIN32_DLLFLAGS) -out:$@ \
|
||||
-implib:$(@:%$(SHRLIB_SUFFIX)=%$(LIB_SUFFIX)) \
|
||||
$(DLL_DEF_FLAG) $(LIBRARY_LD_OBJS) $(LIBRARY_LD_RESS) $(SHRLIB_LDLIBS)
|
||||
|
||||
|
||||
# adjust names of libraries to build
|
||||
#
|
||||
# Adjust names of libraries to build
|
||||
SHRLIB_SUFFIX_BASE = .dll
|
||||
SHRLIB_SUFFIX = $(SHRLIB_SUFFIX_BASE)
|
||||
SHRLIBNAME_YES = $(BUILD_LIBRARY:%=%$(SHRLIB_SUFFIX))
|
||||
LOADABLE_SHRLIBNAME = $(LOADABLE_BUILD_LIBRARY:%=%$(SHRLIB_SUFFIX))
|
||||
TESTSHRLIBNAME_YES = $(TESTBUILD_LIBRARY:%=%$(SHRLIB_SUFFIX_BASE))
|
||||
|
||||
#
|
||||
# When SHARED_LIBRARIES is YES we are building a DLL link library
|
||||
# and when SHARED_LIBRARIES is NO we are building an object library
|
||||
#
|
||||
# When SHARED_LIBRARIES is YES we are building a DLL shared library.
|
||||
# When SHARED_LIBRARIES is NO we are building an object library
|
||||
DLLSTUB_SUFFIX = .lib
|
||||
DLLSTUB_LIBNAME_YES = $(BUILD_LIBRARY:%=%.lib)
|
||||
DLLSTUB_LIBNAME = $(DLLSTUB_LIBNAME_$(SHARED_LIBRARIES))
|
||||
@ -211,7 +210,7 @@ LIBNAME = $(LIBNAME_$(SHARED_LIBRARIES))
|
||||
TESTLIBNAME_NO = $(TESTBUILD_LIBRARY:%=%.lib)
|
||||
TESTLIBNAME = $(TESTLIBNAME_$(SHARED_LIBRARIES))
|
||||
|
||||
# dll install location
|
||||
# dll install location
|
||||
INSTALL_SHRLIB = $(INSTALL_BIN)
|
||||
|
||||
|
||||
@ -271,7 +270,7 @@ LINK.cpp = $(WINLINK) -nologo $(STATIC_LDFLAGS) $(LDFLAGS) $(PROD_LDFLAGS) \
|
||||
#--------------------------------------------------
|
||||
# UseManifestTool.pl checks MS Visual c++ compiler version number to
|
||||
# decide whether or not to use the Manifest Tool command to embed the
|
||||
# linker created .manifest file into a library or product target.
|
||||
# linker created .manifest file into a library or product target.
|
||||
# useManifestTool.pl returns 0(don't use) or 1(use).
|
||||
#
|
||||
MT.exe = mt.exe -nologo -manifest $@.manifest
|
||||
@ -281,4 +280,3 @@ MT_EXE_COMMAND_NO = $(MT.exe) "-outputresource:$@;\#1"
|
||||
MT_EXE_COMMAND1 = $(MT_EXE_COMMAND_$(STATIC_BUILD))
|
||||
MT_DLL_COMMAND = $(MT_DLL_COMMAND$(shell $(PERL) $(TOOLS)/useManifestTool.pl))
|
||||
MT_EXE_COMMAND = $(MT_EXE_COMMAND$(shell $(PERL) $(TOOLS)/useManifestTool.pl))
|
||||
|
||||
|
@ -8,8 +8,7 @@
|
||||
include $(CONFIG)/os/CONFIG.win32-x86.win32-x86
|
||||
-include $(CONFIG)/os/CONFIG_SITE.win32-x86.win32-x86
|
||||
|
||||
OPT_LDFLAGS += /MACHINE:X64
|
||||
# /MACHINE:X64
|
||||
# /MACHINE:IA64 (Itanium)
|
||||
# /MACHINE:X86
|
||||
|
||||
OPT_LDFLAGS += -MACHINE:X64
|
||||
# -MACHINE:X64
|
||||
# -MACHINE:IA64 (Itanium)
|
||||
# -MACHINE:X86
|
||||
|
8
configure/os/CONFIG_SITE.Common.win32-x86-static
Normal file
8
configure/os/CONFIG_SITE.Common.win32-x86-static
Normal file
@ -0,0 +1,8 @@
|
||||
# CONFIG_SITE.Common.win32-x86-static
|
||||
#
|
||||
# Site-specific settings for the win32-x86-static target
|
||||
|
||||
# Whole-program optimization doesn't work with Visual Studio 2010 when
|
||||
# building static binaries. Newer versions of Visual Studio than 2010
|
||||
# may work though, comment out or set this to YES to try.
|
||||
OPT_WHOLE_PROGRAM = NO
|
@ -1,9 +1,8 @@
|
||||
# CONFIG_SITE.Common.windows-x64-static
|
||||
#
|
||||
# Site Specific definitions for windows-x64-static target
|
||||
# Only the local epics system manager should modify this file
|
||||
|
||||
# 64-bit Visual Studio 2010 builds fail when built optimized.
|
||||
# If you are using a newer version you can try removing this:
|
||||
HOST_OPT = NO
|
||||
# Site-specific settings for the windows-x64-static target
|
||||
|
||||
# Whole-program optimization doesn't work with Visual Studio 2010 when
|
||||
# building static binaries. Newer versions of Visual Studio than 2010
|
||||
# may work though, comment out or set this to YES to try.
|
||||
OPT_WHOLE_PROGRAM = NO
|
||||
|
@ -4,10 +4,10 @@
|
||||
#-------------------------------------------------------
|
||||
|
||||
# Early versions of the MinGW cross-build tools can only build
|
||||
# static (non-DLL) libraries. For example RHEL's cross-gcc 4.4.6
|
||||
# needs these uncommented, cross-gcc 4.6.3 for Ubuntu does not:
|
||||
SHARED_LIBRARIES = NO
|
||||
STATIC_BUILD = YES
|
||||
# static (non-DLL) libraries. RHEL's cross-build of gcc 4.4.6
|
||||
# needs these uncommented, cross-gcc 4.6.3 from Ubuntu does not:
|
||||
#SHARED_LIBRARIES = NO
|
||||
#STATIC_BUILD = YES
|
||||
|
||||
# The cross-build tools are in $(GNU_DIR)/bin
|
||||
# Default is /usr
|
||||
@ -20,3 +20,6 @@ CMPLR_PREFIX = i686-w64-mingw32-
|
||||
#CMPLR_PREFIX = i686-pc-mingw32-
|
||||
# Debian?
|
||||
#CMPLR_PREFIX = i586-mingw32msvc-
|
||||
|
||||
# Use static compiler-support libraries
|
||||
OP_SYS_LDFLAGS += -static-libgcc -static-libstdc++
|
||||
|
@ -18,3 +18,6 @@
|
||||
#CMPLR_PREFIX = i686-w64-mingw32-
|
||||
# RHEL:
|
||||
CMPLR_PREFIX = x86_64-w64-mingw32-
|
||||
|
||||
# Use static compiler-support libraries
|
||||
OP_SYS_LDFLAGS += -static-libgcc -static-libstdc++
|
||||
|
@ -4,17 +4,17 @@
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
|
||||
<title>Known Problems in R3.16.0.1</title>
|
||||
<title>Known Problems in R3.16.1</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h1 style="text-align: center">EPICS Base R3.16.0.1: Known Problems</h1>
|
||||
<h1 style="text-align: center">EPICS Base R3.16.1: Known Problems</h1>
|
||||
|
||||
<p>Any patch files linked below should be applied at the root of the
|
||||
base-3.16.0.1 tree. Download them, then use the GNU Patch program as
|
||||
base-3.16.1 tree. Download them, then use the GNU Patch program as
|
||||
follows:</p>
|
||||
|
||||
<blockquote><pre>% <b>cd <i>/path/to/</i>base-3.16.0.1</b>
|
||||
<blockquote><pre>% <b>cd <i>/path/to/</i>base-3.16.1</b>
|
||||
% <b>patch -p0 < <i>/path/to/</i>file.patch</b></pre></blockquote>
|
||||
|
||||
<p>The following problems were known by the developers at the time of this
|
||||
|
@ -1,24 +1,24 @@
|
||||
Installation Instructions
|
||||
|
||||
EPICS Base Release 3.16.0
|
||||
EPICS Base Release 3.16.1
|
||||
|
||||
--------------------------------------------------------------------------
|
||||
|
||||
Table of Contents
|
||||
|
||||
*<2A>What is EPICS base?
|
||||
*<2A>What is new in this release?
|
||||
*<2A>Copyright
|
||||
*<2A>Supported platforms
|
||||
*<2A>Supported compilers
|
||||
*<2A>Software requirements
|
||||
*<2A>Host system storage requirements
|
||||
*<2A>Documentation
|
||||
*<2A>Directory Structure
|
||||
*<2A>Build related components
|
||||
*<2A>Building EPICS base (Unix and Win32)
|
||||
*<2A>Example application and extension
|
||||
*<2A>Multiple host platforms
|
||||
*<2A>What is EPICS base?
|
||||
*<2A>What is new in this release?
|
||||
*<2A>Copyright
|
||||
*<2A>Supported platforms
|
||||
*<2A>Supported compilers
|
||||
*<2A>Software requirements
|
||||
*<2A>Host system storage requirements
|
||||
*<2A>Documentation
|
||||
*<2A>Directory Structure
|
||||
*<2A>Build related components
|
||||
*<2A>Building EPICS base (Unix and Win32)
|
||||
*<2A>Example application and extension
|
||||
*<2A>Multiple host platforms
|
||||
|
||||
--------------------------------------------------------------------------
|
||||
|
||||
|
@ -9,7 +9,7 @@
|
||||
<BODY>
|
||||
<CENTER>
|
||||
<H1>Installation Instructions</H1>
|
||||
<H2>EPICS Base Release 3.16.0</H2><BR>
|
||||
<H2>EPICS Base Release 3.16.1</H2><BR>
|
||||
</CENTER>
|
||||
<HR>
|
||||
<H3> Table of Contents</H3>
|
||||
@ -190,12 +190,12 @@
|
||||
CONFIG.CrossCommon Cross build definitions
|
||||
CONFIG.gnuCommon Gnu compiler build definitions for all archs
|
||||
CONFIG_ADDONS Definitions for <osclass> and DEFAULT options
|
||||
CONFIG_APP_INCLUDE
|
||||
CONFIG_APP_INCLUDE
|
||||
CONFIG_BASE EPICS base tool and location definitions
|
||||
CONFIG_BASE_VERSION Definitions for EPICS base version number
|
||||
CONFIG_COMMON Definitions common to all builds
|
||||
CONFIG_ENV Definitions of EPICS environment variables
|
||||
CONFIG_FILE_TYPE
|
||||
CONFIG_FILE_TYPE
|
||||
CONFIG_SITE Site specific make definitions
|
||||
CONFIG_SITE_ENV Site defaults for EPICS environment variables
|
||||
MAKEFILE Installs CONFIG* RULES* creates
|
||||
@ -206,9 +206,9 @@
|
||||
RULES_ARCHS Definitions and rules for building architectures
|
||||
RULES_BUILD Build and install rules and definitions
|
||||
RULES_DIRS Definitions and rules for building subdirectories
|
||||
RULES_EXPAND
|
||||
RULES_FILE_TYPE
|
||||
RULES_TARGET
|
||||
RULES_EXPAND
|
||||
RULES_FILE_TYPE
|
||||
RULES_TARGET
|
||||
RULES_TOP Rules specific to a <top> dir (uninstall and tar)
|
||||
Sample.Makefile Sample makefile with comments
|
||||
</PRE>
|
||||
@ -340,7 +340,7 @@ Files in the base/startup directory have been provided to
|
||||
<H3><A NAME="0_0_13"> Example application and extension</A></H3>
|
||||
<BLOCKQUOTE>A perl tool, makeBaseApp.pl is included in the distribution
|
||||
file. This script will create a sample application that can be built
|
||||
and then executed to try out this release of base.
|
||||
and then executed to try out this release of base.
|
||||
|
||||
<P>
|
||||
Instructions for building and executing the 3.15 example application
|
||||
@ -350,8 +350,8 @@ Files in the base/startup directory have been provided to
|
||||
create and build an example application in a user created <top>
|
||||
directory. It also explains how to run the example application on a
|
||||
vxWorks ioc or as a process on the host system.
|
||||
By running the example application as a host-based IOC, you will be
|
||||
able to quickly implement a complete EPICS system and be able to run channel
|
||||
By running the example application as a host-based IOC, you will be
|
||||
able to quickly implement a complete EPICS system and be able to run channel
|
||||
access clients on the host system.
|
||||
|
||||
<P>
|
||||
|
@ -3,14 +3,15 @@
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="content-type" content="text/html; charset=iso-8859-1">
|
||||
<title>EPICS Base R3.16.0.2 Release Notes</title>
|
||||
<title>EPICS Base R3.16.1 Release Notes</title>
|
||||
</head>
|
||||
|
||||
<body lang="en">
|
||||
<h1 align="center">EPICS Base Release 3.16.0.2</h1>
|
||||
<h1 align="center">EPICS Base Release 3.16.1</h1>
|
||||
|
||||
<p style="color:red">This version of EPICS Base has not been released yet.</p>
|
||||
|
||||
|
||||
<h2 align="center">Changes made on the 3.16 branch since 3.16.0.1</h2>
|
||||
<!-- Insert new items immediately below this template ...
|
||||
|
||||
@ -138,13 +139,117 @@ callback.h header and removed the need for dbScan.c to reach into the internals
|
||||
of its CALLBACK objects.</p>
|
||||
|
||||
|
||||
<h2 align="center">Changes from the 3.15 branch since 3.15.4</h2>
|
||||
<h2 align="center">Changes from the 3.15 branch since 3.15.5</h2>
|
||||
|
||||
<!-- Insert inherited items immediately below here ... -->
|
||||
|
||||
|
||||
<h2 align="center">Changes from the 3.14 branch since 3.15.4</h2>
|
||||
<h2 align="center">Changes from the 3.14 branch since 3.15.5</h2>
|
||||
|
||||
<!-- Insert inherited items immediately below here ... -->
|
||||
|
||||
<h3>Server bind issue on Windows</h3>
|
||||
|
||||
<p>When a National Instruments network variables CA server is already running on
|
||||
a Windows system and an IOC or PCAS server is started, the IOC's attempt to
|
||||
bind a TCP socket to the CA server port number fails, but Windows returns a
|
||||
different error status value than the IOC is expecting in that circumstance
|
||||
(because the National Instruments code requests exclusive use of that port,
|
||||
unlike the EPICS code) so the IOC fails to start properly. The relevent EPICS
|
||||
bind() checks have now been updated so the IOC will request that a dynamic port
|
||||
number be allocated for this TCP socket instead when this happens.</p>
|
||||
|
||||
<h3>Checking Periodic Scan Rates</h3>
|
||||
|
||||
<p>Code has been added to the IOC startup to better protect it against bad
|
||||
periodic scan rates, including against locales where <q><tt>.</tt></q> is not
|
||||
accepted as a decimal separator character. If the scan period in a menuScan
|
||||
choice string cannot be parsed, the associated periodic scan thread will no
|
||||
longer be started by the IOC and a warning message will be displayed at iocInit
|
||||
time. The <tt>scanppl</tt> command will also flag the faulty menuScan value.</p>
|
||||
|
||||
|
||||
<h2 align="center">Changes made between 3.15.4 and 3.15.5</h2>
|
||||
|
||||
<h3>dbStatic Library Speedup and Cleanup</h3>
|
||||
|
||||
<p>Loading of database files has been optimized to avoid overproportionally
|
||||
long loading times for large databases. As a part of this, the alphabetical
|
||||
ordering of records instances (within a record type) has been dropped. In the
|
||||
unexpected case that applications were relying on the alphabetic order, setting
|
||||
<tt>dbRecordsAbcSorted = 1</tt> before loading the databases will retain the
|
||||
old behavior.</p>
|
||||
|
||||
<p>The routine <tt>dbRenameRecord()</tt> has been removed, as it was intended
|
||||
to be used by database configuration tools linked against a host side version
|
||||
of the dbStatic library that is not being built anymore.</p>
|
||||
|
||||
<h3>Launchpad Bug-fixes</h3>
|
||||
|
||||
<p>In addition to the more detailed change descriptions below, the following
|
||||
Launchpad bugs have also been fixed in this release:</p>
|
||||
|
||||
<ul>
|
||||
<li><a href="https://bugs.launchpad.net/epics-base/+bug/1440186">
|
||||
#1440186</a> Crash due to a too small buffer being provided in
|
||||
dbContextReadNotifyCache</li>
|
||||
<li><a href="https://bugs.launchpad.net/epics-base/+bug/1479316">
|
||||
#1479316</a> Some data races found using Helgrind</li>
|
||||
<li><a href="https://bugs.launchpad.net/epics-base/+bug/1495833">
|
||||
#1495833</a> biRecord prompt groups are nonsensical</li>
|
||||
<li><a href="https://bugs.launchpad.net/epics-base/+bug/1606848">
|
||||
#1606848</a> WSAIoctl SIO_GET_INTERFACE_LIST failed in Windows</li>
|
||||
</ul>
|
||||
|
||||
<h3>Whole-Program Optimization for MS Visual Studio Targets</h3>
|
||||
|
||||
<p>When using the Microsoft compilers a new build system variable is provided
|
||||
that controls whether whole program optimization is used or not. For static
|
||||
builds using Visual Studio 2010 this optimization must be disabled. This is
|
||||
controlled in the files configure/os/CONFIG_SITE.Common.windows-x64-static and
|
||||
configure/os/CONFIG_SITE.Common.win32-x86-static by setting the variable
|
||||
<tt>OPT_WHOLE_PROGRAM = NO</tt> to override the default value
|
||||
<tt>YES</tt> that would otherwise be used.</p>
|
||||
|
||||
<p>Note that enabling this optimization slows down the build process. It is not
|
||||
possible to selectively disable this optimization, when building a particular
|
||||
module say; Microsoft's linker will restart itself automatically with the
|
||||
<tt>-LTCG</tt> flag set and display a warning if it is asked to link any object
|
||||
files that were compiled with the <tt>-GL</tt> flag.</p>
|
||||
|
||||
<h3>Add dynamic (variable length) array support to PCAS</h3>
|
||||
|
||||
<p>Dynamic array sizing support was added to the IOC server (RSRV) in the
|
||||
Base-3.14.12 release, but has not until now been supported in the <q>Portable
|
||||
Channel Access Server</q> (PCAS). Channel Access server applications using the
|
||||
PCAS may not need to be modified at all; if they already push monitors with
|
||||
different gdd array lengths, those variable sizes will be forwarded to any CA
|
||||
clients who have requested variable length updates. The example CAS server
|
||||
application has been modified to demonstrate this feature.</p>
|
||||
|
||||
<p>In implementing the above, the gdd method <tt>gdd::put(const gdd *)</tt> now
|
||||
copies the full-sized array from the source gdd if the destination gdd is of
|
||||
type array, has no allocated memory and a boundary size of 0.</p>
|
||||
|
||||
<h3>Additional epicsTime conversion</h3>
|
||||
|
||||
<p>The EPICS timestamp library (epicsTime) inside libCom's OSI layer has
|
||||
been extended by routines that convert from <tt>struct tm</tt> to the EPICS
|
||||
internal <tt>epicsTime</tt> type, assuming UTC - i.e. without going through
|
||||
the timezone mechanism. This solves issues with converting from the structured
|
||||
type to the EPICS timestamp at driver level from multiple threads at a high
|
||||
repetition rate, where the timezone mechanism was blocking on file access.</p>
|
||||
|
||||
<h3>MinGW Cross-builds from Linux</h3>
|
||||
|
||||
<p>The build configuration files that allow cross-building of the 32-bit
|
||||
win32-x86-mingw cross-target have been adjusted to default to building shared
|
||||
libraries (DLLs) as this is now supported by recent MinGW compilers. The 64-bit
|
||||
windows-x64-mingw cross-target was already being built that way by default. The
|
||||
configuration options to tell the minGW cross-compiler to link programs with
|
||||
static versions of the compiler support libraries have now been moved into the
|
||||
CONFIG_SITE.linux-x86.<i>target</i> files.</p>
|
||||
|
||||
<h3>General Time updates</h3>
|
||||
|
||||
<p>The <tt>iocInit</tt> code now performs a sanity check of the current time
|
||||
|
@ -1146,16 +1146,10 @@ the output.</p>
|
||||
<td>Wide mode "name timestamp value stat sevr" (read PVs as
|
||||
DBR_TIME_xxx)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>-n</td>
|
||||
<td>Print DBF_ENUM values as number (default are enum strings)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>-d <type></td>
|
||||
<td>Request specific dbr type; use string (DBR_ prefix may be omitted)
|
||||
|
||||
<p>or number of one of the following types:</p>
|
||||
|
||||
<td>Request specific dbr type; use string (DBR_ prefix may be omitted)<br>
|
||||
or number of one of the following types:<br>
|
||||
<table border="1">
|
||||
<tbody>
|
||||
<tr>
|
||||
@ -1272,6 +1266,14 @@ the output.</p>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td></td>
|
||||
<td><strong>Enum format:</strong></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>-n</td>
|
||||
<td>Print DBF_ENUM value as number (default is enum string)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td></td>
|
||||
<td><strong>Arrays:</strong></td>
|
||||
@ -1303,15 +1305,15 @@ the output.</p>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>-e <nr></td>
|
||||
<td>Use %e format, with <nr> digits after the decimal point</td>
|
||||
<td>Use %e format, with a precision of <nr> digits</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>-f <nr></td>
|
||||
<td>Use %f format, with <nr> digits after the decimal point</td>
|
||||
<td>Use %f format, with a precision of <nr> digits</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>-g <nr></td>
|
||||
<td>Use %g format, with <nr> digits after the decimal point</td>
|
||||
<td>Use %g format, with a precision of <nr> digits</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>-s</td>
|
||||
@ -1349,6 +1351,14 @@ the output.</p>
|
||||
<td>-0b</td>
|
||||
<td>Print as binary number</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td></td>
|
||||
<td><strong>Alternate output field separator:</strong></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>-F <ofs></td>
|
||||
<td>Use <ofs> as an alternate output field separator</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
@ -1381,9 +1391,10 @@ the output.</p>
|
||||
<td>Wait time, specifies longer CA timeout, default is 1.0 second</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>-m <mask></td>
|
||||
<td>Specify CA event mask to use, with <mask> being any combination
|
||||
of 'v' (value), 'a' (alarm), 'l' (log), 'p' (property). Default: va</td>
|
||||
<td>-m <msk></td>
|
||||
<td>Specify CA event mask to use. <msk> is any combination of<br>
|
||||
'v' (value), 'a' (alarm), 'l' (log/archive), 'p' (property).<br>
|
||||
Default event mask is 'va'</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>-p <prio></td>
|
||||
@ -1405,8 +1416,8 @@ the output.</p>
|
||||
'n' = no timestamps<br>
|
||||
'r' = relative timestamps (time elapsed since start of program)<br>
|
||||
'i' = incremental timestamps (time elapsed since last update)<br>
|
||||
'I' = incremental timestamps (time elapsed since last update, by
|
||||
channel)</td>
|
||||
'I' = incremental timestamps (time since last update, by channel)<br>
|
||||
'r', 'i' or 'I' require 's' or 'c' to select the time source</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td></td>
|
||||
@ -1414,7 +1425,7 @@ the output.</p>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>-n</td>
|
||||
<td>Print DBF_ENUM values as number (default are enum strings)</td>
|
||||
<td>Print DBF_ENUM values as number (default is enum string)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td></td>
|
||||
@ -1422,16 +1433,15 @@ the output.</p>
|
||||
</tr>
|
||||
<tr>
|
||||
<td></td>
|
||||
<td>Value format: Print number of requested values, then list of
|
||||
values</td>
|
||||
<td>Array values: Print number of elements, then list of values</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Default:</td>
|
||||
<td>Print all values</td>
|
||||
<td>Default: Request and print all elements (dynamic arrays supported)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>-# <count></td>
|
||||
<td>Print first <count> elements of an array</td>
|
||||
<td>-# <num></td>
|
||||
<td>Request and print up to <num> elements</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>-S</td>
|
||||
@ -1439,23 +1449,23 @@ the output.</p>
|
||||
</tr>
|
||||
<tr>
|
||||
<td></td>
|
||||
<td><strong>Floating point type format:</strong></td>
|
||||
<td><strong>Floating point format:</strong></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Default:</td>
|
||||
<td>Use %g format</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>-e <nr></td>
|
||||
<td>Use %e format, with <nr> digits after the decimal point</td>
|
||||
<td>-e <num></td>
|
||||
<td>Use %e format, with a precision of <num> digits</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>-f <nr></td>
|
||||
<td>Use %f format, with <nr> digits after the decimal point</td>
|
||||
<td>-f <num></td>
|
||||
<td>Use %f format, with a precision of <num> digits</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>-g <nr></td>
|
||||
<td>Use %g format, with <nr> digits after the decimal point</td>
|
||||
<td>-g <num></td>
|
||||
<td>Use %g format, with a precision of <num> digits</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>-s</td>
|
||||
@ -1497,21 +1507,27 @@ the output.</p>
|
||||
</table>
|
||||
|
||||
<h3><a name="caput">caput</a></h3>
|
||||
<pre>caput [options] <PV name> <value>
|
||||
<pre>caput [options] <PV name> <value> ...
|
||||
caput -a [options] <PV name> <no of elements> <value> ...</pre>
|
||||
|
||||
<h4>Description</h4>
|
||||
|
||||
<p>Put value to a PV.</p>
|
||||
|
||||
<p>The specified value is written to the PV (as a string). The PV value is read
|
||||
before and after the write operation and printed as "Old" and "new" values on
|
||||
stdout.</p>
|
||||
<p>The specified value is written to the PV (as a string). The PV's value is
|
||||
read before and after the write operation and printed as "Old" and "New" values
|
||||
on stdout.</p>
|
||||
|
||||
<p>The array variant writes an array to the specified PV. The first numeric
|
||||
argument specifying the number of array elements is kept for compatibility with
|
||||
the array data format of caget - the actual number of values specified on the
|
||||
command line is used.</p>
|
||||
<p>There are two variants to the arguments for this command. For the scalar
|
||||
variant without the <code>-a</code> flag, all the value arguments provided after
|
||||
the PV name are concatenated with a single space character between them, and the
|
||||
resulting string (up to 40 characters long unless the <code>-S</code> flag is
|
||||
given) is written to the specified PV.</p>
|
||||
|
||||
<p>The array variant with the <code>-a</code> flag writes an array of string
|
||||
values to the specified PV. The numeric argument giving the number of array
|
||||
elements is actually ignored, the array length to be written is actually
|
||||
controlled by the number of values provided on the command line.</p>
|
||||
|
||||
<table border="1">
|
||||
<caption></caption>
|
||||
@ -1550,12 +1566,16 @@ command line is used.</p>
|
||||
<td>-t</td>
|
||||
<td>Terse mode - print only successfully written value, without name</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>-l</td>
|
||||
<td>Long mode "name timestamp value stat sevr" (read PVs as DBR_TIME_xxx)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td></td>
|
||||
<td><strong>Enum Format:</strong></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td></td>
|
||||
<td>Default:</td>
|
||||
<td>Auto - try value as ENUM string, then as index number</td>
|
||||
</tr>
|
||||
<tr>
|
||||
@ -1571,17 +1591,24 @@ command line is used.</p>
|
||||
<td><strong>Arrays:</strong></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>-a</td>
|
||||
<td>Put array data</td>
|
||||
<td>Default:</td>
|
||||
<td>Put scalar</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td></td>
|
||||
<td>Value format: Print number of requested values, then list of
|
||||
values</td>
|
||||
<td>Value format: all value arguments concatenated with spaces</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>-S</td>
|
||||
<td>Put string as an array of char (long string)</td>
|
||||
<td>Put string as an array of chars (long string)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>-a</td>
|
||||
<td>Put array</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td></td>
|
||||
<td>Value format: number of values, then list of values</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
@ -149,9 +149,9 @@ cac::cac (
|
||||
iiuExistenceCount ( 0u ),
|
||||
cacShutdownInProgress ( false )
|
||||
{
|
||||
if ( ! osiSockAttach () ) {
|
||||
throwWithLocation ( caErrorCode (ECA_INTERNAL) );
|
||||
}
|
||||
if ( ! osiSockAttach () ) {
|
||||
throwWithLocation ( udpiiu :: noSocket () );
|
||||
}
|
||||
|
||||
try {
|
||||
long status;
|
||||
|
@ -61,11 +61,6 @@ static const double CA_CONN_VERIFY_PERIOD = 30.0; /* (sec) how often to request
|
||||
*/
|
||||
static const unsigned contiguousMsgCountWhichTriggersFlowControl = 10u;
|
||||
|
||||
class caErrorCode {
|
||||
public:
|
||||
caErrorCode ( int ) {};
|
||||
};
|
||||
|
||||
/*
|
||||
* CA internal functions
|
||||
*/
|
||||
|
@ -504,6 +504,7 @@ void ca_repeater ()
|
||||
if ( sockerrno == SOCK_EADDRINUSE ) {
|
||||
osiSockRelease ();
|
||||
debugPrintf ( ( "CA Repeater: exiting because a repeater is already running\n" ) );
|
||||
delete [] pBuf;
|
||||
return;
|
||||
}
|
||||
char sockErrBuf[64];
|
||||
|
@ -44,12 +44,7 @@
|
||||
|
||||
using namespace std;
|
||||
|
||||
#if 0
|
||||
const unsigned mSecPerSec = 1000u;
|
||||
const unsigned uSecPerSec = 1000u * mSecPerSec;
|
||||
#endif
|
||||
|
||||
tcpSendThread::tcpSendThread (
|
||||
tcpSendThread::tcpSendThread (
|
||||
class tcpiiu & iiuIn, const char * pName,
|
||||
unsigned stackSize, unsigned priority ) :
|
||||
thread ( *this, pName, stackSize, priority ), iiu ( iiuIn )
|
||||
@ -807,28 +802,6 @@ tcpiiu::tcpiiu (
|
||||
}
|
||||
}
|
||||
|
||||
# if 0
|
||||
//
|
||||
// windows has a really strange implementation of thess options
|
||||
// and we can avoid the need for this by using pthread_kill on unix
|
||||
//
|
||||
{
|
||||
struct timeval timeout;
|
||||
double pollInterval = connectionTimeout / 8.0;
|
||||
timeout.tv_sec = static_cast < long > ( pollInterval );
|
||||
timeout.tv_usec = static_cast < long >
|
||||
( ( pollInterval - timeout.tv_sec ) * uSecPerSec );
|
||||
// intentionally ignore status as we dont expect that all systems
|
||||
// will accept this request
|
||||
setsockopt ( this->sock, SOL_SOCKET, SO_SNDTIMEO,
|
||||
( char * ) & timeout, sizeof ( timeout ) );
|
||||
// intentionally ignore status as we dont expect that all systems
|
||||
// will accept this request
|
||||
setsockopt ( this->sock, SOL_SOCKET, SO_RCVTIMEO,
|
||||
( char * ) & timeout, sizeof ( timeout ) );
|
||||
}
|
||||
# endif
|
||||
|
||||
if ( isNameService() ) {
|
||||
pSearchDest->setCircuit ( this );
|
||||
}
|
||||
|
@ -56,7 +56,7 @@ static epicsEventId epId;
|
||||
|
||||
void usage (void)
|
||||
{
|
||||
fprintf (stderr, "\nUsage: caput [options] <PV name> <PV value>\n"
|
||||
fprintf (stderr, "\nUsage: caput [options] <PV name> <PV value> ...\n"
|
||||
" caput -a [options] <PV name> <no of values> <PV value> ...\n\n"
|
||||
" -h: Help: Print this message\n"
|
||||
"Channel Access options:\n"
|
||||
@ -71,9 +71,11 @@ void usage (void)
|
||||
" -n: Force interpretation of values as numbers\n"
|
||||
" -s: Force interpretation of values as strings\n"
|
||||
"Arrays:\n"
|
||||
" Default: Put scalar\n"
|
||||
" Value format: all value arguments concatenated with spaces\n"
|
||||
" -S: Put string as an array of chars (long string)\n"
|
||||
" -a: Put array\n"
|
||||
" Value format: number of requested values, then list of values\n"
|
||||
" -S: Put string as an array of char (long string)\n"
|
||||
" Value format: number of values, then list of values\n"
|
||||
"Alternate output field separator:\n"
|
||||
" -F <ofs>: Use <ofs> as an alternate output field separator\n"
|
||||
"\nExample: caput my_channel 1.2\n"
|
||||
|
@ -930,10 +930,12 @@ bool udpiiu::pushDatagramMsg ( epicsGuard < epicsMutex > & guard,
|
||||
|
||||
caHdr * pbufmsg = ( caHdr * ) &this->xmitBuf[this->nBytesInXmitBuf];
|
||||
*pbufmsg = msg;
|
||||
memcpy ( pbufmsg + 1, pExt, extsize );
|
||||
if ( extsize != alignedExtSize ) {
|
||||
char *pDest = (char *) ( pbufmsg + 1 );
|
||||
memset ( pDest + extsize, '\0', alignedExtSize - extsize );
|
||||
if ( extsize ) {
|
||||
memcpy ( pbufmsg + 1, pExt, extsize );
|
||||
if ( extsize != alignedExtSize ) {
|
||||
char *pDest = (char *) ( pbufmsg + 1 );
|
||||
memset ( pDest + extsize, '\0', alignedExtSize - extsize );
|
||||
}
|
||||
}
|
||||
AlignedWireRef < epicsUInt16 > ( pbufmsg->m_postsize ) = alignedExtSize;
|
||||
this->nBytesInXmitBuf += msgsize;
|
||||
|
@ -1355,8 +1355,9 @@ gddStatus gdd::put ( const gdd * dd )
|
||||
}
|
||||
|
||||
aitUint32 srcAvailSize = srcElemCount - unusedSrcBelow;
|
||||
if ( srcAvailSize > this->getBounds()->size() ) {
|
||||
srcCopySize = this->getBounds()->size();
|
||||
aitUint32 destSize = this->getBounds()->size();
|
||||
if ( destSize > 0 && srcAvailSize > destSize ) {
|
||||
srcCopySize = destSize;
|
||||
}
|
||||
else {
|
||||
srcCopySize = srcAvailSize;
|
||||
|
@ -418,8 +418,10 @@ gddStatus gddApplicationTypeTable::freeDD(gdd* dd)
|
||||
}
|
||||
|
||||
// fprintf(stderr,"Adding DD to free_list %d\n",app);
|
||||
attr_table[group][app].sem.lock ();
|
||||
dd->setNext(attr_table[group][app].free_list);
|
||||
attr_table[group][app].free_list=dd;
|
||||
attr_table[group][app].sem.unlock ();
|
||||
}
|
||||
else if (attr_table[group][app].type==gddApplicationTypeNormal)
|
||||
{
|
||||
|
@ -308,7 +308,7 @@ void gdd::test()
|
||||
pdd->convertOffsetsToAddress();
|
||||
pdd->dump();
|
||||
pdd->unreference();
|
||||
delete buf;
|
||||
delete [] buf;
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -510,7 +510,7 @@ void gddContainer::test(void)
|
||||
fprintf(stderr,"=====RE-DUMP OF ORIGINAL CONTAINER:\n");
|
||||
dump();
|
||||
cdd1->unreference();
|
||||
delete buf;
|
||||
delete [] buf;
|
||||
|
||||
// test copy(), Dup(), copyInfo()
|
||||
fprintf(stderr,"=======CREATING TEST CONTAINER FOR *COPY* TEST:\n");
|
||||
|
@ -170,7 +170,7 @@ static int parseDirectoryFP (FILE *pf, const char *pFileName)
|
||||
|
||||
status = aToIPAddr (hostNameStr, 0u, &ipa);
|
||||
if (status) {
|
||||
fprintf (pf, "Unknown host name=\"%s\" (or bad dotted ip addr) in \"%s\" with PV=\"%s\"?\n",
|
||||
fprintf (stderr, "Unknown host name=\"%s\" (or bad dotted ip addr) in \"%s\" with PV=\"%s\"?\n",
|
||||
hostNameStr, pFileName, pvNameStr);
|
||||
return -1;
|
||||
}
|
||||
|
@ -30,7 +30,7 @@
|
||||
# include "shareLib.h"
|
||||
#endif
|
||||
|
||||
static const unsigned char CA_MINOR_PROTOCOL_REVISION = 12;
|
||||
static const unsigned char CA_MINOR_PROTOCOL_REVISION = 13;
|
||||
|
||||
typedef ca_uint32_t caResId;
|
||||
|
||||
|
@ -40,8 +40,13 @@ class casIntfOS;
|
||||
class casMonitor;
|
||||
class casChannelI;
|
||||
|
||||
caStatus convertContainerMemberToAtomic ( class gdd & dd,
|
||||
aitUint32 appType, aitUint32 elemCount );
|
||||
caStatus convertContainerMemberToAtomic (class gdd & dd,
|
||||
aitUint32 appType, aitUint32 requestedCount, aitUint32 nativeCount);
|
||||
|
||||
// Keep the old signature for backward compatibility
|
||||
inline caStatus convertContainerMemberToAtomic (class gdd & dd,
|
||||
aitUint32 appType, aitUint32 elemCount)
|
||||
{ return convertContainerMemberToAtomic(dd, appType, elemCount, elemCount); }
|
||||
|
||||
class caServerI :
|
||||
public caServerIO,
|
||||
|
@ -21,6 +21,7 @@ casChannelI::casChannelI ( casCoreClient & clientIn,
|
||||
casChannel & chanIn, casPVI & pvIn, ca_uint32_t cidIn ) :
|
||||
privateForPV ( clientIn, *this ),
|
||||
pv ( pvIn ),
|
||||
maxElem( pvIn.nativeCount() ),
|
||||
chan ( chanIn ),
|
||||
cid ( cidIn ),
|
||||
serverDeletePending ( false ),
|
||||
@ -29,7 +30,7 @@ casChannelI::casChannelI ( casCoreClient & clientIn,
|
||||
}
|
||||
|
||||
casChannelI::~casChannelI ()
|
||||
{
|
||||
{
|
||||
this->privateForPV.client().removeFromEventQueue (
|
||||
*this, this->accessRightsEvPending );
|
||||
|
||||
@ -48,7 +49,7 @@ void casChannelI::uninstallFromPV ( casEventSys & eventSys )
|
||||
this->privateForPV.removeSelfFromPV ( this->pv, dest );
|
||||
while ( casMonitor * pMon = dest.get () ) {
|
||||
eventSys.prepareMonitorForDestroy ( *pMon );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void casChannelI::show ( unsigned level ) const
|
||||
@ -68,13 +69,13 @@ caStatus casChannelI::cbFunc (
|
||||
{
|
||||
caStatus stat = S_cas_success;
|
||||
{
|
||||
stat = this->privateForPV.client().accessRightsResponse (
|
||||
stat = this->privateForPV.client().accessRightsResponse (
|
||||
clientGuard, this );
|
||||
}
|
||||
if ( stat == S_cas_success ) {
|
||||
this->accessRightsEvPending = false;
|
||||
}
|
||||
return stat;
|
||||
if ( stat == S_cas_success ) {
|
||||
this->accessRightsEvPending = false;
|
||||
}
|
||||
return stat;
|
||||
}
|
||||
|
||||
caStatus casChannelI::read ( const casCtx & ctx, gdd & prototype )
|
||||
|
@ -1,4 +1,3 @@
|
||||
|
||||
/*************************************************************************\
|
||||
* Copyright (c) 2002 The University of Chicago, as Operator of Argonne
|
||||
* National Laboratory.
|
||||
@ -31,55 +30,62 @@ class casChannelI : public tsDLNode < casChannelI >,
|
||||
public:
|
||||
casChannelI ( casCoreClient & clientIn, casChannel & chanIn,
|
||||
casPVI & pvIn, ca_uint32_t cidIn );
|
||||
~casChannelI ();
|
||||
~casChannelI ();
|
||||
void casChannelDestroyFromInterfaceNotify ();
|
||||
const caResId getCID ();
|
||||
const caResId getSID ();
|
||||
const caResId getCID ();
|
||||
const caResId getSID ();
|
||||
void uninstallFromPV ( casEventSys & eventSys );
|
||||
void installIntoPV ();
|
||||
void installIO ( casAsyncIOI & );
|
||||
void uninstallIO ( casAsyncIOI & );
|
||||
void installMonitor ( casMonitor & mon );
|
||||
casMonitor * removeMonitor ( ca_uint32_t clientIdIn );
|
||||
casPVI & getPVI () const;
|
||||
void clearOutstandingReads ();
|
||||
void postAccessRightsEvent ();
|
||||
casPVI & getPVI () const;
|
||||
void clearOutstandingReads ();
|
||||
void postAccessRightsEvent ();
|
||||
const gddEnumStringTable & enumStringTable () const;
|
||||
void setOwner ( const char * const pUserName,
|
||||
const char * const pHostName );
|
||||
bool readAccess () const;
|
||||
ca_uint32_t getMaxElem () const;
|
||||
void setOwner ( const char * const pUserName,
|
||||
const char * const pHostName );
|
||||
bool readAccess () const;
|
||||
bool writeAccess () const;
|
||||
bool confirmationRequested () const;
|
||||
bool confirmationRequested () const;
|
||||
caStatus read ( const casCtx & ctx, gdd & prototype );
|
||||
caStatus write ( const casCtx & ctx, const gdd & value );
|
||||
caStatus writeNotify ( const casCtx & ctx, const gdd & value );
|
||||
void show ( unsigned level ) const;
|
||||
void show ( unsigned level ) const;
|
||||
private:
|
||||
chanIntfForPV privateForPV;
|
||||
tsDLList < casAsyncIOI > ioList;
|
||||
casPVI & pv;
|
||||
tsDLList < casAsyncIOI > ioList;
|
||||
casPVI & pv;
|
||||
ca_uint32_t maxElem;
|
||||
casChannel & chan;
|
||||
caResId cid; // client id
|
||||
caResId cid; // client id
|
||||
bool serverDeletePending;
|
||||
bool accessRightsEvPending;
|
||||
//epicsShareFunc virtual void destroy ();
|
||||
caStatus cbFunc (
|
||||
bool accessRightsEvPending;
|
||||
//epicsShareFunc virtual void destroy ();
|
||||
caStatus cbFunc (
|
||||
casCoreClient &,
|
||||
epicsGuard < casClientMutex > &,
|
||||
epicsGuard < evSysMutex > & );
|
||||
void postDestroyEvent ();
|
||||
casChannelI ( const casChannelI & );
|
||||
casChannelI & operator = ( const casChannelI & );
|
||||
casChannelI ( const casChannelI & );
|
||||
casChannelI & operator = ( const casChannelI & );
|
||||
};
|
||||
|
||||
inline casPVI & casChannelI::getPVI () const
|
||||
{
|
||||
return this->pv;
|
||||
return this->pv;
|
||||
}
|
||||
|
||||
inline ca_uint32_t casChannelI::getMaxElem () const
|
||||
{
|
||||
return this->maxElem;
|
||||
}
|
||||
|
||||
inline const caResId casChannelI::getCID ()
|
||||
{
|
||||
return this->cid;
|
||||
return this->cid;
|
||||
}
|
||||
|
||||
inline const caResId casChannelI::getSID ()
|
||||
@ -89,7 +95,7 @@ inline const caResId casChannelI::getSID ()
|
||||
|
||||
inline void casChannelI::postAccessRightsEvent ()
|
||||
{
|
||||
this->privateForPV.client().addToEventQueue ( *this, this->accessRightsEvPending );
|
||||
this->privateForPV.client().addToEventQueue ( *this, this->accessRightsEvPending );
|
||||
}
|
||||
|
||||
inline const gddEnumStringTable & casChannelI::enumStringTable () const
|
||||
@ -108,7 +114,7 @@ inline void casChannelI::clearOutstandingReads ()
|
||||
}
|
||||
|
||||
inline void casChannelI::setOwner ( const char * const pUserName,
|
||||
const char * const pHostName )
|
||||
const char * const pHostName )
|
||||
{
|
||||
this->chan.setOwner ( pUserName, pHostName );
|
||||
}
|
||||
|
@ -18,6 +18,8 @@
|
||||
|
||||
#include "caHdrLargeArray.h"
|
||||
|
||||
class casStrmClient;
|
||||
|
||||
class casCtx {
|
||||
public:
|
||||
casCtx();
|
||||
@ -41,6 +43,7 @@ private:
|
||||
casChannelI * pChannel;
|
||||
casPVI * pPV;
|
||||
unsigned nAsyncIO; // checks for improper use of async io
|
||||
friend class casStrmClient;
|
||||
};
|
||||
|
||||
inline const caHdrLargeArray * casCtx::getMsg() const
|
||||
|
@ -5,7 +5,7 @@
|
||||
* Operator of Los Alamos National Laboratory.
|
||||
* EPICS BASE Versions 3.13.7
|
||||
* and higher are distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
* in file LICENSE that is included with this distribution.
|
||||
\*************************************************************************/
|
||||
/*
|
||||
* Author Jeffrey O. Hill
|
||||
@ -30,9 +30,9 @@ casDGClient::pCASMsgHandler const casDGClient::msgHandlers[] =
|
||||
& casDGClient::uknownMessageAction,
|
||||
& casDGClient::uknownMessageAction,
|
||||
|
||||
& casDGClient::uknownMessageAction,
|
||||
& casDGClient::searchAction,
|
||||
& casDGClient::uknownMessageAction,
|
||||
& casDGClient::uknownMessageAction,
|
||||
& casDGClient::searchAction,
|
||||
& casDGClient::uknownMessageAction,
|
||||
& casDGClient::uknownMessageAction,
|
||||
& casDGClient::uknownMessageAction,
|
||||
|
||||
@ -64,9 +64,9 @@ casDGClient::pCASMsgHandler const casDGClient::msgHandlers[] =
|
||||
//
|
||||
casDGClient::casDGClient ( caServerI & serverIn, clientBufMemoryManager & mgrIn ) :
|
||||
casCoreClient ( serverIn ),
|
||||
in ( *this, mgrIn, MAX_UDP_RECV + sizeof ( cadg ) ),
|
||||
in ( *this, mgrIn, MAX_UDP_RECV + sizeof ( cadg ) ),
|
||||
out ( *this, mgrIn ),
|
||||
seqNoOfReq ( 0 ),
|
||||
seqNoOfReq ( 0 ),
|
||||
minor_version_number ( 0 )
|
||||
{
|
||||
}
|
||||
@ -92,15 +92,15 @@ void casDGClient::destroy()
|
||||
//
|
||||
void casDGClient::show (unsigned level) const
|
||||
{
|
||||
printf ( "casDGClient at %p\n",
|
||||
printf ( "casDGClient at %p\n",
|
||||
static_cast <const void *> ( this ) );
|
||||
if (level>=1u) {
|
||||
char buf[64];
|
||||
this->hostName (buf, sizeof(buf));
|
||||
printf ("Client Host=%s\n", buf);
|
||||
this->casCoreClient::show ( level - 1u );
|
||||
this->in.show ( level - 1u );
|
||||
this->out.show ( level - 1u );
|
||||
this->casCoreClient::show ( level - 1u );
|
||||
this->in.show ( level - 1u );
|
||||
this->out.show ( level - 1u );
|
||||
}
|
||||
}
|
||||
|
||||
@ -114,7 +114,7 @@ caStatus casDGClient::uknownMessageAction ()
|
||||
char pHostName[64u];
|
||||
this->lastRecvAddr.stringConvert ( pHostName, sizeof ( pHostName ) );
|
||||
|
||||
caServerI::dumpMsg ( pHostName, "?", mp, this->ctx.getData(),
|
||||
caServerI::dumpMsg ( pHostName, "?", mp, this->ctx.getData(),
|
||||
"bad request code=%u in DG\n", mp->m_cmmd );
|
||||
|
||||
return S_cas_badProtocol;
|
||||
@ -135,7 +135,7 @@ caStatus casDGClient::searchAction()
|
||||
if ( mp->m_postsize <= 1 ) {
|
||||
char pHostName[64u];
|
||||
this->lastRecvAddr.stringConvert ( pHostName, sizeof ( pHostName ) );
|
||||
caServerI::dumpMsg ( pHostName, "?", mp, this->ctx.getData(),
|
||||
caServerI::dumpMsg ( pHostName, "?", mp, this->ctx.getData(),
|
||||
"empty PV name extension in UDP search request?\n" );
|
||||
return S_cas_success;
|
||||
}
|
||||
@ -143,19 +143,19 @@ caStatus casDGClient::searchAction()
|
||||
if ( pChanName[0] == '\0' ) {
|
||||
char pHostName[64u];
|
||||
this->lastRecvAddr.stringConvert ( pHostName, sizeof ( pHostName ) );
|
||||
caServerI::dumpMsg ( pHostName, "?", mp, this->ctx.getData(),
|
||||
caServerI::dumpMsg ( pHostName, "?", mp, this->ctx.getData(),
|
||||
"zero length PV name in UDP search request?\n" );
|
||||
return S_cas_success;
|
||||
}
|
||||
|
||||
// check for an unterminated string before calling server tool
|
||||
// by searching backwards through the string (some early versions
|
||||
// by searching backwards through the string (some early versions
|
||||
// of the client library might not be setting the pad bytes to nill)
|
||||
for ( unsigned i = mp->m_postsize-1; pChanName[i] != '\0'; i-- ) {
|
||||
if ( i <= 1 ) {
|
||||
char pHostName[64u];
|
||||
this->lastRecvAddr.stringConvert ( pHostName, sizeof ( pHostName ) );
|
||||
caServerI::dumpMsg ( pHostName, "?", mp, this->ctx.getData(),
|
||||
caServerI::dumpMsg ( pHostName, "?", mp, this->ctx.getData(),
|
||||
"unterminated PV name in UDP search request?\n" );
|
||||
return S_cas_success;
|
||||
}
|
||||
@ -183,7 +183,7 @@ caStatus casDGClient::searchAction()
|
||||
// ask the server tool if this PV exists
|
||||
//
|
||||
this->userStartedAsyncIO = false;
|
||||
pvExistReturn pver =
|
||||
pvExistReturn pver =
|
||||
this->getCAS()->pvExistTest ( this->ctx, this->lastRecvAddr, pChanName );
|
||||
|
||||
//
|
||||
@ -193,7 +193,7 @@ caStatus casDGClient::searchAction()
|
||||
//
|
||||
if ( this->userStartedAsyncIO ) {
|
||||
if ( pver.getStatus() != pverAsyncCompletion ) {
|
||||
errMessage (S_cas_badParameter,
|
||||
errMessage (S_cas_badParameter,
|
||||
"- assuming asynch IO status from caServer::pvExistTest()");
|
||||
}
|
||||
status = S_cas_success;
|
||||
@ -212,13 +212,13 @@ caStatus casDGClient::searchAction()
|
||||
break;
|
||||
|
||||
case pverAsyncCompletion:
|
||||
errMessage (S_cas_badParameter,
|
||||
errMessage (S_cas_badParameter,
|
||||
"- unexpected asynch IO status from caServer::pvExistTest() ignored");
|
||||
status = S_cas_success;
|
||||
break;
|
||||
|
||||
default:
|
||||
errMessage (S_cas_badParameter,
|
||||
errMessage (S_cas_badParameter,
|
||||
"- invalid return from caServer::pvExistTest() ignored");
|
||||
status = S_cas_success;
|
||||
break;
|
||||
@ -234,14 +234,14 @@ caStatus casDGClient::searchResponse ( const caHdrLargeArray & msg,
|
||||
const pvExistReturn & retVal )
|
||||
{
|
||||
caStatus status;
|
||||
|
||||
|
||||
if ( retVal.getStatus() != pverExistsHere ) {
|
||||
return S_cas_success;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// starting with V4.1 the count field is used (abused)
|
||||
// by the client to store the minor version number of
|
||||
// by the client to store the minor version number of
|
||||
// the client.
|
||||
//
|
||||
// Old versions expect alloc of channel in response
|
||||
@ -250,8 +250,8 @@ caStatus casDGClient::searchResponse ( const caHdrLargeArray & msg,
|
||||
if ( !CA_V44(msg.m_count) ) {
|
||||
char pName[64u];
|
||||
this->hostName (pName, sizeof (pName));
|
||||
errlogPrintf (
|
||||
"client \"%s\" using EPICS R3.11 CA connect protocol was ignored\n",
|
||||
errlogPrintf (
|
||||
"client \"%s\" using EPICS R3.11 CA connect protocol was ignored\n",
|
||||
pName);
|
||||
//
|
||||
// old connect protocol was dropped when the
|
||||
@ -264,19 +264,19 @@ caStatus casDGClient::searchResponse ( const caHdrLargeArray & msg,
|
||||
}
|
||||
|
||||
//
|
||||
// cid field is abused to carry the IP
|
||||
// cid field is abused to carry the IP
|
||||
// address in CA_V48 or higher
|
||||
// (this allows a CA servers to serve
|
||||
// as a directory service)
|
||||
//
|
||||
// data type field is abused to carry the IP
|
||||
// data type field is abused to carry the IP
|
||||
// port number here CA_V44 or higher
|
||||
// (this allows multiple CA servers on one
|
||||
// host)
|
||||
//
|
||||
ca_uint32_t serverAddr;
|
||||
ca_uint16_t serverPort;
|
||||
if ( CA_V48( msg.m_count ) ) {
|
||||
if ( CA_V48( msg.m_count ) ) {
|
||||
struct sockaddr_in ina;
|
||||
if ( retVal.addrIsValid() ) {
|
||||
caNetAddr addr = retVal.getAddr();
|
||||
@ -296,7 +296,7 @@ caStatus casDGClient::searchResponse ( const caHdrLargeArray & msg,
|
||||
ina = addr.getSockIP();
|
||||
//
|
||||
// We dont fill in the servers address here
|
||||
// because the server was not bound to a particular
|
||||
// because the server was not bound to a particular
|
||||
// interface, and we would need to waste CPU performing
|
||||
// the following steps to determine the interface that
|
||||
// will be used:
|
||||
@ -318,24 +318,24 @@ caStatus casDGClient::searchResponse ( const caHdrLargeArray & msg,
|
||||
serverAddr = ~0U;
|
||||
serverPort = ntohs ( inetAddr.sin_port );
|
||||
}
|
||||
|
||||
|
||||
ca_uint16_t * pMinorVersion;
|
||||
epicsGuard < epicsMutex > guard ( this->mutex );
|
||||
status = this->out.copyInHeader ( CA_PROTO_SEARCH,
|
||||
sizeof ( *pMinorVersion ), serverPort, 0,
|
||||
serverAddr, msg.m_available,
|
||||
status = this->out.copyInHeader ( CA_PROTO_SEARCH,
|
||||
sizeof ( *pMinorVersion ), serverPort, 0,
|
||||
serverAddr, msg.m_available,
|
||||
reinterpret_cast <void **> ( &pMinorVersion ) );
|
||||
//
|
||||
// Starting with CA V4.1 the minor version number
|
||||
// is appended to the end of each search reply.
|
||||
// This value is ignored by earlier clients.
|
||||
// This value is ignored by earlier clients.
|
||||
//
|
||||
if ( status == S_cas_success ) {
|
||||
AlignedWireRef < epicsUInt16 > tmp ( *pMinorVersion );
|
||||
tmp = CA_MINOR_PROTOCOL_REVISION;
|
||||
this->out.commitMsg ();
|
||||
}
|
||||
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
@ -346,15 +346,11 @@ caStatus casDGClient::searchResponse ( const caHdrLargeArray & msg,
|
||||
//
|
||||
caStatus casDGClient::searchFailResponse ( const caHdrLargeArray * mp )
|
||||
{
|
||||
caStatus status;
|
||||
|
||||
epicsGuard < epicsMutex > guard ( this->mutex );
|
||||
status = this->out.copyInHeader ( CA_PROTO_NOT_FOUND, 0,
|
||||
this->out.copyInHeader ( CA_PROTO_NOT_FOUND, 0,
|
||||
mp->m_dataType, mp->m_count, mp->m_cid, mp->m_available, 0 );
|
||||
|
||||
if ( status == S_cas_success ) {
|
||||
this->out.commitMsg ();
|
||||
}
|
||||
this->out.commitMsg ();
|
||||
|
||||
return S_cas_success;
|
||||
}
|
||||
@ -408,8 +404,8 @@ void casDGClient::sendBeacon ( ca_uint32_t beaconNumber )
|
||||
//
|
||||
// casDGClient::xSend()
|
||||
//
|
||||
outBufClient::flushCondition casDGClient::xSend ( char *pBufIn,
|
||||
bufSizeT nBytesToSend, bufSizeT & nBytesSent )
|
||||
outBufClient::flushCondition casDGClient::xSend ( char *pBufIn,
|
||||
bufSizeT nBytesToSend, bufSizeT & nBytesSent )
|
||||
{
|
||||
bufSizeT totalBytes = 0;
|
||||
while ( totalBytes < nBytesToSend ) {
|
||||
@ -422,7 +418,7 @@ outBufClient::flushCondition casDGClient::xSend ( char *pBufIn,
|
||||
unsigned sizeDG = pHdr->cadg_nBytes - sizeof ( *pHdr );
|
||||
|
||||
if ( pHdr->cadg_addr.isValid() ) {
|
||||
outBufClient::flushCondition stat =
|
||||
outBufClient::flushCondition stat =
|
||||
this->osdSend ( pDG, sizeDG, pHdr->cadg_addr );
|
||||
if ( stat != outBufClient::flushProgress ) {
|
||||
break;
|
||||
@ -459,7 +455,7 @@ inBufClient::fillCondition casDGClient::xRecv (char *pBufIn, bufSizeT nBytesToRe
|
||||
|
||||
while (pAfter-pCurBuf >= static_cast<int>(MAX_UDP_RECV+sizeof(cadg))) {
|
||||
pHdr = reinterpret_cast < cadg * > ( pCurBuf );
|
||||
stat = this->osdRecv ( reinterpret_cast < char * > ( pHdr + 1 ),
|
||||
stat = this->osdRecv ( reinterpret_cast < char * > ( pHdr + 1 ),
|
||||
MAX_UDP_RECV, parm, nDGBytesRecv, pHdr->cadg_addr);
|
||||
if (stat==casFillProgress) {
|
||||
pHdr->cadg_nBytes = nDGBytesRecv + sizeof(*pHdr);
|
||||
@ -491,7 +487,7 @@ inBufClient::fillCondition casDGClient::xRecv (char *pBufIn, bufSizeT nBytesToRe
|
||||
// this results in many small UDP frames which unfortunately
|
||||
// isnt particularly efficient
|
||||
//
|
||||
caStatus casDGClient::asyncSearchResponse (
|
||||
caStatus casDGClient::asyncSearchResponse (
|
||||
epicsGuard < casClientMutex > &, const caNetAddr & outAddr,
|
||||
const caHdrLargeArray & msg, const pvExistReturn & retVal,
|
||||
ca_uint16_t protocolRevision, ca_uint32_t sequenceNumber )
|
||||
@ -501,7 +497,7 @@ caStatus casDGClient::asyncSearchResponse (
|
||||
}
|
||||
|
||||
void * pRaw;
|
||||
const outBufCtx outctx = this->out.pushCtx
|
||||
const outBufCtx outctx = this->out.pushCtx
|
||||
( sizeof(cadg), MAX_UDP_SEND, pRaw );
|
||||
if ( outctx.pushResult() != outBufCtx::pushCtxSuccess ) {
|
||||
return S_cas_sendBlocked;
|
||||
@ -565,9 +561,9 @@ caStatus casDGClient::processDG ()
|
||||
|
||||
// insert version header at the start of the reply message
|
||||
this->sendVersion ();
|
||||
|
||||
|
||||
cadg * pRespHdr = static_cast < cadg * > ( pRaw );
|
||||
|
||||
|
||||
//
|
||||
// select the next DG in the input stream and start processing it
|
||||
//
|
||||
@ -597,7 +593,7 @@ caStatus casDGClient::processDG ()
|
||||
// a) it used all of the incoming DG or
|
||||
// b) it used all of the outgoing DG
|
||||
//
|
||||
// In either case commit the DG to the protocol stream and
|
||||
// In either case commit the DG to the protocol stream and
|
||||
// release the send lock
|
||||
//
|
||||
// if there are not additional messages passed the version header
|
||||
@ -688,7 +684,7 @@ void casDGClient::hostName ( char *pBufIn, unsigned bufSizeIn ) const
|
||||
void casDGClient::sendVersion ()
|
||||
{
|
||||
epicsGuard < epicsMutex > guard ( this->mutex );
|
||||
caStatus status = this->out.copyInHeader ( CA_PROTO_VERSION, 0,
|
||||
caStatus status = this->out.copyInHeader ( CA_PROTO_VERSION, 0,
|
||||
0, CA_MINOR_PROTOCOL_REVISION, 0, 0, 0 );
|
||||
if ( ! status ) {
|
||||
this->out.commitMsg ();
|
||||
@ -783,8 +779,8 @@ caStatus casDGClient::processMsg ()
|
||||
msgTmp.m_available = AlignedWireRef < epicsUInt32 > ( smallHdr.m_available );
|
||||
|
||||
if ( payloadSize & 0x7 ) {
|
||||
status = this->sendErr (
|
||||
& msgTmp, invalidResID, ECA_INTERNAL,
|
||||
status = this->sendErr (
|
||||
& msgTmp, invalidResID, ECA_INTERNAL,
|
||||
"CAS: Datagram request wasn't 8 byte aligned" );
|
||||
this->in.removeMsg ( bytesLeft );
|
||||
break;
|
||||
@ -793,7 +789,7 @@ caStatus casDGClient::processMsg ()
|
||||
msgSize = hdrSize + payloadSize;
|
||||
if ( bytesLeft < msgSize ) {
|
||||
if ( msgSize > this->in.bufferSize() ) {
|
||||
status = this->sendErr ( & msgTmp, invalidResID, ECA_TOLARGE,
|
||||
status = this->sendErr ( & msgTmp, invalidResID, ECA_TOLARGE,
|
||||
"client's request didnt fit within the CA server's message buffer" );
|
||||
this->in.removeMsg ( bytesLeft );
|
||||
}
|
||||
@ -805,7 +801,7 @@ caStatus casDGClient::processMsg ()
|
||||
if ( this->getCAS().getDebugLevel() > 5u ) {
|
||||
char pHostName[64u];
|
||||
this->lastRecvAddr.stringConvert ( pHostName, sizeof ( pHostName ) );
|
||||
caServerI::dumpMsg ( pHostName, "?",
|
||||
caServerI::dumpMsg ( pHostName, "?",
|
||||
& msgTmp, rawMP + hdrSize, 0 );
|
||||
}
|
||||
|
||||
@ -813,7 +809,7 @@ caStatus casDGClient::processMsg ()
|
||||
|
||||
//
|
||||
// Reset the context to the default
|
||||
// (guarantees that previous message does not get mixed
|
||||
// (guarantees that previous message does not get mixed
|
||||
// up with the current message)
|
||||
//
|
||||
this->ctx.setChannel ( NULL );
|
||||
@ -840,16 +836,16 @@ caStatus casDGClient::processMsg ()
|
||||
}
|
||||
catch ( std::exception & except ) {
|
||||
this->in.removeMsg ( this->in.bytesPresent() );
|
||||
status = this->sendErr (
|
||||
this->ctx.getMsg(), invalidResID, ECA_INTERNAL,
|
||||
this->sendErr (
|
||||
this->ctx.getMsg(), invalidResID, ECA_INTERNAL,
|
||||
"C++ exception \"%s\" in CA circuit server",
|
||||
except.what () );
|
||||
status = S_cas_internal;
|
||||
}
|
||||
catch (...) {
|
||||
this->in.removeMsg ( this->in.bytesPresent() );
|
||||
status = this->sendErr (
|
||||
this->ctx.getMsg(), invalidResID, ECA_INTERNAL,
|
||||
this->sendErr (
|
||||
this->ctx.getMsg(), invalidResID, ECA_INTERNAL,
|
||||
"unexpected C++ exception in CA datagram server" );
|
||||
status = S_cas_internal;
|
||||
}
|
||||
@ -860,37 +856,38 @@ caStatus casDGClient::processMsg ()
|
||||
//
|
||||
// casDGClient::sendErr()
|
||||
//
|
||||
caStatus casDGClient::sendErr ( const caHdrLargeArray *curp,
|
||||
caStatus casDGClient::sendErr ( const caHdrLargeArray *curp,
|
||||
ca_uint32_t cid, const int reportedStatus, const char *pformat, ... )
|
||||
{
|
||||
unsigned stringSize;
|
||||
char msgBuf[1024]; /* allocate plenty of space for the message string */
|
||||
if ( pformat ) {
|
||||
va_list args;
|
||||
va_start ( args, pformat );
|
||||
int status = vsprintf ( msgBuf, pformat, args );
|
||||
if ( status < 0 ) {
|
||||
errPrintf (S_cas_internal, __FILE__, __LINE__,
|
||||
"bad sendErr(%s)", pformat);
|
||||
stringSize = 0u;
|
||||
}
|
||||
else {
|
||||
stringSize = 1u + (unsigned) status;
|
||||
}
|
||||
}
|
||||
else {
|
||||
unsigned stringSize;
|
||||
char msgBuf[1024]; /* allocate plenty of space for the message string */
|
||||
if ( pformat ) {
|
||||
va_list args;
|
||||
va_start ( args, pformat );
|
||||
int status = vsprintf ( msgBuf, pformat, args );
|
||||
if ( status < 0 ) {
|
||||
errPrintf (S_cas_internal, __FILE__, __LINE__,
|
||||
"bad sendErr(%s)", pformat);
|
||||
stringSize = 0u;
|
||||
}
|
||||
else {
|
||||
stringSize = 1u + (unsigned) status;
|
||||
}
|
||||
va_end ( args );
|
||||
}
|
||||
else {
|
||||
stringSize = 0u;
|
||||
}
|
||||
|
||||
unsigned hdrSize = sizeof ( caHdr );
|
||||
if ( ( curp->m_postsize >= 0xffff || curp->m_count >= 0xffff ) &&
|
||||
if ( ( curp->m_postsize >= 0xffff || curp->m_count >= 0xffff ) &&
|
||||
CA_V49( this->minor_version_number ) ) {
|
||||
hdrSize += 2 * sizeof ( ca_uint32_t );
|
||||
}
|
||||
|
||||
caHdr * pReqOut;
|
||||
epicsGuard < epicsMutex > guard ( this->mutex );
|
||||
caStatus status = this->out.copyInHeader ( CA_PROTO_ERROR,
|
||||
caStatus status = this->out.copyInHeader ( CA_PROTO_ERROR,
|
||||
hdrSize + stringSize, 0, 0, cid, reportedStatus,
|
||||
reinterpret_cast <void **> ( & pReqOut ) );
|
||||
if ( ! status ) {
|
||||
@ -900,7 +897,7 @@ caStatus casDGClient::sendErr ( const caHdrLargeArray *curp,
|
||||
* copy back the request protocol
|
||||
* (in network byte order)
|
||||
*/
|
||||
if ( ( curp->m_postsize >= 0xffff || curp->m_count >= 0xffff ) &&
|
||||
if ( ( curp->m_postsize >= 0xffff || curp->m_count >= 0xffff ) &&
|
||||
CA_V49( this->minor_version_number ) ) {
|
||||
ca_uint32_t *pLW = ( ca_uint32_t * ) ( pReqOut + 1 );
|
||||
pReqOut->m_cmmd = htons ( curp->m_cmmd );
|
||||
@ -945,7 +942,7 @@ caStatus casDGClient::echoAction ()
|
||||
void * pPayloadOut;
|
||||
|
||||
epicsGuard < epicsMutex > guard ( this->mutex );
|
||||
caStatus status = this->out.copyInHeader ( mp->m_cmmd, mp->m_postsize,
|
||||
caStatus status = this->out.copyInHeader ( mp->m_cmmd, mp->m_postsize,
|
||||
mp->m_dataType, mp->m_count, mp->m_cid, mp->m_available,
|
||||
& pPayloadOut );
|
||||
if ( ! status ) {
|
||||
|
@ -388,14 +388,12 @@ caStatus casStrmClient::echoAction ( epicsGuard < casClientMutex > & )
|
||||
//
|
||||
// casStrmClient::verifyRequest()
|
||||
//
|
||||
caStatus casStrmClient::verifyRequest ( casChannelI * & pChan )
|
||||
caStatus casStrmClient::verifyRequest (casChannelI * & pChan , bool allowdyn)
|
||||
{
|
||||
const caHdrLargeArray * mp = this->ctx.getMsg();
|
||||
|
||||
//
|
||||
// channel exists for this resource id ?
|
||||
//
|
||||
chronIntId tmpId ( mp->m_cid );
|
||||
chronIntId tmpId ( ctx.msg.m_cid );
|
||||
pChan = this->chanTable.lookup ( tmpId );
|
||||
if ( ! pChan ) {
|
||||
return ECA_BADCHID;
|
||||
@ -404,14 +402,15 @@ caStatus casStrmClient::verifyRequest ( casChannelI * & pChan )
|
||||
//
|
||||
// data type out of range ?
|
||||
//
|
||||
if ( mp->m_dataType > ((unsigned)LAST_BUFFER_TYPE) ) {
|
||||
if ( ctx.msg.m_dataType > ((unsigned)LAST_BUFFER_TYPE) ) {
|
||||
return ECA_BADTYPE;
|
||||
}
|
||||
|
||||
//
|
||||
// element count out of range ?
|
||||
//
|
||||
if ( mp->m_count > pChan->getPVI().nativeCount() || mp->m_count == 0u ) {
|
||||
if ( ctx.msg.m_count > pChan->getMaxElem() ||
|
||||
( !allowdyn && ctx.msg.m_count == 0u ) ) {
|
||||
return ECA_BADCOUNT;
|
||||
}
|
||||
|
||||
@ -444,7 +443,7 @@ caStatus casStrmClient::readAction ( epicsGuard < casClientMutex > & guard )
|
||||
casChannelI * pChan;
|
||||
|
||||
{
|
||||
caStatus status = this->verifyRequest ( pChan );
|
||||
caStatus status = this->verifyRequest ( pChan, CA_V413 ( this->minor_version_number ) );
|
||||
if ( status != ECA_NORMAL ) {
|
||||
if ( pChan ) {
|
||||
return this->sendErr ( guard, mp, pChan->getCID(),
|
||||
@ -531,11 +530,15 @@ caStatus casStrmClient::readResponse ( epicsGuard < casClientMutex > & guard,
|
||||
pChan->getCID(), status, ECA_GETFAIL );
|
||||
}
|
||||
|
||||
ca_uint32_t count = (msg.m_count == 0) ?
|
||||
(ca_uint32_t)desc.getDataSizeElements() :
|
||||
msg.m_count;
|
||||
|
||||
void * pPayload;
|
||||
{
|
||||
unsigned payloadSize = dbr_size_n ( msg.m_dataType, msg.m_count );
|
||||
unsigned payloadSize = dbr_size_n ( msg.m_dataType, count );
|
||||
caStatus localStatus = this->out.copyInHeader ( msg.m_cmmd, payloadSize,
|
||||
msg.m_dataType, msg.m_count, pChan->getCID (),
|
||||
msg.m_dataType, count, pChan->getCID (),
|
||||
msg.m_available, & pPayload );
|
||||
if ( localStatus ) {
|
||||
if ( localStatus==S_cas_hugeRequest ) {
|
||||
@ -551,21 +554,21 @@ caStatus casStrmClient::readResponse ( epicsGuard < casClientMutex > & guard,
|
||||
// (places the data in network format)
|
||||
//
|
||||
int mapDBRStatus = gddMapDbr[msg.m_dataType].conv_dbr(
|
||||
pPayload, msg.m_count, desc, pChan->enumStringTable() );
|
||||
pPayload, count, desc, pChan->enumStringTable() );
|
||||
if ( mapDBRStatus < 0 ) {
|
||||
desc.dump ();
|
||||
errPrintf ( S_cas_badBounds, __FILE__, __LINE__, "- get with PV=%s type=%u count=%u",
|
||||
pChan->getPVI().getName(), msg.m_dataType, msg.m_count );
|
||||
pChan->getPVI().getName(), msg.m_dataType, count );
|
||||
return this->sendErrWithEpicsStatus (
|
||||
guard, & msg, pChan->getCID(), S_cas_badBounds, ECA_GETFAIL );
|
||||
}
|
||||
int cacStatus = caNetConvert (
|
||||
msg.m_dataType, pPayload, pPayload, true, msg.m_count );
|
||||
msg.m_dataType, pPayload, pPayload, true, count );
|
||||
if ( cacStatus != ECA_NORMAL ) {
|
||||
return this->sendErrWithEpicsStatus (
|
||||
guard, & msg, pChan->getCID(), S_cas_internal, cacStatus );
|
||||
}
|
||||
if ( msg.m_dataType == DBR_STRING && msg.m_count == 1u ) {
|
||||
if ( msg.m_dataType == DBR_STRING && count == 1u ) {
|
||||
unsigned reducedPayloadSize = strlen ( static_cast < char * > ( pPayload ) ) + 1u;
|
||||
this->out.commitMsg ( reducedPayloadSize );
|
||||
}
|
||||
@ -585,7 +588,7 @@ caStatus casStrmClient::readNotifyAction ( epicsGuard < casClientMutex > & guard
|
||||
casChannelI * pChan;
|
||||
|
||||
{
|
||||
caStatus status = this->verifyRequest ( pChan );
|
||||
caStatus status = this->verifyRequest ( pChan, CA_V413 ( this->minor_version_number ) );
|
||||
if ( status != ECA_NORMAL ) {
|
||||
return this->readNotifyFailureResponse ( guard, * mp, status );
|
||||
}
|
||||
@ -656,11 +659,15 @@ caStatus casStrmClient::readNotifyResponse ( epicsGuard < casClientMutex > & gua
|
||||
return ecaStatus;
|
||||
}
|
||||
|
||||
ca_uint32_t count = (msg.m_count == 0) ?
|
||||
(ca_uint32_t)desc.getDataSizeElements() :
|
||||
msg.m_count;
|
||||
|
||||
void *pPayload;
|
||||
{
|
||||
unsigned size = dbr_size_n ( msg.m_dataType, msg.m_count );
|
||||
unsigned size = dbr_size_n ( msg.m_dataType, count );
|
||||
caStatus status = this->out.copyInHeader ( msg.m_cmmd, size,
|
||||
msg.m_dataType, msg.m_count, ECA_NORMAL,
|
||||
msg.m_dataType, count, ECA_NORMAL,
|
||||
msg.m_available, & pPayload );
|
||||
if ( status ) {
|
||||
if ( status == S_cas_hugeRequest ) {
|
||||
@ -675,23 +682,23 @@ caStatus casStrmClient::readNotifyResponse ( epicsGuard < casClientMutex > & gua
|
||||
// convert gdd to db_access type
|
||||
//
|
||||
int mapDBRStatus = gddMapDbr[msg.m_dataType].conv_dbr ( pPayload,
|
||||
msg.m_count, desc, pChan->enumStringTable() );
|
||||
count, desc, pChan->enumStringTable() );
|
||||
if ( mapDBRStatus < 0 ) {
|
||||
desc.dump();
|
||||
errPrintf ( S_cas_badBounds, __FILE__, __LINE__,
|
||||
"- get notify with PV=%s type=%u count=%u",
|
||||
pChan->getPVI().getName(), msg.m_dataType, msg.m_count );
|
||||
pChan->getPVI().getName(), msg.m_dataType, count );
|
||||
return this->readNotifyFailureResponse ( guard, msg, ECA_NOCONVERT );
|
||||
}
|
||||
|
||||
int cacStatus = caNetConvert (
|
||||
msg.m_dataType, pPayload, pPayload, true, msg.m_count );
|
||||
msg.m_dataType, pPayload, pPayload, true, count );
|
||||
if ( cacStatus != ECA_NORMAL ) {
|
||||
return this->sendErrWithEpicsStatus (
|
||||
guard, & msg, pChan->getCID(), S_cas_internal, cacStatus );
|
||||
}
|
||||
|
||||
if ( msg.m_dataType == DBR_STRING && msg.m_count == 1u ) {
|
||||
if ( msg.m_dataType == DBR_STRING && count == 1u ) {
|
||||
unsigned reducedPayloadSize = strlen ( static_cast < char * > ( pPayload ) ) + 1u;
|
||||
this->out.commitMsg ( reducedPayloadSize );
|
||||
}
|
||||
@ -727,8 +734,8 @@ caStatus casStrmClient::readNotifyFailureResponse (
|
||||
// to be more efficent if it discovers that the source has less data
|
||||
// than the destination)
|
||||
//
|
||||
caStatus convertContainerMemberToAtomic ( gdd & dd,
|
||||
aitUint32 appType, aitUint32 elemCount )
|
||||
caStatus convertContainerMemberToAtomic ( gdd & dd,
|
||||
aitUint32 appType, aitUint32 requestedCount, aitUint32 nativeCount )
|
||||
{
|
||||
gdd * pVal;
|
||||
if ( dd.isContainer() ) {
|
||||
@ -755,13 +762,13 @@ caStatus convertContainerMemberToAtomic ( gdd & dd,
|
||||
return S_cas_badType;
|
||||
}
|
||||
|
||||
if ( elemCount <= 1 ) {
|
||||
if ( nativeCount <= 1 ) {
|
||||
return S_cas_success;
|
||||
}
|
||||
|
||||
// convert to atomic
|
||||
gddBounds bds;
|
||||
bds.setSize ( elemCount );
|
||||
bds.setSize ( requestedCount );
|
||||
bds.setFirst ( 0u );
|
||||
pVal->setDimension ( 1u, & bds );
|
||||
return S_cas_success;
|
||||
@ -770,9 +777,9 @@ caStatus convertContainerMemberToAtomic ( gdd & dd,
|
||||
//
|
||||
// createDBRDD ()
|
||||
//
|
||||
static caStatus createDBRDD ( unsigned dbrType,
|
||||
unsigned elemCount, gdd * & pDD )
|
||||
{
|
||||
static caStatus createDBRDD ( unsigned dbrType,
|
||||
unsigned requestedCount, unsigned nativeCount, gdd * & pDD )
|
||||
{
|
||||
/*
|
||||
* DBR type has already been checked, but it is possible
|
||||
* that "gddDbrToAit" will not track with changes in
|
||||
@ -799,7 +806,7 @@ static caStatus createDBRDD ( unsigned dbrType,
|
||||
|
||||
// fix the value element count
|
||||
caStatus status = convertContainerMemberToAtomic (
|
||||
*pDescRet, gddAppType_value, elemCount );
|
||||
*pDescRet, gddAppType_value, requestedCount, nativeCount );
|
||||
if ( status != S_cas_success ) {
|
||||
pDescRet->unreference ();
|
||||
return status;
|
||||
@ -849,11 +856,27 @@ caStatus casStrmClient::monitorResponse (
|
||||
casChannelI & chan, const caHdrLargeArray & msg,
|
||||
const gdd & desc, const caStatus completionStatus )
|
||||
{
|
||||
aitUint32 elementCount = 0;
|
||||
if (desc.isContainer()) {
|
||||
aitUint32 index;
|
||||
int gdds = gddApplicationTypeTable::app_table.mapAppToIndex
|
||||
( desc.applicationType(), gddAppType_value, index );
|
||||
if ( gdds ) {
|
||||
return S_cas_badType;
|
||||
}
|
||||
elementCount = desc.getDD(index)->getDataSizeElements();
|
||||
} else {
|
||||
elementCount = desc.getDataSizeElements();
|
||||
}
|
||||
ca_uint32_t count = (msg.m_count == 0) ?
|
||||
(ca_uint32_t)elementCount :
|
||||
msg.m_count;
|
||||
|
||||
void * pPayload = 0;
|
||||
{
|
||||
ca_uint32_t size = dbr_size_n ( msg.m_dataType, msg.m_count );
|
||||
ca_uint32_t size = dbr_size_n ( msg.m_dataType, count );
|
||||
caStatus status = out.copyInHeader ( msg.m_cmmd, size,
|
||||
msg.m_dataType, msg.m_count, ECA_NORMAL,
|
||||
msg.m_dataType, count, ECA_NORMAL,
|
||||
msg.m_available, & pPayload );
|
||||
if ( status ) {
|
||||
if ( status == S_cas_hugeRequest ) {
|
||||
@ -871,7 +894,8 @@ caStatus casStrmClient::monitorResponse (
|
||||
|
||||
gdd * pDBRDD = 0;
|
||||
if ( completionStatus == S_cas_success ) {
|
||||
caStatus status = createDBRDD ( msg.m_dataType, msg.m_count, pDBRDD );
|
||||
caStatus status = createDBRDD ( msg.m_dataType, count,
|
||||
chan.getMaxElem(), pDBRDD );
|
||||
if ( status != S_cas_success ) {
|
||||
caStatus ecaStatus;
|
||||
if ( status == S_cas_badType ) {
|
||||
@ -892,7 +916,7 @@ caStatus casStrmClient::monitorResponse (
|
||||
pDBRDD->unreference ();
|
||||
errPrintf ( S_cas_noConvert, __FILE__, __LINE__,
|
||||
"no conversion between event app type=%d and DBR type=%d Element count=%d",
|
||||
desc.applicationType (), msg.m_dataType, msg.m_count);
|
||||
desc.applicationType (), msg.m_dataType, count);
|
||||
return monitorFailureResponse ( guard, msg, ECA_NOCONVERT );
|
||||
}
|
||||
}
|
||||
@ -915,14 +939,14 @@ caStatus casStrmClient::monitorResponse (
|
||||
}
|
||||
|
||||
int mapDBRStatus = gddMapDbr[msg.m_dataType].conv_dbr (
|
||||
pPayload, msg.m_count, *pDBRDD, chan.enumStringTable() );
|
||||
pPayload, count, *pDBRDD, chan.enumStringTable() );
|
||||
if ( mapDBRStatus < 0 ) {
|
||||
pDBRDD->unreference ();
|
||||
return monitorFailureResponse ( guard, msg, ECA_NOCONVERT );
|
||||
}
|
||||
|
||||
int cacStatus = caNetConvert (
|
||||
msg.m_dataType, pPayload, pPayload, true, msg.m_count );
|
||||
msg.m_dataType, pPayload, pPayload, true, count );
|
||||
if ( cacStatus != ECA_NORMAL ) {
|
||||
pDBRDD->unreference ();
|
||||
return this->sendErrWithEpicsStatus (
|
||||
@ -932,7 +956,7 @@ caStatus casStrmClient::monitorResponse (
|
||||
//
|
||||
// force string message size to be the true size
|
||||
//
|
||||
if ( msg.m_dataType == DBR_STRING && msg.m_count == 1u ) {
|
||||
if ( msg.m_dataType == DBR_STRING && count == 1u ) {
|
||||
ca_uint32_t reducedPayloadSize = strlen ( static_cast < char * > ( pPayload ) ) + 1u;
|
||||
this->out.commitMsg ( reducedPayloadSize );
|
||||
}
|
||||
@ -1842,7 +1866,7 @@ caStatus casStrmClient::privateCreateChanResponse (
|
||||
// the protocol buffer.
|
||||
//
|
||||
assert ( nativeTypeDBR <= 0xffff );
|
||||
aitIndex nativeCount = chan.getPVI().nativeCount();
|
||||
aitIndex nativeCount = chan.getMaxElem();
|
||||
assert ( nativeCount <= 0xffffffff );
|
||||
assert ( hdr.m_cid == chan.getCID() );
|
||||
status = this->out.copyInHeader ( CA_PROTO_CREATE_CHAN, 0,
|
||||
@ -1940,7 +1964,7 @@ caStatus casStrmClient::eventAddAction (
|
||||
|
||||
casChannelI *pciu;
|
||||
{
|
||||
caStatus status = casStrmClient::verifyRequest ( pciu );
|
||||
caStatus status = casStrmClient::verifyRequest ( pciu, CA_V413 ( this->minor_version_number ) );
|
||||
if ( status != ECA_NORMAL ) {
|
||||
if ( pciu ) {
|
||||
return this->sendErr ( guard, mp,
|
||||
@ -2601,8 +2625,8 @@ caStatus casStrmClient::read ()
|
||||
|
||||
{
|
||||
gdd * pDD = 0;
|
||||
caStatus status = createDBRDD ( pHdr->m_dataType,
|
||||
pHdr->m_count, pDD );
|
||||
caStatus status = createDBRDD ( pHdr->m_dataType, pHdr->m_count,
|
||||
this->ctx.getChannel()->getMaxElem(), pDD );
|
||||
if ( status != S_cas_success ) {
|
||||
return status;
|
||||
}
|
||||
@ -2749,6 +2773,7 @@ caStatus casStrmClient::sendErr ( epicsGuard <casClientMutex> &,
|
||||
else {
|
||||
stringSize = 1u + (unsigned) status;
|
||||
}
|
||||
va_end ( args );
|
||||
}
|
||||
else {
|
||||
stringSize = 0u;
|
||||
|
@ -69,7 +69,7 @@ private:
|
||||
bool responseIsPending;
|
||||
|
||||
caStatus createChannel ( const char * pName );
|
||||
caStatus verifyRequest ( casChannelI * & pChan );
|
||||
caStatus verifyRequest ( casChannelI * & pChan, bool allowdyn = false );
|
||||
typedef caStatus ( casStrmClient :: * pCASMsgHandler )
|
||||
( epicsGuard < casClientMutex > & );
|
||||
static pCASMsgHandler const msgHandlers[CA_PROTO_LAST_CMMD+1u];
|
||||
|
@ -4,7 +4,7 @@
|
||||
* Copyright (c) 2002 The Regents of the University of California, as
|
||||
* Operator of Los Alamos National Laboratory.
|
||||
* EPICS BASE is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
* in file LICENSE that is included with this distribution.
|
||||
\*************************************************************************/
|
||||
|
||||
//
|
||||
@ -32,7 +32,7 @@ const unsigned caServerConnectPendQueueSize = 5u;
|
||||
//
|
||||
// casIntfIO::casIntfIO()
|
||||
//
|
||||
casIntfIO::casIntfIO ( const caNetAddr & addrIn ) :
|
||||
casIntfIO::casIntfIO ( const caNetAddr & addrIn ) :
|
||||
sock ( INVALID_SOCKET ),
|
||||
addr ( addrIn.getSockIP() )
|
||||
{
|
||||
@ -40,80 +40,79 @@ casIntfIO::casIntfIO ( const caNetAddr & addrIn ) :
|
||||
osiSocklen_t addrSize;
|
||||
bool portChange;
|
||||
|
||||
if ( ! osiSockAttach () ) {
|
||||
throw S_cas_internal;
|
||||
}
|
||||
if ( ! osiSockAttach () ) {
|
||||
throw S_cas_internal;
|
||||
}
|
||||
|
||||
/*
|
||||
* Setup the server socket
|
||||
*/
|
||||
this->sock = epicsSocketCreate ( AF_INET, SOCK_STREAM, IPPROTO_TCP );
|
||||
if (this->sock==INVALID_SOCKET) {
|
||||
/*
|
||||
* Setup the server socket
|
||||
*/
|
||||
this->sock = epicsSocketCreate ( AF_INET, SOCK_STREAM, IPPROTO_TCP );
|
||||
if (this->sock == INVALID_SOCKET) {
|
||||
char sockErrBuf[64];
|
||||
epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) );
|
||||
printf ( "No socket error was %s\n", sockErrBuf );
|
||||
throw S_cas_noFD;
|
||||
}
|
||||
printf ( "No socket error was %s\n", sockErrBuf );
|
||||
throw S_cas_noFD;
|
||||
}
|
||||
|
||||
epicsSocketEnableAddressReuseDuringTimeWaitState ( this->sock );
|
||||
|
||||
status = bind ( this->sock,
|
||||
reinterpret_cast <sockaddr *> (&this->addr),
|
||||
sizeof(this->addr) );
|
||||
if (status<0) {
|
||||
if (SOCKERRNO == SOCK_EADDRINUSE) {
|
||||
//
|
||||
// enable assignment of a default port
|
||||
// (so the getsockname() call below will
|
||||
// work correctly)
|
||||
//
|
||||
this->addr.sin_port = ntohs (0);
|
||||
status = bind(
|
||||
status = bind ( this->sock,
|
||||
reinterpret_cast <sockaddr *> (&this->addr),
|
||||
sizeof(this->addr) );
|
||||
if (status < 0) {
|
||||
if (SOCKERRNO == SOCK_EADDRINUSE ||
|
||||
SOCKERRNO == SOCK_EACCES) {
|
||||
//
|
||||
// enable assignment of a default port
|
||||
// (so the getsockname() call below will
|
||||
// work correctly)
|
||||
//
|
||||
this->addr.sin_port = ntohs (0);
|
||||
status = bind(
|
||||
this->sock,
|
||||
reinterpret_cast <sockaddr *> (&this->addr),
|
||||
sizeof(this->addr) );
|
||||
}
|
||||
if (status<0) {
|
||||
}
|
||||
if (status < 0) {
|
||||
char sockErrBuf[64];
|
||||
epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) );
|
||||
char buf[64];
|
||||
ipAddrToA (&this->addr, buf, sizeof(buf));
|
||||
errPrintf ( S_cas_bindFail,
|
||||
__FILE__, __LINE__,
|
||||
"- bind TCP IP addr=%s failed because %s",
|
||||
buf, sockErrBuf );
|
||||
char buf[64];
|
||||
ipAddrToA (&this->addr, buf, sizeof(buf));
|
||||
errlogPrintf ( "CAS: Socket bind TCP to %s failed with %s",
|
||||
buf, sockErrBuf );
|
||||
epicsSocketDestroy (this->sock);
|
||||
throw S_cas_bindFail;
|
||||
}
|
||||
throw S_cas_bindFail;
|
||||
}
|
||||
portChange = true;
|
||||
}
|
||||
}
|
||||
else {
|
||||
portChange = false;
|
||||
}
|
||||
|
||||
addrSize = ( osiSocklen_t ) sizeof (this->addr);
|
||||
status = getsockname (
|
||||
this->sock,
|
||||
reinterpret_cast <sockaddr *> ( &this->addr ),
|
||||
addrSize = ( osiSocklen_t ) sizeof (this->addr);
|
||||
status = getsockname (
|
||||
this->sock,
|
||||
reinterpret_cast <sockaddr *> ( &this->addr ),
|
||||
&addrSize );
|
||||
if (status) {
|
||||
if (status) {
|
||||
char sockErrBuf[64];
|
||||
epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) );
|
||||
errlogPrintf ( "CAS: getsockname() error %s\n",
|
||||
sockErrBuf );
|
||||
errlogPrintf ( "CAS: getsockname() error %s\n",
|
||||
sockErrBuf );
|
||||
epicsSocketDestroy (this->sock);
|
||||
throw S_cas_internal;
|
||||
}
|
||||
throw S_cas_internal;
|
||||
}
|
||||
|
||||
//
|
||||
// be sure of this now so that we can fetch the IP
|
||||
// address and port number later
|
||||
//
|
||||
//
|
||||
// be sure of this now so that we can fetch the IP
|
||||
// address and port number later
|
||||
//
|
||||
assert (this->addr.sin_family == AF_INET);
|
||||
|
||||
if ( portChange ) {
|
||||
errlogPrintf ( "cas warning: Configured TCP port was unavailable.\n");
|
||||
errlogPrintf ( "cas warning: Using dynamically assigned TCP port %hu,\n",
|
||||
errlogPrintf ( "cas warning: Using dynamically assigned TCP port %hu,\n",
|
||||
ntohs (this->addr.sin_port) );
|
||||
errlogPrintf ( "cas warning: but now two or more servers share the same UDP port.\n");
|
||||
errlogPrintf ( "cas warning: Depending on your IP kernel this server may not be\n" );
|
||||
@ -121,12 +120,12 @@ casIntfIO::casIntfIO ( const caNetAddr & addrIn ) :
|
||||
}
|
||||
|
||||
status = listen(this->sock, caServerConnectPendQueueSize);
|
||||
if(status < 0) {
|
||||
if (status < 0) {
|
||||
char sockErrBuf[64];
|
||||
epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) );
|
||||
errlogPrintf ( "CAS: listen() error %s\n", sockErrBuf );
|
||||
errlogPrintf ( "CAS: listen() error %s\n", sockErrBuf );
|
||||
epicsSocketDestroy (this->sock);
|
||||
throw S_cas_internal;
|
||||
throw S_cas_internal;
|
||||
}
|
||||
}
|
||||
|
||||
@ -135,17 +134,17 @@ casIntfIO::casIntfIO ( const caNetAddr & addrIn ) :
|
||||
//
|
||||
casIntfIO::~casIntfIO()
|
||||
{
|
||||
if (this->sock != INVALID_SOCKET) {
|
||||
epicsSocketDestroy (this->sock);
|
||||
}
|
||||
if (this->sock != INVALID_SOCKET) {
|
||||
epicsSocketDestroy (this->sock);
|
||||
}
|
||||
|
||||
osiSockRelease ();
|
||||
osiSockRelease ();
|
||||
}
|
||||
|
||||
//
|
||||
// newStreamIO::newStreamClient()
|
||||
//
|
||||
casStreamOS *casIntfIO::newStreamClient ( caServerI & cas,
|
||||
casStreamOS *casIntfIO::newStreamClient ( caServerI & cas,
|
||||
clientBufMemoryManager & bufMgr ) const
|
||||
{
|
||||
static bool oneMsgFlag = false;
|
||||
@ -175,14 +174,14 @@ casStreamOS *casIntfIO::newStreamClient ( caServerI & cas,
|
||||
args.sock = newSock;
|
||||
casStreamOS * pOS = new casStreamOS ( cas, bufMgr, args );
|
||||
if ( ! pOS ) {
|
||||
errMessage ( S_cas_noMemory,
|
||||
errMessage ( S_cas_noMemory,
|
||||
"unable to create data structures for a new client" );
|
||||
epicsSocketDestroy ( newSock );
|
||||
}
|
||||
else {
|
||||
if ( cas.getDebugLevel() > 0u ) {
|
||||
char pName[64u];
|
||||
|
||||
|
||||
pOS->hostName ( pName, sizeof ( pName ) );
|
||||
errlogPrintf ( "CAS: allocated client object for \"%s\"\n", pName );
|
||||
}
|
||||
@ -197,7 +196,7 @@ void casIntfIO::setNonBlocking()
|
||||
{
|
||||
int status;
|
||||
osiSockIoctl_t yes = true;
|
||||
|
||||
|
||||
status = socket_ioctl(this->sock, FIONBIO, &yes);
|
||||
if ( status < 0 ) {
|
||||
char sockErrBuf[64];
|
||||
|
@ -130,8 +130,8 @@ long dbcar(char *precordname, int level)
|
||||
precord->name,
|
||||
pdbFldDes->name,
|
||||
plink->value.pv_link.pvname,
|
||||
pca->nDisconnect,
|
||||
pca->nNoWrite);
|
||||
pca ? pca->nDisconnect : 0,
|
||||
pca ? pca->nNoWrite : 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3940,10 +3940,9 @@ static long putFloatString(
|
||||
char *pdest = (char *) paddr->pfield;
|
||||
long status = 0;
|
||||
int precision = 6;
|
||||
struct rset *prset = 0;
|
||||
struct rset *prset = dbGetRset(paddr);
|
||||
short size = paddr->field_size;
|
||||
|
||||
if (paddr) prset = dbGetRset(paddr);
|
||||
if (prset && (prset->get_precision))
|
||||
status = (*prset->get_precision)(paddr,&precision);
|
||||
if (nRequest==1 && offset==0) {
|
||||
@ -4154,10 +4153,9 @@ static long putDoubleString(
|
||||
char *pdest = (char *) paddr->pfield;
|
||||
long status = 0;
|
||||
int precision = 6;
|
||||
struct rset *prset = 0;
|
||||
struct rset *prset = dbGetRset(paddr);
|
||||
short size = paddr->field_size;
|
||||
|
||||
if (paddr) prset = dbGetRset(paddr);
|
||||
if (prset && (prset->get_precision))
|
||||
status = (*prset->get_precision)(paddr,&precision);
|
||||
if (nRequest==1 && offset==0) {
|
||||
|
@ -159,8 +159,11 @@ void scanStop(void)
|
||||
interruptAccept = FALSE;
|
||||
|
||||
for (i = 0; i < nPeriodic; i++) {
|
||||
papPeriodic[i]->scanCtl = ctlExit;
|
||||
epicsEventSignal(papPeriodic[i]->loopEvent);
|
||||
periodic_scan_list *ppsl = papPeriodic[i];
|
||||
|
||||
if (!ppsl) continue;
|
||||
ppsl->scanCtl = ctlExit;
|
||||
epicsEventSignal(ppsl->loopEvent);
|
||||
epicsEventWait(startStopEvent);
|
||||
}
|
||||
|
||||
@ -205,16 +208,24 @@ void scanRun(void)
|
||||
interruptAccept = TRUE;
|
||||
scanCtl = ctlRun;
|
||||
|
||||
for (i = 0; i < nPeriodic; i++)
|
||||
papPeriodic[i]->scanCtl = ctlRun;
|
||||
for (i = 0; i < nPeriodic; i++) {
|
||||
periodic_scan_list *ppsl = papPeriodic[i];
|
||||
|
||||
if (!ppsl) continue;
|
||||
ppsl->scanCtl = ctlRun;
|
||||
}
|
||||
}
|
||||
|
||||
void scanPause(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = nPeriodic - 1; i >= 0; --i)
|
||||
papPeriodic[i]->scanCtl = ctlPause;
|
||||
for (i = nPeriodic - 1; i >= 0; --i) {
|
||||
periodic_scan_list *ppsl = papPeriodic[i];
|
||||
|
||||
if (!ppsl) continue;
|
||||
ppsl->scanCtl = ctlPause;
|
||||
}
|
||||
|
||||
scanCtl = ctlPause;
|
||||
interruptAccept = FALSE;
|
||||
@ -286,9 +297,11 @@ void scanAdd(struct dbCommon *precord)
|
||||
}
|
||||
addToList(precord, &piosh->iosl[prio].scan_list);
|
||||
} else if (scan >= SCAN_1ST_PERIODIC) {
|
||||
addToList(precord, &papPeriodic[scan - SCAN_1ST_PERIODIC]->scan_list);
|
||||
periodic_scan_list *ppsl = papPeriodic[scan - SCAN_1ST_PERIODIC];
|
||||
|
||||
if (ppsl)
|
||||
addToList(precord, &ppsl->scan_list);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
void scanDelete(struct dbCommon *precord)
|
||||
@ -352,28 +365,48 @@ void scanDelete(struct dbCommon *precord)
|
||||
}
|
||||
deleteFromList(precord, &piosh->iosl[prio].scan_list);
|
||||
} else if (scan >= SCAN_1ST_PERIODIC) {
|
||||
deleteFromList(precord, &papPeriodic[scan - SCAN_1ST_PERIODIC]->scan_list);
|
||||
periodic_scan_list *ppsl = papPeriodic[scan - SCAN_1ST_PERIODIC];
|
||||
|
||||
if (ppsl)
|
||||
deleteFromList(precord, &ppsl->scan_list);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
double scanPeriod(int scan) {
|
||||
periodic_scan_list *ppsl;
|
||||
|
||||
scan -= SCAN_1ST_PERIODIC;
|
||||
if (scan < 0 || scan >= nPeriodic)
|
||||
return 0.0;
|
||||
return papPeriodic[scan]->period;
|
||||
ppsl = papPeriodic[scan];
|
||||
return ppsl ? ppsl->period : 0.0;
|
||||
}
|
||||
|
||||
int scanppl(double period) /* print periodic list */
|
||||
|
||||
int scanppl(double period) /* print periodic scan list(s) */
|
||||
{
|
||||
periodic_scan_list *ppsl;
|
||||
dbMenu *pmenu = dbFindMenu(pdbbase, "menuScan");
|
||||
char message[80];
|
||||
int i;
|
||||
|
||||
if (!pmenu || !papPeriodic) {
|
||||
printf("scanppl: dbScan subsystem not initialized\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (i = 0; i < nPeriodic; i++) {
|
||||
ppsl = papPeriodic[i];
|
||||
if (ppsl == NULL) continue;
|
||||
if (period > 0.0 && (fabs(period - ppsl->period) >.05)) continue;
|
||||
periodic_scan_list *ppsl = papPeriodic[i];
|
||||
|
||||
if (!ppsl) {
|
||||
const char *choice = pmenu->papChoiceValue[i + SCAN_1ST_PERIODIC];
|
||||
|
||||
printf("Periodic scan list for SCAN = '%s' not initialized\n",
|
||||
choice);
|
||||
continue;
|
||||
}
|
||||
if (period > 0.0 &&
|
||||
(fabs(period - ppsl->period) > 0.05))
|
||||
continue;
|
||||
|
||||
sprintf(message, "Records with SCAN = '%s' (%lu over-runs):",
|
||||
ppsl->name, ppsl->overruns);
|
||||
printList(&ppsl->scan_list, message);
|
||||
@ -741,7 +774,7 @@ static void periodicTask(void *arg)
|
||||
if (++overruns >= 10 &&
|
||||
epicsTimeDiffInSeconds(&now, &reported) > report_delay) {
|
||||
errlogPrintf("\ndbScan warning from '%s' scan thread:\n"
|
||||
"\tScan processing averages %.2f seconds (%.2f .. %.2f).\n"
|
||||
"\tScan processing averages %.3f seconds (%.3f .. %.3f).\n"
|
||||
"\tOver-runs have now happened %u times in a row.\n"
|
||||
"\tTo fix this, move some records to a slower scan rate.\n",
|
||||
ppsl->name, ppsl->period + overtime / overruns,
|
||||
@ -788,12 +821,8 @@ static void initPeriodic(void)
|
||||
char *unit;
|
||||
int status = epicsParseDouble(choice, &number, &unit);
|
||||
|
||||
ppsl->scan_list.lock = epicsMutexMustCreate();
|
||||
ellInit(&ppsl->scan_list.list);
|
||||
ppsl->name = choice;
|
||||
if (status || number == 0) {
|
||||
if (status || number <= 0) {
|
||||
errlogPrintf("initPeriodic: Bad menuScan choice '%s'\n", choice);
|
||||
ppsl->period = i;
|
||||
}
|
||||
else if (!*unit ||
|
||||
!epicsStrCaseCmp(unit, "second") ||
|
||||
@ -814,16 +843,24 @@ static void initPeriodic(void)
|
||||
}
|
||||
else {
|
||||
errlogPrintf("initPeriodic: Bad menuScan choice '%s'\n", choice);
|
||||
ppsl->period = i;
|
||||
}
|
||||
if (ppsl->period == 0) {
|
||||
free(ppsl);
|
||||
continue;
|
||||
}
|
||||
|
||||
ppsl->scan_list.lock = epicsMutexMustCreate();
|
||||
ellInit(&ppsl->scan_list.list);
|
||||
ppsl->name = choice;
|
||||
ppsl->scanCtl = ctlPause;
|
||||
ppsl->loopEvent = epicsEventMustCreate(epicsEventEmpty);
|
||||
|
||||
number = ppsl->period / quantum;
|
||||
if ((ppsl->period < 2 * quantum) ||
|
||||
(number / floor(number) > 1.1)) {
|
||||
errlogPrintf("initPeriodic: Scan rate '%s' is not achievable.\n",
|
||||
choice);
|
||||
}
|
||||
ppsl->scanCtl = ctlPause;
|
||||
ppsl->loopEvent = epicsEventMustCreate(epicsEventEmpty);
|
||||
|
||||
papPeriodic[i] = ppsl;
|
||||
}
|
||||
@ -835,6 +872,8 @@ static void deletePeriodic(void)
|
||||
|
||||
for (i = 0; i < nPeriodic; i++) {
|
||||
periodic_scan_list *ppsl = papPeriodic[i];
|
||||
|
||||
if (!ppsl) continue;
|
||||
ellFree(&ppsl->scan_list.list);
|
||||
epicsEventDestroy(ppsl->loopEvent);
|
||||
epicsMutexDestroy(ppsl->scan_list.lock);
|
||||
@ -847,10 +886,11 @@ static void deletePeriodic(void)
|
||||
|
||||
static void spawnPeriodic(int ind)
|
||||
{
|
||||
periodic_scan_list *ppsl;
|
||||
periodic_scan_list *ppsl = papPeriodic[ind];
|
||||
char taskName[20];
|
||||
|
||||
ppsl = papPeriodic[ind];
|
||||
if (!ppsl) return;
|
||||
|
||||
sprintf(taskName, "scan-%g", ppsl->period);
|
||||
periodicTaskId[ind] = epicsThreadCreate(
|
||||
taskName, epicsThreadPriorityScanLow + ind,
|
||||
@ -989,13 +1029,13 @@ static void addToList(struct dbCommon *precord, scan_list *psl)
|
||||
pse->precord = precord;
|
||||
}
|
||||
pse->pscan_list = psl;
|
||||
ptemp = (scan_element *)ellFirst(&psl->list);
|
||||
ptemp = (scan_element *)ellLast(&psl->list);
|
||||
while (ptemp) {
|
||||
if (ptemp->precord->phas > precord->phas) {
|
||||
ellInsert(&psl->list, ellPrevious(&ptemp->node), &pse->node);
|
||||
if (ptemp->precord->phas <= precord->phas) {
|
||||
ellInsert(&psl->list, &ptemp->node, &pse->node);
|
||||
break;
|
||||
}
|
||||
ptemp = (scan_element *)ellNext(&ptemp->node);
|
||||
ptemp = (scan_element *)ellPrevious(&ptemp->node);
|
||||
}
|
||||
if (ptemp == NULL) ellAdd(&psl->list, (void *)pse);
|
||||
psl->modified = TRUE;
|
||||
|
@ -172,6 +172,7 @@ int pft(const char *pname, const char *pvalue)
|
||||
ca_dump_dbr(DBR_ENUM,1, buffer);
|
||||
}
|
||||
printf("\n");
|
||||
dbChannelDelete(chan);
|
||||
return (0);
|
||||
}
|
||||
|
||||
@ -231,6 +232,7 @@ int tpn(const char *pname, const char *pvalue)
|
||||
ppn = calloc(1, sizeof(processNotify));
|
||||
if (!ppn) {
|
||||
printf("calloc failed\n");
|
||||
dbChannelDelete(chan);
|
||||
return -1;
|
||||
}
|
||||
ppn->requestType = putProcessRequest;
|
||||
@ -241,6 +243,8 @@ int tpn(const char *pname, const char *pvalue)
|
||||
ptpnInfo = calloc(1, sizeof(tpnInfo));
|
||||
if (!ptpnInfo) {
|
||||
printf("calloc failed\n");
|
||||
free(ppn);
|
||||
dbChannelDelete(chan);
|
||||
return -1;
|
||||
}
|
||||
ptpnInfo->ppn = ppn;
|
||||
|
@ -541,8 +541,11 @@ MAIN(chfPluginTest)
|
||||
dbChannel *pch;
|
||||
db_field_log *pfl;
|
||||
|
||||
#if defined(WIN32) && (!defined(_MINGW) || __MSVCRT_VERSION__ >= 0x0800)
|
||||
#ifdef _WIN32
|
||||
#if (defined(_MSC_VER) && _MSC_VER < 1900) || \
|
||||
(defined(_MINGW) && defined(_TWO_DIGIT_EXPONENT))
|
||||
_set_output_format(_TWO_DIGIT_EXPONENT);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
testPlan(1433);
|
||||
|
@ -133,25 +133,25 @@ typedef struct dbVariableDef {
|
||||
}dbVariableDef;
|
||||
|
||||
typedef struct dbRecordType {
|
||||
ELLNODE node;
|
||||
ELLLIST attributeList; /*LIST head of attributes*/
|
||||
ELLLIST recList; /*LIST head of sorted dbRecordNodes*/
|
||||
ELLLIST devList; /*List of associated device support*/
|
||||
ELLLIST cdefList; /*LIST of Cdef text items*/
|
||||
char *name;
|
||||
short no_fields; /* number of fields defined */
|
||||
short no_prompt; /* number of fields to configure*/
|
||||
short no_links; /* number of links */
|
||||
short no_aliases; /* number of aliases in recList */
|
||||
short *link_ind; /* addr of array of ind in papFldDes*/
|
||||
char **papsortFldName;/* ptr to array of ptr to fld names*/
|
||||
short *sortFldInd; /* addr of array of ind in papFldDes*/
|
||||
dbFldDes *pvalFldDes; /*pointer dbFldDes for VAL field*/
|
||||
short indvalFlddes; /*ind in papFldDes*/
|
||||
dbFldDes **papFldDes; /* ptr to array of ptr to fldDes*/
|
||||
/*The following are only available on run time system*/
|
||||
struct rset *prset;
|
||||
int rec_size; /*record size in bytes */
|
||||
ELLNODE node;
|
||||
ELLLIST attributeList; /*LIST head of attributes*/
|
||||
ELLLIST recList; /*LIST head of sorted dbRecordNodes*/
|
||||
ELLLIST devList; /*List of associated device support*/
|
||||
ELLLIST cdefList; /*LIST of Cdef text items*/
|
||||
char *name;
|
||||
short no_fields; /* number of fields defined */
|
||||
short no_prompt; /* number of fields to configure*/
|
||||
short no_links; /* number of links */
|
||||
short no_aliases; /* number of aliases in recList */
|
||||
short *link_ind; /* addr of array of ind in papFldDes*/
|
||||
char **papsortFldName;/* ptr to array of ptr to fld names*/
|
||||
short *sortFldInd; /* addr of array of ind in papFldDes*/
|
||||
dbFldDes *pvalFldDes; /*pointer dbFldDes for VAL field*/
|
||||
short indvalFlddes; /*ind in papFldDes*/
|
||||
dbFldDes **papFldDes; /* ptr to array of ptr to fldDes*/
|
||||
/*The following are only available on run time system*/
|
||||
struct rset *prset;
|
||||
int rec_size; /*record size in bytes */
|
||||
}dbRecordType;
|
||||
|
||||
struct dbPvd; /* Contents private to dbPvdLib code */
|
||||
|
@ -50,6 +50,9 @@ epicsExportAddress(int,dbBptNotMonotonic);
|
||||
epicsShareDef int dbQuietMacroWarnings=0;
|
||||
epicsExportAddress(int,dbQuietMacroWarnings);
|
||||
|
||||
epicsShareDef int dbRecordsAbcSorted=0;
|
||||
epicsExportAddress(int,dbRecordsAbcSorted);
|
||||
|
||||
/*private routines */
|
||||
static void yyerrorAbort(char *str);
|
||||
static void allocTemp(void *pvoid);
|
||||
@ -194,7 +197,16 @@ static void freeInputFileList(void)
|
||||
free((void *)pinputFileNow);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static
|
||||
int cmp_dbRecordNode(const ELLNODE *lhs, const ELLNODE *rhs)
|
||||
{
|
||||
dbRecordNode *LHS = (dbRecordNode*)lhs,
|
||||
*RHS = (dbRecordNode*)rhs;
|
||||
|
||||
return strcmp(LHS->recordname, RHS->recordname);
|
||||
}
|
||||
|
||||
static long dbReadCOM(DBBASE **ppdbbase,const char *filename, FILE *fp,
|
||||
const char *path,const char *substitutions)
|
||||
{
|
||||
@ -239,24 +251,25 @@ static long dbReadCOM(DBBASE **ppdbbase,const char *filename, FILE *fp,
|
||||
macSuppressWarning(macHandle,dbQuietMacroWarnings);
|
||||
}
|
||||
pinputFile = dbCalloc(1,sizeof(inputFile));
|
||||
if(filename) {
|
||||
pinputFile->filename = macEnvExpand(filename);
|
||||
if (filename) {
|
||||
pinputFile->filename = macEnvExpand(filename);
|
||||
}
|
||||
if(!fp) {
|
||||
FILE *fp1;
|
||||
if (!fp) {
|
||||
FILE *fp1 = 0;
|
||||
|
||||
if(pinputFile->filename) pinputFile->path = dbOpenFile(pdbbase,pinputFile->filename,&fp1);
|
||||
if(!pinputFile->filename || !fp1) {
|
||||
errPrintf(0,__FILE__, __LINE__,
|
||||
"dbRead opening file %s",pinputFile->filename);
|
||||
free((void *)pinputFile->filename);
|
||||
free((void *)pinputFile);
|
||||
if (pinputFile->filename)
|
||||
pinputFile->path = dbOpenFile(pdbbase, pinputFile->filename, &fp1);
|
||||
if (!pinputFile->filename || !fp1) {
|
||||
errPrintf(0, __FILE__, __LINE__,
|
||||
"dbRead opening file %s",pinputFile->filename);
|
||||
free(pinputFile->filename);
|
||||
free(pinputFile);
|
||||
status = -1;
|
||||
goto cleanup;
|
||||
}
|
||||
pinputFile->fp = fp1;
|
||||
}
|
||||
pinputFile->fp = fp1;
|
||||
} else {
|
||||
pinputFile->fp = fp;
|
||||
pinputFile->fp = fp;
|
||||
}
|
||||
pinputFile->line_num = 0;
|
||||
pinputFileNow = pinputFile;
|
||||
@ -294,6 +307,15 @@ static long dbReadCOM(DBBASE **ppdbbase,const char *filename, FILE *fp,
|
||||
dbFinishEntry(pdbEntry);
|
||||
}
|
||||
cleanup:
|
||||
if(dbRecordsAbcSorted) {
|
||||
ELLNODE *cur;
|
||||
for(cur = ellFirst(&pdbbase->recordTypeList); cur; cur=ellNext(cur))
|
||||
{
|
||||
dbRecordType *rtype = CONTAINER(cur, dbRecordType, node);
|
||||
|
||||
ellSortStable(&rtype->recList, &cmp_dbRecordNode);
|
||||
}
|
||||
}
|
||||
if(macHandle) macDeleteHandle(macHandle);
|
||||
macHandle = NULL;
|
||||
if(mac_input_buffer) free((void *)mac_input_buffer);
|
||||
|
@ -1418,7 +1418,6 @@ long dbCreateRecord(DBENTRY *pdbentry,const char *precordName)
|
||||
dbFldDes *pdbFldDes;
|
||||
PVDENTRY *ppvd;
|
||||
ELLLIST *preclist = NULL;
|
||||
dbRecordNode *precnode = NULL;
|
||||
dbRecordNode *pNewRecNode = NULL;
|
||||
long status = 0;
|
||||
|
||||
@ -1426,7 +1425,7 @@ long dbCreateRecord(DBENTRY *pdbentry,const char *precordName)
|
||||
/*Get size of NAME field*/
|
||||
pdbFldDes = precordType->papFldDes[0];
|
||||
if(!pdbFldDes || (strcmp(pdbFldDes->name,"NAME")!=0))
|
||||
return(S_dbLib_nameLength);
|
||||
return(S_dbLib_nameLength);
|
||||
if((int)strlen(precordName)>=pdbFldDes->size) return(S_dbLib_nameLength);
|
||||
/* clear callers entry */
|
||||
zeroDbentry(pdbentry);
|
||||
@ -1441,18 +1440,7 @@ long dbCreateRecord(DBENTRY *pdbentry,const char *precordName)
|
||||
if((status = dbAllocRecord(pdbentry,precordName))) return(status);
|
||||
pNewRecNode->recordname = dbRecordName(pdbentry);
|
||||
ellInit(&pNewRecNode->infoList);
|
||||
/* install record node in list in sorted postion */
|
||||
status = dbFirstRecord(pdbentry);
|
||||
while(status==0) {
|
||||
if(strcmp(precordName,dbGetRecordName(pdbentry)) < 0) break;
|
||||
status = dbNextRecord(pdbentry);
|
||||
}
|
||||
if(status==0) {
|
||||
precnode = pdbentry->precnode;
|
||||
ellInsert(preclist,ellPrevious(&precnode->node),&pNewRecNode->node);
|
||||
} else {
|
||||
ellAdd(preclist,&pNewRecNode->node);
|
||||
}
|
||||
ellAdd(preclist, &pNewRecNode->node);
|
||||
pdbentry->precnode = pNewRecNode;
|
||||
ppvd = dbPvdAdd(pdbentry->pdbbase,precordType,pNewRecNode);
|
||||
if(!ppvd) {errMessage(-1,"Logic Err: Could not add to PVD");return(-1);}
|
||||
@ -1630,52 +1618,6 @@ char * dbGetRecordName(DBENTRY *pdbentry)
|
||||
return precnode->recordname;
|
||||
}
|
||||
|
||||
long dbRenameRecord(DBENTRY *pdbentry,const char *newName)
|
||||
{
|
||||
dbBase *pdbbase = pdbentry->pdbbase;
|
||||
dbRecordType *precordType = pdbentry->precordType;
|
||||
dbFldDes *pdbFldDes;
|
||||
dbRecordNode *precnode = pdbentry->precnode;
|
||||
PVDENTRY *ppvd;
|
||||
ELLLIST *preclist;
|
||||
dbRecordNode *plistnode;
|
||||
long status;
|
||||
DBENTRY dbentry;
|
||||
|
||||
if(!precordType) return(S_dbLib_recordTypeNotFound);
|
||||
/*Get size of NAME field*/
|
||||
pdbFldDes = precordType->papFldDes[0];
|
||||
if(!pdbFldDes || (strcmp(pdbFldDes->name,"NAME")!=0))
|
||||
return(S_dbLib_nameLength);
|
||||
if((int)strlen(newName)>=pdbFldDes->size) return(S_dbLib_nameLength);
|
||||
if (!precnode || dbIsAlias(pdbentry)) return S_dbLib_recNotFound;
|
||||
dbInitEntry(pdbentry->pdbbase,&dbentry);
|
||||
status = dbFindRecord(&dbentry,newName);
|
||||
dbFinishEntry(&dbentry);
|
||||
if(!status) return(S_dbLib_recExists);
|
||||
dbPvdDelete(pdbbase,precnode);
|
||||
pdbentry->pflddes = precordType->papFldDes[0];
|
||||
if((status = dbGetFieldAddress(pdbentry))) return(status);
|
||||
strcpy(pdbentry->pfield,newName);
|
||||
ppvd = dbPvdAdd(pdbbase,precordType,precnode);
|
||||
if(!ppvd) {errMessage(-1,"Logic Err: Could not add to PVD");return(-1);}
|
||||
/*remove from record list and reinstall in sorted order*/
|
||||
preclist = &precordType->recList;
|
||||
ellDelete(preclist,&precnode->node);
|
||||
plistnode = (dbRecordNode *)ellFirst(preclist);
|
||||
while(plistnode) {
|
||||
pdbentry->precnode = plistnode;
|
||||
if(strcmp(newName,dbGetRecordName(pdbentry)) >=0) break;
|
||||
plistnode = (dbRecordNode *)ellNext(&plistnode->node);
|
||||
}
|
||||
if(plistnode)
|
||||
ellInsert(preclist,ellPrevious(&plistnode->node),&precnode->node);
|
||||
else
|
||||
ellAdd(preclist,&precnode->node);
|
||||
/*Leave pdbentry pointing to newly renamed record*/
|
||||
return(dbFindRecord(pdbentry,newName));
|
||||
}
|
||||
|
||||
long dbVisibleRecord(DBENTRY *pdbentry)
|
||||
{
|
||||
dbRecordNode *precnode = pdbentry->precnode;
|
||||
@ -1709,7 +1651,6 @@ long dbCreateAlias(DBENTRY *pdbentry, const char *alias)
|
||||
dbRecordNode *pnewnode;
|
||||
PVDENTRY *ppvd;
|
||||
ELLLIST *preclist = NULL;
|
||||
long status;
|
||||
|
||||
if (!precordType) return S_dbLib_recordTypeNotFound;
|
||||
if (!precnode) return S_dbLib_recNotFound;
|
||||
@ -1725,18 +1666,7 @@ long dbCreateAlias(DBENTRY *pdbentry, const char *alias)
|
||||
if (!(precnode->flags & DBRN_FLAGS_ISALIAS))
|
||||
precnode->flags |= DBRN_FLAGS_HASALIAS;
|
||||
ellInit(&pnewnode->infoList);
|
||||
/* install record node in list in sorted postion */
|
||||
status = dbFirstRecord(pdbentry);
|
||||
while (!status) {
|
||||
if (strcmp(alias, dbGetRecordName(pdbentry)) < 0) break;
|
||||
status = dbNextRecord(pdbentry);
|
||||
}
|
||||
if (!status) {
|
||||
precnode = pdbentry->precnode;
|
||||
ellInsert(preclist, ellPrevious(&precnode->node), &pnewnode->node);
|
||||
} else {
|
||||
ellAdd(preclist, &pnewnode->node);
|
||||
}
|
||||
ellAdd(preclist, &pnewnode->node);
|
||||
precordType->no_aliases++;
|
||||
pdbentry->precnode = pnewnode;
|
||||
ppvd = dbPvdAdd(pdbentry->pdbbase, precordType, pnewnode);
|
||||
|
@ -146,8 +146,6 @@ epicsShareFunc long dbNextRecord(DBENTRY *pdbentry);
|
||||
epicsShareFunc int dbGetNRecords(DBENTRY *pdbentry);
|
||||
epicsShareFunc int dbGetNAliases(DBENTRY *pdbentry);
|
||||
epicsShareFunc char * dbGetRecordName(DBENTRY *pdbentry);
|
||||
epicsShareFunc long dbRenameRecord(DBENTRY *pdbentry,
|
||||
const char *newName);
|
||||
epicsShareFunc long dbCopyRecord(DBENTRY *pdbentry,
|
||||
const char *newRecordName, int overWriteOK);
|
||||
|
||||
|
@ -11,6 +11,7 @@ variable(asCaDebug,int)
|
||||
|
||||
# Static database access variables
|
||||
variable(dbRecordsOnceOnly,int)
|
||||
variable(dbRecordsAbcSorted,int)
|
||||
variable(dbBptNotMonotonic,int)
|
||||
variable(dbQuietMacroWarnings,int)
|
||||
|
||||
|
@ -26,6 +26,7 @@
|
||||
#include "epicsEvent.h"
|
||||
#include "epicsMutex.h"
|
||||
#include "epicsStdio.h"
|
||||
#include "epicsString.h"
|
||||
#include "epicsThread.h"
|
||||
#include "epicsTime.h"
|
||||
#include "errlog.h"
|
||||
@ -663,7 +664,7 @@ static void read_reply ( void *pArg, struct dbChannel *dbch,
|
||||
static int read_action ( caHdrLargeArray *mp, void *pPayloadIn, struct client *pClient )
|
||||
{
|
||||
struct channel_in_use *pciu = MPTOPCIU ( mp );
|
||||
const int readAccess = asCheckGet ( pciu->asClientPVT );
|
||||
int readAccess;
|
||||
ca_uint32_t payloadSize;
|
||||
void *pPayload;
|
||||
int status;
|
||||
@ -675,6 +676,7 @@ static int read_action ( caHdrLargeArray *mp, void *pPayloadIn, struct client *p
|
||||
logBadId ( pClient, mp, 0 );
|
||||
return RSRV_ERROR;
|
||||
}
|
||||
readAccess = asCheckGet ( pciu->asClientPVT );
|
||||
|
||||
SEND_LOCK ( pClient );
|
||||
|
||||
@ -748,7 +750,7 @@ static int read_action ( caHdrLargeArray *mp, void *pPayloadIn, struct client *p
|
||||
*/
|
||||
if ( mp->m_dataType == DBR_STRING && mp->m_count == 1 ) {
|
||||
char * pStr = (char *) pPayload;
|
||||
size_t strcnt = strlen ( pStr );
|
||||
size_t strcnt = epicsStrnLen( pStr, payloadSize );
|
||||
if ( strcnt < payloadSize ) {
|
||||
payloadSize = ( ca_uint32_t ) ( strcnt + 1u );
|
||||
}
|
||||
@ -883,7 +885,7 @@ static int write_action ( caHdrLargeArray *mp,
|
||||
static int host_name_action ( caHdrLargeArray *mp, void *pPayload,
|
||||
struct client *client )
|
||||
{
|
||||
size_t size;
|
||||
ca_uint32_t size;
|
||||
char *pName;
|
||||
char *pMalloc;
|
||||
int chanCount;
|
||||
@ -907,9 +909,9 @@ static int host_name_action ( caHdrLargeArray *mp, void *pPayload,
|
||||
}
|
||||
|
||||
pName = (char *) pPayload;
|
||||
size = strlen(pName)+1;
|
||||
if (size > 512) {
|
||||
log_header ( "bad (very long) host name",
|
||||
size = epicsStrnLen(pName, mp->m_postsize)+1;
|
||||
if (size > 512 || size > mp->m_postsize) {
|
||||
log_header ( "bad (very long) host name",
|
||||
client, mp, pPayload, 0 );
|
||||
SEND_LOCK(client);
|
||||
send_err(
|
||||
@ -962,7 +964,7 @@ static int host_name_action ( caHdrLargeArray *mp, void *pPayload,
|
||||
static int client_name_action ( caHdrLargeArray *mp, void *pPayload,
|
||||
struct client *client )
|
||||
{
|
||||
size_t size;
|
||||
ca_uint32_t size;
|
||||
char *pName;
|
||||
char *pMalloc;
|
||||
int chanCount;
|
||||
@ -986,9 +988,9 @@ static int client_name_action ( caHdrLargeArray *mp, void *pPayload,
|
||||
}
|
||||
|
||||
pName = (char *) pPayload;
|
||||
size = strlen(pName)+1;
|
||||
if (size > 512) {
|
||||
log_header ("a very long user name was specified",
|
||||
size = epicsStrnLen(pName, mp->m_postsize)+1;
|
||||
if (size > 512 || size > mp->m_postsize) {
|
||||
log_header ("a very long user name was specified",
|
||||
client, mp, pPayload, 0);
|
||||
SEND_LOCK(client);
|
||||
send_err(
|
||||
|
@ -232,16 +232,17 @@ SOCKET* rsrv_grab_tcp(unsigned short *port)
|
||||
ok = 0;
|
||||
break;
|
||||
}
|
||||
/* if SOCK_EADDRINUSE then try again with a different port number.
|
||||
* otherwise, fail hard
|
||||
/* if SOCK_EADDRINUSE or SOCK_EACCES try again with a different
|
||||
* port number, otherwise fail hard.
|
||||
*/
|
||||
if(errcode!=SOCK_EADDRINUSE) {
|
||||
if (errcode != SOCK_EADDRINUSE &&
|
||||
errcode != SOCK_EACCES) {
|
||||
char name[40];
|
||||
char sockErrBuf[64];
|
||||
epicsSocketConvertErrnoToString (
|
||||
sockErrBuf, sizeof ( sockErrBuf ) );
|
||||
ipAddrToDottedIP(&scratch.ia, name, sizeof(name));
|
||||
cantProceed( "CAS: Socket bind %s error was \"%s\"\n",
|
||||
cantProceed( "CAS: Socket bind %s error was %s\n",
|
||||
name, sockErrBuf );
|
||||
}
|
||||
ok = 0;
|
||||
@ -852,7 +853,7 @@ static void log_one_client (struct client *client, unsigned level)
|
||||
printf(
|
||||
"\tUnprocessed request bytes = %u, Undelivered response bytes = %u\n",
|
||||
client->recv.cnt - client->recv.stk,
|
||||
client->send.stk );
|
||||
client->send.stk );
|
||||
printf(
|
||||
"\tState = %s%s%s\n",
|
||||
state[client->disconnect?1:0],
|
||||
|
@ -2,7 +2,7 @@
|
||||
* Copyright (c) 2002 The University of Saskatchewan
|
||||
* EPICS BASE Versions 3.13.7
|
||||
* and higher are distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
* in file LICENSE that is included with this distribution.
|
||||
\*************************************************************************/
|
||||
/*
|
||||
* RTEMS startup task for EPICS
|
||||
@ -287,27 +287,27 @@ initialize_remote_filesystem(char **argv, int hasLocalFilesystem)
|
||||
mount_point[l] = '\0';
|
||||
argv[1] = rtems_bsdnet_bootp_cmdline;
|
||||
/*
|
||||
* Its probably common to embed the mount point in the server
|
||||
* Its probably common to embed the mount point in the server
|
||||
* name so, when this is occurring, dont clobber the mount point
|
||||
* by appending the first node from the command path. This allows
|
||||
* the mount point to be a different path then the server's mount
|
||||
* the mount point to be a different path then the server's mount
|
||||
* path.
|
||||
*
|
||||
* This allows for example a line similar to as follows the DHCP
|
||||
* This allows for example a line similar to as follows the DHCP
|
||||
* configuration file.
|
||||
*
|
||||
* server-name "159.233@192.168.0.123:/vol/vol0/bootRTEMS";
|
||||
*/
|
||||
if ( server_name ) {
|
||||
const size_t allocSize = strlen ( server_name ) + 2;
|
||||
char * const pServerName = mustMalloc( allocSize,
|
||||
char * const pServerName = mustMalloc( allocSize,
|
||||
"NFS mount paths");
|
||||
char * const pServerPath = mustMalloc ( allocSize,
|
||||
char * const pServerPath = mustMalloc ( allocSize,
|
||||
"NFS mount paths");
|
||||
const int scanfStatus = sscanf (
|
||||
server_name,
|
||||
"%[^:] : / %s",
|
||||
pServerName,
|
||||
const int scanfStatus = sscanf (
|
||||
server_name,
|
||||
"%[^:] : / %s",
|
||||
pServerName,
|
||||
pServerPath + 1u );
|
||||
if ( scanfStatus == 2 ) {
|
||||
pServerPath[0u]= '/';
|
||||
@ -535,7 +535,7 @@ exitHandler(void)
|
||||
rtems_task
|
||||
Init (rtems_task_argument ignored)
|
||||
{
|
||||
int i;
|
||||
int result;
|
||||
char *argv[3] = { NULL, NULL, NULL };
|
||||
char *cp;
|
||||
rtems_task_priority newpri;
|
||||
@ -661,8 +661,8 @@ Init (rtems_task_argument ignored)
|
||||
atexit(exitHandler);
|
||||
errlogFlush();
|
||||
printf ("***** Starting EPICS application *****\n");
|
||||
i = main ((sizeof argv / sizeof argv[0]) - 1, argv);
|
||||
result = main ((sizeof argv / sizeof argv[0]) - 1, argv);
|
||||
printf ("***** IOC application terminating *****\n");
|
||||
epicsThreadSleep(1.0);
|
||||
epicsExit(0);
|
||||
epicsExit(result);
|
||||
}
|
||||
|
@ -68,7 +68,7 @@ INP{link} {
|
||||
\n { line_num ++;}
|
||||
|
||||
. {
|
||||
char message[20];
|
||||
char message[40];
|
||||
YY_BUFFER_STATE *dummy=0;
|
||||
|
||||
sprintf(message,"invalid character '%c'",yytext[0]);
|
||||
|
@ -10,3 +10,4 @@
|
||||
SRC_DIRS += $(LIBCOM)/ellLib
|
||||
INC += ellLib.h
|
||||
Com_SRCS += ellLib.c
|
||||
Com_SRCS += ellSort.c
|
||||
|
@ -56,6 +56,8 @@ epicsShareFunc void ellInsert (ELLLIST *plist, ELLNODE *pPrev, ELLNODE *pNode);
|
||||
epicsShareFunc ELLNODE * ellNth (ELLLIST *pList, int nodeNum);
|
||||
epicsShareFunc ELLNODE * ellNStep (ELLNODE *pNode, int nStep);
|
||||
epicsShareFunc int ellFind (ELLLIST *pList, ELLNODE *pNode);
|
||||
typedef int (*pListCmp)(const ELLNODE* A, const ELLNODE* B);
|
||||
epicsShareFunc void ellSortStable(ELLLIST *pList, pListCmp);
|
||||
epicsShareFunc void ellFree2 (ELLLIST *pList, FREEFUNC freeFunc);
|
||||
epicsShareFunc void ellVerify (ELLLIST *pList);
|
||||
|
||||
|
83
src/libCom/ellLib/ellSort.c
Normal file
83
src/libCom/ellLib/ellSort.c
Normal file
@ -0,0 +1,83 @@
|
||||
/*************************************************************************\
|
||||
* Copyright (c) 2014 Brookhaven Science Assoc., as Operator of Argonne
|
||||
* National Laboratory.
|
||||
* Copyright (c) 2016 Michael Davidsaver
|
||||
* EPICS BASE is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
\*************************************************************************/
|
||||
|
||||
/*
|
||||
* Use of mergesort algorithm based on analysis by
|
||||
* http://www.chiark.greenend.org.uk/~sgtatham/algorithms/listsort.html
|
||||
*/
|
||||
#include <stdlib.h>
|
||||
|
||||
#define epicsExportSharedSymbols
|
||||
#include "epicsAssert.h"
|
||||
#include "ellLib.h"
|
||||
|
||||
static void ellMoveN(ELLLIST* pTo, ELLLIST* pFrom, int count )
|
||||
{
|
||||
for(;count && ellCount(pFrom); count--) {
|
||||
ELLNODE *node = ellGet(pFrom);
|
||||
ellAdd(pTo, node);
|
||||
}
|
||||
}
|
||||
|
||||
/* Stable (MergeSort) to given list.
|
||||
* The comparison function cmp(A,B) is expected
|
||||
* to return -1 for A<B, 0 for A==B, and 1 for A>B.
|
||||
*/
|
||||
void ellSortStable(ELLLIST *pList, pListCmp cmp)
|
||||
{
|
||||
ELLLIST INP, P, Q;
|
||||
size_t insize = 1; /* initial sub-list size */
|
||||
if(ellCount(pList)<=1)
|
||||
return;
|
||||
|
||||
ellInit(&INP);
|
||||
ellInit(&P);
|
||||
ellInit(&Q);
|
||||
|
||||
/* Process is to iteratively sort
|
||||
* a sequence of sub-lists of size 'insize'
|
||||
*/
|
||||
|
||||
while(insize < ellCount(pList)) {
|
||||
|
||||
assert(ellCount(&INP)==0);
|
||||
|
||||
/* shift previous results to inputs */
|
||||
ellConcat(&INP, pList);
|
||||
|
||||
while(ellCount(&INP))
|
||||
{
|
||||
ELLNODE *p, *q;
|
||||
|
||||
/* Pull out the next pair of sub-lists */
|
||||
ellMoveN(&Q, &INP, insize);
|
||||
ellMoveN(&P, &INP, insize);
|
||||
|
||||
/* merge these sub-lists */
|
||||
while((p=ellFirst(&P)) && (q=ellFirst(&Q)))
|
||||
{
|
||||
if((*cmp)(p,q) < 0) {
|
||||
ellAdd(pList, ellGet(&P));
|
||||
} else {
|
||||
ellAdd(pList, ellGet(&Q));
|
||||
}
|
||||
}
|
||||
|
||||
/* concatenate any remaining to result */
|
||||
if(ellFirst(&P))
|
||||
ellConcat(pList, &P);
|
||||
else if(ellFirst(&Q))
|
||||
ellConcat(pList, &Q);
|
||||
|
||||
assert(!ellFirst(&P) && !ellFirst(&Q));
|
||||
}
|
||||
|
||||
insize *= 2;
|
||||
}
|
||||
|
||||
}
|
@ -559,6 +559,7 @@ iocshBody (const char *pathname, const char *commandLine, const char *macros)
|
||||
|
||||
if (macros) {
|
||||
if (macParseDefns(NULL, macros, &defines) < 0) {
|
||||
free(redirects);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
@ -571,6 +572,7 @@ iocshBody (const char *pathname, const char *commandLine, const char *macros)
|
||||
if (handle == NULL) {
|
||||
if (macCreateHandle(&handle, pairs)) {
|
||||
errlogMessage("iocsh: macCreateHandle failed.");
|
||||
free(redirects);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
@ -84,131 +84,132 @@ static int sighupPipe[2];
|
||||
|
||||
/*
|
||||
*
|
||||
* main()
|
||||
* main()
|
||||
*
|
||||
*/
|
||||
int main(void)
|
||||
{
|
||||
struct sockaddr_in serverAddr; /* server's address */
|
||||
struct timeval timeout;
|
||||
int status;
|
||||
struct ioc_log_server *pserver;
|
||||
struct sockaddr_in serverAddr; /* server's address */
|
||||
struct timeval timeout;
|
||||
int status;
|
||||
struct ioc_log_server *pserver;
|
||||
|
||||
osiSockIoctl_t optval;
|
||||
osiSockIoctl_t optval;
|
||||
|
||||
status = getConfig();
|
||||
if(status<0){
|
||||
fprintf(stderr, "iocLogServer: EPICS environment underspecified\n");
|
||||
fprintf(stderr, "iocLogServer: failed to initialize\n");
|
||||
return IOCLS_ERROR;
|
||||
}
|
||||
status = getConfig();
|
||||
if (status<0) {
|
||||
fprintf(stderr, "iocLogServer: EPICS environment underspecified\n");
|
||||
fprintf(stderr, "iocLogServer: failed to initialize\n");
|
||||
return IOCLS_ERROR;
|
||||
}
|
||||
|
||||
pserver = (struct ioc_log_server *)
|
||||
calloc(1, sizeof *pserver);
|
||||
if(!pserver){
|
||||
fprintf(stderr, "iocLogServer: %s\n", strerror(errno));
|
||||
return IOCLS_ERROR;
|
||||
}
|
||||
pserver = (struct ioc_log_server *)
|
||||
calloc(1, sizeof *pserver);
|
||||
if (!pserver) {
|
||||
fprintf(stderr, "iocLogServer: %s\n", strerror(errno));
|
||||
return IOCLS_ERROR;
|
||||
}
|
||||
|
||||
pserver->pfdctx = (void *) fdmgr_init();
|
||||
if(!pserver->pfdctx){
|
||||
fprintf(stderr, "iocLogServer: %s\n", strerror(errno));
|
||||
return IOCLS_ERROR;
|
||||
}
|
||||
pserver->pfdctx = (void *) fdmgr_init();
|
||||
if (!pserver->pfdctx) {
|
||||
fprintf(stderr, "iocLogServer: %s\n", strerror(errno));
|
||||
return IOCLS_ERROR;
|
||||
}
|
||||
|
||||
/*
|
||||
* Open the socket. Use ARPA Internet address format and stream
|
||||
* sockets. Format described in <sys/socket.h>.
|
||||
*/
|
||||
pserver->sock = epicsSocketCreate(AF_INET, SOCK_STREAM, 0);
|
||||
if (pserver->sock==INVALID_SOCKET) {
|
||||
/*
|
||||
* Open the socket. Use ARPA Internet address format and stream
|
||||
* sockets. Format described in <sys/socket.h>.
|
||||
*/
|
||||
pserver->sock = epicsSocketCreate(AF_INET, SOCK_STREAM, 0);
|
||||
if (pserver->sock == INVALID_SOCKET) {
|
||||
char sockErrBuf[64];
|
||||
epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) );
|
||||
fprintf(stderr, "iocLogServer: sock create err: %s\n", sockErrBuf);
|
||||
return IOCLS_ERROR;
|
||||
}
|
||||
|
||||
fprintf(stderr, "iocLogServer: sock create err: %s\n", sockErrBuf);
|
||||
free(pserver);
|
||||
return IOCLS_ERROR;
|
||||
}
|
||||
|
||||
epicsSocketEnableAddressReuseDuringTimeWaitState ( pserver->sock );
|
||||
|
||||
/* Zero the sock_addr structure */
|
||||
memset((void *)&serverAddr, 0, sizeof serverAddr);
|
||||
serverAddr.sin_family = AF_INET;
|
||||
serverAddr.sin_port = htons(ioc_log_port);
|
||||
/* Zero the sock_addr structure */
|
||||
memset((void *)&serverAddr, 0, sizeof serverAddr);
|
||||
serverAddr.sin_family = AF_INET;
|
||||
serverAddr.sin_port = htons(ioc_log_port);
|
||||
|
||||
/* get server's Internet address */
|
||||
status = bind ( pserver->sock,
|
||||
(struct sockaddr *)&serverAddr,
|
||||
sizeof (serverAddr) );
|
||||
if (status<0) {
|
||||
/* get server's Internet address */
|
||||
status = bind ( pserver->sock,
|
||||
(struct sockaddr *)&serverAddr,
|
||||
sizeof (serverAddr) );
|
||||
if (status < 0) {
|
||||
char sockErrBuf[64];
|
||||
epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) );
|
||||
fprintf(stderr, "iocLogServer: bind err: %s\n", sockErrBuf );
|
||||
fprintf (stderr,
|
||||
"iocLogServer: a server is already installed on port %u?\n",
|
||||
(unsigned)ioc_log_port);
|
||||
return IOCLS_ERROR;
|
||||
}
|
||||
fprintf(stderr, "iocLogServer: bind err: %s\n", sockErrBuf );
|
||||
fprintf (stderr,
|
||||
"iocLogServer: a server is already installed on port %u?\n",
|
||||
(unsigned)ioc_log_port);
|
||||
return IOCLS_ERROR;
|
||||
}
|
||||
|
||||
/* listen and accept new connections */
|
||||
status = listen(pserver->sock, 10);
|
||||
if (status<0) {
|
||||
/* listen and accept new connections */
|
||||
status = listen(pserver->sock, 10);
|
||||
if (status < 0) {
|
||||
char sockErrBuf[64];
|
||||
epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) );
|
||||
fprintf(stderr, "iocLogServer: listen err %s\n", sockErrBuf);
|
||||
return IOCLS_ERROR;
|
||||
}
|
||||
fprintf(stderr, "iocLogServer: listen err %s\n", sockErrBuf);
|
||||
return IOCLS_ERROR;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set non blocking IO
|
||||
* to prevent dead locks
|
||||
*/
|
||||
optval = TRUE;
|
||||
status = socket_ioctl(
|
||||
pserver->sock,
|
||||
FIONBIO,
|
||||
&optval);
|
||||
if(status<0){
|
||||
/*
|
||||
* Set non blocking IO
|
||||
* to prevent dead locks
|
||||
*/
|
||||
optval = TRUE;
|
||||
status = socket_ioctl(
|
||||
pserver->sock,
|
||||
FIONBIO,
|
||||
&optval);
|
||||
if (status < 0){
|
||||
char sockErrBuf[64];
|
||||
epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) );
|
||||
fprintf(stderr, "iocLogServer: ioctl FIONBIO err %s\n", sockErrBuf);
|
||||
return IOCLS_ERROR;
|
||||
}
|
||||
fprintf(stderr, "iocLogServer: ioctl FIONBIO err %s\n", sockErrBuf);
|
||||
return IOCLS_ERROR;
|
||||
}
|
||||
|
||||
# ifdef UNIX
|
||||
status = setupSIGHUP(pserver);
|
||||
if (status<0) {
|
||||
return IOCLS_ERROR;
|
||||
}
|
||||
# endif
|
||||
# ifdef UNIX
|
||||
status = setupSIGHUP(pserver);
|
||||
if (status < 0) {
|
||||
return IOCLS_ERROR;
|
||||
}
|
||||
# endif
|
||||
|
||||
status = openLogFile(pserver);
|
||||
if (status<0) {
|
||||
fprintf(stderr,
|
||||
"File access problems to `%s' because `%s'\n",
|
||||
ioc_log_file_name,
|
||||
strerror(errno));
|
||||
return IOCLS_ERROR;
|
||||
}
|
||||
status = openLogFile(pserver);
|
||||
if (status < 0) {
|
||||
fprintf(stderr,
|
||||
"File access problems to `%s' because `%s'\n",
|
||||
ioc_log_file_name,
|
||||
strerror(errno));
|
||||
return IOCLS_ERROR;
|
||||
}
|
||||
|
||||
status = fdmgr_add_callback(
|
||||
pserver->pfdctx,
|
||||
pserver->sock,
|
||||
fdi_read,
|
||||
acceptNewClient,
|
||||
pserver);
|
||||
if(status<0){
|
||||
fprintf(stderr,
|
||||
"iocLogServer: failed to add read callback\n");
|
||||
return IOCLS_ERROR;
|
||||
}
|
||||
status = fdmgr_add_callback(
|
||||
pserver->pfdctx,
|
||||
pserver->sock,
|
||||
fdi_read,
|
||||
acceptNewClient,
|
||||
pserver);
|
||||
if (status < 0) {
|
||||
fprintf(stderr,
|
||||
"iocLogServer: failed to add read callback\n");
|
||||
return IOCLS_ERROR;
|
||||
}
|
||||
|
||||
|
||||
while(TRUE){
|
||||
timeout.tv_sec = 60; /* 1 min */
|
||||
timeout.tv_usec = 0;
|
||||
fdmgr_pend_event(pserver->pfdctx, &timeout);
|
||||
fflush(pserver->poutfile);
|
||||
}
|
||||
while (TRUE) {
|
||||
timeout.tv_sec = 60; /* 1 min */
|
||||
timeout.tv_usec = 0;
|
||||
fdmgr_pend_event(pserver->pfdctx, &timeout);
|
||||
fflush(pserver->poutfile);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@ -964,6 +965,7 @@ static int getDirectory(void)
|
||||
"Problem reading o/p from `%s' because `%s'\n",
|
||||
ioc_log_file_command,
|
||||
strerror(errno));
|
||||
(void) pclose(pipe);
|
||||
return IOCLS_ERROR;
|
||||
}
|
||||
(void) pclose(pipe);
|
||||
|
@ -256,6 +256,18 @@ int epicsStrPrintEscaped(FILE *fp, const char *s, size_t len)
|
||||
return nout;
|
||||
}
|
||||
|
||||
/* Until Base requires POSIX 2008 we must provide our own implementation */
|
||||
size_t epicsStrnLen(const char *s, size_t maxlen)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
for (i=0; i<maxlen; i++) {
|
||||
if(s[i]=='\0')
|
||||
return i;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
int epicsStrGlobMatch(const char *str, const char *pattern)
|
||||
{
|
||||
const char *cp = NULL, *mp = NULL;
|
||||
|
@ -33,6 +33,7 @@ epicsShareFunc int epicsStrnCaseCmp(const char *s1, const char *s2, size_t len);
|
||||
epicsShareFunc char * epicsStrDup(const char *s);
|
||||
epicsShareFunc int epicsStrPrintEscaped(FILE *fp, const char *s, size_t n);
|
||||
#define epicsStrSnPrintEscaped epicsStrnEscapedFromRaw
|
||||
epicsShareFunc size_t epicsStrnLen(const char *s, size_t maxlen);
|
||||
epicsShareFunc int epicsStrGlobMatch(const char *str, const char *pattern);
|
||||
epicsShareFunc char * epicsStrtok_r(char *s, const char *delim, char **lasts);
|
||||
epicsShareFunc unsigned int epicsStrHash(const char *str, unsigned int seed);
|
||||
|
@ -380,7 +380,7 @@ static gtProvider * findProvider(ELLLIST *plist, epicsMutexId lock,
|
||||
|
||||
for (ptp = (gtProvider *)ellFirst(plist);
|
||||
ptp; ptp = (gtProvider *)ellNext(&ptp->node)) {
|
||||
if (ptp->priority == ptp->priority &&
|
||||
if (ptp->priority == priority &&
|
||||
!strcmp(ptp->name, name))
|
||||
break;
|
||||
}
|
||||
|
@ -6,15 +6,15 @@
|
||||
* Copyright (c) 2007 UChicago Argonne LLC, as Operator of Argonne
|
||||
* National Laboratory.
|
||||
* EPICS BASE is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
* in file LICENSE that is included with this distribution.
|
||||
\*************************************************************************/
|
||||
/* epicsTime.cpp */
|
||||
/* Author Jeffrey O. Hill */
|
||||
|
||||
// Notes:
|
||||
// 1) The epicsTime::nSec field is not public and so it could be
|
||||
// changed to work more like the fractional seconds field in the NTP time
|
||||
// stamp. That would significantly improve the precision of epicsTime on
|
||||
// 1) The epicsTime::nSec field is not public and so it could be
|
||||
// changed to work more like the fractional seconds field in the NTP time
|
||||
// stamp. That would significantly improve the precision of epicsTime on
|
||||
// 64 bit architectures.
|
||||
//
|
||||
|
||||
@ -26,7 +26,7 @@
|
||||
#include <string.h>
|
||||
#include <limits.h>
|
||||
#include <float.h>
|
||||
#include <string> // vxWorks 6.0 requires this include
|
||||
#include <string> // vxWorks 6.0 requires this include
|
||||
|
||||
#define epicsExportSharedSymbols
|
||||
#include "locationException.h"
|
||||
@ -37,7 +37,7 @@
|
||||
#include "osiSock.h" /* pull in struct timeval */
|
||||
#include "epicsStdio.h"
|
||||
|
||||
static const char pEpicsTimeVersion[] =
|
||||
static const char pEpicsTimeVersion[] =
|
||||
"@(#) " EPICS_VERSION_STRING ", Common Utilities Library " __DATE__;
|
||||
|
||||
//
|
||||
@ -60,11 +60,13 @@ static const unsigned long NTP_TIME_AT_EPICS_EPOCH =
|
||||
//
|
||||
// epicsTime (const unsigned long secIn, const unsigned long nSecIn)
|
||||
//
|
||||
inline epicsTime::epicsTime (const unsigned long secIn,
|
||||
const unsigned long nSecIn) :
|
||||
secPastEpoch ( nSecIn / nSecPerSec + secIn ),
|
||||
nSec ( nSecIn % nSecPerSec )
|
||||
inline epicsTime::epicsTime (const unsigned long secIn, const unsigned long nSecIn) :
|
||||
secPastEpoch ( secIn ), nSec ( nSecIn )
|
||||
{
|
||||
if (nSecIn >= nSecPerSec) {
|
||||
this->secPastEpoch += nSecIn / nSecPerSec;
|
||||
this->nSec = nSecIn % nSecPerSec;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
@ -93,7 +95,7 @@ epicsTimeLoadTimeInit::epicsTimeLoadTimeInit ()
|
||||
* the ANSI epoch (1/1/1970 00:00:00UTC)
|
||||
* Convert this offset into time_t units, however this must not be
|
||||
* calculated using local time (i.e. using mktime() or similar), since
|
||||
* in the UK the ANSI Epoch had daylight saving time in effect, and
|
||||
* in the UK the ANSI Epoch had daylight saving time in effect, and
|
||||
* the value calculated would be 3600 seconds wrong.*/
|
||||
this->epicsEpochOffset =
|
||||
(double) POSIX_TIME_AT_EPICS_EPOCH / this->time_tSecPerTick;
|
||||
@ -103,7 +105,7 @@ epicsTimeLoadTimeInit::epicsTimeLoadTimeInit ()
|
||||
this->epicsEpochOffset >= 0) {
|
||||
// We can use simpler code on Posix-compliant systems
|
||||
this->useDiffTimeOptimization = true;
|
||||
this->epicsEpochOffsetAsAnUnsignedLong =
|
||||
this->epicsEpochOffsetAsAnUnsignedLong =
|
||||
static_cast<unsigned long>(this->epicsEpochOffset);
|
||||
} else {
|
||||
// Forced to use the slower but correct code
|
||||
@ -113,47 +115,26 @@ epicsTimeLoadTimeInit::epicsTimeLoadTimeInit ()
|
||||
}
|
||||
|
||||
//
|
||||
// epicsTime::addNanoSec ()
|
||||
// private epicsTime::addNanoSec ()
|
||||
//
|
||||
// The nano-second field of several of the the UNIX time stamp formats
|
||||
// field is stored in the C type "long".
|
||||
// Most formats keep the nSec value as an unsigned long, so are +ve.
|
||||
// struct timeval's tv_usec may be -1, but I think that means error,
|
||||
// so this private method never needs to handle -ve offsets.
|
||||
//
|
||||
void epicsTime :: addNanoSec ( long nSecAdj )
|
||||
{
|
||||
//
|
||||
// After optimizing this function we now have a larger code which
|
||||
// uses only unsigned integer, and not floating point, arithmetic.
|
||||
// This change benefits embedded CPU's lacking a floating point
|
||||
// co-processor at the expense of some additional code to maintain.
|
||||
//
|
||||
// We hope that all CPU's we run on provide at least an integer
|
||||
// divide instruction which should enable this implementation
|
||||
// to be more efficient than implementations based on branching;
|
||||
// this is presuming that we will run on pipelined architectures.
|
||||
//
|
||||
// Overflow and underflow is expected; in the future we might
|
||||
// operate close to, the modulo of, the EPICS epic.
|
||||
//
|
||||
// We are depending on the normalize operation in the private
|
||||
// constructor used below.
|
||||
//
|
||||
// joh 11-04-2012
|
||||
//
|
||||
if ( nSecAdj >= 0 ) {
|
||||
const unsigned long nSecPlus =
|
||||
static_cast <unsigned long> ( nSecAdj );
|
||||
const unsigned long nSecPlusAdj = nSecPlus % nSecPerSec;
|
||||
const unsigned long secPlusAdj = nSecPlus / nSecPerSec;
|
||||
*this = epicsTime ( this->secPastEpoch+secPlusAdj,
|
||||
this->nSec+nSecPlusAdj );
|
||||
if (nSecAdj <= 0)
|
||||
return;
|
||||
|
||||
if (static_cast<unsigned long>(nSecAdj) >= nSecPerSec) {
|
||||
this->secPastEpoch += nSecAdj / nSecPerSec;
|
||||
nSecAdj %= nSecPerSec;
|
||||
}
|
||||
else {
|
||||
const unsigned long nSecMinus =
|
||||
static_cast <unsigned long> ( -nSecAdj );
|
||||
const unsigned long nSecMinusAdj = nSecMinus % nSecPerSec;
|
||||
const unsigned long secMinusAdj = nSecMinus / nSecPerSec;
|
||||
*this = epicsTime ( this->secPastEpoch - secMinusAdj - 1u,
|
||||
this->nSec + nSecPerSec - nSecMinusAdj );
|
||||
|
||||
this->nSec += nSecAdj; // Can't overflow
|
||||
if (this->nSec >= nSecPerSec) {
|
||||
this->secPastEpoch++;
|
||||
this->nSec -= nSecPerSec;
|
||||
}
|
||||
}
|
||||
|
||||
@ -166,11 +147,11 @@ epicsTime::epicsTime ( const time_t_wrapper & ansiTimeTicks )
|
||||
static epicsTimeLoadTimeInit & lti = * new epicsTimeLoadTimeInit ();
|
||||
|
||||
//
|
||||
// try to directly map time_t into an unsigned long integer because this is
|
||||
// try to directly map time_t into an unsigned long integer because this is
|
||||
// faster on systems w/o hardware floating point and a simple integer type time_t.
|
||||
//
|
||||
if ( lti.useDiffTimeOptimization ) {
|
||||
// LONG_MAX is used here and not ULONG_MAX because some systems (linux)
|
||||
// LONG_MAX is used here and not ULONG_MAX because some systems (linux)
|
||||
// still store time_t as a long.
|
||||
if ( ansiTimeTicks.ts > 0 && ansiTimeTicks.ts <= LONG_MAX ) {
|
||||
unsigned long ticks = static_cast < unsigned long > ( ansiTimeTicks.ts );
|
||||
@ -186,8 +167,8 @@ epicsTime::epicsTime ( const time_t_wrapper & ansiTimeTicks )
|
||||
}
|
||||
|
||||
//
|
||||
// otherwise map time_t, which ANSI C and POSIX define as any arithmetic type,
|
||||
// into type double
|
||||
// otherwise map time_t, which ANSI C and POSIX define as any arithmetic type,
|
||||
// into type double
|
||||
//
|
||||
double sec = ansiTimeTicks.ts * lti.time_tSecPerTick - lti.epicsEpochOffset;
|
||||
|
||||
@ -209,14 +190,14 @@ epicsTime::epicsTime ( const time_t_wrapper & ansiTimeTicks )
|
||||
this->nSec = static_cast <unsigned long> ( ( sec-this->secPastEpoch ) * nSecPerSec );
|
||||
}
|
||||
|
||||
epicsTime::epicsTime (const epicsTimeStamp &ts)
|
||||
epicsTime::epicsTime (const epicsTimeStamp &ts)
|
||||
{
|
||||
if ( ts.nsec < nSecPerSec ) {
|
||||
this->secPastEpoch = ts.secPastEpoch;
|
||||
this->nSec = ts.nsec;
|
||||
}
|
||||
else {
|
||||
throw std::logic_error (
|
||||
throw std::logic_error (
|
||||
"epicsTimeStamp has overflow in nano-seconds field" );
|
||||
}
|
||||
}
|
||||
@ -262,7 +243,7 @@ epicsTime::operator time_t_wrapper () const
|
||||
return wrap;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// map type double into time_t which ansi C defines as some arithmetic type
|
||||
//
|
||||
@ -317,20 +298,82 @@ epicsTime::operator gm_tm_nano_sec () const
|
||||
//
|
||||
epicsTime::epicsTime (const local_tm_nano_sec &tm)
|
||||
{
|
||||
static const time_t mktimeFailure = static_cast <time_t> (-1);
|
||||
time_t_wrapper ansiTimeTicks;
|
||||
struct tm tmp = tm.ansi_tm;
|
||||
time_t_wrapper ansiTimeTicks = { mktime (&tmp) };
|
||||
|
||||
ansiTimeTicks.ts = mktime (&tmp);
|
||||
if (ansiTimeTicks.ts == mktimeFailure) {
|
||||
static const time_t mktimeError = static_cast <time_t> (-1);
|
||||
if (ansiTimeTicks.ts == mktimeError) {
|
||||
throwWithLocation ( formatProblemWithStructTM () );
|
||||
}
|
||||
|
||||
*this = epicsTime (ansiTimeTicks);
|
||||
*this = epicsTime(ansiTimeTicks);
|
||||
this->addNanoSec(tm.nSec);
|
||||
}
|
||||
|
||||
unsigned long nSecAdj = tm.nSec % nSecPerSec;
|
||||
unsigned long secAdj = tm.nSec / nSecPerSec;
|
||||
*this = epicsTime ( this->secPastEpoch+secAdj, this->nSec+nSecAdj );
|
||||
//
|
||||
// epicsTime (const gm_tm_nano_sec &tm)
|
||||
//
|
||||
|
||||
// do conversion avoiding the timezone mechanism
|
||||
static inline int is_leap(int year)
|
||||
{
|
||||
if (year % 400 == 0)
|
||||
return 1;
|
||||
if (year % 100 == 0)
|
||||
return 0;
|
||||
if (year % 4 == 0)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int days_from_0(int year)
|
||||
{
|
||||
year--;
|
||||
return 365 * year + (year / 400) - (year / 100) + (year / 4);
|
||||
}
|
||||
|
||||
static inline int days_from_1970(int year)
|
||||
{
|
||||
static const int days_from_0_to_1970 = days_from_0(1970);
|
||||
return days_from_0(year) - days_from_0_to_1970;
|
||||
}
|
||||
|
||||
static inline int days_from_1jan(int year, int month, int day)
|
||||
{
|
||||
static const int days[2][12] =
|
||||
{
|
||||
{ 0,31,59,90,120,151,181,212,243,273,304,334},
|
||||
{ 0,31,60,91,121,152,182,213,244,274,305,335}
|
||||
};
|
||||
return days[is_leap(year)][month-1] + day - 1;
|
||||
}
|
||||
|
||||
epicsTime::epicsTime (const gm_tm_nano_sec &tm)
|
||||
{
|
||||
int year = tm.ansi_tm.tm_year + 1900;
|
||||
int month = tm.ansi_tm.tm_mon;
|
||||
if (month > 11) {
|
||||
year += month / 12;
|
||||
month %= 12;
|
||||
} else if (month < 0) {
|
||||
int years_diff = (-month + 11) / 12;
|
||||
year -= years_diff;
|
||||
month += 12 * years_diff;
|
||||
}
|
||||
month++;
|
||||
|
||||
int day = tm.ansi_tm.tm_mday;
|
||||
int day_of_year = days_from_1jan(year, month, day);
|
||||
int days_since_epoch = days_from_1970(year) + day_of_year;
|
||||
|
||||
time_t_wrapper ansiTimeTicks;
|
||||
ansiTimeTicks.ts = ((days_since_epoch
|
||||
* 24 + tm.ansi_tm.tm_hour)
|
||||
* 60 + tm.ansi_tm.tm_min)
|
||||
* 60 + tm.ansi_tm.tm_sec;
|
||||
|
||||
*this = epicsTime(ansiTimeTicks);
|
||||
this->addNanoSec(tm.nSec);
|
||||
}
|
||||
|
||||
//
|
||||
@ -372,9 +415,9 @@ epicsTime::operator struct timeval () const
|
||||
// a direct assignment. On other systems I dont know that we can
|
||||
// guarantee that time_t and timeval :: tv_sec will have the
|
||||
// same epoch or have the same scaling factor to discrete seconds.
|
||||
// For example, on windows time_t changed recently to a 64 bit
|
||||
// For example, on windows time_t changed recently to a 64 bit
|
||||
// quantity but timeval is still a long. That can cause problems
|
||||
// on 32 bit systems. So technically, we should have an os
|
||||
// on 32 bit systems. So technically, we should have an os
|
||||
// dependent conversion between time_t and timeval :: tv_sec?
|
||||
ts.tv_sec = ansiTimeTicks.ts;
|
||||
ts.tv_usec = static_cast < long > ( this->nSec / nSecPerUSec );
|
||||
@ -391,9 +434,9 @@ epicsTime::epicsTime (const struct timeval &ts)
|
||||
// a direct assignment. On other systems I dont know that we can
|
||||
// guarantee that time_t and timeval :: tv_sec will have the
|
||||
// same epoch or have the same scaling factor to discrete seconds.
|
||||
// For example, on windows time_t changed recently to a 64 bit
|
||||
// For example, on windows time_t changed recently to a 64 bit
|
||||
// quantity but timeval is still a long. That can cause problems
|
||||
// on 32 bit systems. So technically, we should have an os
|
||||
// on 32 bit systems. So technically, we should have an os
|
||||
// dependent conversion between time_t and timeval :: tv_sec?
|
||||
ansiTimeTicks.ts = ts.tv_sec;
|
||||
*this = epicsTime (ansiTimeTicks);
|
||||
@ -415,7 +458,7 @@ epicsTime::operator l_fp () const
|
||||
{
|
||||
l_fp ts;
|
||||
ts.l_ui = this->secPastEpoch + NTP_TIME_AT_EPICS_EPOCH;
|
||||
ts.l_uf = static_cast < unsigned long >
|
||||
ts.l_uf = static_cast < unsigned long >
|
||||
( ( this->nSec * NTP_FRACTION_DENOMINATOR ) / nSecPerSec );
|
||||
return ts;
|
||||
}
|
||||
@ -426,14 +469,14 @@ epicsTime::operator l_fp () const
|
||||
epicsTime::epicsTime ( const l_fp & ts )
|
||||
{
|
||||
this->secPastEpoch = ts.l_ui - NTP_TIME_AT_EPICS_EPOCH;
|
||||
this->nSec = static_cast < unsigned long >
|
||||
this->nSec = static_cast < unsigned long >
|
||||
( ( ts.l_uf / NTP_FRACTION_DENOMINATOR ) * nSecPerSec );
|
||||
}
|
||||
|
||||
epicsTime::operator epicsTimeStamp () const
|
||||
{
|
||||
if ( this->nSec >= nSecPerSec ) {
|
||||
throw std::logic_error (
|
||||
throw std::logic_error (
|
||||
"epicsTimeStamp has overflow in nano-seconds field?" );
|
||||
}
|
||||
epicsTimeStamp ts;
|
||||
@ -444,7 +487,7 @@ epicsTime::operator epicsTimeStamp () const
|
||||
// on 64 bit hosts than the original epicsTimeStamp::secPastEpoch. The
|
||||
// epicsTimeStamp::secPastEpoch is based on epicsUInt32 so that it will
|
||||
// match the original network protocol. Of course one can anticipate
|
||||
// that eventually, a epicsUInt64 based network time stamp will be
|
||||
// that eventually, a epicsUInt64 based network time stamp will be
|
||||
// introduced when 64 bit architectures are more ubiquitous.
|
||||
//
|
||||
// Truncation usually works fine here because the routines in this code
|
||||
@ -458,14 +501,14 @@ epicsTime::operator epicsTimeStamp () const
|
||||
return ts;
|
||||
}
|
||||
|
||||
// Break up a format string into "<strftime prefix>%0<nnn>f<postfix>"
|
||||
// (where <nnn> in an unsigned integer)
|
||||
// Break up a format string into "<strftime prefix>%0<nnn>f<postfix>"
|
||||
// (where <nnn> in an unsigned integer)
|
||||
// Result:
|
||||
// A) Copies a prefix which is valid for ANSI strftime into the supplied
|
||||
// buffer setting the buffer to an empty string if no prefix is present.
|
||||
// B) Indicates whether a valid "%0<n>f]" is present or not and if so
|
||||
// B) Indicates whether a valid "%0<n>f]" is present or not and if so
|
||||
// specifying its nnnn
|
||||
// C) returning a pointer to the postfix (which might be passed again
|
||||
// C) returning a pointer to the postfix (which might be passed again
|
||||
// to fracFormatFind.
|
||||
static const char * fracFormatFind (
|
||||
const char * const pFormat,
|
||||
@ -529,8 +572,8 @@ static const char * fracFormatFind (
|
||||
//
|
||||
// size_t epicsTime::strftime ()
|
||||
//
|
||||
size_t epicsTime::strftime (
|
||||
char * pBuff, size_t bufLength, const char * pFormat ) const
|
||||
size_t epicsTime::strftime (
|
||||
char * pBuff, size_t bufLength, const char * pFormat ) const
|
||||
{
|
||||
if ( bufLength == 0u ) {
|
||||
return 0u;
|
||||
@ -547,15 +590,15 @@ size_t epicsTime::strftime (
|
||||
const char * pFmt = pFormat;
|
||||
size_t bufLenLeft = bufLength;
|
||||
while ( *pFmt != '\0' && bufLenLeft > 1 ) {
|
||||
// look for "%0<n>f" at the end (used for fractional second formatting)
|
||||
// look for "%0<n>f" at the end (used for fractional second formatting)
|
||||
char strftimePrefixBuf [256];
|
||||
bool fracFmtFound;
|
||||
unsigned long fracWid = 0;
|
||||
pFmt = fracFormatFind (
|
||||
pFmt,
|
||||
pFmt,
|
||||
strftimePrefixBuf, sizeof ( strftimePrefixBuf ),
|
||||
fracFmtFound, fracWid );
|
||||
|
||||
|
||||
// nothing more in the string, then quit
|
||||
if ( ! ( strftimePrefixBuf[0] != '\0' || fracFmtFound ) ) {
|
||||
break;
|
||||
@ -657,7 +700,7 @@ void epicsTime::show ( unsigned level ) const
|
||||
if ( level > 1 ) {
|
||||
// this also suppresses the "defined, but not used"
|
||||
// warning message
|
||||
printf ( "epicsTime: revision \"%s\"\n",
|
||||
printf ( "epicsTime: revision \"%s\"\n",
|
||||
pEpicsTimeVersion );
|
||||
}
|
||||
|
||||
@ -704,10 +747,10 @@ epicsTime epicsTime::operator + (const double &rhs) const
|
||||
}
|
||||
|
||||
//
|
||||
// operator -
|
||||
// operator -
|
||||
//
|
||||
// To make this code robust during timestamp rollover events
|
||||
// time stamp differences greater than one half full scale are
|
||||
// time stamp differences greater than one half full scale are
|
||||
// interpreted as rollover situations:
|
||||
//
|
||||
// when RHS is greater than THIS:
|
||||
@ -729,7 +772,7 @@ double epicsTime::operator - (const epicsTime &rhs) const
|
||||
// so the unsigned to signed conversion is ok
|
||||
//
|
||||
if (this->nSec>=rhs.nSec) {
|
||||
nSecRes = this->nSec - rhs.nSec;
|
||||
nSecRes = this->nSec - rhs.nSec;
|
||||
}
|
||||
else {
|
||||
nSecRes = rhs.nSec - this->nSec;
|
||||
@ -746,7 +789,7 @@ double epicsTime::operator - (const epicsTime &rhs) const
|
||||
if (secRes > ULONG_MAX/2) {
|
||||
//
|
||||
// In this situation where the difference is more than
|
||||
// 68 years assume that the seconds counter has rolled
|
||||
// 68 years assume that the seconds counter has rolled
|
||||
// over and compute the "wrap around" difference
|
||||
//
|
||||
secRes = 1 + (ULONG_MAX-secRes);
|
||||
@ -761,7 +804,7 @@ double epicsTime::operator - (const epicsTime &rhs) const
|
||||
if (secRes > ULONG_MAX/2) {
|
||||
//
|
||||
// In this situation where the difference is more than
|
||||
// 68 years assume that the seconds counter has rolled
|
||||
// 68 years assume that the seconds counter has rolled
|
||||
// over and compute the "wrap around" difference
|
||||
//
|
||||
secRes = 1 + (ULONG_MAX-secRes);
|
||||
@ -770,7 +813,7 @@ double epicsTime::operator - (const epicsTime &rhs) const
|
||||
}
|
||||
}
|
||||
|
||||
return secRes + nSecRes/nSecPerSec;
|
||||
return secRes + nSecRes/nSecPerSec;
|
||||
}
|
||||
|
||||
//
|
||||
@ -791,7 +834,7 @@ bool epicsTime::operator <= (const epicsTime &rhs) const
|
||||
else {
|
||||
//
|
||||
// In this situation where the difference is more than
|
||||
// 69 years assume that the seconds counter has rolled
|
||||
// 69 years assume that the seconds counter has rolled
|
||||
// over and compute the "wrap around" result
|
||||
//
|
||||
rc = false;
|
||||
@ -808,7 +851,7 @@ bool epicsTime::operator <= (const epicsTime &rhs) const
|
||||
else {
|
||||
//
|
||||
// In this situation where the difference is more than
|
||||
// 69 years assume that the seconds counter has rolled
|
||||
// 69 years assume that the seconds counter has rolled
|
||||
// over and compute the "wrap around" result
|
||||
//
|
||||
rc = true;
|
||||
@ -843,7 +886,7 @@ bool epicsTime::operator < (const epicsTime &rhs) const
|
||||
else {
|
||||
//
|
||||
// In this situation where the difference is more than
|
||||
// 69 years assume that the seconds counter has rolled
|
||||
// 69 years assume that the seconds counter has rolled
|
||||
// over and compute the "wrap around" result
|
||||
//
|
||||
rc = false;
|
||||
@ -860,7 +903,7 @@ bool epicsTime::operator < (const epicsTime &rhs) const
|
||||
else {
|
||||
//
|
||||
// In this situation where the difference is more than
|
||||
// 69 years assume that the seconds counter has rolled
|
||||
// 69 years assume that the seconds counter has rolled
|
||||
// over and compute the "wrap around" result
|
||||
//
|
||||
rc = true;
|
||||
@ -881,7 +924,7 @@ extern "C" {
|
||||
//
|
||||
// ANSI C interface
|
||||
//
|
||||
// its too bad that these cant be implemented with inline functions
|
||||
// its too bad that these cant be implemented with inline functions
|
||||
// at least when running the GNU compiler
|
||||
//
|
||||
epicsShareFunc int epicsShareAPI epicsTimeToTime_t (time_t *pDest, const epicsTimeStamp *pSrc)
|
||||
@ -944,6 +987,19 @@ extern "C" {
|
||||
}
|
||||
return epicsTimeOK;
|
||||
}
|
||||
epicsShareFunc int epicsShareAPI epicsTimeFromGMTM (epicsTimeStamp *pDest, const struct tm *pSrc, unsigned long nSecSrc)
|
||||
{
|
||||
try {
|
||||
gm_tm_nano_sec tmns;
|
||||
tmns.ansi_tm = *pSrc;
|
||||
tmns.nSec = nSecSrc;
|
||||
*pDest = epicsTime (tmns);
|
||||
}
|
||||
catch (...) {
|
||||
return S_time_conversion;
|
||||
}
|
||||
return epicsTimeOK;
|
||||
}
|
||||
epicsShareFunc int epicsShareAPI epicsTimeToTimespec (struct timespec *pDest, const epicsTimeStamp *pSrc)
|
||||
{
|
||||
try {
|
||||
@ -1075,4 +1131,3 @@ extern "C" {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -106,10 +106,12 @@ public:
|
||||
epicsTime & operator = ( const local_tm_nano_sec & );
|
||||
|
||||
/*
|
||||
* convert to ANSI Cs "struct tm" (with nano seconds)
|
||||
* convert to and from ANSI Cs "struct tm" (with nano seconds)
|
||||
* adjusted for GM time (UTC)
|
||||
*/
|
||||
operator gm_tm_nano_sec () const;
|
||||
epicsTime ( const gm_tm_nano_sec & );
|
||||
epicsTime & operator = ( const gm_tm_nano_sec & );
|
||||
|
||||
/* convert to and from POSIX RTs "struct timespec" */
|
||||
operator struct timespec () const;
|
||||
@ -201,13 +203,15 @@ epicsShareFunc int epicsShareAPI epicsTimeToTime_t (
|
||||
epicsShareFunc int epicsShareAPI epicsTimeFromTime_t (
|
||||
epicsTimeStamp * pDest, time_t src );
|
||||
|
||||
/*convert to and from ANSI C's "struct tm" with nano seconds */
|
||||
/* convert to and from ANSI C's "struct tm" with nano seconds */
|
||||
epicsShareFunc int epicsShareAPI epicsTimeToTM (
|
||||
struct tm * pDest, unsigned long * pNSecDest, const epicsTimeStamp * pSrc );
|
||||
epicsShareFunc int epicsShareAPI epicsTimeToGMTM (
|
||||
struct tm * pDest, unsigned long * pNSecDest, const epicsTimeStamp * pSrc );
|
||||
epicsShareFunc int epicsShareAPI epicsTimeFromTM (
|
||||
epicsTimeStamp * pDest, const struct tm * pSrc, unsigned long nSecSrc );
|
||||
epicsShareFunc int epicsShareAPI epicsTimeFromGMTM (
|
||||
epicsTimeStamp * pDest, const struct tm * pSrc, unsigned long nSecSrc );
|
||||
|
||||
/* convert to and from POSIX RT's "struct timespec" */
|
||||
epicsShareFunc int epicsShareAPI epicsTimeToTimespec (
|
||||
@ -319,6 +323,11 @@ inline epicsTime & epicsTime::operator = ( const local_tm_nano_sec & rhs )
|
||||
return *this = epicsTime ( rhs );
|
||||
}
|
||||
|
||||
inline epicsTime & epicsTime::operator = ( const gm_tm_nano_sec & rhs )
|
||||
{
|
||||
return *this = epicsTime ( rhs );
|
||||
}
|
||||
|
||||
inline epicsTime & epicsTime::operator = ( const struct timespec & rhs )
|
||||
{
|
||||
*this = epicsTime ( rhs );
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*************************************************************************\
|
||||
* Copyright (c) 2002 The University of Saskatchewan
|
||||
* EPICS BASE is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
* in file LICENSE that is included with this distribution.
|
||||
\*************************************************************************/
|
||||
/*
|
||||
* Author: Eric Norum
|
||||
@ -38,6 +38,7 @@ typedef socklen_t osiSocklen_t;
|
||||
#define SOCK_ENOBUFS ENOBUFS
|
||||
#define SOCK_ECONNRESET ECONNRESET
|
||||
#define SOCK_ETIMEDOUT ETIMEDOUT
|
||||
#define SOCK_EACCES EACCES
|
||||
#define SOCK_EADDRINUSE EADDRINUSE
|
||||
#define SOCK_EADDRNOTAVAIL EADDRNOTAVAIL
|
||||
#define SOCK_ECONNREFUSED ECONNREFUSED
|
||||
|
@ -4,7 +4,7 @@
|
||||
* Copyright (c) 2002 The Regents of the University of California, as
|
||||
* Operator of Los Alamos National Laboratory.
|
||||
* EPICS BASE is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
* in file LICENSE that is included with this distribution.
|
||||
\*************************************************************************/
|
||||
|
||||
/*
|
||||
@ -42,6 +42,7 @@ typedef socklen_t osiSocklen_t;
|
||||
#define SOCK_ENOBUFS ENOBUFS
|
||||
#define SOCK_ECONNRESET ECONNRESET
|
||||
#define SOCK_ETIMEDOUT ETIMEDOUT
|
||||
#define SOCK_EACCES EACCES
|
||||
#define SOCK_EADDRINUSE EADDRINUSE
|
||||
#define SOCK_EADDRNOTAVAIL EADDRNOTAVAIL
|
||||
#define SOCK_ECONNREFUSED ECONNREFUSED
|
||||
@ -72,4 +73,3 @@ typedef socklen_t osiSocklen_t;
|
||||
#define ifreq_size(pifreq) (sizeof(pifreq->ifr_name))
|
||||
|
||||
#endif /*osdSockH*/
|
||||
|
||||
|
@ -171,7 +171,7 @@ static int receiveMessage(
|
||||
return -1;
|
||||
}
|
||||
rsize = receiveMessage(id, id->localBuf, id->maxSize, wait, delay);
|
||||
if ((rsize < 0) || (rsize > size))
|
||||
if (rsize > size)
|
||||
return -1;
|
||||
memcpy(buffer, id->localBuf, rsize);
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*************************************************************************\
|
||||
* Copyright (c) 2002 The University of Saskatchewan
|
||||
* EPICS BASE is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
* in file LICENSE that is included with this distribution.
|
||||
\*************************************************************************/
|
||||
/*
|
||||
* RTEMS osdSock.h
|
||||
@ -35,7 +35,7 @@ int select(int n, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, str
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
typedef int SOCKET;
|
||||
#define INVALID_SOCKET (-1)
|
||||
#define SOCKERRNO errno
|
||||
@ -49,6 +49,7 @@ typedef socklen_t osiSocklen_t;
|
||||
#define SOCK_ENOBUFS ENOBUFS
|
||||
#define SOCK_ECONNRESET ECONNRESET
|
||||
#define SOCK_ETIMEDOUT ETIMEDOUT
|
||||
#define SOCK_EACCES EACCES
|
||||
#define SOCK_EADDRINUSE EADDRINUSE
|
||||
#define SOCK_EADDRNOTAVAIL EADDRNOTAVAIL
|
||||
#define SOCK_ECONNREFUSED ECONNREFUSED
|
||||
@ -75,7 +76,7 @@ typedef socklen_t osiSocklen_t;
|
||||
|
||||
#ifndef INADDR_NONE
|
||||
# define INADDR_NONE (0xffffffff)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/*
|
||||
* For shutdown()
|
||||
|
@ -427,8 +427,6 @@ void epicsThreadGetName (epicsThreadId id, char *name, size_t size)
|
||||
struct taskVar *v;
|
||||
int haveName = 0;
|
||||
|
||||
if (size <= 0)
|
||||
return;
|
||||
taskVarLock ();
|
||||
for (v=taskVarHead ; v != NULL ; v=v->forw) {
|
||||
if (v->id == tid) {
|
||||
@ -690,6 +688,7 @@ void epicsThreadShow (epicsThreadId id, unsigned int level)
|
||||
for (v = taskVarHead ; v != NULL ; v = v->forw) {
|
||||
if ((rtems_id)id == v->id) {
|
||||
epicsThreadShowInfo (v, level);
|
||||
taskVarUnlock ();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -4,7 +4,7 @@
|
||||
* Copyright (c) 2002 The Regents of the University of California, as
|
||||
* Operator of Los Alamos National Laboratory.
|
||||
* EPICS BASE is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
* in file LICENSE that is included with this distribution.
|
||||
\*************************************************************************/
|
||||
|
||||
#ifndef osdSockH
|
||||
@ -14,7 +14,7 @@
|
||||
#include <errno.h>
|
||||
|
||||
/*
|
||||
* winsock2.h changes the structure alignment to 4 if
|
||||
* winsock2.h changes the structure alignment to 4 if
|
||||
* WIN32 isnt set which can be a source of confusion
|
||||
*/
|
||||
#ifndef WIN32
|
||||
@ -48,6 +48,7 @@ typedef int osiSocklen_t;
|
||||
#define SOCK_ENOBUFS WSAENOBUFS
|
||||
#define SOCK_ECONNRESET WSAECONNRESET
|
||||
#define SOCK_ETIMEDOUT WSAETIMEDOUT
|
||||
#define SOCK_EACCES WSAEACCES
|
||||
#define SOCK_EADDRINUSE WSAEADDRINUSE
|
||||
#define SOCK_EADDRNOTAVAIL WSAEADDRNOTAVAIL
|
||||
#define SOCK_ECONNREFUSED WSAECONNREFUSED
|
||||
|
@ -523,13 +523,8 @@ static win32ThreadParam * epicsThreadParmCreate ( const char *pName )
|
||||
|
||||
pParmWIN32 = calloc ( 1, sizeof ( *pParmWIN32 ) + strlen ( pName ) + 1 );
|
||||
if ( pParmWIN32 ) {
|
||||
if ( pName ) {
|
||||
pParmWIN32->pName = (char *) ( pParmWIN32 + 1 );
|
||||
strcpy ( pParmWIN32->pName, pName );
|
||||
}
|
||||
else {
|
||||
pParmWIN32->pName = 0;
|
||||
}
|
||||
pParmWIN32->pName = (char *) ( pParmWIN32 + 1 );
|
||||
strcpy ( pParmWIN32->pName, pName );
|
||||
pParmWIN32->isSuspended = 0;
|
||||
}
|
||||
return pParmWIN32;
|
||||
|
@ -4,7 +4,7 @@
|
||||
* Copyright (c) 2002 The Regents of the University of California, as
|
||||
* Operator of Los Alamos National Laboratory.
|
||||
* EPICS BASE is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
* in file LICENSE that is included with this distribution.
|
||||
\*************************************************************************/
|
||||
/*
|
||||
* cygwin32 specific include
|
||||
@ -49,6 +49,7 @@ typedef int osiSocklen_t;
|
||||
#define SOCK_ENOBUFS ENOBUFS
|
||||
#define SOCK_ECONNRESET ECONNRESET
|
||||
#define SOCK_ETIMEDOUT ETIMEDOUT
|
||||
#define SOCK_EACCES EACCES
|
||||
#define SOCK_EADDRINUSE EADDRINUSE
|
||||
#define SOCK_EADDRNOTAVAIL EADDRNOTAVAIL
|
||||
#define SOCK_ECONNREFUSED ECONNREFUSED
|
||||
@ -67,4 +68,3 @@ typedef int osiSocklen_t;
|
||||
#define ifreq_size(pifreq) (sizeof(pifreq->ifr_name))
|
||||
|
||||
#endif /*osdSockH*/
|
||||
|
||||
|
@ -4,7 +4,7 @@
|
||||
* Copyright (c) 2002 The Regents of the University of California, as
|
||||
* Operator of Los Alamos National Laboratory.
|
||||
* EPICS BASE is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
* in file LICENSE that is included with this distribution.
|
||||
\*************************************************************************/
|
||||
|
||||
#ifndef osdSockH
|
||||
@ -29,7 +29,7 @@
|
||||
#define IPPORT_USERRESERVED 5000
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
typedef int SOCKET;
|
||||
#define INVALID_SOCKET (-1)
|
||||
#define SOCKERRNO errno
|
||||
@ -43,6 +43,7 @@ typedef socklen_t osiSocklen_t;
|
||||
#define SOCK_ENOBUFS ENOBUFS
|
||||
#define SOCK_ECONNRESET ECONNRESET
|
||||
#define SOCK_ETIMEDOUT ETIMEDOUT
|
||||
#define SOCK_EACCES EACCES
|
||||
#define SOCK_EADDRINUSE EADDRINUSE
|
||||
#define SOCK_EADDRNOTAVAIL EADDRNOTAVAIL
|
||||
#define SOCK_ECONNREFUSED ECONNREFUSED
|
||||
@ -77,4 +78,3 @@ typedef socklen_t osiSocklen_t;
|
||||
#endif
|
||||
|
||||
#endif /*osdSockH*/
|
||||
|
||||
|
@ -39,6 +39,7 @@ typedef socklen_t osiSocklen_t;
|
||||
#define SOCK_ENOBUFS ENOBUFS
|
||||
#define SOCK_ECONNRESET ECONNRESET
|
||||
#define SOCK_ETIMEDOUT ETIMEDOUT
|
||||
#define SOCK_EACCES EACCES
|
||||
#define SOCK_EADDRINUSE EADDRINUSE
|
||||
#define SOCK_EADDRNOTAVAIL EADDRNOTAVAIL
|
||||
#define SOCK_ECONNREFUSED ECONNREFUSED
|
||||
|
@ -4,7 +4,7 @@
|
||||
* Copyright (c) 2002 The Regents of the University of California, as
|
||||
* Operator of Los Alamos National Laboratory.
|
||||
* EPICS BASE is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
* in file LICENSE that is included with this distribution.
|
||||
\*************************************************************************/
|
||||
|
||||
/*
|
||||
@ -39,7 +39,7 @@ typedef int osiSockIoctl_t;
|
||||
|
||||
#if SOLARIS > 6 || defined ( _SOCKLEN_T )
|
||||
typedef uint32_t osiSocklen_t;
|
||||
#else
|
||||
#else
|
||||
typedef int osiSocklen_t;
|
||||
#endif
|
||||
|
||||
@ -51,6 +51,7 @@ typedef int osiSockIoctl_t;
|
||||
#define SOCK_ENOBUFS ENOBUFS
|
||||
#define SOCK_ECONNRESET ECONNRESET
|
||||
#define SOCK_ETIMEDOUT ETIMEDOUT
|
||||
#define SOCK_EACCES EACCES
|
||||
#define SOCK_EADDRINUSE EADDRINUSE
|
||||
#define SOCK_EADDRNOTAVAIL EADDRNOTAVAIL
|
||||
#define SOCK_ECONNREFUSED ECONNREFUSED
|
||||
@ -80,9 +81,8 @@ typedef int osiSockIoctl_t;
|
||||
|
||||
#ifndef INADDR_NONE
|
||||
# define INADDR_NONE (0xffffffff)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#define ifreq_size(pifreq) (sizeof(pifreq->ifr_name))
|
||||
|
||||
#endif /*osdSockH*/
|
||||
|
||||
|
@ -4,7 +4,7 @@
|
||||
* Copyright (c) 2002 The Regents of the University of California, as
|
||||
* Operator of Los Alamos National Laboratory.
|
||||
* EPICS BASE is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
* in file LICENSE that is included with this distribution.
|
||||
\*************************************************************************/
|
||||
/*
|
||||
* vxWorks specific socket include
|
||||
@ -44,7 +44,7 @@ int sysClkRateGet(void);
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
typedef int SOCKET;
|
||||
#define INVALID_SOCKET (-1)
|
||||
#define SOCKERRNO errno
|
||||
@ -72,6 +72,7 @@ typedef int osiSocklen_t;
|
||||
#define SOCK_ENOBUFS ENOBUFS
|
||||
#define SOCK_ECONNRESET ECONNRESET
|
||||
#define SOCK_ETIMEDOUT ETIMEDOUT
|
||||
#define SOCK_EACCES EACCES
|
||||
#define SOCK_EADDRINUSE EADDRINUSE
|
||||
#define SOCK_EADDRNOTAVAIL EADDRNOTAVAIL
|
||||
#define SOCK_ECONNREFUSED ECONNREFUSED
|
||||
@ -93,7 +94,7 @@ typedef int osiSocklen_t;
|
||||
|
||||
#ifndef INADDR_NONE
|
||||
# define INADDR_NONE (0xffffffff)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if defined(_SIZEOF_ADDR_IFREQ)
|
||||
# define ifreq_size(pifreq) _SIZEOF_ADDR_IFREQ(*pifreq)
|
||||
@ -104,5 +105,3 @@ typedef int osiSocklen_t;
|
||||
#endif
|
||||
|
||||
#endif /*osdSockH*/
|
||||
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
/*************************************************************************\
|
||||
* Copyright (c) 2016 Michael Davidsaver
|
||||
* Copyright (c) 2009 UChicago Argonne LLC, as Operator of Argonne
|
||||
* National Laboratory.
|
||||
* Copyright (c) 2002 The Regents of the University of California, as
|
||||
@ -11,6 +12,7 @@
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "ellLib.h"
|
||||
#include "dbDefs.h"
|
||||
#include "epicsUnitTest.h"
|
||||
#include "testMain.h"
|
||||
|
||||
@ -20,15 +22,13 @@ struct myItem {
|
||||
int num;
|
||||
};
|
||||
|
||||
MAIN(epicsEllTest)
|
||||
static void testList(void)
|
||||
{
|
||||
ELLLIST list1;
|
||||
ELLLIST list2 = ELLLIST_INIT;
|
||||
int i1 = 1;
|
||||
struct myItem *pitem, *pfirst, *pick;
|
||||
|
||||
testPlan(70);
|
||||
|
||||
list1.count = 27;
|
||||
list1.node.next = (ELLNODE *) 0x01010101;
|
||||
list1.node.previous = (ELLNODE *) 0x10101010;
|
||||
@ -192,6 +192,77 @@ MAIN(epicsEllTest)
|
||||
|
||||
ellFree2(&list1, free);
|
||||
testOk1(ellCount(&list1) == 0);
|
||||
}
|
||||
|
||||
typedef struct { int A, B; } input_t;
|
||||
|
||||
static int myItemCmp(const ELLNODE *a, const ELLNODE *b)
|
||||
{
|
||||
struct myItem *A = CONTAINER(a, struct myItem, node),
|
||||
*B = CONTAINER(b, struct myItem, node);
|
||||
|
||||
if (A->num < B->num) return -1;
|
||||
else if(A->num > B->num) return 1;
|
||||
else if(A->list < B->list) return -1;
|
||||
else if(A->list > B->list) return 1;
|
||||
else return 0;
|
||||
}
|
||||
|
||||
static const input_t input[] = {
|
||||
{-4, 0},
|
||||
{-5, 0},
|
||||
{0,0},
|
||||
{50,0},
|
||||
{0,1},
|
||||
{5,0},
|
||||
{5,1}
|
||||
};
|
||||
|
||||
static
|
||||
void testSort(const input_t *inp, size_t ninp)
|
||||
{
|
||||
unsigned i;
|
||||
ELLLIST list = ELLLIST_INIT;
|
||||
struct myItem *alloc = calloc(ninp, sizeof(*alloc));
|
||||
|
||||
if(!alloc) testAbort("testSort allocation fails");
|
||||
|
||||
for(i=0; i<ninp; i++) {
|
||||
struct myItem *it = &alloc[i];
|
||||
|
||||
it->num = inp[i].A;
|
||||
it->list= inp[i].B;
|
||||
|
||||
ellAdd(&list, &it->node);
|
||||
}
|
||||
|
||||
ellSortStable(&list, &myItemCmp);
|
||||
|
||||
testOk(ellCount(&list)==ninp, "output length %u == %u", (unsigned)ellCount(&list), (unsigned)ninp);
|
||||
if(ellCount(&list)==0) {
|
||||
testSkip(ninp-1, "all items lost");
|
||||
}
|
||||
|
||||
{
|
||||
struct myItem *prev = CONTAINER(ellFirst(&list), struct myItem, node),
|
||||
*next;
|
||||
|
||||
for(next = CONTAINER(ellNext(&prev->node), struct myItem, node);
|
||||
next;
|
||||
prev = next, next = CONTAINER(ellNext(&next->node), struct myItem, node))
|
||||
{
|
||||
int cond = (prev->num<next->num) || (prev->num==next->num && prev->list<next->list);
|
||||
testOk(cond, "%d:%d < %d:%d", prev->num, prev->list, next->num, next->list);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MAIN(epicsEllTest)
|
||||
{
|
||||
testPlan(77);
|
||||
|
||||
testList();
|
||||
testSort(input, NELEMENTS(input));
|
||||
|
||||
return testDone();
|
||||
}
|
||||
|
@ -250,5 +250,12 @@ MAIN(epicsEventTest)
|
||||
eventWaitTest();
|
||||
eventWakeupTest();
|
||||
|
||||
free(name);
|
||||
free(id);
|
||||
epicsRingPointerDelete(pinfo->ring);
|
||||
epicsMutexDestroy(pinfo->lockRing);
|
||||
epicsEventDestroy(event);
|
||||
free(pinfo);
|
||||
|
||||
return testDone();
|
||||
}
|
||||
|
@ -279,5 +279,11 @@ MAIN(epicsMutexTest)
|
||||
|
||||
epicsMutexPerformance ();
|
||||
|
||||
free(pinfo);
|
||||
free(arg);
|
||||
free(name);
|
||||
free(id);
|
||||
epicsMutexDestroy(mutex);
|
||||
|
||||
return testDone();
|
||||
}
|
||||
|
@ -37,7 +37,14 @@ static void testEpicsSnprintf(void) {
|
||||
const char *expected = exbuffer;
|
||||
int size;
|
||||
int rtn, rlen;
|
||||
|
||||
|
||||
#ifdef _WIN32
|
||||
#if (defined(_MSC_VER) && _MSC_VER < 1900) || \
|
||||
(defined(_MINGW) && defined(_TWO_DIGIT_EXPONENT))
|
||||
_set_output_format(_TWO_DIGIT_EXPONENT);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
sprintf(exbuffer, format, ivalue, fvalue, svalue);
|
||||
rlen = strlen(expected)+1;
|
||||
|
||||
@ -121,11 +128,7 @@ void testStdoutRedir (const char *report)
|
||||
|
||||
MAIN(epicsStdioTest)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
testPlan(166);
|
||||
#else
|
||||
testPlan(163);
|
||||
#endif
|
||||
testEpicsSnprintf();
|
||||
#ifdef __rtems__
|
||||
/* ensure there is a writeable area */
|
||||
|
@ -87,7 +87,7 @@ MAIN(epicsStringTest)
|
||||
char *s;
|
||||
int status;
|
||||
|
||||
testPlan(402);
|
||||
testPlan(406);
|
||||
|
||||
testChars();
|
||||
|
||||
@ -122,6 +122,11 @@ MAIN(epicsStringTest)
|
||||
testOk1(epicsStrHash(abcd, 0) == epicsMemHash(abcde, 4, 0));
|
||||
testOk1(epicsStrHash(abcd, 0) != epicsMemHash("abcd\0", 5, 0));
|
||||
|
||||
testOk1(epicsStrnLen("abcd", 5)==4);
|
||||
testOk1(epicsStrnLen("abcd", 4)==4);
|
||||
testOk1(epicsStrnLen("abcd", 3)==3);
|
||||
testOk1(epicsStrnLen("abcd", 0)==0);
|
||||
|
||||
testGlob();
|
||||
|
||||
memset(result, 'x', sizeof(result));
|
||||
|
@ -43,7 +43,7 @@ MAIN(epicsTimeTest)
|
||||
const int wasteTime = 100000;
|
||||
const int nTimes = 10;
|
||||
|
||||
testPlan(15 + nTimes * 18);
|
||||
testPlan(15 + nTimes * 19);
|
||||
|
||||
try {
|
||||
const epicsTimeStamp epochTS = {0, 0};
|
||||
@ -200,6 +200,11 @@ MAIN(epicsTimeTest)
|
||||
epicsTime beginANSI = ansiDate;
|
||||
testOk1(beginANSI + diff == now);
|
||||
|
||||
// test struct gmtm round-trip conversion
|
||||
gm_tm_nano_sec ansiGmDate = begin;
|
||||
epicsTime beginGMANSI = ansiGmDate;
|
||||
testOk1(beginGMANSI + diff == now);
|
||||
|
||||
// test struct timespec round-trip conversion
|
||||
struct timespec ts = begin;
|
||||
epicsTime beginTS = ts;
|
||||
|
@ -115,5 +115,9 @@ MAIN(ringBytesTest)
|
||||
testOk(n==1, "ring get %d", 1);
|
||||
check(ring, RINGSIZE);
|
||||
|
||||
epicsRingBytesDelete(ring);
|
||||
epicsEventDestroy(consumerEvent);
|
||||
free(pinfo);
|
||||
|
||||
return testDone();
|
||||
}
|
||||
|
@ -36,12 +36,14 @@ recordtype(fanout) {
|
||||
}
|
||||
field(OFFS,DBF_SHORT) {
|
||||
prompt("Offset for Specified")
|
||||
interest(1)
|
||||
promptgroup("30 - Action")
|
||||
interest(1)
|
||||
initial("0")
|
||||
}
|
||||
field(SHFT,DBF_SHORT) {
|
||||
prompt("Shift for Mask mode")
|
||||
interest(1)
|
||||
promptgroup("30 - Action")
|
||||
interest(1)
|
||||
initial("-1")
|
||||
}
|
||||
field(LNK0,DBF_FWDLINK) {
|
||||
|
@ -36,11 +36,13 @@ recordtype(seq) {
|
||||
}
|
||||
field(OFFS,DBF_SHORT) {
|
||||
prompt("Offset for Specified")
|
||||
promptgroup("30 - Action")
|
||||
interest(1)
|
||||
initial("0")
|
||||
}
|
||||
field(SHFT,DBF_SHORT) {
|
||||
prompt("Shift for Mask mode")
|
||||
promptgroup("30 - Action")
|
||||
interest(1)
|
||||
initial("-1")
|
||||
}
|
||||
|
@ -109,6 +109,7 @@ int main(int argc,char **argv)
|
||||
pmynode[npv] = callocMustSucceed(1, sizeof(MYNODE), "caMonitor");
|
||||
npv++;
|
||||
}
|
||||
fclose(fp);
|
||||
SEVCHK(ca_context_create(ca_disable_preemptive_callback),"ca_context_create");
|
||||
SEVCHK(ca_add_exception_event(exceptionCallback,NULL),
|
||||
"ca_add_exception_event");
|
||||
|
@ -3,9 +3,8 @@
|
||||
* National Laboratory.
|
||||
* Copyright (c) 2002 The Regents of the University of California, as
|
||||
* Operator of Los Alamos National Laboratory.
|
||||
* EPICS BASE Versions 3.13.7
|
||||
* and higher are distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
* EPICS BASE is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
\*************************************************************************/
|
||||
|
||||
//
|
||||
@ -30,23 +29,23 @@ exAsyncPV::exAsyncPV ( exServer & cas, pvInfo & setup,
|
||||
//
|
||||
caStatus exAsyncPV::read (const casCtx &ctx, gdd &valueIn)
|
||||
{
|
||||
exAsyncReadIO *pIO;
|
||||
|
||||
if ( this->simultAsychReadIOCount >= this->cas.maxSimultAsyncIO () ) {
|
||||
return S_casApp_postponeAsyncIO;
|
||||
}
|
||||
exAsyncReadIO *pIO;
|
||||
|
||||
pIO = new exAsyncReadIO ( this->cas, ctx,
|
||||
*this, valueIn, this->asyncDelay );
|
||||
if ( ! pIO ) {
|
||||
if ( this->simultAsychReadIOCount >= this->cas.maxSimultAsyncIO () ) {
|
||||
return S_casApp_postponeAsyncIO;
|
||||
}
|
||||
|
||||
pIO = new exAsyncReadIO ( this->cas, ctx,
|
||||
*this, valueIn, this->asyncDelay );
|
||||
if ( ! pIO ) {
|
||||
if ( this->simultAsychReadIOCount > 0 ) {
|
||||
return S_casApp_postponeAsyncIO;
|
||||
}
|
||||
else {
|
||||
return S_casApp_noMemory;
|
||||
return S_casApp_noMemory;
|
||||
}
|
||||
}
|
||||
this->simultAsychReadIOCount++;
|
||||
}
|
||||
this->simultAsychReadIOCount++;
|
||||
return S_casApp_asyncCompletion;
|
||||
}
|
||||
|
||||
@ -54,24 +53,24 @@ caStatus exAsyncPV::read (const casCtx &ctx, gdd &valueIn)
|
||||
// exAsyncPV::writeNotify()
|
||||
//
|
||||
caStatus exAsyncPV::writeNotify ( const casCtx &ctx, const gdd &valueIn )
|
||||
{
|
||||
if ( this->simultAsychWriteIOCount >= this->cas.maxSimultAsyncIO() ) {
|
||||
return S_casApp_postponeAsyncIO;
|
||||
}
|
||||
{
|
||||
if ( this->simultAsychWriteIOCount >= this->cas.maxSimultAsyncIO() ) {
|
||||
return S_casApp_postponeAsyncIO;
|
||||
}
|
||||
|
||||
exAsyncWriteIO * pIO = new
|
||||
exAsyncWriteIO * pIO = new
|
||||
exAsyncWriteIO ( this->cas, ctx, *this,
|
||||
valueIn, this->asyncDelay );
|
||||
if ( ! pIO ) {
|
||||
valueIn, this->asyncDelay );
|
||||
if ( ! pIO ) {
|
||||
if ( this->simultAsychReadIOCount > 0 ) {
|
||||
return S_casApp_postponeAsyncIO;
|
||||
}
|
||||
else {
|
||||
return S_casApp_noMemory;
|
||||
return S_casApp_noMemory;
|
||||
}
|
||||
}
|
||||
this->simultAsychWriteIOCount++;
|
||||
return S_casApp_asyncCompletion;
|
||||
this->simultAsychWriteIOCount++;
|
||||
return S_casApp_asyncCompletion;
|
||||
}
|
||||
|
||||
//
|
||||
@ -79,24 +78,24 @@ caStatus exAsyncPV::writeNotify ( const casCtx &ctx, const gdd &valueIn )
|
||||
//
|
||||
caStatus exAsyncPV::write ( const casCtx &ctx, const gdd &valueIn )
|
||||
{
|
||||
// implement the discard intermediate values, but last value
|
||||
// implement the discard intermediate values, but last value
|
||||
// sent always applied behavior that IOCs provide excepting
|
||||
// that we will alow N requests to pend instead of a limit
|
||||
// of only one imposed in the IOC
|
||||
if ( this->simultAsychWriteIOCount >= this->cas.maxSimultAsyncIO() ) {
|
||||
if ( this->simultAsychWriteIOCount >= this->cas.maxSimultAsyncIO() ) {
|
||||
pStandbyValue.set ( & valueIn );
|
||||
return S_casApp_success;
|
||||
}
|
||||
|
||||
exAsyncWriteIO * pIO = new
|
||||
exAsyncWriteIO ( this->cas, ctx, *this,
|
||||
valueIn, this->asyncDelay );
|
||||
if ( ! pIO ) {
|
||||
pStandbyValue.set ( & valueIn );
|
||||
return S_casApp_success;
|
||||
return S_casApp_success;
|
||||
}
|
||||
this->simultAsychWriteIOCount++;
|
||||
return S_casApp_asyncCompletion;
|
||||
|
||||
exAsyncWriteIO * pIO = new
|
||||
exAsyncWriteIO ( this->cas, ctx, *this,
|
||||
valueIn, this->asyncDelay );
|
||||
if ( ! pIO ) {
|
||||
pStandbyValue.set ( & valueIn );
|
||||
return S_casApp_success;
|
||||
}
|
||||
this->simultAsychWriteIOCount++;
|
||||
return S_casApp_asyncCompletion;
|
||||
}
|
||||
|
||||
// Implementing a specialized update for exAsyncPV
|
||||
@ -150,7 +149,7 @@ void exAsyncPV::removeWriteIO ()
|
||||
exAsyncWriteIO::exAsyncWriteIO ( exServer & cas,
|
||||
const casCtx & ctxIn, exAsyncPV & pvIn,
|
||||
const gdd & valueIn, double asyncDelay ) :
|
||||
casAsyncWriteIO ( ctxIn ), pv ( pvIn ),
|
||||
casAsyncWriteIO ( ctxIn ), pv ( pvIn ),
|
||||
timer ( cas.createTimer () ), pValue(valueIn)
|
||||
{
|
||||
this->timer.start ( *this, asyncDelay );
|
||||
@ -168,7 +167,7 @@ exAsyncWriteIO::~exAsyncWriteIO()
|
||||
if ( this->pValue.valid () ) {
|
||||
this->pv.updateFromAsyncWrite ( *this->pValue );
|
||||
}
|
||||
this->pv.removeWriteIO();
|
||||
this->pv.removeWriteIO();
|
||||
}
|
||||
|
||||
//
|
||||
@ -179,9 +178,9 @@ epicsTimerNotify::expireStatus exAsyncWriteIO::
|
||||
expire ( const epicsTime & /* currentTime */ )
|
||||
{
|
||||
assert ( this->pValue.valid () );
|
||||
caStatus status = this->pv.updateFromAsyncWrite ( *this->pValue );
|
||||
this->pValue.set ( 0 );
|
||||
this->postIOCompletion ( status );
|
||||
caStatus status = this->pv.updateFromAsyncWrite ( *this->pValue );
|
||||
this->pValue.set ( 0 );
|
||||
this->postIOCompletion ( status );
|
||||
return noRestart;
|
||||
}
|
||||
|
||||
@ -191,7 +190,7 @@ epicsTimerNotify::expireStatus exAsyncWriteIO::
|
||||
exAsyncReadIO::exAsyncReadIO ( exServer & cas, const casCtx & ctxIn,
|
||||
exAsyncPV & pvIn, gdd & protoIn,
|
||||
double asyncDelay ) :
|
||||
casAsyncReadIO ( ctxIn ), pv ( pvIn ),
|
||||
casAsyncReadIO ( ctxIn ), pv ( pvIn ),
|
||||
timer ( cas.createTimer() ), pProto ( protoIn )
|
||||
{
|
||||
this->timer.start ( *this, asyncDelay );
|
||||
@ -202,7 +201,7 @@ exAsyncReadIO::exAsyncReadIO ( exServer & cas, const casCtx & ctxIn,
|
||||
//
|
||||
exAsyncReadIO::~exAsyncReadIO()
|
||||
{
|
||||
this->pv.removeReadIO ();
|
||||
this->pv.removeReadIO ();
|
||||
this->timer.destroy ();
|
||||
}
|
||||
|
||||
@ -213,16 +212,16 @@ exAsyncReadIO::~exAsyncReadIO()
|
||||
epicsTimerNotify::expireStatus
|
||||
exAsyncReadIO::expire ( const epicsTime & /* currentTime */ )
|
||||
{
|
||||
//
|
||||
// map between the prototype in and the
|
||||
// current value
|
||||
//
|
||||
caStatus status = this->pv.exPV::readNoCtx ( this->pProto );
|
||||
//
|
||||
// map between the prototype in and the
|
||||
// current value
|
||||
//
|
||||
caStatus status = this->pv.exPV::readNoCtx ( this->pProto );
|
||||
|
||||
//
|
||||
// post IO completion
|
||||
//
|
||||
this->postIOCompletion ( status, *this->pProto );
|
||||
//
|
||||
// post IO completion
|
||||
//
|
||||
this->postIOCompletion ( status, *this->pProto );
|
||||
|
||||
return noRestart;
|
||||
}
|
||||
|
@ -3,9 +3,8 @@
|
||||
* National Laboratory.
|
||||
* Copyright (c) 2002 The Regents of the University of California, as
|
||||
* Operator of Los Alamos National Laboratory.
|
||||
* EPICS BASE Versions 3.13.7
|
||||
* and higher are distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
* EPICS BASE is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
\*************************************************************************/
|
||||
//
|
||||
// Example EPICS CA server
|
||||
@ -42,7 +41,7 @@ exPV::exPV ( exServer & casIn, pvInfo & setup,
|
||||
//
|
||||
// no dataless PV allowed
|
||||
//
|
||||
assert (this->info.getElementCount()>=1u);
|
||||
assert (this->info.getCapacity()>=1u);
|
||||
|
||||
//
|
||||
// start a very slow background scan
|
||||
|
@ -3,9 +3,8 @@
|
||||
* National Laboratory.
|
||||
* Copyright (c) 2002 The Regents of the University of California, as
|
||||
* Operator of Los Alamos National Laboratory.
|
||||
* EPICS BASE Versions 3.13.7
|
||||
* and higher are distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
* EPICS BASE is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
\*************************************************************************/
|
||||
//
|
||||
// fileDescriptorManager.process(delay);
|
||||
@ -304,7 +303,7 @@ exPV *pvInfo::createPV ( exServer & cas, bool preCreateFlag,
|
||||
// depending on the io type and the number
|
||||
// of elements
|
||||
//
|
||||
if (this->elementCount==1u) {
|
||||
if (this->capacity==1u) {
|
||||
switch (this->ioType){
|
||||
case excasIoSync:
|
||||
pNewPV = new exScalarPV ( cas, *this, preCreateFlag, scanOn );
|
||||
|
@ -3,9 +3,8 @@
|
||||
* National Laboratory.
|
||||
* Copyright (c) 2002 The Regents of the University of California, as
|
||||
* Operator of Los Alamos National Laboratory.
|
||||
* EPICS BASE Versions 3.13.7
|
||||
* and higher are distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
* EPICS BASE is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
\*************************************************************************/
|
||||
//
|
||||
// Example EPICS CA server
|
||||
@ -76,8 +75,10 @@ public:
|
||||
double getLopr () const;
|
||||
aitEnum getType () const;
|
||||
excasIoType getIOType () const;
|
||||
unsigned getElementCount () const;
|
||||
void unlinkPV ();
|
||||
unsigned getCapacity () const;
|
||||
unsigned getElementCount () const;
|
||||
void setElementCount (unsigned);
|
||||
void unlinkPV ();
|
||||
exPV *createPV ( exServer & exCAS, bool preCreateFlag,
|
||||
bool scanOn, double asyncDelay );
|
||||
void deletePV ();
|
||||
@ -88,7 +89,8 @@ private:
|
||||
const double lopr;
|
||||
aitEnum type;
|
||||
const excasIoType ioType;
|
||||
const unsigned elementCount;
|
||||
const unsigned capacity;
|
||||
unsigned elementCount;
|
||||
exPV * pPV;
|
||||
pvInfo & operator = ( const pvInfo & );
|
||||
};
|
||||
@ -441,8 +443,8 @@ inline pvInfo::pvInfo ( double scanPeriodIn, const char *pNameIn,
|
||||
|
||||
scanPeriod ( scanPeriodIn ), pName ( pNameIn ),
|
||||
hopr ( hoprIn ), lopr ( loprIn ), type ( typeIn ),
|
||||
ioType ( ioTypeIn ), elementCount ( countIn ),
|
||||
pPV ( 0 )
|
||||
ioType ( ioTypeIn ), capacity ( countIn ),
|
||||
elementCount ( 0 ), pPV ( 0 )
|
||||
{
|
||||
}
|
||||
|
||||
@ -454,8 +456,8 @@ inline pvInfo::pvInfo ( const pvInfo & copyIn ) :
|
||||
|
||||
scanPeriod ( copyIn.scanPeriod ), pName ( copyIn.pName ),
|
||||
hopr ( copyIn.hopr ), lopr ( copyIn.lopr ), type ( copyIn.type ),
|
||||
ioType ( copyIn.ioType ), elementCount ( copyIn.elementCount ),
|
||||
pPV ( copyIn.pPV )
|
||||
ioType ( copyIn.ioType ), capacity ( copyIn.capacity ),
|
||||
elementCount ( copyIn.elementCount ), pPV ( copyIn.pPV )
|
||||
{
|
||||
}
|
||||
|
||||
@ -509,12 +511,22 @@ inline excasIoType pvInfo::getIOType () const
|
||||
return this->ioType;
|
||||
}
|
||||
|
||||
inline unsigned pvInfo::getElementCount () const
|
||||
{
|
||||
return this->elementCount;
|
||||
inline unsigned pvInfo::getCapacity () const
|
||||
{
|
||||
return this->capacity;
|
||||
}
|
||||
|
||||
inline void pvInfo::unlinkPV ()
|
||||
inline unsigned pvInfo::getElementCount () const
|
||||
{
|
||||
return this->elementCount;
|
||||
}
|
||||
|
||||
inline void pvInfo::setElementCount (unsigned newCount)
|
||||
{
|
||||
this->elementCount = newCount;
|
||||
}
|
||||
|
||||
inline void pvInfo::unlinkPV ()
|
||||
{
|
||||
this->pPV = NULL;
|
||||
}
|
||||
|
@ -3,9 +3,8 @@
|
||||
* National Laboratory.
|
||||
* Copyright (c) 2002 The Regents of the University of California, as
|
||||
* Operator of Los Alamos National Laboratory.
|
||||
* EPICS BASE Versions 3.13.7
|
||||
* and higher are distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
* EPICS BASE is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
\*************************************************************************/
|
||||
|
||||
#include "exServer.h"
|
||||
@ -48,7 +47,7 @@ unsigned exVectorPV::maxDimension() const
|
||||
aitIndex exVectorPV::maxBound (unsigned dimension) const
|
||||
{
|
||||
if (dimension==0u) {
|
||||
return this->info.getElementCount();
|
||||
return this->info.getCapacity();
|
||||
}
|
||||
else {
|
||||
return 0u;
|
||||
@ -60,45 +59,40 @@ aitIndex exVectorPV::maxBound (unsigned dimension) const
|
||||
//
|
||||
void exVectorPV::scan()
|
||||
{
|
||||
caStatus status;
|
||||
double radians;
|
||||
smartGDDPointer pDD;
|
||||
aitFloat32 *pF, *pFE;
|
||||
const aitFloat32 *pCF;
|
||||
float newValue;
|
||||
float limit;
|
||||
exVecDestructor *pDest;
|
||||
int gddStatus;
|
||||
static epicsTime startTime = epicsTime::getCurrent();
|
||||
|
||||
//
|
||||
// update current time (so we are not required to do
|
||||
// this every time that we write the PV which impacts
|
||||
// throughput under sunos4 because gettimeofday() is
|
||||
// slow)
|
||||
// update current time
|
||||
//
|
||||
this->currentTime = epicsTime::getCurrent();
|
||||
|
||||
pDD = new gddAtomic (gddAppType_value, aitEnumFloat64,
|
||||
1u, this->info.getElementCount());
|
||||
|
||||
// demonstrate a changing array size
|
||||
unsigned ramp = 15 & (unsigned) (this->currentTime - startTime);
|
||||
unsigned newSize = this->info.getCapacity();
|
||||
if (newSize > ramp) {
|
||||
newSize -= ramp;
|
||||
}
|
||||
|
||||
smartGDDPointer pDD = new gddAtomic (gddAppType_value, aitEnumFloat64,
|
||||
1u, newSize);
|
||||
if ( ! pDD.valid () ) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// smart pointer class manages reference count after this point
|
||||
//
|
||||
gddStatus = pDD->unreference();
|
||||
assert (!gddStatus);
|
||||
gddStatus gdds = pDD->unreference();
|
||||
assert(!gdds);
|
||||
|
||||
//
|
||||
// allocate array buffer
|
||||
//
|
||||
pF = new aitFloat32 [this->info.getElementCount()];
|
||||
aitFloat64 * pF = new aitFloat64 [newSize];
|
||||
if (!pF) {
|
||||
return;
|
||||
}
|
||||
|
||||
pDest = new exVecDestructor;
|
||||
exVecDestructor * pDest = new exVecDestructor;
|
||||
if (!pDest) {
|
||||
delete [] pF;
|
||||
return;
|
||||
@ -114,37 +108,39 @@ void exVectorPV::scan()
|
||||
// double check for reasonable bounds on the
|
||||
// current value
|
||||
//
|
||||
pCF=NULL;
|
||||
if ( this->pValue.valid () ) {
|
||||
if (this->pValue->dimension()==1u) {
|
||||
const gddBounds *pB = this->pValue->getBounds();
|
||||
if (pB[0u].size()==this->info.getElementCount()) {
|
||||
pCF = *this->pValue;
|
||||
}
|
||||
}
|
||||
const aitFloat64 *pCF = NULL, *pCFE = NULL;
|
||||
if (this->pValue.valid () &&
|
||||
this->pValue->dimension() == 1u) {
|
||||
const gddBounds *pB = this->pValue->getBounds();
|
||||
|
||||
pCF = *this->pValue;
|
||||
pCFE = &pCF[pB->size()];
|
||||
}
|
||||
|
||||
pFE = &pF[this->info.getElementCount()];
|
||||
while (pF<pFE) {
|
||||
radians = (rand () * 2.0 * myPI)/RAND_MAX;
|
||||
if (pCF) {
|
||||
aitFloat64 * pFE = &pF[newSize];
|
||||
while (pF < pFE) {
|
||||
double radians = (rand () * 2.0 * myPI)/RAND_MAX;
|
||||
double newValue;
|
||||
if (pCF && pCF < pCFE) {
|
||||
newValue = *pCF++;
|
||||
}
|
||||
else {
|
||||
newValue = 0.0f;
|
||||
}
|
||||
newValue += (float) (sin (radians) / 10.0);
|
||||
limit = (float) this->info.getHopr();
|
||||
newValue += (sin (radians) / 10.0);
|
||||
double limit = this->info.getHopr();
|
||||
newValue = epicsMin (newValue, limit);
|
||||
limit = (float) this->info.getLopr();
|
||||
limit = this->info.getLopr();
|
||||
newValue = epicsMax (newValue, limit);
|
||||
*(pF++) = newValue;
|
||||
*pF++ = newValue;
|
||||
}
|
||||
|
||||
aitTimeStamp gddts = this->currentTime;
|
||||
pDD->setTimeStamp ( & gddts );
|
||||
|
||||
status = this->update ( *pDD );
|
||||
caStatus status = this->update ( *pDD );
|
||||
this->info.setElementCount(newSize);
|
||||
|
||||
if ( status != S_casApp_success ) {
|
||||
errMessage (status, "vector scan update failed\n");
|
||||
}
|
||||
@ -166,7 +162,7 @@ void exVectorPV::scan()
|
||||
//
|
||||
caStatus exVectorPV::updateValue ( const gdd & value )
|
||||
{
|
||||
|
||||
aitUint32 newSize = 0;
|
||||
//
|
||||
// Check bounds of incoming request
|
||||
// (and see if we are replacing all elements -
|
||||
@ -183,7 +179,9 @@ caStatus exVectorPV::updateValue ( const gdd & value )
|
||||
if ( pb[0u].first() != 0u ) {
|
||||
return S_casApp_outOfBounds;
|
||||
}
|
||||
else if ( pb[0u].size() > this->info.getElementCount() ) {
|
||||
|
||||
newSize = pb[0u].size();
|
||||
if ( newSize > this->info.getCapacity() ) {
|
||||
return S_casApp_outOfBounds;
|
||||
}
|
||||
}
|
||||
@ -193,14 +191,14 @@ caStatus exVectorPV::updateValue ( const gdd & value )
|
||||
//
|
||||
return S_casApp_outOfBounds;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Create a new array data descriptor
|
||||
// (so that old values that may be referenced on the
|
||||
// event queue are not replaced)
|
||||
//
|
||||
smartGDDPointer pNewValue ( new gddAtomic ( gddAppType_value, aitEnumFloat64,
|
||||
1u, this->info.getElementCount() ) );
|
||||
smartGDDPointer pNewValue ( new gddAtomic ( gddAppType_value, aitEnumFloat64,
|
||||
1u, newSize ) );
|
||||
if ( ! pNewValue.valid() ) {
|
||||
return S_casApp_noMemory;
|
||||
}
|
||||
@ -211,21 +209,20 @@ caStatus exVectorPV::updateValue ( const gdd & value )
|
||||
//
|
||||
gddStatus gdds = pNewValue->unreference( );
|
||||
assert ( ! gdds );
|
||||
|
||||
|
||||
//
|
||||
// allocate array buffer
|
||||
//
|
||||
aitFloat64 * pF = new aitFloat64 [this->info.getElementCount()];
|
||||
aitFloat64 * pF = new aitFloat64 [newSize];
|
||||
if (!pF) {
|
||||
return S_casApp_noMemory;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Install (and initialize) array buffer
|
||||
// if no old values exist
|
||||
//
|
||||
unsigned count = this->info.getElementCount();
|
||||
for ( unsigned i = 0u; i < count; i++ ) {
|
||||
for ( unsigned i = 0u; i < newSize; i++ ) {
|
||||
pF[i] = 0.0f;
|
||||
}
|
||||
|
||||
@ -240,7 +237,7 @@ caStatus exVectorPV::updateValue ( const gdd & value )
|
||||
// (do this before we increment pF)
|
||||
//
|
||||
pNewValue->putRef ( pF, pDest );
|
||||
|
||||
|
||||
//
|
||||
// copy in the values that they are writing
|
||||
//
|
||||
@ -248,9 +245,10 @@ caStatus exVectorPV::updateValue ( const gdd & value )
|
||||
if ( gdds ) {
|
||||
return S_cas_noConvert;
|
||||
}
|
||||
|
||||
|
||||
this->pValue = pNewValue;
|
||||
|
||||
this->info.setElementCount(newSize);
|
||||
|
||||
return S_casApp_success;
|
||||
}
|
||||
|
||||
@ -261,6 +259,6 @@ caStatus exVectorPV::updateValue ( const gdd & value )
|
||||
//
|
||||
void exVecDestructor::run ( void *pUntyped )
|
||||
{
|
||||
aitFloat32 * pf = reinterpret_cast < aitFloat32 * > ( pUntyped );
|
||||
aitFloat64 * pf = reinterpret_cast < aitFloat64 * > ( pUntyped );
|
||||
delete [] pf;
|
||||
}
|
||||
|
@ -34,3 +34,10 @@ CHECK_RELEASE = YES
|
||||
# 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
|
||||
|
||||
|
@ -35,3 +35,9 @@ EPICS_BASE = _EPICS_BASE_
|
||||
# Set RULES here if you want to use build rules from somewhere
|
||||
# other than EPICS_BASE:
|
||||
#RULES = $(MODULES)/build-rules
|
||||
|
||||
# These allow developers to override the RELEASE variable settings
|
||||
# without having to modify the configure/RELEASE file itself.
|
||||
-include $(TOP)/../RELEASE.local
|
||||
-include $(TOP)/configure/RELEASE.local
|
||||
|
||||
|
@ -101,7 +101,7 @@ sub OutputBreaktables {
|
||||
my ($out, $breaktables) = @_;
|
||||
while (my ($name, $breaktable) = each %{$breaktables}) {
|
||||
printf $out "breaktable(\"%s\") {\n", $name;
|
||||
printf $out " point(%s, %s)\n", @{$_}
|
||||
printf $out " %s, %s\n", @{$_}
|
||||
foreach $breaktable->points;
|
||||
print $out "}\n";
|
||||
}
|
||||
|
@ -106,7 +106,7 @@ sub equals {
|
||||
my ($new, $known) = @_;
|
||||
return 0 if ! $known->fields;
|
||||
return 1 if ! $new->fields;
|
||||
dieContext("Duplicate definition of record type '$a->{NAME}'");
|
||||
dieContext("Duplicate definition of record type '$known->{NAME}'");
|
||||
}
|
||||
|
||||
sub toDeclaration {
|
||||
|
@ -5,7 +5,7 @@
|
||||
# Copyright (c) 2002 The Regents of the University of California, as
|
||||
# Operator of Los Alamos National Laboratory.
|
||||
# EPICS BASE is distributed subject to a Software License Agreement found
|
||||
# in file LICENSE that is included with this distribution.
|
||||
# in file LICENSE that is included with this distribution.
|
||||
#*************************************************************************
|
||||
#
|
||||
# Convert configure/RELEASE file(s) into something else.
|
||||
@ -121,15 +121,16 @@ sub dllPath {
|
||||
unlink $outfile;
|
||||
open(OUT, ">$outfile") or die "$! creating $outfile";
|
||||
print OUT "\@ECHO OFF\n";
|
||||
print OUT "PATH \%PATH\%;", join(';', binDirs()), "\n";
|
||||
# This SET syntax is essential for supporting embedded spaces and '&'
|
||||
# characters in both the PATH variable and the new directory components
|
||||
print OUT "SET \"PATH=", join(';', binDirs(), '%PATH%'), "\"\n";
|
||||
close OUT;
|
||||
}
|
||||
|
||||
sub relPaths {
|
||||
unlink $outfile;
|
||||
open(OUT, ">$outfile") or die "$! creating $outfile";
|
||||
print OUT "export PATH=\$PATH:",
|
||||
join(':', map {m/\s/ ? "\"$_\"" : $_ } binDirs()), "\n";
|
||||
print OUT "export PATH=\"", join(':', binDirs(), '$PATH'), "\"\n";
|
||||
close OUT;
|
||||
}
|
||||
|
||||
@ -153,21 +154,21 @@ sub binDirs {
|
||||
sub cdCommands {
|
||||
die "Architecture not set (use -a option)" unless ($arch);
|
||||
my @includes = grep !m/^(RULES | TEMPLATE_TOP)$/x, @apps;
|
||||
|
||||
|
||||
unlink($outfile);
|
||||
open(OUT,">$outfile") or die "$! creating $outfile";
|
||||
|
||||
|
||||
my $startup = $cwd;
|
||||
$startup =~ s/^$root/$iocroot/o if ($opt_t);
|
||||
$startup =~ s/([\\"])/\\$1/g; # escape back-slashes and double-quotes
|
||||
|
||||
|
||||
print OUT "startup = \"$startup\"\n";
|
||||
|
||||
|
||||
my $ioc = $cwd;
|
||||
$ioc =~ s/^.*\///; # iocname is last component of directory name
|
||||
|
||||
|
||||
print OUT "putenv(\"IOC=$ioc\")\n";
|
||||
|
||||
|
||||
foreach my $app (@includes) {
|
||||
my $iocpath = my $path = $macros{$app};
|
||||
$iocpath =~ s/^$root/$iocroot/o if ($opt_t);
|
||||
@ -189,15 +190,15 @@ sub cdCommands {
|
||||
#
|
||||
sub envPaths {
|
||||
my @includes = grep !m/^ (RULES | TEMPLATE_TOP) $/x, @apps;
|
||||
|
||||
|
||||
unlink($outfile);
|
||||
open(OUT,">$outfile") or die "$! creating $outfile";
|
||||
|
||||
|
||||
my $ioc = $cwd;
|
||||
$ioc =~ s/^.*\///; # iocname is last component of directory name
|
||||
|
||||
|
||||
print OUT "epicsEnvSet(\"IOC\",\"$ioc\")\n";
|
||||
|
||||
|
||||
foreach my $app (@includes) {
|
||||
my $iocpath = my $path = $macros{$app};
|
||||
$iocpath =~ s/^$root/$iocroot/o if ($opt_t);
|
||||
@ -224,7 +225,7 @@ sub checkRelease {
|
||||
expandRelease(\%check);
|
||||
delete $check{TOP};
|
||||
delete $check{EPICS_HOST_ARCH};
|
||||
|
||||
|
||||
while (my ($parent, $ppath) = each %check) {
|
||||
if (exists $macros{$parent} &&
|
||||
AbsPath($macros{$parent}) ne AbsPath($ppath)) {
|
||||
@ -269,4 +270,3 @@ sub checkRelease {
|
||||
print "\n" if $status;
|
||||
exit $status;
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user