Compare commits
227 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6fab3cafc5 | ||
|
|
e4811d5a28 | ||
|
|
34af1a12bc | ||
|
|
0301e60629 | ||
|
|
759a737983 | ||
|
|
eeddf54b84 | ||
|
|
6f6ab3b758 | ||
|
|
59d16c56a1 | ||
|
|
58edb984c9 | ||
|
|
eb3207ee21 | ||
|
|
098ee6c063 | ||
|
|
54c02e2c24 | ||
|
|
efc7cbaf7f | ||
|
|
ede35aa152 | ||
|
|
a8e8d22c31 | ||
| c2df1c6fe5 | |||
| c5a7548526 | |||
| dc70dfd625 | |||
| 20e20cbf2b | |||
| 2e44ca586f | |||
| c9619d013a | |||
| ec650e8c3f | |||
|
|
d47fa4caa4 | ||
|
|
f0369058bb | ||
|
|
df96c0170a | ||
|
|
a411a19b2a | ||
|
|
c0db7a0beb | ||
|
|
52dfb89741 | ||
|
|
b652e8230a | ||
|
|
0efffc1bcb | ||
|
|
d7030ae8a5 | ||
|
|
24f30ede22 | ||
|
|
04ee3f2888 | ||
|
|
a373327b59 | ||
|
|
1fa98d489f | ||
|
|
5cef60a3e4 | ||
|
|
20fb6b9ef5 | ||
|
|
a249561677 | ||
|
|
96887926bf | ||
|
|
c8647ffab7 | ||
|
|
e28c914966 | ||
|
|
f086be93a9 | ||
|
|
6feaaebd75 | ||
|
|
5d92d406a1 | ||
|
|
ad1a13730a | ||
|
|
a24a84c315 | ||
|
|
6e7a715380 | ||
|
|
b88b35d117 | ||
|
|
0a20825c6e | ||
|
|
424d678abf | ||
|
|
007279cf29 | ||
|
|
752ca97f7e | ||
|
|
bf0b4d2f61 | ||
|
|
87d5c01853 | ||
|
|
956af730a9 | ||
|
|
32564b1a94 | ||
|
|
5b690d5467 | ||
|
|
9efebb6d3d | ||
|
|
1df973b234 | ||
|
|
c71ebd1221 | ||
|
|
167807f0c4 | ||
|
|
e6b8ecd55e | ||
|
|
9048aa3438 | ||
|
|
5ba8080f6e | ||
|
|
a0a7a94944 | ||
|
|
42c7dbcd21 | ||
|
|
4b8edd2b6f | ||
|
|
d82ab819ef | ||
|
|
5f02bad3fc | ||
|
|
83a685867c | ||
|
|
05311e0c94 | ||
|
|
35f4d3f8dd | ||
|
|
9864f79d67 | ||
|
|
91800787ed | ||
|
|
05b27067cf | ||
|
|
ccdd2808d9 | ||
|
|
00183fcd4b | ||
|
|
1be06fc0e9 | ||
|
|
7cdc44434c | ||
|
|
f3dfb36117 | ||
|
|
500e901e3d | ||
|
|
a3b09ef1b8 | ||
|
|
55d4801eb0 | ||
|
|
af41b95b98 | ||
|
|
c6605b5d87 | ||
|
|
9b9ed04ae2 | ||
|
|
50f9b12e7d | ||
|
|
bfd2684e58 | ||
|
|
95fcf690ad | ||
|
|
0fa3337404 | ||
|
|
e5b6ef9893 | ||
|
|
5b64eaac73 | ||
|
|
30d4a583d0 | ||
|
|
6249ef08f7 | ||
|
|
b5105740e6 | ||
|
|
818f33e78f | ||
|
|
571165df26 | ||
|
|
4df48c91f4 | ||
|
|
32652414b3 | ||
|
|
9c8aaa93cd | ||
|
|
abe819b51d | ||
|
|
cf278b4159 | ||
|
|
50594a8cc3 | ||
|
|
ce420588db | ||
|
|
4d3a27fdb0 | ||
|
|
6176a81a76 | ||
|
|
a722bb9289 | ||
|
|
19daf6136f | ||
|
|
0552f2bb72 | ||
|
|
9bb11e6e88 | ||
|
|
2f8272d3a1 | ||
|
|
7cf2a1daca | ||
|
|
d019e9787a | ||
|
|
4b884c15b1 | ||
|
|
0ace4e4a30 | ||
|
|
3cf2d3737f | ||
|
|
5997018fb0 | ||
|
|
2afa4ea390 | ||
|
|
f0c86c5cb3 | ||
|
|
8e4ffd46a0 | ||
|
|
f109371268 | ||
|
|
e566c52a78 | ||
|
|
8d0bfcbc5b | ||
|
|
3fadf4a26c | ||
|
|
9ebd841738 | ||
|
|
94b0e2621f | ||
|
|
a44ddd586e | ||
|
|
6fbf95ab18 | ||
|
|
5485adacb9 | ||
|
|
6ecc384116 | ||
|
|
66537d01bd | ||
|
|
b6f69241e1 | ||
|
|
4fc549234c | ||
|
|
0bcffb56bb | ||
|
|
60001c0571 | ||
|
|
37b95bc242 | ||
|
|
4e9bb9fe50 | ||
|
|
371cdde6df | ||
|
|
cbae8d37b3 | ||
|
|
9eb88f27d9 | ||
|
|
e329fa3296 | ||
|
|
492672c718 | ||
|
|
b4d7ebd648 | ||
|
|
0f0f9f49d6 | ||
|
|
6d3821adb8 | ||
|
|
92e97af610 | ||
|
|
b0e2e8426f | ||
|
|
3e371da8ec | ||
|
|
91941af992 | ||
|
|
d2644baf38 | ||
|
|
62092d4399 | ||
|
|
6c5d56688d | ||
|
|
a6779df21c | ||
|
|
d8c5379453 | ||
|
|
7a9801a4cb | ||
|
|
f33add020e | ||
|
|
3bebe6e873 | ||
|
|
2fbaa7f926 | ||
|
|
e55e4d7646 | ||
|
|
5efc0cbfce | ||
|
|
3a182a9eea | ||
|
|
0e56e202fc | ||
|
|
d9fd73a850 | ||
|
|
d508962211 | ||
|
|
7cb80d5a17 | ||
|
|
051e3f3bc3 | ||
|
|
db216e63a7 | ||
|
|
7a3c9eaaa0 | ||
|
|
4bece4ad92 | ||
|
|
36f11fba95 | ||
|
|
7017e54930 | ||
|
|
43b623d80e | ||
|
|
132f9105d0 | ||
|
|
1655d68ec4 | ||
|
|
e4a81bb361 | ||
|
|
d1094ee787 | ||
|
|
34baa485bc | ||
| c5012d9f73 | |||
|
|
7c168f20f4 | ||
|
|
1c3aa01846 | ||
|
|
191ff137f1 | ||
|
|
156945c458 | ||
|
|
31fcb77412 | ||
|
|
631f514c7c | ||
|
|
6e496e80d1 | ||
|
|
de7ad13b3c | ||
|
|
2256c979b0 | ||
|
|
5e2a52401f | ||
|
|
7529577e3b | ||
| 6173baed1e | |||
| 71efed8c1b | |||
| 9a09436ac8 | |||
|
|
1c96fd1cb4 | ||
|
|
8d078a0c7d | ||
|
|
8a4051964f | ||
|
|
5ef537684e | ||
|
|
c1dcd728d7 | ||
|
|
78684e0e57 | ||
|
|
f57acd2c10 | ||
|
|
ba5ade1852 | ||
|
|
8a0fc0373b | ||
|
|
4340e76445 | ||
|
|
ce910f52c3 | ||
|
|
219ab33625 | ||
|
|
e9e576f4bb | ||
|
|
5bb1138b87 | ||
|
|
955dcfc7b5 | ||
|
|
07a371703f | ||
|
|
1950a8240c | ||
|
|
a662cae239 | ||
|
|
2b3c6f2e26 | ||
|
|
b1d9c57101 | ||
|
|
446e0d4af8 | ||
|
|
2f51653a9e | ||
|
|
b9899213d4 | ||
|
|
0c12b02d4f | ||
|
|
ac12ccad38 | ||
|
|
8fdaa13c97 | ||
|
|
29fa0621d7 | ||
|
|
6063de9a8b | ||
|
|
465920fcf1 | ||
|
|
b9e9537376 | ||
|
|
fb46786ccb | ||
|
|
51191e6155 | ||
|
|
2f0a7c7342 | ||
|
|
edb9208b01 | ||
|
|
4f0cc20e2b |
@@ -33,6 +33,7 @@ skip_commits:
|
||||
- '.github/*'
|
||||
- '.tools/*'
|
||||
- '.gitattributes'
|
||||
- '.lgtm.yml'
|
||||
- '**/*.html'
|
||||
- '**/*.md'
|
||||
|
||||
|
||||
@@ -39,6 +39,7 @@ skip_commits:
|
||||
- 'startup/*'
|
||||
- '.github/*'
|
||||
- '.tools/*'
|
||||
- '.lgtm.yml'
|
||||
- '.gitattributes'
|
||||
- '**/*.html'
|
||||
- '**/*.md'
|
||||
|
||||
2
.ci
2
.ci
Submodule .ci updated: 75bae77c1d...8a2666a9de
11
.github/workflows/ci-scripts-build.yml
vendored
11
.github/workflows/ci-scripts-build.yml
vendored
@@ -15,6 +15,7 @@ on:
|
||||
- 'startup/*'
|
||||
- '.appveyor/*'
|
||||
- '.tools/*'
|
||||
- '.lgtm.yml'
|
||||
- '.gitattributes'
|
||||
- '**/*.html'
|
||||
- '**/*.md'
|
||||
@@ -24,6 +25,7 @@ on:
|
||||
- 'startup/*'
|
||||
- '.appveyor/*'
|
||||
- '.tools/*'
|
||||
- '.lgtm.yml'
|
||||
- '.gitattributes'
|
||||
- '**/*.html'
|
||||
- '**/*.md'
|
||||
@@ -85,7 +87,6 @@ jobs:
|
||||
configuration: default
|
||||
rtems: "5"
|
||||
rtems_target: RTEMS-pc686-qemu
|
||||
test: NO
|
||||
name: "Ub-20 gcc-9 + RT-5.1 pc686"
|
||||
|
||||
- os: ubuntu-20.04
|
||||
@@ -137,6 +138,7 @@ jobs:
|
||||
rtems: "4.10"
|
||||
name: "Ub-20 gcc-9 + RT-4.10"
|
||||
rtems_target: RTEMS-pc386-qemu
|
||||
test: NO
|
||||
|
||||
- os: ubuntu-20.04
|
||||
cmp: gcc
|
||||
@@ -186,7 +188,7 @@ jobs:
|
||||
- name: Build main module
|
||||
run: python .ci/cue.py build
|
||||
- name: Run main module tests
|
||||
run: python .ci/cue.py -T 20M test
|
||||
run: python .ci/cue.py -T 60M test
|
||||
- name: Upload tapfiles Artifact
|
||||
if: ${{ always() }}
|
||||
uses: actions/upload-artifact@v2
|
||||
@@ -219,11 +221,6 @@ jobs:
|
||||
cmp: gcc
|
||||
configuration: default
|
||||
|
||||
- name: "CentOS-8"
|
||||
image: centos:8
|
||||
cmp: gcc
|
||||
configuration: default
|
||||
|
||||
- name: "Fedora-33"
|
||||
image: fedora:33
|
||||
cmp: gcc
|
||||
|
||||
50
.lgtm.yml
Normal file
50
.lgtm.yml
Normal file
@@ -0,0 +1,50 @@
|
||||
# Configuration for lgtm.com
|
||||
#
|
||||
|
||||
path_classifiers:
|
||||
test:
|
||||
- exclude: /
|
||||
- test
|
||||
- "modules/*/test*"
|
||||
library:
|
||||
- modules/libcom/src/yacc
|
||||
- modules/libcom/src/flex
|
||||
template:
|
||||
- src/template
|
||||
- modules/ca/src/template
|
||||
- modules/database/src/template
|
||||
|
||||
extraction:
|
||||
cpp:
|
||||
prepare:
|
||||
packages:
|
||||
- "libreadline-dev"
|
||||
index:
|
||||
build_command:
|
||||
- "g++ --version"
|
||||
- "make --version"
|
||||
- "perl --version"
|
||||
- "make -sj2 || echo '*** Build failed, ignored for lgtm ***'"
|
||||
|
||||
python:
|
||||
index:
|
||||
include:
|
||||
- src/tools
|
||||
|
||||
# Interpreted languages to be excluded
|
||||
javascript:
|
||||
index:
|
||||
exclude:
|
||||
- "*"
|
||||
|
||||
# Compiled languages to be excluded
|
||||
java:
|
||||
index:
|
||||
build_command: "echo No Java code in this project"
|
||||
csharp:
|
||||
index:
|
||||
build_command: "echo No C# code in this project"
|
||||
go:
|
||||
index:
|
||||
build_command: "echo No Go code in this project"
|
||||
|
||||
@@ -34,6 +34,11 @@ ifeq ($(origin EPICS_HOST_ARCH), undefined)
|
||||
EHA :=
|
||||
endif
|
||||
|
||||
# Make Perl hash iteration reproducible.
|
||||
# See: https://reproducible-builds.org/docs/stable-outputs/
|
||||
#
|
||||
export PERL_HASH_SEED = 0
|
||||
|
||||
-include $(CONFIG)/RELEASE
|
||||
-include $(CONFIG)/RELEASE.$(EPICS_HOST_ARCH)
|
||||
-include $(CONFIG)/RELEASE.$(EPICS_HOST_ARCH).Common
|
||||
|
||||
@@ -47,6 +47,7 @@ OPT_CXXFLAGS_NO = -g
|
||||
|
||||
CODE_LDFLAGS = $(PROF_CXXFLAGS_$(PROFILE)) $(GPROF_CXXFLAGS_$(GPROF))
|
||||
CODE_LDFLAGS += $(ASAN_LDFLAGS_$(ENABLE_ASAN))
|
||||
OPT_LDFLAGS_NO = -g
|
||||
|
||||
PIPE_CFLAGS_YES_YES = -pipe
|
||||
PIPE_CFLAGS = $(PIPE_CFLAGS_$(GCC_PIPE)_$(GNU))
|
||||
|
||||
@@ -48,11 +48,11 @@ EPICS_VERSION = 7
|
||||
EPICS_REVISION = 0
|
||||
|
||||
# EPICS_MODIFICATION must be a number >=0 and <256
|
||||
EPICS_MODIFICATION = 6
|
||||
EPICS_MODIFICATION = 7
|
||||
|
||||
# EPICS_PATCH_LEVEL must be a number (win32 resource file requirement)
|
||||
# Not included in the official EPICS version number if zero
|
||||
EPICS_PATCH_LEVEL = 1
|
||||
EPICS_PATCH_LEVEL = 0
|
||||
|
||||
# Immediately after an official release the EPICS_PATCH_LEVEL is incremented
|
||||
# and the -DEV suffix is added (similar to the Maven -SNAPSHOT versions)
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
EPICS_CA_MAJOR_VERSION = 4
|
||||
EPICS_CA_MINOR_VERSION = 14
|
||||
EPICS_CA_MAINTENANCE_VERSION = 1
|
||||
EPICS_CA_MAINTENANCE_VERSION = 2
|
||||
|
||||
# Development flag, set to zero for release versions
|
||||
|
||||
|
||||
@@ -56,6 +56,7 @@ GNU_DIR = /usr
|
||||
# Directories
|
||||
|
||||
INSTALL_LOCATION = $(TOP)
|
||||
INSTALL_ABSOLUTE = $(abspath $(INSTALL_LOCATION))
|
||||
|
||||
INSTALL_LOCATION_LIB = $(INSTALL_LOCATION)/lib
|
||||
INSTALL_LOCATION_BIN = $(INSTALL_LOCATION)/bin
|
||||
@@ -71,23 +72,30 @@ INSTALL_DBD = $(INSTALL_LOCATION)/dbd
|
||||
INSTALL_DB = $(INSTALL_LOCATION)/db
|
||||
INSTALL_CONFIG = $(INSTALL_LOCATION)/configure
|
||||
|
||||
FINAL_LOCATION = $(shell $(PERL) $(TOOLS)/fullPathName.pl $(INSTALL_LOCATION))
|
||||
#-------------------------------------------------------
|
||||
# These are default settings that may be overridden later
|
||||
|
||||
# Directory for OS independant build created files
|
||||
COMMON_DIR = ../O.Common
|
||||
# Eventual install path (to be compiled into binaries)
|
||||
FINAL_LOCATION = $(INSTALL_ABSOLUTE)
|
||||
|
||||
# IOC's absolute path to $(TOP), may be overridden inside the application
|
||||
IOCS_APPL_TOP = $(shell $(FULLPATHNAME) $(INSTALL_LOCATION))
|
||||
# IOC's view of install path
|
||||
IOCS_APPL_TOP = $(INSTALL_ABSOLUTE)
|
||||
|
||||
#-------------------------------------------------------
|
||||
# Silencing the build - suppress messages during 'make -s'
|
||||
# How to portably check the flags to make
|
||||
define checkflags
|
||||
make-$1 = $(findstring $1,$(filter-out --%,$(MAKEFLAGS)))
|
||||
endef
|
||||
# This is extensible to most single letter flags:
|
||||
$(foreach flag,s q, $(eval $(call checkflags,$(flag))))
|
||||
|
||||
# Silent builds - suppress messages during 'make -s'
|
||||
NOP = :
|
||||
ECHO = @$(if $(filter -s,$(MFLAGS)),$(NOP),echo)
|
||||
QUIET_FLAG := $(if $(filter -s,$(MFLAGS)),-q,)
|
||||
ECHO = @$(if $(make-s),$(NOP),echo)
|
||||
QUIET_FLAG := $(if $(make-s),-q,)
|
||||
|
||||
#-------------------------------------------------------
|
||||
# Convert 'make -q' flag into '-i' for genVersionHeader.pl
|
||||
QUESTION_FLAG := $(if $(filter -q,$(MFLAGS)),-i,)
|
||||
QUESTION_FLAG := $(if $(make-q),-i,)
|
||||
|
||||
#-------------------------------------------------------
|
||||
ifdef T_A
|
||||
@@ -133,6 +141,8 @@ LIB_SUFFIX =
|
||||
SHRLIB_PREFIX = $(LIB_PREFIX)
|
||||
DLLSTUB_PREFIX = $(LIB_PREFIX)
|
||||
DLLSTUB_SUFFIX = $(LIB_SUFFIX)
|
||||
LOADABLE_SHRLIB_PREFIX = $(SHRLIB_PREFIX)
|
||||
LOADABLE_SHRLIB_SUFFIX = $(SHRLIB_SUFFIX)
|
||||
|
||||
BUILDLIB_PREFIX_YES = $(DLLSTUB_PREFIX)
|
||||
BUILDLIB_PREFIX_NO = $(LIB_PREFIX)
|
||||
@@ -153,12 +163,14 @@ CMPLR_SRC_DIRS += . $(foreach dir, .. $(SRC_DIRS), \
|
||||
ALL_SRC_DIRS = $(CMPLR_SRC_DIRS) $(OS_SRC_DIRS) $(GENERIC_SRC_DIRS)
|
||||
|
||||
#--------------------------------------------------
|
||||
# Directory for OS independant build created files
|
||||
COMMON_DIR = ../O.Common
|
||||
|
||||
# compile line include directories
|
||||
INSTALL_INCLUDES += \
|
||||
-I$(INSTALL_INCLUDE)/compiler/$(CMPLR_CLASS) \
|
||||
-I$(INSTALL_INCLUDE)/os/$(OS_CLASS) \
|
||||
-I$(INSTALL_INCLUDE)
|
||||
SRC_INCLUDES = -I$(COMMON_DIR) $(addprefix -I, $(wildcard $(ALL_SRC_DIRS)))
|
||||
INSTALL_INCLUDE_DIRS = $(INSTALL_INCLUDE)/compiler/$(CMPLR_CLASS) \
|
||||
$(INSTALL_INCLUDE)/os/$(OS_CLASS) $(INSTALL_INCLUDE)
|
||||
INSTALL_INCLUDES += $(addprefix -I, $(INSTALL_INCLUDE_DIRS))
|
||||
SRC_INCLUDES = $(addprefix -I, $(COMMON_DIR) $(wildcard $(ALL_SRC_DIRS)))
|
||||
|
||||
#--------------------------------------------------
|
||||
# Target filename definitions
|
||||
@@ -172,23 +184,27 @@ TESTSHRLIBNAME = $(TESTSHRLIBNAME_$(SHARED_LIBRARIES))
|
||||
#--------------------------------------------------
|
||||
# obj files
|
||||
|
||||
TARGET_OBJS = $($*_LDOBJS) $(addsuffix $(OBJ),$(basename $($*_OBJS) $($*_SRCS)))
|
||||
TARGET_OBJS = $($*_LDOBJS) $(addsuffix $(OBJ), \
|
||||
$(basename $($*_OBJS) $($*_SRCS)))
|
||||
|
||||
PRODUCT_OBJS = $(addsuffix $(OBJ),$(basename $(SRCS) $(USR_SRCS) $(PROD_SRCS) $(USR_OBJS) $(PROD_OBJS)))
|
||||
PRODUCT_OBJS = $(addsuffix $(OBJ), \
|
||||
$(basename $(SRCS) $(USR_SRCS) $(PROD_SRCS) $(USR_OBJS) $(PROD_OBJS)))
|
||||
PROD_LD_OBJS = $(TARGET_OBJS) $(PRODUCT_OBJS)
|
||||
|
||||
LIBRARY_OBJS = $(addsuffix $(OBJ),$(basename $(SRCS) $(USR_SRCS) $(LIB_SRCS) $(LIBSRCS) $(USR_OBJS) $(LIB_OBJS)))
|
||||
LIBRARY_OBJS = $(addsuffix $(OBJ), \
|
||||
$(basename $(SRCS) $(USR_SRCS) $(LIB_SRCS) $(LIBSRCS) $(USR_OBJS) $(LIB_OBJS)))
|
||||
LIBRARY_LD_OBJS = $(TARGET_OBJS) $(LIBRARY_OBJS)
|
||||
|
||||
#--------------------------------------------------
|
||||
# Windows resource files
|
||||
|
||||
TARGET_RESS = $(if $(RES),$(addsuffix $(RES),$(basename $($*_RCS))),)
|
||||
TARGET_RESS = $(if $(RES), $(addsuffix $(RES), $(basename $($*_RCS))))
|
||||
|
||||
PROD_RESS = $(if $(RES),$(addsuffix $(RES),$(basename $(RCS) $(PROD_RCS))),)
|
||||
PROD_RESS = $(if $(RES), $(addsuffix $(RES), $(basename $(RCS) $(PROD_RCS))))
|
||||
PROD_LD_RESS = $(TARGET_RESS) $(PROD_RESS)
|
||||
|
||||
LIBRARY_RESS = $(if $(RES),$(addsuffix $(RES),$(basename $(RCS) $(LIB_RCS) $(LIBRARY_RCS))),)
|
||||
LIBRARY_RESS = $(if $(RES), $(addsuffix $(RES), \
|
||||
$(basename $(RCS) $(LIB_RCS) $(LIBRARY_RCS))))
|
||||
LIBRARY_LD_RESS = $(TARGET_RESS) $(LIBRARY_RESS)
|
||||
|
||||
#--------------------------------------------------
|
||||
@@ -257,6 +273,7 @@ WARN_CXXFLAGS = $(WARN_CXXFLAGS_$($(BUILD_CLASS)_WARN))
|
||||
OPT_CPPFLAGS = $(OPT_CPPFLAGS_$($(BUILD_CLASS)_OPT))
|
||||
OPT_CFLAGS = $(OPT_CFLAGS_$($(BUILD_CLASS)_OPT))
|
||||
OPT_CXXFLAGS = $(OPT_CXXFLAGS_$($(BUILD_CLASS)_OPT))
|
||||
OPT_LDFLAGS = $(OPT_LDFLAGS_$($(BUILD_CLASS)_OPT))
|
||||
|
||||
# Static build flags
|
||||
STATIC_CFLAGS = $(STATIC_CFLAGS_$(STATIC_BUILD))
|
||||
@@ -265,19 +282,19 @@ STATIC_LDFLAGS = $(STATIC_LDFLAGS_$(STATIC_BUILD))
|
||||
STATIC_LDLIBS = $(STATIC_LDLIBS_$(STATIC_BUILD))
|
||||
|
||||
#--------------------------------------------------
|
||||
# cflags for shared library src files (from SHRLIB_CFLAGS)
|
||||
LIBRARY_SRCS=$(basename $(foreach lib,$(LIBRARY) $(TESTLIBRARY) $(LOADABLE_LIBRARY),$($(lib)_OBJSNAME) $(LIBRARY_OBJS)))
|
||||
LIBRARY_SRC_CFLAGS=$($(patsubst $*,SHRLIB,$(findstring $*,$(LIBRARY_SRCS)))_CFLAGS)
|
||||
# cflags for shared library src files
|
||||
LIBRARY_SRCS = $(basename $(foreach lib, \
|
||||
$(LIBRARY) $(TESTLIBRARY) $(LOADABLE_LIBRARY), \
|
||||
$($(lib)_OBJSNAME) $(LIBRARY_OBJS)))
|
||||
LIBRARY_SRC_CFLAGS = $(if $(findstring $*, $(LIBRARY_SRCS)), $(SHRLIB_CFLAGS))
|
||||
|
||||
#--------------------------------------------------
|
||||
# prefix, suffix, and ldflags for loadable shared libraries
|
||||
TARGET_LIB_LDFLAGS=$($(patsubst $*,LOADABLE_,$(findstring $*,$(LOADABLE_LIBRARY)))SHRLIB_LDFLAGS)
|
||||
LOADABLE_SHRLIB_PREFIX=$(SHRLIB_PREFIX)
|
||||
LOADABLE_SHRLIB_SUFFIX=$(SHRLIB_SUFFIX)
|
||||
# ldflags for loadable and shared libraries
|
||||
TARGET_LIB_LDFLAGS = $(if $(findstring $*, $(LOADABLE_LIBRARY)), \
|
||||
$(LOADABLE_SHRLIB_LDFLAGS), $(SHRLIB_LDFLAGS))
|
||||
|
||||
#--------------------------------------------------
|
||||
# Command-line input support default
|
||||
COMMANDLINE_LIBRARY = EPICS
|
||||
OP_SYS_LDLIBS += $(LDLIBS_$(COMMANDLINE_LIBRARY))
|
||||
OP_SYS_LDFLAGS += $(LDFLAGS_$(COMMANDLINE_LIBRARY))
|
||||
RUNTIME_LDFLAGS += $(RUNTIME_LDFLAGS_$(COMMANDLINE_LIBRARY))
|
||||
@@ -285,31 +302,31 @@ RUNTIME_LDFLAGS += $(RUNTIME_LDFLAGS_$(COMMANDLINE_LIBRARY))
|
||||
#--------------------------------------------------
|
||||
# Flags
|
||||
|
||||
INCLUDES = -I. $(SRC_INCLUDES) $(INSTALL_INCLUDES) $(RELEASE_INCLUDES)\
|
||||
$(TARGET_INCLUDES) $(USR_INCLUDES) $(CMD_INCLUDES) $(OP_SYS_INCLUDES)\
|
||||
$($(BUILD_CLASS)_INCLUDES)
|
||||
INCLUDES = -I. $(SRC_INCLUDES) $(INSTALL_INCLUDES) $(RELEASE_INCLUDES) \
|
||||
$(TARGET_INCLUDES) $(USR_INCLUDES) $(CMD_INCLUDES) $(OP_SYS_INCLUDES) \
|
||||
$($(BUILD_CLASS)_INCLUDES)
|
||||
|
||||
CFLAGS = $($(BUILD_CLASS)_CFLAGS) $(POSIX_CFLAGS) $(OPT_CFLAGS)\
|
||||
$(DEBUG_CFLAGS) $(PIPE_CFLAGS) $(WARN_CFLAGS) $(TARGET_CFLAGS)\
|
||||
$(USR_CFLAGS) $(CMD_CFLAGS) $(ARCH_DEP_CFLAGS) $(CODE_CFLAGS)\
|
||||
$(STATIC_CFLAGS) $(OP_SYS_CFLAGS) $(LIBRARY_SRC_CFLAGS)
|
||||
CFLAGS = $($(BUILD_CLASS)_CFLAGS) $(POSIX_CFLAGS) $(OPT_CFLAGS) \
|
||||
$(DEBUG_CFLAGS) $(PIPE_CFLAGS) $(WARN_CFLAGS) $(TARGET_CFLAGS) \
|
||||
$(USR_CFLAGS) $(CMD_CFLAGS) $(ARCH_DEP_CFLAGS) $(CODE_CFLAGS) \
|
||||
$(STATIC_CFLAGS) $(OP_SYS_CFLAGS) $(LIBRARY_SRC_CFLAGS)
|
||||
|
||||
CXXFLAGS = $($(BUILD_CLASS)_CXXFLAGS) $(POSIX_CXXFLAGS) $(OPT_CXXFLAGS)\
|
||||
$(DEBUG_CXXFLAGS) $(PIPE_CFLAGS) $(WARN_CXXFLAGS) $(TARGET_CXXFLAGS)\
|
||||
$(USR_CXXFLAGS) $(CMD_CXXFLAGS) $(ARCH_DEP_CXXFLAGS) $(CODE_CXXFLAGS)\
|
||||
$(STATIC_CXXFLAGS) $(OP_SYS_CXXFLAGS) $(LIBRARY_SRC_CFLAGS)
|
||||
CXXFLAGS = $($(BUILD_CLASS)_CXXFLAGS) $(POSIX_CXXFLAGS) $(OPT_CXXFLAGS) \
|
||||
$(DEBUG_CXXFLAGS) $(PIPE_CFLAGS) $(WARN_CXXFLAGS) $(TARGET_CXXFLAGS) \
|
||||
$(USR_CXXFLAGS) $(CMD_CXXFLAGS) $(ARCH_DEP_CXXFLAGS) $(CODE_CXXFLAGS) \
|
||||
$(STATIC_CXXFLAGS) $(OP_SYS_CXXFLAGS) $(LIBRARY_SRC_CFLAGS)
|
||||
|
||||
LDFLAGS = $(OPT_LDFLAGS) $(TARGET_LDFLAGS) $(USR_LDFLAGS) $(CMD_LDFLAGS)\
|
||||
$(POSIX_LDFLAGS) $(ARCH_DEP_LDFLAGS) $(DEBUG_LDFLAGS) $(OP_SYS_LDFLAGS)\
|
||||
$($(BUILD_CLASS)_LDFLAGS) $(RUNTIME_LDFLAGS) $(CODE_LDFLAGS)
|
||||
LDFLAGS = $(OPT_LDFLAGS) $(TARGET_LDFLAGS) $(USR_LDFLAGS) $(CMD_LDFLAGS) \
|
||||
$(POSIX_LDFLAGS) $(ARCH_DEP_LDFLAGS) $(DEBUG_LDFLAGS) $(OP_SYS_LDFLAGS) \
|
||||
$($(BUILD_CLASS)_LDFLAGS) $(RUNTIME_LDFLAGS) $(CODE_LDFLAGS)
|
||||
|
||||
LDLIBS = $(POSIX_LDLIBS) $(ARCH_DEP_LDLIBS) $(DEBUG_LDLIBS) $(OP_SYS_LDLIBS)\
|
||||
$(GNU_LDLIBS_$(GNU))
|
||||
LDLIBS = $(POSIX_LDLIBS) $(ARCH_DEP_LDLIBS) $(DEBUG_LDLIBS) $(OP_SYS_LDLIBS) \
|
||||
$(GNU_LDLIBS_$(GNU))
|
||||
|
||||
CPPFLAGS = $($(BUILD_CLASS)_CPPFLAGS) $(POSIX_CPPFLAGS) $(OPT_CPPFLAGS)\
|
||||
$(DEBUG_CPPFLAGS) $(WARN_CPPFLAGS) $(BASE_CPPFLAGS) $(TARGET_CPPFLAGS)\
|
||||
$(USR_CPPFLAGS) $(CMD_CPPFLAGS) $(ARCH_DEP_CPPFLAGS) $(OP_SYS_CPPFLAGS)\
|
||||
$(OP_SYS_INCLUDE_CPPFLAGS) $(CODE_CPPFLAGS) $(API_CPPFLAGS)
|
||||
CPPFLAGS = $($(BUILD_CLASS)_CPPFLAGS) $(POSIX_CPPFLAGS) $(OPT_CPPFLAGS) \
|
||||
$(DEBUG_CPPFLAGS) $(WARN_CPPFLAGS) $(BASE_CPPFLAGS) $(TARGET_CPPFLAGS) \
|
||||
$(USR_CPPFLAGS) $(CMD_CPPFLAGS) $(ARCH_DEP_CPPFLAGS) $(OP_SYS_CPPFLAGS) \
|
||||
$(OP_SYS_INCLUDE_CPPFLAGS) $(CODE_CPPFLAGS) $(API_CPPFLAGS)
|
||||
|
||||
#--------------------------------------------------
|
||||
# ar definition default
|
||||
@@ -396,7 +413,7 @@ INSTALL_DOCS = $(DOCS:%= $(INSTALL_DOC)/%)
|
||||
INSTALL_HTMLS = $(HTMLS:%= $(INSTALL_HTML)/$(HTMLS_DIR)/%)
|
||||
|
||||
INSTALL_TEMPLATE = $(addprefix $(INSTALL_TEMPLATES_SUBDIR)/, \
|
||||
$(subst $(CONFIG),top/configure,$(TEMPLATES)))
|
||||
$(subst $(CONFIG),top/configure,$(TEMPLATES)))
|
||||
INSTALL_CONFIGS = $(CONFIGS:%= $(INSTALL_CONFIG)/%)
|
||||
|
||||
INSTALL_BIN_INSTALLS = $(addprefix $(INSTALL_BIN)/,$(notdir $(BIN_INSTALLS)))
|
||||
@@ -431,42 +448,41 @@ INSTALL_INC += $(foreach inc, $(INC), \
|
||||
$(CMPLR_INSTALL_INC) \
|
||||
$(OS_INSTALL_INC) \
|
||||
$(GENERIC_INSTALL_INC) \
|
||||
$(GENERATED_INSTALL_INC) ) )
|
||||
INSTALL_INC += $(addprefix $(INSTALL_INCLUDE)/os/$(OS_CLASS)/, $(INC_$(OS_CLASS)) )
|
||||
$(GENERATED_INSTALL_INC)))
|
||||
INSTALL_INC += $(addprefix $(INSTALL_INCLUDE)/os/$(OS_CLASS)/, $(INC_$(OS_CLASS)))
|
||||
|
||||
#
|
||||
# Rule 0
|
||||
#
|
||||
CMPLR_INSTALL_INC = $(addprefix $(INSTALL_INCLUDE)/compiler/$(CMPLR_CLASS)/, $(INSTALL_INC_jjj) )
|
||||
INSTALL_INC_jjj = $(foreach dir, $(CMPLR_SRC_DIRS), $(INSTALL_INC_iii) )
|
||||
INSTALL_INC_iii = $(subst $(dir)/, , $(INSTALL_INC_hhh) )
|
||||
INSTALL_INC_hhh = $(wildcard $(addsuffix /$(inc), $(dir)) )
|
||||
CMPLR_INSTALL_INC = $(addprefix $(INSTALL_INCLUDE)/compiler/$(CMPLR_CLASS)/, \
|
||||
$(foreach dir, $(CMPLR_SRC_DIRS), \
|
||||
$(subst $(dir)/,, $(wildcard $(addsuffix /$(inc), $(dir))))))
|
||||
|
||||
#
|
||||
# Rule 1
|
||||
#
|
||||
OS_INSTALL_INC = $(addprefix $(INSTALL_INCLUDE)/os/$(OS_CLASS)/, $(INSTALL_INC_ggg) )
|
||||
INSTALL_INC_ggg = $(foreach dir, $(OS_SRC_DIRS), $(INSTALL_INC_fff) )
|
||||
INSTALL_INC_fff = $(subst $(dir)/, , $(INSTALL_INC_eee) )
|
||||
INSTALL_INC_eee = $(wildcard $(addsuffix /$(inc), $(dir)) )
|
||||
OS_INSTALL_INC = $(addprefix $(INSTALL_INCLUDE)/os/$(OS_CLASS)/, \
|
||||
$(foreach dir, $(OS_SRC_DIRS), \
|
||||
$(subst $(dir)/,, $(wildcard $(addsuffix /$(inc), $(dir))))))
|
||||
|
||||
#
|
||||
# Rule 2
|
||||
#
|
||||
GENERIC_INSTALL_INC = $(addprefix $(INSTALL_INCLUDE)/, $(INSTALL_INC_ccc) )
|
||||
INSTALL_INC_ccc = $(foreach dir, .. $(SRC_DIRS), $(INSTALL_INC_bbb) )
|
||||
INSTALL_INC_bbb = $(subst $(dir)/, , $(INSTALL_INC_aaa) )
|
||||
INSTALL_INC_aaa = $(wildcard $(addsuffix /$(inc), $(dir)) )
|
||||
GENERIC_INSTALL_INC = $(addprefix $(INSTALL_INCLUDE)/, \
|
||||
$(foreach dir, .. $(SRC_DIRS), \
|
||||
$(subst $(dir)/,, $(wildcard $(addsuffix /$(inc), $(dir))))))
|
||||
|
||||
#
|
||||
# Rule 3
|
||||
#
|
||||
GENERATED_INSTALL_INC = $(INSTALL_INCLUDE)/$(inc)
|
||||
|
||||
COMMON_INC += $(filter $(COMMON_DIR)/%, $(foreach file, $(INC), \
|
||||
$(firstword $(SOURCE_INC) $(COMMON_DIR)/$(file) ) ) )
|
||||
SOURCE_INC = $(wildcard $(file) $(SOURCE_INC_bbb) )
|
||||
SOURCE_INC_bbb = $(foreach dir, $(ALL_SRC_DIRS), $(SOURCE_INC_aaa) )
|
||||
SOURCE_INC_aaa = $(addsuffix /$(file), $(dir) )
|
||||
#---------------------------------------------------------------
|
||||
# Files listed in INC that must first be created in O.Common
|
||||
COMMON_INC += $(filter $(COMMON_DIR)/%, \
|
||||
$(foreach file, $(INC), \
|
||||
$(firstword $(wildcard $(file) \
|
||||
$(foreach dir, $(ALL_SRC_DIRS), \
|
||||
$(addsuffix /$(file), $(dir)))) $(COMMON_DIR)/$(file))))
|
||||
|
||||
endif
|
||||
|
||||
@@ -5,22 +5,16 @@
|
||||
# in file LICENSE that is included with this distribution.
|
||||
#*************************************************************************
|
||||
|
||||
# Set EPICS_DATABASE if necessary
|
||||
ifndef EPICS_DATABASE
|
||||
EPICS_DATABASE = $(if $(BUILDING_DATABASE),$(INSTALL_LOCATION),$(EPICS_BASE))
|
||||
# Our locally-built tools
|
||||
DBEXPAND = $(PERL) $(EPICS_BASE_HOST_BIN)/dbdExpand.pl
|
||||
DBTORECORDTYPEH = $(PERL) $(EPICS_BASE_HOST_BIN)/dbdToRecordtypeH.pl
|
||||
DBTOMENUH = $(PERL) $(EPICS_BASE_HOST_BIN)/dbdToMenuH.pl
|
||||
DBDTOHTML = $(PERL) $(EPICS_BASE_HOST_BIN)/dbdToHtml.pl
|
||||
REGISTERRECORDDEVICEDRIVER = $(PERL) $(EPICS_BASE_HOST_BIN)/registerRecordDeviceDriver.pl
|
||||
|
||||
# Paths to tools built here
|
||||
EPICS_DATABASE_HOST_BIN = $(EPICS_DATABASE)/bin/$(EPICS_HOST_ARCH)
|
||||
endif
|
||||
|
||||
# Set location of locally-built tools
|
||||
MAKEBPT = $(EPICS_DATABASE_HOST_BIN)/makeBpt$(HOSTEXE)
|
||||
DBEXPAND = $(PERL) $(EPICS_DATABASE_HOST_BIN)/dbdExpand.pl
|
||||
DBTORECORDTYPEH = $(PERL) $(EPICS_DATABASE_HOST_BIN)/dbdToRecordtypeH.pl
|
||||
DBTOMENUH = $(PERL) $(EPICS_DATABASE_HOST_BIN)/dbdToMenuH.pl
|
||||
DBDTOHTML = $(PERL) $(EPICS_DATABASE_HOST_BIN)/dbdToHtml.pl
|
||||
REGISTERRECORDDEVICEDRIVER = $(PERL) $(EPICS_DATABASE_HOST_BIN)/registerRecordDeviceDriver.pl
|
||||
MSI3_15 = $(EPICS_DATABASE_HOST_BIN)/msi$(HOSTEXE)
|
||||
# Windows can need these paths to be quoted
|
||||
MAKEBPT = "$(EPICS_BASE_HOST_BIN)/makeBpt$(HOSTEXE)"
|
||||
MSI3_15 = "$(EPICS_BASE_HOST_BIN)/msi$(HOSTEXE)"
|
||||
|
||||
# Libraries needed to link a basic IOC
|
||||
EPICS_BASE_IOC_LIBS = dbRecStd dbCore ca Com
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# Version number for the database APIs and shared library
|
||||
|
||||
EPICS_DATABASE_MAJOR_VERSION = 3
|
||||
EPICS_DATABASE_MINOR_VERSION = 21
|
||||
EPICS_DATABASE_MINOR_VERSION = 22
|
||||
EPICS_DATABASE_MAINTENANCE_VERSION = 0
|
||||
|
||||
# Development flag, set to zero for release versions
|
||||
|
||||
@@ -5,9 +5,10 @@
|
||||
# in file LICENSE that is included with this distribution.
|
||||
#*************************************************************************
|
||||
|
||||
# Set location of locally generated tools
|
||||
YACC = $(abspath $(EPICS_BASE)/bin/$(EPICS_HOST_ARCH))/antelope$(HOSTEXE)
|
||||
LEX = $(abspath $(EPICS_BASE)/bin/$(EPICS_HOST_ARCH))/e_flex$(HOSTEXE) \
|
||||
# Our locally-built tools
|
||||
# Windows can need these paths to be quoted
|
||||
YACC = "$(EPICS_BASE_HOST_BIN)/antelope$(HOSTEXE)"
|
||||
LEX = "$(EPICS_BASE_HOST_BIN)/e_flex$(HOSTEXE)" \
|
||||
-S$(EPICS_BASE)/include/flex.skel.static
|
||||
|
||||
# Default stack size for osiThread
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# Version number for the libcom APIs and shared library
|
||||
|
||||
EPICS_LIBCOM_MAJOR_VERSION = 3
|
||||
EPICS_LIBCOM_MINOR_VERSION = 21
|
||||
EPICS_LIBCOM_MINOR_VERSION = 22
|
||||
EPICS_LIBCOM_MAINTENANCE_VERSION = 0
|
||||
|
||||
# Development flag, set to zero for release versions
|
||||
|
||||
@@ -366,6 +366,7 @@ $(COMMON_DIR)/menu%.h: ../menu%.dbd
|
||||
# DBD files
|
||||
|
||||
$(COMMON_DIR)/bpt%.dbd: bpt%.data
|
||||
$(ECHO) "Converting data from $<"
|
||||
@$(RM) $(notdir $@)
|
||||
$(MAKEBPT) $< $(notdir $@)
|
||||
@$(MV) $(notdir $@) $@
|
||||
@@ -526,15 +527,15 @@ $(foreach file, $(DB_INSTALLS), $(eval $(call DB_INSTALLS_template, $(file))))
|
||||
#---------------------------------------------------------------
|
||||
# register record,device,driver support
|
||||
|
||||
%_registerRecordDeviceDriver.cpp: $(COMMON_DIR)/%.dbd
|
||||
%_registerRecordDeviceDriver.cpp: $(COMMON_DIR)/%.dbd $(EPICS_BASE_HOST_BIN)/registerRecordDeviceDriver.pl
|
||||
@$(RM) $@
|
||||
$(REGISTERRECORDDEVICEDRIVER) $(REGRDDFLAGS) -o $@ $< $(basename $@) $(IOCS_APPL_TOP)
|
||||
|
||||
%_registerRecordDeviceDriver.cpp: %.dbd
|
||||
%_registerRecordDeviceDriver.cpp: %.dbd $(EPICS_BASE_HOST_BIN)/registerRecordDeviceDriver.pl
|
||||
@$(RM) $@
|
||||
$(REGISTERRECORDDEVICEDRIVER) $(REGRDDFLAGS) -o $@ $< $(basename $@) $(IOCS_APPL_TOP)
|
||||
|
||||
%_registerRecordDeviceDriver.cpp: ../%.dbd
|
||||
%_registerRecordDeviceDriver.cpp: ../%.dbd $(EPICS_BASE_HOST_BIN)/registerRecordDeviceDriver.pl
|
||||
@$(RM) $@
|
||||
$(REGISTERRECORDDEVICEDRIVER) $(REGRDDFLAGS) -o $@ $< $(basename $@) $(IOCS_APPL_TOP)
|
||||
|
||||
|
||||
@@ -207,7 +207,7 @@ endif
|
||||
checkRelease:
|
||||
+$(CONVERTRELEASE) checkRelease
|
||||
warnRelease:
|
||||
$(CONVERTRELEASE) checkRelease
|
||||
-$(CONVERTRELEASE) checkRelease
|
||||
noCheckRelease:
|
||||
ifeq ($(EPICS_HOST_ARCH),$(T_A))
|
||||
$(info Warning: RELEASE file consistency checks have been disabled)
|
||||
@@ -373,11 +373,11 @@ $(MODNAME): %$(MODEXT): %$(EXE)
|
||||
# Automated testing
|
||||
|
||||
runtests: run-tap-tests
|
||||
run-tap-tests: $(TESTSCRIPTS.t)
|
||||
run-tap-tests: | build
|
||||
ifneq ($(TESTSCRIPTS.t),)
|
||||
ifdef RUNTESTS_ENABLED
|
||||
$(ECHO) "$(PROVE) $^"
|
||||
@$(PROVE) $^ || $(PROVE_FAILURE)
|
||||
$(ECHO) "$(PROVE) $(TESTSCRIPTS.t)"
|
||||
@$(PROVE) $(TESTSCRIPTS.t) || $(PROVE_FAILURE)
|
||||
endif
|
||||
endif
|
||||
|
||||
@@ -390,8 +390,8 @@ test-results: tap-results
|
||||
tap-results: $(TAPFILES)
|
||||
ifneq ($(strip $(TAPFILES)),)
|
||||
ifdef RUNTESTS_ENABLED
|
||||
$(ECHO) "$(PROVE.tap) $^"
|
||||
@$(PROVE.tap) $^ || $(PROVE_FAILURE)
|
||||
$(ECHO) "$(PROVE.tap) $(TAPFILES)"
|
||||
@$(PROVE.tap) $(TAPFILES) || $(PROVE_FAILURE)
|
||||
endif
|
||||
|
||||
CURRENT_TAPFILES := $(wildcard $(TAPFILES))
|
||||
@@ -407,7 +407,7 @@ ifneq ($(CURRENT_JUNITFILES),)
|
||||
endif
|
||||
|
||||
# A .tap file is the output from running the associated test script
|
||||
$(TAPFILES.t): %.tap: %.t
|
||||
$(TAPFILES.t): %.tap: %.t | build
|
||||
ifdef RUNTESTS_ENABLED
|
||||
$(ECHO) "$(PERL) $< -tap > $@"
|
||||
@$(PERL) $< -tap > $@ || $(TAPFILE_FAILURE)
|
||||
|
||||
@@ -27,7 +27,7 @@ $(foreach file,$(MAKEFILE_LIST), \
|
||||
|
||||
PRINT_Var = $(@:PRINT.%=%)
|
||||
PRINT.%:
|
||||
@echo $(PRINT_Var) = '$($(PRINT_Var))'
|
||||
@+echo $(PRINT_Var) = '$($(PRINT_Var))'
|
||||
|
||||
.PHONY: PRINT PRINT.%
|
||||
|
||||
|
||||
@@ -47,7 +47,7 @@ vpath %@ $(USR_VPATH) $(ALL_SRC_DIRS)
|
||||
# INC += myVersion.h
|
||||
|
||||
# Default settings
|
||||
EXPAND_TOOL ?= $(PERL) $(TOOLS)/expandVars.pl
|
||||
EXPAND_TOOL ?= $(PERL) $(TOOLS)/expandVars.pl $(QUIET_FLAG)
|
||||
|
||||
EXPANDARCH = -a $(T_A)
|
||||
EXPANDFLAGS += -t $(INSTALL_LOCATION)
|
||||
@@ -55,22 +55,17 @@ EXPANDFLAGS += $(addprefix -D ,$(EXPAND_VARS) $($@_EXPAND_VARS))
|
||||
EXPANDFLAGS += $(foreach var, $(EXPAND_ME) $($@_EXPAND_ME), \
|
||||
-D$(var)="$(strip $($(var)))")
|
||||
|
||||
# The names of files to be expanded must end with '@'
|
||||
# Output files
|
||||
EXPANDED = $(EXPAND:%@=%)
|
||||
EXPANDED_COM = $(EXPAND_COMMON:%@=%)
|
||||
EXPANDED_COMMON = $(EXPANDED_COM:%=$(COMMON_DIR)/%)
|
||||
EXPANDED_COMMON = $(EXPAND_COMMON:%@=$(COMMON_DIR)/%)
|
||||
|
||||
$(EXPANDED): %: %@
|
||||
$(ECHO) "Expanding $< to $@"
|
||||
@$(RM) $@
|
||||
$(EXPAND_TOOL) $(EXPANDARCH) $(EXPANDFLAGS) $($@_EXPANDFLAGS) $< $@
|
||||
|
||||
$(EXPANDED_COM): %: %@
|
||||
$(EXPANDED_COMMON): $(COMMON_DIR)/%: %@
|
||||
$(ECHO) "Expanding $< to $(COMMON_DIR)/$@"
|
||||
@$(RM) $@
|
||||
$(EXPAND_TOOL) $(EXPANDFLAGS) $($@_EXPANDFLAGS) $< $@
|
||||
$(EXPANDED_COMMON): $(COMMON_DIR)/%: %
|
||||
@$(MV) $< $@
|
||||
|
||||
clean: expand_clean
|
||||
|
||||
|
||||
@@ -22,13 +22,14 @@
|
||||
# 7. Submodules must have a configure/CONFIG_SITE file that contains
|
||||
# -include $(TOP)/../CONFIG_SITE.local
|
||||
|
||||
# Add checked-out submodules to DIRS
|
||||
LIVE_SUBMODULES = $(subst /Makefile,,$(wildcard $(addsuffix /Makefile, $(SUBMODULES))))
|
||||
DIRS += $(LIVE_SUBMODULES)
|
||||
# Add checked-out submodules to DIRS, unless INSTALL_LOCATION is empty
|
||||
LIVE_SUBMODULES = $(subst /Makefile,, \
|
||||
$(wildcard $(addsuffix /Makefile, $(SUBMODULES))))
|
||||
live = $(if $(wildcard $(INSTALL_CONFIG)/RULES_TOP),LIVE,DEAD)
|
||||
DIRS += $($(live)_SUBMODULES)
|
||||
|
||||
include $(CONFIG)/RULES_DIRS
|
||||
|
||||
INSTALL_LOCATION_ABS := $(abspath $(INSTALL_LOCATION))
|
||||
RELEASE_LOCAL := RELEASE.$(EPICS_HOST_ARCH).local
|
||||
|
||||
# Ensure that RELEASE.<host>.local exists before doing anything else
|
||||
@@ -38,14 +39,13 @@ all host $(DIRS) $(ARCHS) $(ACTIONS) $(dirActionTargets) $(dirArchTargets) \
|
||||
# Convenience target
|
||||
RELEASE.host: $(RELEASE_LOCAL)
|
||||
|
||||
$(RELEASE_LOCAL): Makefile CONFIG_SITE.local
|
||||
$(RELEASE_LOCAL): Makefile $(CONFIG)/CONFIG_SITE \
|
||||
$(wildcard $(CONFIG)/CONFIG_SITE.local)
|
||||
$(ECHO) Creating $@ with
|
||||
$(ECHO) " $(PARENT_MODULE) = $(INSTALL_LOCATION_ABS)"
|
||||
@echo $(PARENT_MODULE) = $(INSTALL_LOCATION_ABS)> $@
|
||||
realclean:
|
||||
$(RM) $(wildcard RELEASE.*.local)
|
||||
$(ECHO) " $(PARENT_MODULE) = $(INSTALL_ABSOLUTE)"
|
||||
@echo $(PARENT_MODULE) = $(INSTALL_ABSOLUTE)> $@
|
||||
|
||||
.PHONY: RELEASE.host realclean
|
||||
.PHONY: RELEASE.host
|
||||
|
||||
# Testing: Combine test failure logs from the live submodules
|
||||
TESTS_FAILED_LOGS = $(wildcard $(addsuffix /$(TESTS_FAILED_LOG), \
|
||||
|
||||
@@ -23,35 +23,42 @@ ifndef DISABLE_TOP_RULES
|
||||
# Rules for a regular application top directory
|
||||
#
|
||||
|
||||
distclean: realclean cvsclean realuninstall
|
||||
# When run by 'make distclean' the realuninstall target also
|
||||
# removes any modules/RELEASE.<host>.local files
|
||||
distclean: realclean cvsclean realuninstall
|
||||
|
||||
realuninstall: uninstallDirs
|
||||
realuninstall: uninstallDirs
|
||||
$(RMDIR) $(INSTALL_LOCATION_BIN) $(INSTALL_LOCATION_LIB)
|
||||
ifeq (modules,$(filter modules,$(DIRS)))
|
||||
ifeq (distclean,$(filter distclean,$(MAKECMDGOALS)))
|
||||
$(RM) $(wildcard modules/RELEASE.*.local)
|
||||
endif
|
||||
endif
|
||||
|
||||
UNINSTALL_DIRS += $(INSTALL_DB) $(INSTALL_DBD) $(INSTALL_DOC) $(INSTALL_HTML)
|
||||
UNINSTALL_DIRS += $(INSTALL_INCLUDE) $(INSTALL_TEMPLATES) $(DIRECTORY_TARGETS)
|
||||
ifneq ($(INSTALL_LOCATION),$(TOP))
|
||||
UNINSTALL_DIRS += $(INSTALL_CONFIG)
|
||||
endif
|
||||
uninstallDirs: | clean
|
||||
UNINSTALL_DIRS += $(INSTALL_DB) $(INSTALL_DBD) $(INSTALL_DOC) $(INSTALL_HTML)
|
||||
UNINSTALL_DIRS += $(INSTALL_INCLUDE) $(INSTALL_TEMPLATES) $(DIRECTORY_TARGETS)
|
||||
ifneq ($(INSTALL_LOCATION),$(TOP))
|
||||
UNINSTALL_DIRS += $(INSTALL_CONFIG)
|
||||
endif
|
||||
uninstallDirs:
|
||||
$(RMDIR) $(UNINSTALL_DIRS)
|
||||
|
||||
# Remove the bin and lib directories if they have no sub-directories
|
||||
#
|
||||
EMPTY_INSTALL_DIRS = \
|
||||
$(if $(wildcard $(INSTALL_LOCATION_BIN)/*),,$(INSTALL_LOCATION_BIN)) \
|
||||
$(if $(wildcard $(INSTALL_LOCATION_LIB)/*),,$(INSTALL_LOCATION_LIB))
|
||||
uninstall: archuninstall uninstallDirs | clean
|
||||
EMPTY_INSTALL_DIRS = \
|
||||
$(if $(wildcard $(INSTALL_LOCATION_BIN)/*),,$(INSTALL_LOCATION_BIN)) \
|
||||
$(if $(wildcard $(INSTALL_LOCATION_LIB)/*),,$(INSTALL_LOCATION_LIB))
|
||||
uninstall: archuninstall uninstallDirs
|
||||
$(RMDIR) $(EMPTY_INSTALL_DIRS)
|
||||
|
||||
archuninstall: $(addprefix uninstall$(DIVIDER),$(BUILD_ARCHS))
|
||||
archuninstall: $(addprefix uninstall$(DIVIDER),$(BUILD_ARCHS))
|
||||
|
||||
uninstall$(DIVIDER)%: | clean
|
||||
uninstall$(DIVIDER)%:
|
||||
$(RMDIR) $(addsuffix /$(subst uninstall$(DIVIDER),,$@), \
|
||||
$(INSTALL_LOCATION_BIN) $(INSTALL_LOCATION_LIB))
|
||||
|
||||
# Only run this at the top of the parent
|
||||
runtests test-results:
|
||||
# Only run this at the top of the parent
|
||||
runtests test-results:
|
||||
@$(SHOWTESTFAILURES)
|
||||
|
||||
else
|
||||
@@ -76,7 +83,6 @@ help:
|
||||
@echo " inc - Installs header, dbd and html files"
|
||||
@echo " build - Builds and installs all targets"
|
||||
@echo " install - Builds and installs all targets"
|
||||
@echo " buildInstall - Same as install (deprecated)"
|
||||
@echo " clean - Removes the O.<arch> dirs created by running make"
|
||||
@echo " In O.<arch> dir, clean removes build created files"
|
||||
@echo " realclean - Removes ALL O.<arch> dirs"
|
||||
@@ -101,7 +107,8 @@ ifndef DISABLE_TOP_RULES
|
||||
@echo " uninstall$(DIVIDER)<arch> - Remove bin & lib directories for <arch> only."
|
||||
@echo " uninstall - Remove install directories created by this hostarch."
|
||||
@echo " realuninstall - Removes ALL install dirs"
|
||||
@echo " distclean - Same as realclean cvsclean realuninstall."
|
||||
@echo " distclean - Does realclean cvsclean realuninstall and deletes any"
|
||||
@echo " generated modules/RELEASE.<host>.local files"
|
||||
endif
|
||||
@echo " help - Prints this list of valid make targets "
|
||||
@echo "Object targets are supported by the O.<arch> level Makefile .e.g"
|
||||
|
||||
@@ -77,7 +77,7 @@ CPPFLAGS += $($(BUILD_CLASS)_CPPFLAGS) $(POSIX_CPPFLAGS) $(OPT_CPPFLAGS)\
|
||||
$(USR_CPPFLAGS) $(CMD_CPPFLAGS) $(ARCH_DEP_CPPFLAGS) $(OP_SYS_CPPFLAGS)\
|
||||
$(OP_SYS_INCLUDE_CPPFLAGS) $(CODE_CPPFLAGS)
|
||||
|
||||
ECHO = @$(if $(filter -s,$(MFLAGS)),$(NOP),echo)
|
||||
ECHO = @$(if $(make-s),$(NOP),echo)
|
||||
|
||||
# Originally set in os/CONFIG.UnixCommon.Common
|
||||
MKDIR = mkdir -p
|
||||
|
||||
@@ -11,14 +11,15 @@ MUNCH_SUFFIX = .boot
|
||||
define MUNCH_CMD
|
||||
$(RTEMS_TOOLS)/bin/$(OBJCOPY_FOR_TARGET) -O binary -R .comment -S $< rtems
|
||||
gzip -f9 rtems
|
||||
$(RTEMS_TOOLS)/bin/$(LD_FOR_TARGET) -o $@ \
|
||||
$(RTEMS_TOOLS)/bin/$(LD_FOR_TARGET) -o $@.elf \
|
||||
$(PROJECT_RELEASE)/lib/bootloader.o \
|
||||
--just-symbols=$< \
|
||||
-b binary rtems.gz \
|
||||
--no-warn-mismatch \
|
||||
--no-warn-mismatch \
|
||||
-T $(PROJECT_RELEASE)/lib/ppcboot.lds \
|
||||
-Map $<.map
|
||||
rm -f rtems.gz
|
||||
$(RTEMS_TOOLS)/bin/$(OBJCOPY_FOR_TARGET) -O binary $@.elf $@
|
||||
rm -f rtems.gz $@.elf
|
||||
endef
|
||||
|
||||
include $(CONFIG)/os/CONFIG.Common.RTEMS
|
||||
|
||||
@@ -56,7 +56,7 @@ SHRLIB_LDLIBS = $(addprefix -l, $($*_LDLIBS) $(LIB_LIBS) $(USR_LIBS)) \
|
||||
|
||||
SHRLIB_DEPLIB_DIRS = $(foreach word, \
|
||||
$(sort $(INSTALL_LIB)/ $(dir $($*_DEPLIBS) $(SHRLIB_DEPLIBS))), \
|
||||
$(shell $(FULLPATHNAME) $(word)))
|
||||
$(abspath $(word)))
|
||||
|
||||
SHRLIBDIR_LDFLAGS += $(SHRLIB_DEPLIB_DIRS:%=-L%)
|
||||
|
||||
@@ -85,7 +85,7 @@ PROD_LDLIBS += $($(firstword $(LDLIBS_STATIC_$(STATIC_BUILD)) \
|
||||
|
||||
PROD_DEPLIB_DIRS = $(foreach word, \
|
||||
$(sort $(INSTALL_LIB)/ $(dir $($*_DEPLIBS) $(PROD_DEPLIBS))), \
|
||||
$(shell $(FULLPATHNAME) $(word)))
|
||||
$(abspath $(word)))
|
||||
|
||||
PRODDIR_LDFLAGS += $(PROD_DEPLIB_DIRS:%=-L%)
|
||||
|
||||
|
||||
@@ -77,7 +77,7 @@ OP_SYS_LDFLAGS += -dynamic -Z -L$(SDK_DIR)/usr/lib -L$(SDK_DIR)/usr/lib/system
|
||||
# Shared libraries
|
||||
SHRLIB_VERSION = $(EPICS_VERSION).$(EPICS_REVISION).$(EPICS_MODIFICATION)
|
||||
SHRLIB_LDFLAGS = -dynamiclib -flat_namespace -undefined suppress \
|
||||
-install_name $(shell $(FULLPATHNAME) $(INSTALL_LIB))/$@ \
|
||||
-install_name $(abspath $(INSTALL_LIB))/$@ \
|
||||
-compatibility_version $(EPICS_VERSION).$(EPICS_REVISION) \
|
||||
-current_version $(SHRLIB_VERSION)
|
||||
SHRLIB_SUFFIX_BASE = .dylib
|
||||
|
||||
21
configure/os/CONFIG.Common.vxWorks-e500v2
Normal file
21
configure/os/CONFIG.Common.vxWorks-e500v2
Normal file
@@ -0,0 +1,21 @@
|
||||
# CONFIG.Common.vxWorks-e500v2
|
||||
#
|
||||
# Definitions for vxWorks-e500v2 target archs (MVME2500)
|
||||
# Sites may override these definitions in CONFIG_SITE.Common.vxWorks-e500v2
|
||||
#-------------------------------------------------------
|
||||
|
||||
# Include definitions common to all vxWorks target archs
|
||||
include $(CONFIG)/os/CONFIG.Common.vxWorksCommon
|
||||
|
||||
# Vx GNU cross compiler suffix
|
||||
CMPLR_SUFFIX = ppc
|
||||
|
||||
ARCH_CLASS = ppc
|
||||
|
||||
# Architecture specific build flags
|
||||
ARCH_DEP_CFLAGS += -te500v2 -mhard-float
|
||||
ARCH_DEP_CPPFLAGS += -DCPU=PPC85XX
|
||||
ARCH_DEP_CFLAGS += -DCPU_VARIANT=_ppc85XX_e500v2
|
||||
ARCH_DEP_CFLAGS += -mlongcall
|
||||
|
||||
GNU_TARGET = powerpc-wrs-vxworks
|
||||
@@ -12,7 +12,7 @@ ARCH_CLASS = x86
|
||||
POSIX = NO
|
||||
|
||||
# Definitions used when COMMANDLINE_LIBRARY is READLINE
|
||||
LDLIBS_READLINE = -lreadline -lcurses
|
||||
LDLIBS_READLINE = -lreadline -ltermcap
|
||||
|
||||
ARCH_DEP_CFLAGS += -m32
|
||||
ARCH_DEP_LDFLAGS += -m32
|
||||
|
||||
@@ -67,7 +67,7 @@ GNU = NO
|
||||
# Darwin shared libraries
|
||||
#
|
||||
SHRLIB_LDFLAGS = -dynamiclib -flat_namespace -undefined dynamic_lookup \
|
||||
-install_name $(shell $(FULLPATHNAME) $(INSTALL_LIB))/$@ \
|
||||
-install_name $(abspath $(INSTALL_LIB))/$@ \
|
||||
$(addprefix -compatibility_version , $(SHRLIB_VERSION)) \
|
||||
$(addprefix -current_version , $(SHRLIB_VERSION))
|
||||
SHRLIB_SUFFIX_BASE = .dylib
|
||||
|
||||
@@ -6,3 +6,8 @@
|
||||
|
||||
# Include common gnu compiler definitions
|
||||
include $(CONFIG)/CONFIG.gnuCommon
|
||||
|
||||
STATIC_LDFLAGS_YES= -Wl,-Bstatic
|
||||
STATIC_LDFLAGS_NO=
|
||||
STATIC_LDLIBS_YES= -Wl,-Bdynamic
|
||||
STATIC_LDLIBS_NO=
|
||||
|
||||
@@ -21,4 +21,4 @@ LOADABLE_SHRLIB_LDFLAGS = -shared \
|
||||
GNU_LDLIBS_YES =
|
||||
|
||||
# Link with system libraries
|
||||
OP_SYS_LDLIBS = -lpsapi -lws2_32 -ladvapi32 -luser32 -lkernel32 -lwinmm -ldbghelp
|
||||
OP_SYS_LDLIBS += -lpsapi -lws2_32 -ladvapi32 -luser32 -lkernel32 -lwinmm -ldbghelp
|
||||
|
||||
@@ -32,4 +32,4 @@ LOADABLE_SHRLIB_LDFLAGS = -shared \
|
||||
GNU_LDLIBS_YES =
|
||||
|
||||
# Link with system libraries
|
||||
OP_SYS_LDLIBS = -lpsapi -lws2_32 -ladvapi32 -luser32 -lkernel32 -lwinmm -ldbghelp
|
||||
OP_SYS_LDLIBS += -lpsapi -lws2_32 -ladvapi32 -luser32 -lkernel32 -lwinmm -ldbghelp
|
||||
|
||||
@@ -34,5 +34,6 @@
|
||||
# WARNING: Variables that are set in $(CONFIG)/CONFIG.gnuCommon cannot be
|
||||
# overridden in this file for native builds, e.g. variables such as
|
||||
# OPT_CFLAGS_YES, WARN_CFLAGS, SHRLIB_LDFLAGS
|
||||
# They must be set in CONFIG_SITE.linux-aarch64.linux-aarch64 instead.
|
||||
# They must be set in CONFIG_SITE.linux-aarch64.linux-aarch64 or for
|
||||
# cross-builds in CONFIG_SITE.<host-arch>.linux-aarch64 instead.
|
||||
|
||||
|
||||
@@ -15,11 +15,6 @@
|
||||
# to inform the system of the shared library location.
|
||||
|
||||
|
||||
# Use GNU Readline if the header file is installed
|
||||
COMMANDLINE_LIBRARY = $(strip $(if $(wildcard \
|
||||
$(firstword $(READLINE_DIR) $(GNU_DIR))/include/readline/readline.h), \
|
||||
READLINE, EPICS))
|
||||
|
||||
# If libreadline needs additional libraries to be linked with it, try
|
||||
# uncommenting each of the lines below in turn, starting with the top
|
||||
# one and working downwards, until the build succeeds. Do a 'make rebuild'
|
||||
|
||||
@@ -12,10 +12,6 @@
|
||||
GNU_DIR = /usr/local/vw/microblaze-2.0/microblazeel-unknown-linux-gnu
|
||||
|
||||
|
||||
# Use GNU Readline if the header file is installed
|
||||
COMMANDLINE_LIBRARY = $(strip $(if $(wildcard \
|
||||
$(GNU_DIR)/include/readline/readline.h), READLINE, EPICS))
|
||||
|
||||
# If libreadline needs additional libraries to be linked with it, try
|
||||
# uncommenting each of the lines below in turn, starting with the top
|
||||
# one and working downwards, until the build succeeds. Do a 'make rebuild'
|
||||
|
||||
@@ -15,10 +15,6 @@
|
||||
# to inform the system of the shared library location.
|
||||
|
||||
|
||||
# Use GNU Readline if the header file is installed
|
||||
COMMANDLINE_LIBRARY = $(strip $(if $(wildcard \
|
||||
$(GNU_DIR)/include/readline/readline.h), READLINE, EPICS))
|
||||
|
||||
# If libreadline needs additional libraries to be linked with it, try
|
||||
# uncommenting each of the lines below in turn, starting with the top
|
||||
# one and working downwards, until the build succeeds. Do a 'make rebuild'
|
||||
|
||||
@@ -15,10 +15,6 @@
|
||||
# to inform the system of the shared library location.
|
||||
|
||||
|
||||
# Use GNU Readline if the header file is installed
|
||||
COMMANDLINE_LIBRARY = $(strip $(if $(wildcard \
|
||||
$(GNU_DIR)/include/readline/readline.h), READLINE, EPICS))
|
||||
|
||||
# If libreadline needs additional libraries to be linked with it, try
|
||||
# uncommenting each of the lines below in turn, starting with the top
|
||||
# one and working downwards, until the build succeeds. Do a 'make rebuild'
|
||||
|
||||
@@ -3,11 +3,6 @@
|
||||
# Site-specific settings for the linux-xscale_be target
|
||||
|
||||
|
||||
# Use GNU Readline if the header file is installed
|
||||
COMMANDLINE_LIBRARY = $(strip $(if $(wildcard \
|
||||
$(firstword $(READLINE_DIR) $(GNU_DIR))/include/readline/readline.h), \
|
||||
READLINE, EPICS))
|
||||
|
||||
# If libreadline needs additional libraries to be linked with it, try
|
||||
# uncommenting each of the lines below in turn, starting with the top
|
||||
# one and working downwards, until the build succeeds. Do a 'make rebuild'
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# CONFIG_SITE.linux-x86.linux-aarch64
|
||||
# CONFIG_SITE.linux-x86_64.linux-aarch64
|
||||
#
|
||||
# Site specific definitions for linux-x86 host - linux-aarch64 target builds
|
||||
# Site specific definitions for linux-x86_64 host - linux-aarch64 target builds
|
||||
#-------------------------------------------------------
|
||||
|
||||
# Set GNU crosscompiler target name
|
||||
|
||||
@@ -35,3 +35,15 @@ OS_API = posix
|
||||
OS_API = score
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#ifdef __has_include
|
||||
# if defined(__rtems__) && __RTEMS_MAJOR__<5 && __has_include(<libtecla.h>)
|
||||
COMMANDLINE_LIBRARY ?= LIBTECLA
|
||||
# elif __has_include(<readline/readline.h>)
|
||||
COMMANDLINE_LIBRARY ?= READLINE
|
||||
# else
|
||||
COMMANDLINE_LIBRARY ?= EPICS
|
||||
# endif
|
||||
#else
|
||||
COMMANDLINE_LIBRARY ?= EPICS
|
||||
#endif
|
||||
|
||||
@@ -387,7 +387,7 @@ install directories.
|
||||
|
||||
EPICS executables and perl scripts are installed into the
|
||||
`$(INSTALL_LOCATION)/bin/<arch>` directories. Libraries are installed
|
||||
into $`(INSTALL_LOCATION)/lib/<arch>`. The default definition for
|
||||
into `$(INSTALL_LOCATION)/lib/<arch>`. The default definition for
|
||||
`$(INSTALL_LOCATION)` is `$(TOP)` which is the root directory in the
|
||||
distribution directory structure, `base`. Intermediate object files
|
||||
are stored in `O.<arch>` source subdirectories during the build
|
||||
|
||||
@@ -2,14 +2,240 @@
|
||||
|
||||
These release notes describe changes that have been made since the previous
|
||||
release of this series of EPICS Base. **Note that changes which were merged up
|
||||
from commits to the 3.15 branch are not described at the top of this file but
|
||||
lower down, under the 3.15 release to which they were originally committed.**
|
||||
Thus it is important to read more than just the first section to understand
|
||||
everything that has changed in each release.
|
||||
from commits to the 3.15 branch are described
|
||||
[further down this file](#changes-incorporated-from-the-315-branch)
|
||||
under the 3.15 release to which they were originally committed.** Thus it is
|
||||
important to read more than just the first section to understand everything that
|
||||
has changed in each release.
|
||||
|
||||
The PVA submodules each have their own individual sets of release notes which
|
||||
should also be read to understand what has changed since earlier releases.
|
||||
|
||||
|
||||
## EPICS Release 7.0.7
|
||||
|
||||
### Doxygen Annotations
|
||||
|
||||
Thanks to several attendees at the 2022 EPICS Codeathon the number of header
|
||||
files with Doxygen annotations in the EPICS Core has again increased.
|
||||
|
||||
### Build System updates
|
||||
|
||||
The top-level make targets `uninstall`, `archuninstall` and similar no
|
||||
longer trigger the `clean` target which empties build directories, this
|
||||
was a bug introduced in 7.0.5.
|
||||
|
||||
The `make distclean` target now properly deletes the generated file(s)
|
||||
`modules/RELEASE.<host>.local` which are essential to build the external
|
||||
submodules under the `modules` directory, and should not crash if the
|
||||
build is configured with `INSTALL_LOCATION` pointing to an empty external directory (i.e. if you run `make distclean` twice in succession). When
|
||||
`INSTALL_LOCATION` is set in the files `configure/CONFIG_SITE` or
|
||||
`configure/CONFIG_SITE.local` the `modules/RELEASE.<host>.local` file
|
||||
will now be regenerated in case the install path has been modified.
|
||||
|
||||
Note that passing `INSTALL_LOCATION=<path>` on the make command-line will
|
||||
only work if you have run `make distclean` immediately beforehand, as the
|
||||
`modules/RELEASE.<host>.local` file must be recreated using the new path.
|
||||
|
||||
### Enhancements to `capr.pl`
|
||||
|
||||
The `capr.pl` script can now display records from older Base versions to
|
||||
which fields have since been added, and shows long strings and array data
|
||||
up to 10 elements, use the new `-n` option to increase that number.
|
||||
The script is fully event-driven and prints all the field data received by
|
||||
the end of the CA wait time (`-w` option which defaults to 2 seconds).
|
||||
The interest level can now be specified using the `-l` option before the
|
||||
PV name, and the new `-D` flag outputs debugging information.
|
||||
|
||||
### Time Synchronization on VxWorks
|
||||
|
||||
VxWorks 6.9 can do its own OS clock time synchronization, if it has been
|
||||
configured by setting `SNTPC_PRIMARY_IPV4_ADDR`. Since EPICS 3.15.3 the
|
||||
IOC time support code has checked for the existence of the VxWorks time
|
||||
synchronization task and avoided starting the EPICS one if the OS task
|
||||
exists and the OS clock gives a "recent" time (i.e. after when EPICS was
|
||||
compiled), unless the environment variable `EPICS_TS_FORCE_NTPTIME` is
|
||||
also set. However a logic error in that code required the environment
|
||||
variable to be set in more cases than it should have.
|
||||
|
||||
This error has been fixed and the IOC should work normally if the VxWorks
|
||||
task is configured and running. The `TIMEZONE` value for the year is also
|
||||
now calculated at initialization in this configuration, previously it was
|
||||
only done when the IOC synchronzation task was used. Setting the above
|
||||
environment variable will now cause the IOC support code to shut down the
|
||||
VxWorks synchronization thread (if running) before starting the EPICS one.
|
||||
|
||||
Running the iocsh command `ClockTime_Report` now shows whether the VxWorks
|
||||
task is running as well as giving the state of the IOC synchronization task.
|
||||
The `ClockTime_Init` command can also be used to stop or restart the IOC
|
||||
time synchronization task while the IOC is running, depending on the `0` or
|
||||
`1` parameter passed to it. This last change also applies to RTEMS IOCs.
|
||||
|
||||
### Incompatible change to `struct db_field_log`
|
||||
|
||||
This change may cause channel filters which manipulate array updates
|
||||
to fail to compile.
|
||||
|
||||
To avoid potential speculation issues arising from overlapping code pointers
|
||||
with data values, `union dbfl_ref` is modified to remove the `dtor` member.
|
||||
`dtor` is moved out into the enclosing `struct db_field_log`.
|
||||
|
||||
So eg. using a `db_field_log* p`, the expression `p->u.r.dtor` must be
|
||||
changed to `(p)->dtor`.
|
||||
|
||||
### Fix undef ts on first camonitor update of NORD from waveformRecord
|
||||
|
||||
The order over operations when processing a waveformRecord is adjusted
|
||||
so that updates to NORD is posted with the correct timestamp.
|
||||
|
||||
### Automatic COMMANDLINE_LIBRARY w/ newer compilers
|
||||
|
||||
When built with a compiler supporting `__has_include<>`, the presence
|
||||
of the `<readline/readline.h>` will be used to automatically determine
|
||||
a default value for `COMMANDLINE_LIBRARY`.
|
||||
|
||||
Mingw builds with readline support now link `-ltermcap` instead of `-lcurses`.
|
||||
|
||||
This should not effect sites which set explicitly set `COMMANDLINE_LIBRARY`
|
||||
as the only definition in Base now has the form `COMMANDLINE_LIBRARY ?= ...`.
|
||||
|
||||
### Perl CA support for empty long strings
|
||||
|
||||
The Perl CA bindings have been fixed to handle zero-length long string data
|
||||
properly.
|
||||
|
||||
### `aao` gains `OMSL` and `DOL`
|
||||
|
||||
The `aao` record types gains the same `DOL` functionality found
|
||||
in other output record types (`ao`, `longout`, etc.)
|
||||
|
||||
### Server exports `RSRV_SERVER_PORT`
|
||||
|
||||
During `iocInit()`, the environment variable `RSRV_SERVER_PORT` is set
|
||||
with the TCP port number selected.
|
||||
|
||||
### `dbdExpand.pl` sorts all items by name
|
||||
|
||||
DBD files generated by the `dbdExpand.pl` script are now sorted within each
|
||||
item type by the primary name of the item. The result should resolve any
|
||||
issues with reproducable builds. No option is provided to prevent the sorting,
|
||||
previously the order was essentially random and varied each time.
|
||||
|
||||
### `dbExpand.pl` sorts records by name
|
||||
|
||||
Records are now output by this program in order, sorted by name. The new flag
|
||||
`-s` can be given to output the records in the same order they were read in,
|
||||
instead of sorting them.
|
||||
|
||||
Note that there are currently no build rules provided with Base which make use
|
||||
of this program.
|
||||
|
||||
### Simulation Mode RAW Support for Output Record Types
|
||||
|
||||
SIMM=RAW support has been added for the relevant output record types
|
||||
(ao, bo, mbbo, mbboDirect).
|
||||
RAW simulation mode will have those records do the appropriate conversion
|
||||
and write RVAL to the location pointed to by SIOL.
|
||||
|
||||
### Fixed leak from a non-EPICS thread
|
||||
|
||||
On some targets, if a thread not created by `epicsThreadCreate*()` directly
|
||||
or indirectly calls an `epicsThread*()` function, a specific tracking struct
|
||||
is allocated.
|
||||
|
||||
Prior to this release, on POSIX and WIN32 targets, this
|
||||
struct would not be `free()`d, resulting in a memory leak.
|
||||
|
||||
This release fixed the leak on POSIX targets.
|
||||
|
||||
See the associated github [issue 241](https://github.com/epics-base/epics-base/issues/241)
|
||||
for WIN32 status.
|
||||
|
||||
### Fix `CHECK_RELEASE = WARN`
|
||||
|
||||
This now works again, it was broken in 2019 (7.0.3.1) by an errant commit.
|
||||
|
||||
### Document `DISP` as design-time field
|
||||
|
||||
The DISP field can be set to a non-zero value to prevent records being changed
|
||||
from outside the IOC (this is ancient behavior), but has never been documented
|
||||
as being usable at design-time (DCT=Yes in the Record Reference tables). This
|
||||
has now been changed.
|
||||
|
||||
### Make `epicsInt8` signed on all architectures
|
||||
|
||||
The `epicsInt8` and thus `DBF_CHAR` types have always been unsigned on
|
||||
architectures where `char` is unsigned, for example on many PowerPC CPU
|
||||
architectures. This was counter-intuitive, and resulted in IOC behavior
|
||||
differing between architectures when converting `DBF_CHAR` values into a
|
||||
signed integer or floating point type.
|
||||
|
||||
**WARNING**: This fix may change behavior of existing databases on target
|
||||
architectures with unsigned `char` (mainly PowerPC) when using input links to
|
||||
read from `CHAR` arrays. Architectures with signed `char` (usually x86) should
|
||||
be unaffected, although some compilers might generate new warnings.
|
||||
|
||||
### Allow hexadecimal and octal numbers in hardware links
|
||||
|
||||
[GH:213](https://github.com/epics-base/epics-base/pull/213)
|
||||
|
||||
Several types of hardware links (`VME_IO`, `CAMAC_IO`, etc) now accept
|
||||
hexadecimal and octal numbers. (Hexadecimal numbers had already been valid
|
||||
up to EPICS R3.15.) This change may introduce incompatibilities when using
|
||||
numbers with leading `0` as they will now be parsed as octal.
|
||||
|
||||
### Fix embedded implementations of `epicsEvent`
|
||||
|
||||
[GH:202](https://github.com/epics-base/epics-base/issues/202) and
|
||||
[GH:206](https://github.com/epics-base/epics-base/pull/206)
|
||||
|
||||
Heinz Junkes provided a new implementation of the `epicsEvent` API suitable for
|
||||
RTEMS Posix targets (RTEMS 5.1 and later). In review a few issues related to
|
||||
overflow of timeout values surfaced in this and other embedded implementations,
|
||||
and these were also been fixed in this Pull Request. The API documentation for
|
||||
this and some other routines has also been updated.
|
||||
|
||||
### Breakpoint Table Names
|
||||
|
||||
The names of breakpoint tables were made unnecessarily strict when DBD file
|
||||
processing was moved to Perl for the 3.15 release series. Table names may now
|
||||
contain the special characters `_` `-` `:` `;` `.` `[` `]` `<` `>` in addition
|
||||
to letters and digits.
|
||||
|
||||
### Fix for `undefined` in configure/RELEASE files
|
||||
|
||||
Prevents `Use of uninitialized value` warnings from convertRelease.pl.
|
||||
|
||||
### Colorized Messages for errlog
|
||||
|
||||
Many internal error messages now emit ANSI escape sequences to highlight the
|
||||
words "ERROR" and "WARNING" in an attempt to make occurrences more noticeable
|
||||
during IOC startup.
|
||||
|
||||
The macros `ERL_ERROR` and `ERL_WARNING` are defined for external usage,
|
||||
and expand as string constants. eg.
|
||||
|
||||
```c
|
||||
#include <errlog.h>
|
||||
#ifndef ERL_ERROR
|
||||
# define ERL_ERROR "ERROR"
|
||||
#endif
|
||||
void fn() {
|
||||
...
|
||||
errlogPrintf(ERL_ERROR ": something bad happens :(\n");
|
||||
```
|
||||
|
||||
ANSI escapes are automatically removed from errlog output not destined
|
||||
for a terminal. For example, for logClient, if stderr is redirected,
|
||||
or if unsupported (`$TERM` not set, or Windows < 10).
|
||||
|
||||
### `dbnd` filter pass through `DBE_ALARM|DBE_PROPERTY`
|
||||
|
||||
The `dbnd` server side filter now passes through alarm and property
|
||||
change events, even when not exceeding the deadband.
|
||||
|
||||
-----
|
||||
|
||||
## EPICS Release 7.0.6.1
|
||||
|
||||
### `mbboDirectRecord` enhancements
|
||||
@@ -27,7 +253,7 @@ monitors posted on the bit fields.
|
||||
Some scripts now make use of features that were introduced to this Perl version
|
||||
that was released in 2009.
|
||||
|
||||
### DB Links to DBF_MENU fields fixed
|
||||
### DB Links to `DBF_MENU` fields fixed
|
||||
|
||||
[GH:183](https://github.com/epics-base/epics-base/issues/183)
|
||||
These were broken in a previous release, but now work again.
|
||||
@@ -369,10 +595,10 @@ broken.
|
||||
|
||||
This fixes [lauchpad bug #1896295](https://bugs.launchpad.net/bugs/1896295).
|
||||
|
||||
### Support for Apple M1 (arm64) Processors
|
||||
### Support for Apple M1/M2 (arm64) Processors
|
||||
|
||||
Thanks to Jeong Han Lee this release comes with build support for Apple's new
|
||||
M1 CPUs running macOS, using the target name `darwin-aarch64`.
|
||||
M1/M2 CPUs running macOS, using the target name `darwin-aarch64`.
|
||||
|
||||
It should also be possible to build universal binaries containing code for
|
||||
both the Intel and arm64 processors under either target name: In the
|
||||
@@ -1388,7 +1614,7 @@ editing and history. This functionality has now been restored, see Launchpad
|
||||
### Constant link types
|
||||
|
||||
Constant links can now hold 64-bit integer values, either as scalars or
|
||||
arrays. Only base 10 is supported by the JSON parser though, the JSON standard
|
||||
arrays. Only base 10 is supported by the JSON parser though, the JSON standard
|
||||
doesn't allow for hexadecimal numbers.
|
||||
|
||||
### Upgraded the YAJL JSON Library
|
||||
@@ -1986,6 +2212,17 @@ header and removed the need for dbScan.c to reach into the internals of its
|
||||
|
||||
## Changes from the 3.15 branch since 3.15.9
|
||||
|
||||
### Support for Apple M1/M2 (arm64) Processors
|
||||
|
||||
Thanks to Jeong Han Lee this release comes with build support for Apple's new
|
||||
M1/M2 CPUs running macOS, using the target name `darwin-aarch64`.
|
||||
|
||||
### Set thread names on Windows
|
||||
|
||||
On MS Windows, epicsThread names are made available to the OS and debugger
|
||||
using `SetThreadDescription()` if available as well as using the older
|
||||
exception mechanism.
|
||||
|
||||
### Fix timers on MS Windows for non-EPICS threads
|
||||
|
||||
The waitable timer changes in 3.15.9 broke calls to `epicsThreadSleep()` and
|
||||
|
||||
@@ -2,6 +2,16 @@
|
||||
# EPICS BASE is distributed subject to a Software License Agreement found
|
||||
# in file LICENSE that is included with this distribution.
|
||||
#*************************************************************************
|
||||
#
|
||||
# modules/CONFIG_SITE.local
|
||||
#
|
||||
# Despite the .local in its name, this file *is* included in EPICS Base
|
||||
# sources and should *not* be modified by sites. This trick is necessary
|
||||
# to allow external submodules to be placed inside the modules directory
|
||||
# without having to modify them, as long as their configure/CONFIG_SITE
|
||||
# file includes the standard line
|
||||
# -include $(TOP)/../CONFIG_SITE.local
|
||||
# that causes this file to be read in during submodule builds.
|
||||
|
||||
# The name our submodules know us by:
|
||||
PARENT_MODULE = EPICS_BASE
|
||||
|
||||
@@ -32,7 +32,13 @@ static const iocshArg *acctstArgs[] =
|
||||
&acctstArg3,
|
||||
&acctstArg4
|
||||
};
|
||||
static const iocshFuncDef acctstFuncDef = {"acctst", 5, acctstArgs};
|
||||
static const iocshFuncDef acctstFuncDef = {
|
||||
"acctst",
|
||||
5,
|
||||
acctstArgs,
|
||||
"Execute a Channel Access regression test.\n\n"
|
||||
"For more information, see the 'acctst' documentation in the Channel Access reference.\n",
|
||||
};
|
||||
|
||||
|
||||
/* Wrapper called by iocsh, selects the argument types that print needs */
|
||||
|
||||
@@ -392,9 +392,19 @@ void ca_client_context :: vSignal (
|
||||
}
|
||||
|
||||
epicsTime current = epicsTime::getCurrent ();
|
||||
char date[64];
|
||||
current.strftime ( date, sizeof ( date ), "%a %b %d %Y %H:%M:%S.%f");
|
||||
this->printFormated ( " Current Time: %s\n", date );
|
||||
try {
|
||||
char date[64];
|
||||
current.strftime ( date, sizeof ( date ), "%a %b %d %Y %H:%M:%S.%f");
|
||||
this->printFormated ( " Current Time: %s\n", date );
|
||||
}
|
||||
catch ( std::exception & except ) {
|
||||
errlogPrintf (
|
||||
"CA client library thread \"%s\" caught C++ exception \"%s\"\n",
|
||||
epicsThreadGetNameSelf (), except.what () );
|
||||
epicsTimeStamp now = current;
|
||||
this->printFormated ( " Current Time: %u.%u\n",
|
||||
now.secPastEpoch, now.nsec );
|
||||
}
|
||||
|
||||
/*
|
||||
* Terminate execution if unsuccessful
|
||||
|
||||
@@ -559,19 +559,19 @@ LIBCA_API chid epicsStdCall ca_evid_to_chid ( evid id );
|
||||
/*
|
||||
* ca_pend_event()
|
||||
*
|
||||
* timeOut R wait for this delay in seconds
|
||||
* timeout R wait for this delay in seconds
|
||||
*/
|
||||
LIBCA_API int epicsStdCall ca_pend_event (ca_real timeOut);
|
||||
LIBCA_API int epicsStdCall ca_pend_event (ca_real timeout);
|
||||
#define ca_poll() ca_pend_event(1e-12)
|
||||
|
||||
/*
|
||||
* ca_pend_io()
|
||||
*
|
||||
* timeOut R wait for this delay in seconds but return early
|
||||
* timeout R wait for this delay in seconds but return early
|
||||
* if all get requests (or search requests with null
|
||||
* connection handler pointer have completed)
|
||||
*/
|
||||
LIBCA_API int epicsStdCall ca_pend_io (ca_real timeOut);
|
||||
LIBCA_API int epicsStdCall ca_pend_io (ca_real timeout);
|
||||
|
||||
/* calls ca_pend_io() if early is true otherwise ca_pend_event() is called */
|
||||
LIBCA_API int epicsStdCall ca_pend (ca_real timeout, int early);
|
||||
|
||||
@@ -188,7 +188,7 @@ int main ( int argc, char ** argv )
|
||||
epicsSocketConvertErrnoToString (
|
||||
sockErrBuf, sizeof ( sockErrBuf ) );
|
||||
epicsSocketDestroy ( sock );
|
||||
errlogPrintf ("casw: error from recv was = \"%s\"\n",
|
||||
errlogPrintf ("casw: " ERL_ERROR " from recv was = \"%s\"\n",
|
||||
sockErrBuf );
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -88,6 +88,7 @@ public:
|
||||
bool push ( const T & value );
|
||||
template < class T >
|
||||
unsigned push ( const T * pValue, unsigned nElem );
|
||||
unsigned push ( const char * pValue, unsigned nElem );
|
||||
unsigned push ( const epicsInt8 * pValue, unsigned nElem );
|
||||
unsigned push ( const epicsUInt8 * pValue, unsigned nElem );
|
||||
unsigned push ( const epicsOldString * pValue, unsigned nElem );
|
||||
@@ -208,6 +209,11 @@ inline unsigned comBuf :: push ( const epicsUInt8 *pValue, unsigned nElem )
|
||||
return copyInBytes ( pValue, nElem );
|
||||
}
|
||||
|
||||
inline unsigned comBuf :: push ( const char *pValue, unsigned nElem )
|
||||
{
|
||||
return copyInBytes ( pValue, nElem );
|
||||
}
|
||||
|
||||
inline unsigned comBuf :: push ( const epicsOldString * pValue, unsigned nElem )
|
||||
{
|
||||
unsigned index = this->nextWriteIndex;
|
||||
|
||||
@@ -46,7 +46,7 @@ void comQueRecv::clear ()
|
||||
this->nBytesPending = 0u;
|
||||
}
|
||||
|
||||
unsigned comQueRecv::copyOutBytes ( epicsInt8 *pBuf, unsigned nBytes )
|
||||
unsigned comQueRecv::copyOutBytes ( char *pBuf, unsigned nBytes )
|
||||
{
|
||||
unsigned totalBytes = 0u;
|
||||
do {
|
||||
|
||||
@@ -33,7 +33,7 @@ public:
|
||||
comQueRecv ( comBufMemoryManager & );
|
||||
~comQueRecv ();
|
||||
unsigned occupiedBytes () const;
|
||||
unsigned copyOutBytes ( epicsInt8 *pBuf, unsigned nBytes );
|
||||
unsigned copyOutBytes ( char *pBuf, unsigned nBytes );
|
||||
unsigned removeBytes ( unsigned nBytes );
|
||||
void pushLastComBufReceived ( comBuf & );
|
||||
void clear ();
|
||||
|
||||
@@ -79,7 +79,7 @@ extern "C" int epicsStdCall addAddrToChannelAccessAddressList
|
||||
const char *pStr;
|
||||
const char *pToken;
|
||||
struct sockaddr_in addr;
|
||||
char buf[32u]; /* large enough to hold an IP address */
|
||||
char buf[256u]; /* large enough to hold an IP address or hostname */
|
||||
int status, ret = -1;
|
||||
|
||||
pStr = envGetConfigParamPtr (pEnv);
|
||||
|
||||
@@ -178,7 +178,7 @@ void tcpSendThread::run ()
|
||||
char sockErrBuf[64];
|
||||
epicsSocketConvertErrnoToString (
|
||||
sockErrBuf, sizeof ( sockErrBuf ) );
|
||||
errlogPrintf ("CAC TCP clean socket shutdown error was %s\n",
|
||||
errlogPrintf ("CAC TCP clean socket shutdown " ERL_ERROR " was %s\n",
|
||||
sockErrBuf );
|
||||
}
|
||||
}
|
||||
@@ -194,7 +194,7 @@ void tcpSendThread::run ()
|
||||
char sockErrBuf[64];
|
||||
epicsSocketConvertErrnoToString (
|
||||
sockErrBuf, sizeof ( sockErrBuf ) );
|
||||
errlogPrintf ("CAC TCP clean socket shutdown error was %s\n",
|
||||
errlogPrintf ("CAC TCP clean socket shutdown " ERL_ERROR " was %s\n",
|
||||
sockErrBuf );
|
||||
}
|
||||
}
|
||||
@@ -283,7 +283,7 @@ unsigned tcpiiu::sendBytes ( const void *pBuf,
|
||||
char sockErrBuf[64];
|
||||
epicsSocketConvertErrnoToString (
|
||||
sockErrBuf, sizeof ( sockErrBuf ) );
|
||||
errlogPrintf ( "CAC: unexpected TCP send error: %s\n",
|
||||
errlogPrintf ( "CAC: unexpected TCP send " ERL_ERROR ": %s\n",
|
||||
sockErrBuf );
|
||||
}
|
||||
|
||||
@@ -957,7 +957,7 @@ void tcpiiu::initiateAbortShutdown (
|
||||
char sockErrBuf[64];
|
||||
epicsSocketConvertErrnoToString (
|
||||
sockErrBuf, sizeof ( sockErrBuf ) );
|
||||
errlogPrintf ( "CAC TCP socket linger set error was %s\n",
|
||||
errlogPrintf ( "CAC TCP socket linger set " ERL_ERROR " was %s\n",
|
||||
sockErrBuf );
|
||||
}
|
||||
this->discardingPendingData = true;
|
||||
@@ -988,7 +988,7 @@ void tcpiiu::initiateAbortShutdown (
|
||||
char sockErrBuf[64];
|
||||
epicsSocketConvertErrnoToString (
|
||||
sockErrBuf, sizeof ( sockErrBuf ) );
|
||||
errlogPrintf ("CAC TCP socket shutdown error was %s\n",
|
||||
errlogPrintf ("CAC TCP socket shutdown " ERL_ERROR " was %s\n",
|
||||
sockErrBuf );
|
||||
}
|
||||
}
|
||||
@@ -1058,7 +1058,7 @@ void tcpiiu::show ( unsigned level ) const
|
||||
this->_receiveThreadIsBusy );
|
||||
}
|
||||
if ( level > 2u ) {
|
||||
::printf ( "\tvirtual circuit socket identifier %d\n", this->sock );
|
||||
::printf ( "\tvirtual circuit socket identifier %d\n", (int)this->sock );
|
||||
::printf ( "\tsend thread flush signal:\n" );
|
||||
this->sendThreadFlushEvent.show ( level-2u );
|
||||
::printf ( "\tsend thread:\n" );
|
||||
|
||||
@@ -202,7 +202,7 @@ udpiiu::udpiiu (
|
||||
char sockErrBuf[64];
|
||||
epicsSocketConvertErrnoToString (
|
||||
sockErrBuf, sizeof ( sockErrBuf ) );
|
||||
errlogPrintf("CAC: failed to set mcast ttl %d\n", ttl);
|
||||
errlogPrintf("CAC: failed to set mcast ttl %d\n", (int)ttl);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -266,7 +266,7 @@ udpiiu::udpiiu (
|
||||
epicsSocketConvertErrnoToString (
|
||||
sockErrBuf, sizeof ( sockErrBuf ) );
|
||||
epicsSocketDestroy ( this->sock );
|
||||
errlogPrintf ( "CAC: getsockname () error was \"%s\"\n", sockErrBuf );
|
||||
errlogPrintf ( "CAC: getsockname () " ERL_ERROR " was \"%s\"\n", sockErrBuf );
|
||||
throwWithLocation ( noSocket () );
|
||||
}
|
||||
if ( tmpAddr.sa.sa_family != AF_INET) {
|
||||
@@ -428,7 +428,7 @@ void udpRecvThread::run ()
|
||||
char sockErrBuf[64];
|
||||
epicsSocketConvertErrnoToString (
|
||||
sockErrBuf, sizeof ( sockErrBuf ) );
|
||||
errlogPrintf ( "CAC: UDP recv error was \"%s\"\n",
|
||||
errlogPrintf ( "CAC: UDP recv " ERL_ERROR " was \"%s\"\n",
|
||||
sockErrBuf );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,6 +12,13 @@
|
||||
* here and just generates unnecessary compiler warnings. */
|
||||
#define REENTRINC
|
||||
|
||||
/* Clang-12 and later generates many warnings about compound token */
|
||||
#ifdef __has_warning
|
||||
# if __has_warning("-Wcompound-token-split-by-macro")
|
||||
# pragma clang diagnostic ignored "-Wcompound-token-split-by-macro"
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#include "EXTERN.h"
|
||||
#include "perl.h"
|
||||
#include "XSUB.h"
|
||||
@@ -201,6 +208,8 @@ SV * newSVdbr(struct event_handler_args *peha) {
|
||||
if (is_primitive) {
|
||||
if (value_type == DBR_CHAR) {
|
||||
/* Long string => Perl scalar */
|
||||
if (peha->count == 0)
|
||||
return newSVpvn(peha->dbr, 0);
|
||||
((char *)peha->dbr) [peha->count - 1] = 0;
|
||||
return newSVpv(peha->dbr, 0);
|
||||
}
|
||||
@@ -271,8 +280,12 @@ SV * newSVdbr(struct event_handler_args *peha) {
|
||||
char *str = dbr_value_ptr(peha->dbr, peha->type);
|
||||
|
||||
/* Long string => Perl scalar */
|
||||
str[peha->count - 1] = 0;
|
||||
val = newSVpv(str, 0);
|
||||
if (peha->count == 0)
|
||||
val = newSVpvn(str, 0);
|
||||
else {
|
||||
str[peha->count - 1] = 0;
|
||||
val = newSVpv(str, 0);
|
||||
}
|
||||
} else if (peha->count == 1) {
|
||||
/* Single value => Perl scalar */
|
||||
val = newSVdbf(value_type,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#!/usr/bin/env perl
|
||||
#*************************************************************************
|
||||
# Copyright (c) 2005 UChicago Argonne LLC, as Operator of Argonne
|
||||
# Copyright (c) 2022 UChicago Argonne LLC, as Operator of Argonne
|
||||
# National Laboratory.
|
||||
# SPDX-License-Identifier: EPICS
|
||||
# EPICS BASE is distributed subject to a Software License Agreement found
|
||||
@@ -14,26 +14,24 @@
|
||||
#
|
||||
#######################################################################
|
||||
|
||||
use 5.10.1;
|
||||
use strict;
|
||||
|
||||
use FindBin qw($Bin);
|
||||
use lib ("$Bin/../../lib/perl");
|
||||
use FindBin qw($RealBin);
|
||||
use lib ("$RealBin/../../lib/perl");
|
||||
|
||||
use Getopt::Std;
|
||||
use EPICS::Path;
|
||||
use Cwd 'abs_path';
|
||||
use CA;
|
||||
|
||||
######### Globals ##########
|
||||
|
||||
our ($opt_h, $opt_f, $opt_r);
|
||||
our $opt_d = $ENV{EPICS_CAPR_DBD_FILE} || "$Bin/../../dbd/softIoc.dbd";
|
||||
our $opt_w = 1;
|
||||
our ($opt_h, $opt_f, $opt_l, $opt_r, $opt_D);
|
||||
our $opt_d = $ENV{EPICS_CAPR_DBD_FILE} // abs_path("$RealBin/../../dbd/softIoc.dbd");
|
||||
our $opt_w = 2;
|
||||
our $opt_n = 10;
|
||||
|
||||
my %record = (); # Empty hash to put dbd data in
|
||||
my $iIdx = 0; # Array indexes for interest, data type and base
|
||||
my $tIdx = 1;
|
||||
my $bIdx = 2;
|
||||
my %device = (); # Empty hash to record which rec types have device support
|
||||
|
||||
# EPICS field types
|
||||
my %fieldType = (
|
||||
@@ -58,13 +56,14 @@ my %fieldType = (
|
||||
);
|
||||
|
||||
# globals for sub caget
|
||||
my %connected;
|
||||
my $unconnected_count;
|
||||
my %callback_data;
|
||||
my %timed_out;
|
||||
my $callback_incomplete;
|
||||
|
||||
######### Main program ############
|
||||
|
||||
HELP_MESSAGE() unless getopts('hd:f:rw:');
|
||||
HELP_MESSAGE() unless getopts('hDd:f:l:n:rw:');
|
||||
HELP_MESSAGE() if $opt_h;
|
||||
|
||||
die "File $opt_d not found. (\"capr.pl -h\" gives help)\n"
|
||||
@@ -76,7 +75,7 @@ print "Using $opt_d\n\n";
|
||||
# Print a list of record types
|
||||
if ($opt_r) {
|
||||
print ("Record types found:\n");
|
||||
printList(0);
|
||||
printList($opt_l);
|
||||
exit;
|
||||
}
|
||||
|
||||
@@ -100,7 +99,7 @@ if (@ARGV) {
|
||||
} else {
|
||||
# Drop any ".FIELD" part
|
||||
s/\. \w+ $//x;
|
||||
printRecord($_, 0);
|
||||
printRecord($_, $opt_l);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -130,9 +129,11 @@ sub parseDbd {
|
||||
my $thisRecord;
|
||||
my $thisField;
|
||||
my $thisType;
|
||||
my $thisSize = 0;
|
||||
my $field = {};
|
||||
my $interest = 0;
|
||||
my $thisBase = 'DECIMAL';
|
||||
my $special = '';
|
||||
|
||||
while (@dbd) {
|
||||
$_ = shift @dbd;
|
||||
@@ -155,29 +156,40 @@ sub parseDbd {
|
||||
unless $level == 2 && $isAfield;
|
||||
$interest = $1;
|
||||
}
|
||||
elsif ( m/size \s* \( \s* ([0-9]+) \s* \)/x ) {
|
||||
die "File format error at line $i of file\n $opt_d\n"
|
||||
unless $level == 2 && $isAfield;
|
||||
$thisSize = $1;
|
||||
}
|
||||
elsif ( m/special \s* \( \s* (\w+) \s* \)/x ) {
|
||||
die "File format error at line $i of file\n $opt_d\n"
|
||||
unless $level == 2 && $isAfield;
|
||||
$special = $1;
|
||||
}
|
||||
elsif ( m/base \s* \( \s* (\w+) \s* \)/x ) {
|
||||
die "File format error at line $i of file\n $opt_d\n"
|
||||
unless $level == 2 && $isAfield;
|
||||
$thisBase = $1;
|
||||
}
|
||||
elsif ( m/device \s* \( (\w+) \s* ,/x ) {
|
||||
die "File format error at line $i of file\n $opt_d\n"
|
||||
unless $level == 0;
|
||||
$device{$1}++;
|
||||
}
|
||||
if ( m/\{/ ) {
|
||||
$level++;
|
||||
}
|
||||
if ( m/\}/ ) {
|
||||
if ($level == 2 && $isAfield) {
|
||||
my $params = [];
|
||||
$params->[$iIdx] = $interest;
|
||||
$params->[$tIdx] = $thisType;
|
||||
$params->[$bIdx] = $thisBase;
|
||||
my $params = {};
|
||||
$params->{interest} = $interest;
|
||||
$params->{dbfType} = $thisType;
|
||||
$params->{base} = $thisBase;
|
||||
$params->{special} = $special;
|
||||
$params->{size} = $thisSize;
|
||||
$field->{$thisField} = $params;
|
||||
|
||||
# Reset default values
|
||||
$isAfield = 0;
|
||||
$interest = 0; # reset default
|
||||
$thisBase = 'DECIMAL'; # reset default
|
||||
$interest = 0;
|
||||
$thisBase = 'DECIMAL';
|
||||
$special = '';
|
||||
$thisSize = 0;
|
||||
}
|
||||
elsif ($level == 1 && $isArecord) {
|
||||
$isArecord = 0;
|
||||
@@ -207,21 +219,23 @@ sub getRecType {
|
||||
|
||||
# Given the record type and field, returns the interest level, data type
|
||||
# and number base for the field
|
||||
# Usage: ($dataType, $interest, $base) = getFieldParams($recType, $field);
|
||||
# Usage: ($dataType, $interest, $base, $special, $size) = getFieldParams($recType, $field);
|
||||
sub getFieldParams {
|
||||
my ($recType, $field) = @_;
|
||||
|
||||
my $params = $record{$recType}{$field} or
|
||||
die "Can't find params for $recType.$field";
|
||||
exists($fieldType{$params->[$tIdx]}) ||
|
||||
exists($fieldType{$params->{dbfType}}) ||
|
||||
die "Field data type $field for $recType not found in dbd file --";
|
||||
exists($params->[$iIdx]) ||
|
||||
exists($params->{interest}) ||
|
||||
die "Interest level for $field in $recType not found in dbd file --";
|
||||
|
||||
my $fType = $fieldType{$params->[$tIdx]};
|
||||
my $fInterest = $params->[$iIdx];
|
||||
my $fBase = $params->[$bIdx];
|
||||
return ($fType, $fInterest, $fBase);
|
||||
my $fType = $fieldType{$params->{dbfType}};
|
||||
my $fInterest = $params->{interest};
|
||||
my $fBase = $params->{base};
|
||||
my $fSpecial = $params->{special};
|
||||
my $fSize = $params->{size};
|
||||
return ($fType, $fInterest, $fBase, $fSpecial, $fSize);
|
||||
}
|
||||
|
||||
# Prints field name and data for given field. Formats output so
|
||||
@@ -233,27 +247,37 @@ sub printField {
|
||||
my $screenWidth = 80;
|
||||
my ($outStr, $wide);
|
||||
|
||||
my $field = "$fieldName:";
|
||||
|
||||
if ( $dataType eq 'DBF_STRING' ) {
|
||||
$outStr = sprintf('%-5s %s', $field, $fieldData);
|
||||
} elsif ( $base eq 'HEX' ) {
|
||||
my $val = ( $dataType eq 'DBF_CHAR' ) ? ord($fieldData) : $fieldData;
|
||||
$outStr = sprintf('%-5s 0x%x', $field, $val);
|
||||
} elsif ( $dataType eq 'DBF_DOUBLE' || $dataType eq 'DBF_FLOAT' ) {
|
||||
$outStr = sprintf('%-5s %.8f', $field, $fieldData);
|
||||
} elsif ( $dataType eq 'DBF_CHAR' ) {
|
||||
$outStr = sprintf('%-5s %d', $field, ord($fieldData));
|
||||
} else {
|
||||
# DBF_INT64, DBF_LONG, DBF_SHORT,
|
||||
# DBF_UINT64, DBF_ULONG, DBF_USHORT, DBF_UCHAR,
|
||||
$outStr = sprintf('%-5s %d', $field, $fieldData);
|
||||
if (ref $fieldData eq 'ARRAY') {
|
||||
my $elems = scalar @{$fieldData};
|
||||
my $field = "$fieldName\[$elems\]:";
|
||||
my $count = $elems > $opt_n ? $opt_n : $elems;
|
||||
my @show = @{$fieldData}[0 .. $count - 1];
|
||||
$outStr = sprintf('%-5s %s', $field, join(', ', @show));
|
||||
$outStr .= ", ..." if $elems > $count;
|
||||
}
|
||||
else {
|
||||
my $field = "$fieldName:";
|
||||
if ( $dataType eq 'DBF_STRING' ) {
|
||||
$outStr = sprintf('%-5s %s', $field, $fieldData);
|
||||
} elsif ( $base eq 'HEX' ) {
|
||||
my $val = ( $dataType eq 'DBF_CHAR' ) ? ord($fieldData) : $fieldData;
|
||||
$outStr = sprintf('%-5s 0x%x', $field, $val);
|
||||
} elsif ( $dataType eq 'DBF_DOUBLE' || $dataType eq 'DBF_FLOAT' ) {
|
||||
$outStr = sprintf('%-5s %.8f', $field, $fieldData);
|
||||
} elsif ( $dataType eq 'DBF_UCHAR' ) {
|
||||
$outStr = sprintf('%-5s %d', $field, ord($fieldData));
|
||||
} else {
|
||||
# DBF_INT64, DBF_LONG, DBF_SHORT,
|
||||
# DBF_UINT64, DBF_ULONG, DBF_USHORT, DBF_UCHAR,
|
||||
$outStr = sprintf('%-5s %d', $field, $fieldData);
|
||||
}
|
||||
}
|
||||
|
||||
my $len = length($outStr);
|
||||
if ($len <= 20) { $wide = 20; }
|
||||
elsif ( $len <= 40 ) { $wide = 40; }
|
||||
elsif ( $len <= 60 ) { $wide = 60; }
|
||||
if ($len < 20) { $wide = 20; }
|
||||
elsif ( $len < 40 ) { $wide = 40; }
|
||||
elsif ( $len < 60 ) { $wide = 60; }
|
||||
else { $wide = 80;}
|
||||
|
||||
my $pad = $wide - $len;
|
||||
@@ -269,49 +293,64 @@ sub printField {
|
||||
return $col;
|
||||
}
|
||||
|
||||
# Query for a list of fields simultaneously.
|
||||
# The results are filled in the the %callback_data global hash
|
||||
# and the result of the operation is the number of read pvs
|
||||
# Query the native values of a list of PVs simultaneously.
|
||||
# The data is returned in the the %callback_data global hash.
|
||||
# The return value is the number of read pvs
|
||||
#
|
||||
# NOTE: Not re-entrant because results are written to global hash
|
||||
# %callback_data
|
||||
#
|
||||
# Usage: $fields_read = caget( @pvlist )
|
||||
sub caget {
|
||||
my @chans = map { CA->new($_); } @_;
|
||||
|
||||
#clear any previous results;
|
||||
%connected = ();
|
||||
$unconnected_count = scalar @_;
|
||||
%callback_data = ();
|
||||
%timed_out = ();
|
||||
$callback_incomplete = 0;
|
||||
|
||||
eval { CA->pend_io($opt_w); };
|
||||
if ($@) {
|
||||
if ($@ =~ m/^ECA_TIMEOUT/) {
|
||||
my $name = $chans[0]->name;
|
||||
my $err = (@chans > 1) ? 'some fields' : "'$name'";
|
||||
print "Channel connect timed out: $err not found.\n";
|
||||
foreach my $chan (@chans) {
|
||||
$timed_out{$chan->name} = !$chan->is_connected;
|
||||
}
|
||||
@chans = grep { $_->is_connected } @chans;
|
||||
} else {
|
||||
die $@;
|
||||
}
|
||||
}
|
||||
my @chans = map {
|
||||
print " Creating channel for $_\n" if $opt_D;
|
||||
CA->new($_, \&canew_callback);
|
||||
} @_;
|
||||
my $channel_count = scalar @chans;
|
||||
return 0 unless $channel_count gt 0;
|
||||
|
||||
map {
|
||||
$_->get_callback(\&caget_callback, $_->field_type);
|
||||
} @chans;
|
||||
print " $channel_count channels created.\n" if $opt_D;
|
||||
|
||||
my $fields_read = $callback_incomplete = @chans;
|
||||
CA->pend_event(0.1)
|
||||
while $callback_incomplete;
|
||||
return $fields_read;
|
||||
my $elapsed = 0;
|
||||
do {
|
||||
print " Waiting for $unconnected_count channels to connect\n"
|
||||
if $unconnected_count && $opt_D;
|
||||
print " Waiting for data from $callback_incomplete channels\n"
|
||||
if $callback_incomplete && $opt_D;
|
||||
CA->pend_event(0.1);
|
||||
$elapsed += 0.1;
|
||||
} until (($elapsed > $opt_w) or
|
||||
(scalar %connected && $callback_incomplete == 0));
|
||||
my $data_count = scalar keys %callback_data;
|
||||
printf " Got data from %d of %d channels\n", $data_count, $channel_count
|
||||
if $opt_D;
|
||||
return $data_count;
|
||||
}
|
||||
|
||||
sub canew_callback {
|
||||
my ($chan, $up) = @_;
|
||||
return unless $up;
|
||||
$connected{$chan->name} = $chan;
|
||||
$unconnected_count--;
|
||||
my $ftype = $chan->field_type;
|
||||
my $count = $chan->element_count;
|
||||
$ftype = 'DBR_LONG' if $ftype eq 'DBR_CHAR' && $count == 1;
|
||||
print " Getting ${\$chan->name} as $ftype\n" if $opt_D;
|
||||
# We have to fetch all elements so we can show how many there are
|
||||
$chan->get_callback(\&caget_callback, $ftype);
|
||||
$callback_incomplete++;
|
||||
}
|
||||
|
||||
sub caget_callback {
|
||||
my ($chan, $status, $data) = @_;
|
||||
die $status if $status;
|
||||
print " Got ${\$chan->name} = '$data'\n" if $opt_D;
|
||||
$callback_data{$chan->name} = $data;
|
||||
$callback_incomplete--;
|
||||
}
|
||||
@@ -333,69 +372,83 @@ sub printRecord {
|
||||
my @ftypes = (); #types, from parser
|
||||
my @bases = (); #bases, from parser
|
||||
foreach my $field (sort keys %{$record{$recType}}) {
|
||||
# Skip DTYP field if this rec type doesn't have device support defined
|
||||
next if $field eq 'DTYP' && !exists($device{$recType});
|
||||
|
||||
my ($fType, $fInterest, $base) = getFieldParams($recType, $field);
|
||||
# FIXME: Support waveform.VAL fields etc.
|
||||
unless( $fType eq 'DBF_NOACCESS' ) {
|
||||
if ($interest >= $fInterest ) {
|
||||
my $fToGet = "$name.$field";
|
||||
push @fields_pr, $field;
|
||||
push @readlist, $fToGet;
|
||||
push @ftypes, $fType;
|
||||
push @bases, $base;
|
||||
}
|
||||
my ($fType, $fInterest, $base, $special, $size) =
|
||||
getFieldParams($recType, $field);
|
||||
next if $fInterest > $interest;
|
||||
my $fToGet = "$name.$field";
|
||||
if ($fType eq 'DBF_NOACCESS') {
|
||||
next unless $special eq 'SPC_DBADDR';
|
||||
$fType = 'DBF_STRING';
|
||||
}
|
||||
elsif ($fType eq 'DBF_STRING' && $size >= 40) {
|
||||
$fToGet .= '$';
|
||||
}
|
||||
push @fields_pr, $field;
|
||||
push @readlist, $fToGet;
|
||||
push @ftypes, $fType;
|
||||
push @bases, $base;
|
||||
}
|
||||
my $fields_read = caget( @readlist );
|
||||
|
||||
my @missing;
|
||||
|
||||
# print while iterating over lists gathered
|
||||
my $col = 0;
|
||||
for (my $i=0; $i < scalar @readlist; $i++) {
|
||||
my $field = $fields_pr[$i];
|
||||
my $fToGet = $readlist[$i];
|
||||
my ($fType, $data, $base);
|
||||
next if $timed_out{$fToGet};
|
||||
push @missing, $field unless exists $callback_data{$fToGet};
|
||||
next unless exists $callback_data{$fToGet};
|
||||
$fType = $ftypes[$i];
|
||||
$base = $bases[$i];
|
||||
$data = $callback_data{$fToGet};
|
||||
$col = printField($field, $data, $fType, $base, $col);
|
||||
}
|
||||
print("\n"); # Final newline
|
||||
|
||||
printf "\nUnreadable fields: %s\n", join(', ', @missing) if @missing && $opt_D;
|
||||
}
|
||||
|
||||
# Prints list of record types found in dbd file. If level > 0
|
||||
# then the fields of that record type, their interest levels and types are
|
||||
# also printed.
|
||||
# Diagnostic routine, usage: void printList(level);
|
||||
# then uses printRecordList to display all fields in each record type.
|
||||
sub printList {
|
||||
my $level = shift;
|
||||
|
||||
foreach my $rkey (sort keys(%record)) {
|
||||
print(" $rkey\n");
|
||||
if ($level > 0) {
|
||||
foreach my $fkey (keys %{$record{$rkey}}) {
|
||||
print("\tField $fkey - interest $record{$rkey}{$fkey}[$iIdx] ");
|
||||
print("- type $record{$rkey}{$fkey}[$tIdx] ");
|
||||
print("- base $record{$rkey}{$fkey}[$bIdx]\n");
|
||||
}
|
||||
printRecordList($rkey);
|
||||
}
|
||||
else {
|
||||
print(" $rkey\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Prints list of fields with interest levels for given record type
|
||||
# Diagnostic routine, usage: void printRecordList("recordType");
|
||||
# Prints list of fields and metadata for given record type
|
||||
sub printRecordList {
|
||||
my $type = shift;
|
||||
|
||||
if (exists($record{$type}) ) {
|
||||
print("Record type - $type\n");
|
||||
foreach my $fkey (sort keys %{$record{$type}}) {
|
||||
printf('%-8s', $fkey);
|
||||
printf(" interest = $record{$type}{$fkey}[$iIdx]");
|
||||
printf(" type = %-12s ",$record{$type}{$fkey}[$tIdx]);
|
||||
print (" base = $record{$type}{$fkey}[$bIdx]\n");
|
||||
my $param = $record{$type}{$fkey};
|
||||
my $dbfType = $param->{dbfType};
|
||||
printf " %-8s", $fkey;
|
||||
printf " interest = %d", $param->{interest};
|
||||
printf " type = %-12s", $dbfType;
|
||||
if ($dbfType eq 'DBF_STRING') {
|
||||
printf " size = %s\n", $param->{size};
|
||||
}
|
||||
elsif ($dbfType =~ m/DBF_U?(CHAR|SHORT|INT|LONG|INT64)/) {
|
||||
printf " base = %s\n", $param->{base};
|
||||
}
|
||||
elsif ($dbfType eq 'DBF_NOACCESS') {
|
||||
printf " special = %s\n", $param->{special};
|
||||
}
|
||||
else {
|
||||
print "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
@@ -411,16 +464,20 @@ sub HELP_MESSAGE {
|
||||
" capr.pl [options] <record name> [<interest>]\n",
|
||||
"\n",
|
||||
" -h Print this help message.\n",
|
||||
" -D Print debug messages.\n",
|
||||
"Channel Access options:\n",
|
||||
" -w <sec>: Wait time, specifies CA timeout, default is $opt_w second\n",
|
||||
"Database Definitions:\n",
|
||||
" -d <file.dbd>: The file containing record type definitions.\n",
|
||||
" This can be set using the EPICS_CAPR_DBD_FILE environment variable.\n",
|
||||
" Default: ", AbsPath($opt_d), "\n",
|
||||
" Default: ", abs_path($opt_d), "\n",
|
||||
"Output Options:\n",
|
||||
" -r Lists all record types in the selected dbd file.\n",
|
||||
" -f <record type>: Lists all fields with their interest level, data type\n",
|
||||
" and number base for the given record_type.\n",
|
||||
" -f <record type>: Lists all fields with interest level, data type\n",
|
||||
" and other information for the given record_type.\n",
|
||||
" -l <interest>: interest level\n",
|
||||
" -n <elems>: Maximum number of array elements to display\n",
|
||||
" Default: $opt_n\n",
|
||||
"\n",
|
||||
"Base version: ", CA->version, "\n";
|
||||
exit 1;
|
||||
|
||||
@@ -241,7 +241,7 @@ void asCaStart(void)
|
||||
epicsThreadGetStackSize(epicsThreadStackBig),
|
||||
(EPICSTHREADFUNC)asCaTask,0);
|
||||
if(threadid==0) {
|
||||
errMessage(0,"asCaStart: taskSpawn Failure\n");
|
||||
errMessage(0,"asCaStart: taskSpawn Failure");
|
||||
}
|
||||
}
|
||||
epicsMutexMustLock(asCaTaskLock);
|
||||
|
||||
@@ -11,5 +11,6 @@
|
||||
|
||||
# This is a Makefile fragment, see src/ioc/Makefile.
|
||||
|
||||
$(patsubst %,$(COMMON_DIR)/%,$(BPT_DBD)) : \
|
||||
$(COMMON_DIR)/bpt%.dbd : $(MAKEBPT)
|
||||
$(addprefix $(COMMON_DIR)/,$(BPT_DBD)) : $(COMMON_DIR)/bpt%.dbd : \
|
||||
$(EPICS_BASE_HOST_BIN)/makeBpt$(HOSTEXE)
|
||||
# Don't try to use $(MAKEBPT) above
|
||||
|
||||
@@ -85,7 +85,7 @@ static epicsEventId startStopEvent;
|
||||
static char *threadNamePrefix[NUM_CALLBACK_PRIORITIES] = {
|
||||
"cbLow", "cbMedium", "cbHigh"
|
||||
};
|
||||
#define FULL_MSG(name) "callbackRequest: " name " ring buffer full\n"
|
||||
#define FULL_MSG(name) "callbackRequest: " ERL_ERROR " " name " ring buffer full\n"
|
||||
static char *fullMessage[NUM_CALLBACK_PRIORITIES] = {
|
||||
FULL_MSG("cbLow"), FULL_MSG("cbMedium"), FULL_MSG("cbHigh")
|
||||
};
|
||||
@@ -326,17 +326,17 @@ int callbackRequest(epicsCallback *pcallback)
|
||||
cbQueueSet *mySet;
|
||||
|
||||
if (!pcallback) {
|
||||
epicsInterruptContextMessage("callbackRequest: pcallback was NULL\n");
|
||||
epicsInterruptContextMessage("callbackRequest: " ERL_ERROR " pcallback was NULL\n");
|
||||
return S_db_notInit;
|
||||
}
|
||||
priority = pcallback->priority;
|
||||
if (priority < 0 || priority >= NUM_CALLBACK_PRIORITIES) {
|
||||
epicsInterruptContextMessage("callbackRequest: Bad priority\n");
|
||||
epicsInterruptContextMessage("callbackRequest: " ERL_ERROR " Bad priority\n");
|
||||
return S_db_badChoice;
|
||||
}
|
||||
mySet = &callbackQueue[priority];
|
||||
if (!mySet->queue) {
|
||||
epicsInterruptContextMessage("callbackRequest: Callbacks not initialized\n");
|
||||
epicsInterruptContextMessage("callbackRequest: " ERL_ERROR " Callbacks not initialized\n");
|
||||
return S_db_notInit;
|
||||
}
|
||||
if (mySet->queueOverflow) return S_db_bufFull;
|
||||
|
||||
@@ -1104,9 +1104,9 @@ static long dbPutFieldLink(DBADDR *paddr,
|
||||
if (link_info.ltype == PV_LINK &&
|
||||
(link_info.modifiers & (pvlOptCA | pvlOptCP | pvlOptCPP)) == 0) {
|
||||
chan = dbChannelCreate(link_info.target);
|
||||
if (chan && dbChannelOpen(chan) != 0) {
|
||||
errlogPrintf("ERROR: dbPutFieldLink %s.%s=%s: dbChannelOpen() failed\n",
|
||||
precord->name, pfldDes->name, link_info.target);
|
||||
if (chan && (status = dbChannelOpen(chan)) != 0) {
|
||||
errlogPrintf(ERL_ERROR ": dbPutFieldLink %s.%s=%s: dbChannelOpen() failed w/ 0x%lx\n",
|
||||
precord->name, pfldDes->name, link_info.target, status);
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -196,6 +196,73 @@ static void caLinkDec(caLink *pca)
|
||||
if (callback) callback(userPvt);
|
||||
}
|
||||
|
||||
struct waitPvt {
|
||||
caLink *pca;
|
||||
epicsEventId evt;
|
||||
};
|
||||
enum testEvent {
|
||||
testEventConnect,
|
||||
testEventCount,
|
||||
};
|
||||
|
||||
static
|
||||
void testdbCaWaitForEventCB(void *raw)
|
||||
{
|
||||
struct waitPvt *pvt = raw;
|
||||
|
||||
epicsMutexMustLock(pvt->pca->lock);
|
||||
epicsEventMustTrigger(pvt->evt);
|
||||
epicsMutexUnlock(pvt->pca->lock);
|
||||
}
|
||||
|
||||
static
|
||||
void testdbCaWaitForEvent(DBLINK *plink, unsigned long cnt, enum testEvent event)
|
||||
{
|
||||
caLink *pca;
|
||||
epicsEventId evt = epicsEventMustCreate(epicsEventEmpty);
|
||||
|
||||
dbScanLock(plink->precord);
|
||||
|
||||
assert(plink->type==CA_LINK);
|
||||
pca = (caLink *)plink->value.pv_link.pvt;
|
||||
|
||||
epicsMutexMustLock(pca->lock);
|
||||
assert(!pca->monitor && !pca->connect && !pca->userPvt);
|
||||
|
||||
while(!pca->isConnected || (event==testEventCount && pca->nUpdate < cnt)) {
|
||||
struct waitPvt pvt = {pca, evt};
|
||||
pca->connect = &testdbCaWaitForEventCB;
|
||||
pca->monitor = &testdbCaWaitForEventCB;
|
||||
pca->userPvt = &pvt;
|
||||
|
||||
epicsMutexUnlock(pca->lock);
|
||||
dbScanUnlock(plink->precord);
|
||||
|
||||
epicsEventMustWait(evt);
|
||||
|
||||
dbScanLock(plink->precord);
|
||||
epicsMutexMustLock(pca->lock);
|
||||
|
||||
pca->connect = NULL;
|
||||
pca->monitor = NULL;
|
||||
pca->userPvt = NULL;
|
||||
}
|
||||
|
||||
epicsEventDestroy(evt);
|
||||
epicsMutexUnlock(pca->lock);
|
||||
dbScanUnlock(plink->precord);
|
||||
}
|
||||
|
||||
void testdbCaWaitForConnect(DBLINK *plink)
|
||||
{
|
||||
testdbCaWaitForEvent(plink, 0, testEventConnect);
|
||||
}
|
||||
|
||||
void testdbCaWaitForUpdateCount(DBLINK *plink, unsigned long cnt)
|
||||
{
|
||||
testdbCaWaitForEvent(plink, cnt, testEventCount);
|
||||
}
|
||||
|
||||
/* Block until worker thread has processed all previously queued actions.
|
||||
* Does not prevent additional actions from being queued.
|
||||
*/
|
||||
@@ -232,22 +299,6 @@ void dbCaSync(void)
|
||||
epicsEventDestroy(wake);
|
||||
}
|
||||
|
||||
DBCORE_API unsigned long dbCaGetUpdateCount(struct link *plink)
|
||||
{
|
||||
caLink *pca = (caLink *)plink->value.pv_link.pvt;
|
||||
unsigned long ret;
|
||||
|
||||
if (!pca) return (unsigned long)-1;
|
||||
|
||||
epicsMutexMustLock(pca->lock);
|
||||
|
||||
ret = pca->nUpdate;
|
||||
|
||||
epicsMutexUnlock(pca->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void dbCaCallbackProcess(void *userPvt)
|
||||
{
|
||||
struct link *plink = (struct link *)userPvt;
|
||||
@@ -785,6 +836,8 @@ static void connectionCallback(struct connection_handler_args arg)
|
||||
caLink *pca;
|
||||
short link_action = 0;
|
||||
struct link *plink;
|
||||
dbCaCallback connect = 0;
|
||||
void *userPvt = 0;
|
||||
|
||||
pca = ca_puser(arg.chid);
|
||||
assert(pca);
|
||||
@@ -851,11 +904,16 @@ static void connectionCallback(struct connection_handler_args arg)
|
||||
}
|
||||
pca->gotAttributes = 0;
|
||||
if (pca->dbrType != DBR_STRING) {
|
||||
/* will run connect() callback later */
|
||||
link_action |= CA_GET_ATTRIBUTES;
|
||||
} else {
|
||||
connect = pca->connect;
|
||||
userPvt = pca->userPvt;
|
||||
}
|
||||
done:
|
||||
if (link_action) addAction(pca, link_action);
|
||||
epicsMutexUnlock(pca->lock);
|
||||
if (connect) connect(userPvt);
|
||||
}
|
||||
|
||||
static void eventCallback(struct event_handler_args arg)
|
||||
@@ -881,10 +939,10 @@ static void eventCallback(struct event_handler_args arg)
|
||||
if (precord) {
|
||||
if (arg.status != ECA_NORDACCESS &&
|
||||
arg.status != ECA_GETFAIL)
|
||||
errlogPrintf("dbCa: eventCallback record %s error %s\n",
|
||||
errlogPrintf("dbCa: eventCallback record %s " ERL_ERROR " %s\n",
|
||||
precord->name, ca_message(arg.status));
|
||||
} else {
|
||||
errlogPrintf("dbCa: eventCallback error %s\n",
|
||||
errlogPrintf("dbCa: eventCallback " ERL_ERROR " %s\n",
|
||||
ca_message(arg.status));
|
||||
}
|
||||
goto done;
|
||||
@@ -1029,10 +1087,10 @@ static void getAttribEventCallback(struct event_handler_args arg)
|
||||
if (arg.status != ECA_NORMAL) {
|
||||
dbCommon *precord = plink->precord;
|
||||
if (precord) {
|
||||
errlogPrintf("dbCa: getAttribEventCallback record %s error %s\n",
|
||||
errlogPrintf("dbCa: getAttribEventCallback record %s " ERL_ERROR " %s\n",
|
||||
precord->name, ca_message(arg.status));
|
||||
} else {
|
||||
errlogPrintf("dbCa: getAttribEventCallback error %s\n",
|
||||
errlogPrintf("dbCa: getAttribEventCallback " ERL_ERROR " %s\n",
|
||||
ca_message(arg.status));
|
||||
}
|
||||
epicsMutexUnlock(pca->lock);
|
||||
@@ -1058,6 +1116,7 @@ static void getAttribEventCallback(struct event_handler_args arg)
|
||||
|
||||
static void dbCaTask(void *arg)
|
||||
{
|
||||
epicsEventId requestSync = NULL;
|
||||
taskwdInsert(0, NULL, NULL);
|
||||
SEVCHK(ca_context_create(ca_enable_preemptive_callback),
|
||||
"dbCaTask calling ca_context_create");
|
||||
@@ -1078,13 +1137,20 @@ static void dbCaTask(void *arg)
|
||||
|
||||
epicsMutexMustLock(workListLock);
|
||||
if (!(pca = (caLink *)ellGet(&workList))){ /* Take off list head */
|
||||
if(requestSync) {
|
||||
/* dbCaSync() requires workListLock to be held here */
|
||||
epicsEventMustTrigger(requestSync);
|
||||
requestSync = NULL;
|
||||
}
|
||||
epicsMutexUnlock(workListLock);
|
||||
if (dbCaCtl == ctlExit) goto shutdown;
|
||||
break; /* workList is empty */
|
||||
}
|
||||
link_action = pca->link_action;
|
||||
if (link_action&CA_SYNC)
|
||||
epicsEventMustTrigger((epicsEventId)pca->userPvt); /* dbCaSync() requires workListLock to be held here */
|
||||
if (link_action&CA_SYNC) {
|
||||
assert(!requestSync);
|
||||
requestSync = pca->userPvt;
|
||||
}
|
||||
pca->link_action = 0;
|
||||
if (link_action & CA_CLEAR_CHANNEL) --removesOutstanding;
|
||||
epicsMutexUnlock(workListLock); /* Give back immediately */
|
||||
|
||||
@@ -48,8 +48,12 @@ DBCORE_API long dbCaPutLink(struct link *plink,short dbrType,
|
||||
extern struct ca_client_context * dbCaClientContext;
|
||||
|
||||
#ifdef EPICS_DBCA_PRIVATE_API
|
||||
/* Wait CA link work queue to become empty. eg. after from dbPut() to OUT */
|
||||
DBCORE_API void dbCaSync(void);
|
||||
DBCORE_API unsigned long dbCaGetUpdateCount(struct link *plink);
|
||||
/* Wait for the data update counter to reach the specified value. */
|
||||
DBCORE_API void testdbCaWaitForUpdateCount(DBLINK *plink, unsigned long cnt);
|
||||
/* Wait for CA link to become connected */
|
||||
DBCORE_API void testdbCaWaitForConnect(DBLINK *plink);
|
||||
#endif
|
||||
|
||||
/* These macros are for backwards compatibility */
|
||||
|
||||
@@ -9,14 +9,22 @@
|
||||
* in file LICENSE that is included with this distribution.
|
||||
\*************************************************************************/
|
||||
|
||||
/*
|
||||
* Author: Andrew Johnson <anj@aps.anl.gov>
|
||||
* Ralph Lange <Ralph.Lange@bessy.de>
|
||||
*/
|
||||
|
||||
#ifndef INC_dbChannel_H
|
||||
#define INC_dbChannel_H
|
||||
|
||||
/** \file dbChannel.h
|
||||
*
|
||||
* \author Andrew Johnson (ANL)
|
||||
* \author Ralph Lange (BESSY)
|
||||
*
|
||||
* \brief The dbChannel API gives access to record fields.
|
||||
*
|
||||
* The dbChannel API is used internally by the IOC and by link types, device
|
||||
* support and IOC servers (RSRV and QSRV) to access record fields, either
|
||||
* directly or through one or more server-side filters as specified in the
|
||||
* channel name used when creating the channel.
|
||||
*/
|
||||
|
||||
#include "dbDefs.h"
|
||||
#include "dbAddr.h"
|
||||
#include "ellLib.h"
|
||||
@@ -30,200 +38,531 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*
|
||||
/**
|
||||
* event subscription
|
||||
*/
|
||||
typedef struct evSubscrip {
|
||||
ELLNODE node;
|
||||
struct dbChannel *chan;
|
||||
EVENTFUNC *user_sub;
|
||||
void *user_arg;
|
||||
struct event_que *ev_que;
|
||||
db_field_log **pLastLog;
|
||||
unsigned long npend; /* n times this event is on the queue */
|
||||
unsigned long nreplace; /* n times replacing event on the queue */
|
||||
unsigned char select;
|
||||
char useValque;
|
||||
char callBackInProgress;
|
||||
char enabled;
|
||||
ELLNODE node;
|
||||
struct dbChannel * chan;
|
||||
EVENTFUNC * user_sub;
|
||||
void * user_arg;
|
||||
struct event_que * ev_que;
|
||||
db_field_log ** pLastLog;
|
||||
unsigned long npend; /**< n times this event is on the queue */
|
||||
unsigned long nreplace; /**< n times replacing event on the queue */
|
||||
unsigned char select;
|
||||
char useValque;
|
||||
char callBackInProgress;
|
||||
char enabled;
|
||||
} evSubscrip;
|
||||
|
||||
typedef struct chFilter chFilter;
|
||||
|
||||
/* A dbChannel points to a record field, and can have multiple filters */
|
||||
/** \brief A Database Channel object
|
||||
*
|
||||
* A dbChannel is created from a user-supplied channel name, and holds
|
||||
* pointers to the record & field and information about any filters that
|
||||
* were specified with it. The dbChannel macros defined in this header
|
||||
* file should always be used to read data from a dbChannel object, the
|
||||
* internal implementation may change without notice.
|
||||
*/
|
||||
typedef struct dbChannel {
|
||||
const char *name;
|
||||
dbAddr addr; /* address structure for record/field */
|
||||
long final_no_elements; /* final number of elements (arrays) */
|
||||
short final_field_size; /* final size of element */
|
||||
short final_type; /* final type of database field */
|
||||
ELLLIST filters; /* list of filters as created from JSON */
|
||||
ELLLIST pre_chain; /* list of filters to be called pre-event-queue */
|
||||
ELLLIST post_chain; /* list of filters to be called post-event-queue */
|
||||
const char *name; /**< Channel name */
|
||||
dbAddr addr; /**< Pointers to record & field */
|
||||
long final_no_elements; /**< Final number of array elements */
|
||||
short final_field_size; /**< Final size of each element */
|
||||
short final_type; /**< Final type of database field */
|
||||
ELLLIST filters; /**< Filters used by dbChannel */
|
||||
ELLLIST pre_chain; /**< Filters on pre-event-queue chain */
|
||||
ELLLIST post_chain; /**< Filters on post-event-queue chain */
|
||||
} dbChannel;
|
||||
|
||||
/* Prototype for the channel event function that is called in filter stacks
|
||||
/** \brief Event filter function type
|
||||
*
|
||||
* When invoked the scan lock for the record associated with 'chan' _may_ be locked.
|
||||
* Unless dbfl_has_copy(pLog), it must call dbScanLock before accessing the data,
|
||||
* as this indicates the data is still owned by the record.
|
||||
* Prototype for channel event filter functions.
|
||||
*
|
||||
* This function has ownership of the field log pLog, if it wishes to discard
|
||||
* this update it should free the field log with db_delete_field_log() and
|
||||
* then return NULL.
|
||||
* When these functions are called the scan lock for the record associated
|
||||
* with \p chan _may_ already be locked, but they must use dbfl_has_copy()
|
||||
* to determine whether the data in \p pLog belongs to the record. If that
|
||||
* returns 0 the function must call dbScanLock() before accessing the data.
|
||||
*
|
||||
* A filter function owns the field log \p pLog when called. To discard an
|
||||
* update it should free the field log using db_delete_field_log() and
|
||||
* return NULL.
|
||||
*/
|
||||
typedef db_field_log* (chPostEventFunc)(void *pvt, dbChannel *chan, db_field_log *pLog);
|
||||
|
||||
/* Return values from chFilterIf->parse_* routines: */
|
||||
/** \brief Result returned by chFilterIf parse routines.
|
||||
*
|
||||
* The parsing functions from a chFilterIf must return either \p parse_stop
|
||||
* (in event of an error) or \p parse_continue.
|
||||
*/
|
||||
typedef enum {
|
||||
parse_stop, parse_continue
|
||||
} parse_result;
|
||||
|
||||
/* These routines must be implemented by each filter plug-in */
|
||||
/** \brief Channel Filter Interface
|
||||
*
|
||||
* Routines to be implemented by each Channel Filter.
|
||||
*/
|
||||
typedef struct chFilterIf {
|
||||
/* cleanup pointer passed to dbRegisterFilter().
|
||||
* Called during DB shutdown
|
||||
/** \brief Release private filter data.
|
||||
*
|
||||
* Called during database shutdown to release resources allocated by
|
||||
* the filter.
|
||||
* \param puser The user-pointer passed into dbRegisterFilter().
|
||||
*/
|
||||
void (* priv_free)(void *puser);
|
||||
/* Parsing event handlers: */
|
||||
parse_result (* parse_start)(chFilter *filter);
|
||||
/* If parse_start() returns parse_continue for a filter, one of
|
||||
|
||||
/** \name Parsing event handlers
|
||||
*
|
||||
* A filter that doesn't accept a particular JSON value type may use a
|
||||
* \p NULL pointer to the parsing handler for that value type, which is
|
||||
* equivalent to a routine that always returns \p parse_stop.
|
||||
*/
|
||||
|
||||
/** \brief Create new filter instance.
|
||||
*
|
||||
* Called when a new filter instance is requested. Filter may allocate
|
||||
* resources for this instance and store in \p filter->puser.
|
||||
* If parse_start() returns \p parse_continue for a filter, one of
|
||||
* parse_abort() or parse_end() will later be called for that same
|
||||
* filter.
|
||||
* \param filter Pointer to instance data.
|
||||
* \returns \p parse_stop on error, or \p parse_continue
|
||||
*/
|
||||
parse_result (* parse_start)(chFilter *filter);
|
||||
|
||||
/** \brief Parsing of filter instance is being cancelled.
|
||||
*
|
||||
* This function should release any memory allocated for the given
|
||||
* \p filter instance; no further parsing handlers will be called for it.
|
||||
* \param filter Pointer to instance data.
|
||||
*/
|
||||
void (* parse_abort)(chFilter *filter);
|
||||
/* If parse_abort() is called it should release any memory allocated
|
||||
* for this filter; no further parse_...() calls will be made;
|
||||
|
||||
/** \brief Parsing of filter instance has completed successfully.
|
||||
*
|
||||
* The parser has reached the end of this instance and no further parsing
|
||||
* handlers will be called for it. The filter must check the instance
|
||||
* data and indicate whether it was complete or not.
|
||||
* \param filter Pointer to instance data.
|
||||
* \returns \p parse_stop on error, or \p parse_continue
|
||||
*/
|
||||
parse_result (* parse_end)(chFilter *filter);
|
||||
/* If parse_end() returns parse_stop it should have released any
|
||||
* memory allocated for this filter; no further parse_...() calls will
|
||||
* be made in this case.
|
||||
|
||||
/** \brief Parser saw \p null value.
|
||||
*
|
||||
* Optional.
|
||||
* Null values are rarely accepted by channel filters.
|
||||
* \param filter Pointer to instance data.
|
||||
* \returns \p parse_stop on error, or \p parse_continue
|
||||
*/
|
||||
|
||||
parse_result (* parse_null)(chFilter *filter);
|
||||
parse_result (* parse_boolean)(chFilter *filter, int boolVal);
|
||||
parse_result (* parse_integer)(chFilter *filter, long integerVal);
|
||||
parse_result (* parse_double)(chFilter *filter, double doubleVal);
|
||||
parse_result (* parse_string)(chFilter *filter, const char *stringVal,
|
||||
size_t stringLen); /* NB: stringVal is not zero-terminated: */
|
||||
|
||||
/** \brief Parser saw boolean value.
|
||||
*
|
||||
* Optional.
|
||||
* \param filter Pointer to instance data.
|
||||
* \param boolVal true/false Value.
|
||||
* \returns \p parse_stop on error, or \p parse_continue
|
||||
*/
|
||||
parse_result (* parse_boolean)(chFilter *filter, int boolVal);
|
||||
|
||||
/** \brief Parser saw integer value.
|
||||
*
|
||||
* Optional.
|
||||
* \param filter Pointer to instance data.
|
||||
* \param integerVal Value.
|
||||
* \returns \p parse_stop on error, or \p parse_continue
|
||||
*/
|
||||
parse_result (* parse_integer)(chFilter *filter, long integerVal);
|
||||
|
||||
/** \brief Parser saw double value.
|
||||
*
|
||||
* Optional.
|
||||
* \param filter Pointer to instance data.
|
||||
* \param doubleVal Value.
|
||||
* \returns \p parse_stop on error, or \p parse_continue
|
||||
*/
|
||||
parse_result (* parse_double)(chFilter *filter, double doubleVal);
|
||||
|
||||
/** \brief Parser saw string value.
|
||||
*
|
||||
* Optional.
|
||||
* \param filter Pointer to instance data.
|
||||
* \param stringVal Value, not zero-terminated.
|
||||
* \param stringLen Number of chars in \p stringVal.
|
||||
* \returns \p parse_stop on error, or \p parse_continue
|
||||
*/
|
||||
parse_result (* parse_string)(chFilter *filter, const char *stringVal,
|
||||
size_t stringLen);
|
||||
|
||||
/** \brief Parser saw start of a JSON map value.
|
||||
*
|
||||
* Optional.
|
||||
* Inside a JSON map all data consists of key/value pairs.
|
||||
* \param filter Pointer to instance data.
|
||||
* \returns \p parse_stop on error, or \p parse_continue
|
||||
*/
|
||||
parse_result (* parse_start_map)(chFilter *filter);
|
||||
|
||||
/** \brief Parser saw a JSON map key.
|
||||
*
|
||||
* Optional.
|
||||
* \param filter Pointer to instance data.
|
||||
* \param key Value not zero-terminated.
|
||||
* \param stringLen Number of chars in \p key
|
||||
* \returns \p parse_stop on error, or \p parse_continue
|
||||
*/
|
||||
parse_result (* parse_map_key)(chFilter *filter, const char *key,
|
||||
size_t stringLen); /* NB: key is not zero-terminated: */
|
||||
size_t stringLen);
|
||||
|
||||
/** \brief Parser saw end of a JSON map value.
|
||||
*
|
||||
* Optional.
|
||||
* \param filter Pointer to instance data.
|
||||
* \returns \p parse_stop on error, or \p parse_continue
|
||||
*/
|
||||
parse_result (* parse_end_map)(chFilter *filter);
|
||||
|
||||
/** \brief Parser saw start of a JSON array value.
|
||||
*
|
||||
* Optional.
|
||||
* Data inside a JSON array doesn't have to be all of the same type.
|
||||
* \param filter Pointer to instance data.
|
||||
* \returns \p parse_stop on error, or \p parse_continue
|
||||
*/
|
||||
parse_result (* parse_start_array)(chFilter *filter);
|
||||
|
||||
/** \brief Parser saw end of a JSON array value.
|
||||
*
|
||||
* Optional.
|
||||
* \param filter Pointer to instance data.
|
||||
* \returns \p parse_stop on error, or \p parse_continue
|
||||
*/
|
||||
parse_result (* parse_end_array)(chFilter *filter);
|
||||
|
||||
/* Channel operations: */
|
||||
/** \name Channel operations */
|
||||
|
||||
/** \brief Open filter on channel.
|
||||
*
|
||||
* Optional, initialize instance.
|
||||
* \param filter Pointer to instance data.
|
||||
* \returns 0, or an error status value.
|
||||
*/
|
||||
long (* channel_open)(chFilter *filter);
|
||||
void (* channel_register_pre) (chFilter *filter, chPostEventFunc **cb_out, void **arg_out, db_field_log *probe);
|
||||
void (* channel_register_post)(chFilter *filter, chPostEventFunc **cb_out, void **arg_out, db_field_log *probe);
|
||||
void (* channel_report)(chFilter *filter, int level, const unsigned short indent);
|
||||
|
||||
/** \brief Get pre-chain filter function.
|
||||
*
|
||||
* Optional.
|
||||
* Returns pre-chain filter function and context.
|
||||
* \param[in] filter Pointer to instance data.
|
||||
* \param[out] cb_out Write filter function pointer here.
|
||||
* \param[out] arg_out Write private data pointer here.
|
||||
* \param[in,out] probe db_field_log with metadata for adjusting.
|
||||
*/
|
||||
void (* channel_register_pre) (chFilter *filter,
|
||||
chPostEventFunc **cb_out, void **arg_out, db_field_log *probe);
|
||||
|
||||
/** \brief Get post-chain filter function.
|
||||
*
|
||||
* Optional, return post-chain filter function and context.
|
||||
* \param[in] filter Pointer to instance data.
|
||||
* \param[out] cb_out Write filter function pointer here.
|
||||
* \param[out] arg_out Write private data pointer here.
|
||||
* \param[in,out] probe db_field_log with metadata for adjusting.
|
||||
*/
|
||||
void (* channel_register_post)(chFilter *filter,
|
||||
chPostEventFunc **cb_out, void **arg_out, db_field_log *probe);
|
||||
|
||||
/** \brief Print information about filter to stdout.
|
||||
*
|
||||
* Optional.
|
||||
* \param filter Pointer to instance data.
|
||||
* \param level Higher levels may provide more detail.
|
||||
* \param indent Indent all lines by this many spaces.
|
||||
*/
|
||||
void (* channel_report)(chFilter *filter,
|
||||
int level, const unsigned short indent);
|
||||
|
||||
/** \brief Close filter.
|
||||
*
|
||||
* Optional, releases resources allocated for this instance.
|
||||
* \param filter Pointer to instance data.
|
||||
*/
|
||||
void (* channel_close)(chFilter *filter);
|
||||
} chFilterIf;
|
||||
|
||||
/* A chFilterPlugin holds data for a filter plugin */
|
||||
/** \brief Filter plugin data
|
||||
*
|
||||
* A chFilterPlugin object holds data about a filter plugin.
|
||||
*/
|
||||
typedef struct chFilterPlugin {
|
||||
ELLNODE node;
|
||||
const char *name;
|
||||
const chFilterIf *fif;
|
||||
void *puser;
|
||||
ELLNODE node; /**< \brief List node (dbBase->filterList) */
|
||||
const char *name; /**< \brief Filter name */
|
||||
const chFilterIf *fif; /**< \brief Filter interface routines */
|
||||
void *puser; /**< \brief For use by the plugin */
|
||||
} chFilterPlugin;
|
||||
|
||||
/* A chFilter holds data for a single filter instance */
|
||||
/** \brief Filter instance data
|
||||
*
|
||||
* A chFilter holds data about a single filter instance.
|
||||
*/
|
||||
struct chFilter {
|
||||
ELLNODE list_node;
|
||||
ELLNODE pre_node;
|
||||
ELLNODE post_node;
|
||||
dbChannel *chan;
|
||||
const chFilterPlugin *plug;
|
||||
chPostEventFunc *pre_func;
|
||||
void *pre_arg;
|
||||
chPostEventFunc *post_func;
|
||||
void *post_arg;
|
||||
void *puser;
|
||||
ELLNODE list_node; /**< \brief List node (dbChannel->filters) */
|
||||
ELLNODE pre_node; /**< \brief List node (dbChannel->pre_chain) */
|
||||
ELLNODE post_node; /**< \brief List node (dbChannel->post_chain) */
|
||||
dbChannel *chan; /**< \brief The dbChannel we belong to */
|
||||
const chFilterPlugin *plug; /**< \brief The plugin that created us */
|
||||
chPostEventFunc *pre_func; /**< \brief pre-chain filter function */
|
||||
void *pre_arg; /**< \brief pre-chain context pointer */
|
||||
chPostEventFunc *post_func; /**< \brief post-chain filter function */
|
||||
void *post_arg; /**< \brief post-chain context pointer */
|
||||
void *puser; /**< \brief For use by the plugin */
|
||||
};
|
||||
|
||||
struct dbCommon;
|
||||
struct dbFldDes;
|
||||
|
||||
DBCORE_API void dbChannelInit (void);
|
||||
/** \brief Initialize the dbChannel subsystem. */
|
||||
DBCORE_API void dbChannelInit(void);
|
||||
|
||||
/** \brief Cleanup the dbChannel subsystem. */
|
||||
DBCORE_API void dbChannelExit(void);
|
||||
|
||||
/** \brief Test the given PV name for existance.
|
||||
*
|
||||
* This routine looks up the given record and field name, but does not check
|
||||
* whether any field modifiers given after the field name are correct.
|
||||
* This is sufficient for the correct server to quickly direct searches to the
|
||||
* IOC that owns that PV name. Field modifiers will be checked when
|
||||
* dbChannelCreate() is later called with the same name.
|
||||
* \param name Channel name.
|
||||
* \returns 0, or an error status value.
|
||||
*/
|
||||
DBCORE_API long dbChannelTest(const char *name);
|
||||
|
||||
/** \brief Create a dbChannel object for the given PV name.
|
||||
*
|
||||
* \param name Channel name.
|
||||
* \return Pointer to dbChannel object, or NULL if invalid.
|
||||
*/
|
||||
DBCORE_API dbChannel * dbChannelCreate(const char *name);
|
||||
|
||||
/** \brief Open a dbChannel for doing I/O.
|
||||
*
|
||||
* \param chan Pointer to the dbChannel object.
|
||||
* \returns 0, or an error status value.
|
||||
*/
|
||||
DBCORE_API long dbChannelOpen(dbChannel *chan);
|
||||
|
||||
/*Following is also defined in db_convert.h*/
|
||||
/** \brief Request (DBR) type conversion array.
|
||||
*
|
||||
* This converter array is declared in db_convert.h but redeclared
|
||||
* here as it is needed by the dbChannel...CAType macros defined here.
|
||||
*/
|
||||
DBCORE_API extern unsigned short dbDBRnewToDBRold[];
|
||||
|
||||
/* In the following macros pChan is dbChannel* */
|
||||
/** \name dbChannel Inspection Macros */
|
||||
|
||||
/* evaluates to const char* */
|
||||
/** \brief Name that defined the channel.
|
||||
*
|
||||
* \param pChan Pointer to the dbChannel object.
|
||||
* \returns const char*
|
||||
*/
|
||||
#define dbChannelName(pChan) ((pChan)->name)
|
||||
|
||||
/* evaluates to struct dbCommon* */
|
||||
/** \brief Record the channel connects to.
|
||||
*
|
||||
* \param pChan Pointer to the dbChannel object.
|
||||
* \returns struct dbCommon*
|
||||
*/
|
||||
#define dbChannelRecord(pChan) ((pChan)->addr.precord)
|
||||
|
||||
/* evaluates to struct dbFldDes* */
|
||||
/** \brief Field descriptor for the field pointed to.
|
||||
*
|
||||
* \param pChan Pointer to the dbChannel object.
|
||||
* \returns struct dbFldDes*
|
||||
*/
|
||||
#define dbChannelFldDes(pChan) ((pChan)->addr.pfldDes)
|
||||
|
||||
/* evaluates to long */
|
||||
/** \brief Number of array elements in the field.
|
||||
*
|
||||
* \param pChan Pointer to the dbChannel object.
|
||||
* \returns long
|
||||
*/
|
||||
#define dbChannelElements(pChan) ((pChan)->addr.no_elements)
|
||||
|
||||
/* evaluates to short */
|
||||
/** \brief Data type (DBF type) of the field.
|
||||
*
|
||||
* \param pChan Pointer to the dbChannel object.
|
||||
* \returns short
|
||||
*/
|
||||
#define dbChannelFieldType(pChan) ((pChan)->addr.field_type)
|
||||
|
||||
/* evaluates to short */
|
||||
/** \brief Request type (DBR type) of the field.
|
||||
*
|
||||
* \param pChan Pointer to the dbChannel object.
|
||||
* \returns short
|
||||
*/
|
||||
#define dbChannelExportType(pChan) ((pChan)->addr.dbr_field_type)
|
||||
|
||||
/* evaluates to short */
|
||||
/** \brief CA data type of the field.
|
||||
*
|
||||
* \param pChan Pointer to the dbChannel object.
|
||||
* \returns short
|
||||
*/
|
||||
#define dbChannelExportCAType(pChan) (dbDBRnewToDBRold[dbChannelExportType(pChan)])
|
||||
|
||||
/* evaluates to short */
|
||||
/** \brief Field (element if array) size in bytes.
|
||||
*
|
||||
* \param pChan Pointer to the dbChannel object.
|
||||
* \returns short
|
||||
*/
|
||||
#define dbChannelFieldSize(pChan) ((pChan)->addr.field_size)
|
||||
|
||||
/* evaluates to long */
|
||||
/** \brief Array length after filtering.
|
||||
*
|
||||
* \param pChan Pointer to the dbChannel object.
|
||||
* \returns long
|
||||
*/
|
||||
#define dbChannelFinalElements(pChan) ((pChan)->final_no_elements)
|
||||
|
||||
/* evaluates to short */
|
||||
/** \brief Data type after filtering.
|
||||
*
|
||||
* \param pChan Pointer to the dbChannel object.
|
||||
* \returns short
|
||||
*/
|
||||
#define dbChannelFinalFieldType(pChan) ((pChan)->final_type)
|
||||
|
||||
/* evaluates to short */
|
||||
/** \brief Channel CA data type after filtering.
|
||||
*
|
||||
* \param pChan Pointer to the dbChannel object.
|
||||
* \returns short
|
||||
*/
|
||||
#define dbChannelFinalCAType(pChan) (dbDBRnewToDBRold[(pChan)->final_type])
|
||||
|
||||
/* evaluates to short */
|
||||
/** \brief Field/element size after filtering, in bytes.
|
||||
*
|
||||
* \param pChan Pointer to the dbChannel object.
|
||||
* \returns short */
|
||||
#define dbChannelFinalFieldSize(pChan) ((pChan)->final_field_size)
|
||||
|
||||
/* evaluates to short */
|
||||
/** \brief Field special attribute.
|
||||
*
|
||||
* \param pChan Pointer to the dbChannel object.
|
||||
* \returns short
|
||||
*/
|
||||
#define dbChannelSpecial(pChan) ((pChan)->addr.special)
|
||||
|
||||
/* Channel filters do not get to interpose here since there are many
|
||||
/** \brief Pointer to the record field.
|
||||
*
|
||||
* Channel filters do not get to interpose here since there are many
|
||||
* places where the field pointer is compared with the address of a
|
||||
* specific record field, so they can't modify the pointer value.
|
||||
* \param pChan Pointer to the dbChannel object.
|
||||
* \returns void *
|
||||
*/
|
||||
/* evaluates to void* */
|
||||
#define dbChannelField(pChan) ((pChan)->addr.pfield)
|
||||
|
||||
|
||||
/** \name dbChannel Operation Functions */
|
||||
|
||||
/** \brief dbGet() through a dbChannel.
|
||||
*
|
||||
* Calls dbGet() for the field that \p chan refers to.
|
||||
* Only call this routine if the record is already locked.
|
||||
* \param[in] chan Pointer to the dbChannel object.
|
||||
* \param[in] type Request type from dbFldTypes.h.
|
||||
* \param[out] pbuffer Pointer to data buffer.
|
||||
* \param[in,out] options Request options from dbAccessDefs.h.
|
||||
* \param[in,out] nRequest Pointer to the element count.
|
||||
* \param[in] pfl Pointer to a db_field_log or NULL.
|
||||
* \returns 0, or an error status value.
|
||||
*/
|
||||
DBCORE_API long dbChannelGet(dbChannel *chan, short type,
|
||||
void *pbuffer, long *options, long *nRequest, void *pfl);
|
||||
|
||||
/** \brief dbGetField() through a dbChannel.
|
||||
*
|
||||
* Get values from a PV through a channel.
|
||||
* This routine locks the record, calls
|
||||
* dbChannelGet(), then unlocks the record again.
|
||||
* \param[in] chan Pointer to the dbChannel object.
|
||||
* \param[in] type Request type from dbFldTypes.h.
|
||||
* \param[out] pbuffer Pointer to data buffer.
|
||||
* \param[in,out] options Request options from dbAccessDefs.h.
|
||||
* \param[in,out] nRequest Pointer to the element count.
|
||||
* \param[in] pfl Pointer to a db_field_log or NULL.
|
||||
* \returns 0, or an error status value.
|
||||
*/
|
||||
DBCORE_API long dbChannelGetField(dbChannel *chan, short type,
|
||||
void *pbuffer, long *options, long *nRequest, void *pfl);
|
||||
|
||||
/** \brief dbPut() through a dbChannel.
|
||||
*
|
||||
* Put values to a PV through a channel. Only call this routine if the
|
||||
* record is already locked.
|
||||
* Calls dbPut() for the field that \p chan refers to.
|
||||
* \param chan[in] Pointer to the dbChannel object.
|
||||
* \param type[in] Request type from dbFldTypes.h.
|
||||
* \param pbuffer[in] Pointer to data buffer.
|
||||
* \param nRequest[in] Number of elements in pbuffer.
|
||||
* \returns 0, or an error status value.
|
||||
*/
|
||||
DBCORE_API long dbChannelPut(dbChannel *chan, short type,
|
||||
const void *pbuffer, long nRequest);
|
||||
|
||||
/** \brief dbPutField() through a dbChannel.
|
||||
*
|
||||
* Put values to a PV through a channel.
|
||||
* This routine calls dbPutField() for the field that \p chan refers to.
|
||||
* \param chan[in] Pointer to the dbChannel object.
|
||||
* \param type[in] Request type from dbFldTypes.h.
|
||||
* \param pbuffer[in] Pointer to data buffer.
|
||||
* \param nRequest[in] Number of elements in pbuffer.
|
||||
* \returns 0, or an error status value.
|
||||
*/
|
||||
DBCORE_API long dbChannelPutField(dbChannel *chan, short type,
|
||||
const void *pbuffer, long nRequest);
|
||||
|
||||
/** \brief Print report on a channel.
|
||||
*
|
||||
* Print information about the channel to stdout.
|
||||
* \param chan Pointer to the dbChannel object.
|
||||
* \param level Higher levels may provide more detail.
|
||||
* \param indent Indent all lines by this many spaces.
|
||||
*/
|
||||
DBCORE_API void dbChannelShow(dbChannel *chan, int level,
|
||||
const unsigned short indent);
|
||||
|
||||
/** \brief Print report on a channel's filters.
|
||||
*
|
||||
* Print information about the channel's filters to stdout.
|
||||
* \param chan Pointer to the dbChannel object.
|
||||
* \param level Higher levels may provide more detail.
|
||||
* \param indent Indent all lines by this many spaces.
|
||||
*/
|
||||
DBCORE_API void dbChannelFilterShow(dbChannel *chan, int level,
|
||||
const unsigned short indent);
|
||||
|
||||
/** \brief Delete a channel.
|
||||
*
|
||||
* Releases resources owned by this channel and its filters.
|
||||
* \param chan Pointer to the dbChannel object.
|
||||
*/
|
||||
DBCORE_API void dbChannelDelete(dbChannel *chan);
|
||||
|
||||
DBCORE_API void dbRegisterFilter(const char *key, const chFilterIf *fif, void *puser);
|
||||
DBCORE_API db_field_log* dbChannelRunPreChain(dbChannel *chan, db_field_log *pLogIn);
|
||||
DBCORE_API db_field_log* dbChannelRunPostChain(dbChannel *chan, db_field_log *pLogIn);
|
||||
|
||||
/** \name Other routines */
|
||||
|
||||
DBCORE_API void dbRegisterFilter(const char *key,
|
||||
const chFilterIf *fif, void *puser);
|
||||
DBCORE_API db_field_log* dbChannelRunPreChain(dbChannel *chan,
|
||||
db_field_log *pLogIn);
|
||||
DBCORE_API db_field_log* dbChannelRunPostChain(dbChannel *chan,
|
||||
db_field_log *pLogIn);
|
||||
DBCORE_API const chFilterPlugin * dbFindFilter(const char *key, size_t len);
|
||||
DBCORE_API void dbChannelGetArrayInfo(dbChannel *chan,
|
||||
void **pfield, long *no_elements, long *offset);
|
||||
|
||||
@@ -230,6 +230,8 @@ The B<SPVT> field is for internal use by the scanning system.
|
||||
}
|
||||
field(DISP,DBF_UCHAR) {
|
||||
prompt("Disable putField")
|
||||
promptgroup("10 - Common")
|
||||
interest(1)
|
||||
}
|
||||
field(PROC,DBF_UCHAR) {
|
||||
prompt("Force Processing")
|
||||
@@ -456,9 +458,10 @@ record. If left empty, the record is placed in group C<DEFAULT>.
|
||||
|
||||
The B<ASP> field is private for use by the access security system.
|
||||
|
||||
The B<DISP> field controls whether puts from outside the IOC are allowed to
|
||||
modify the fields of this record at all. If the field is set to TRUE all puts
|
||||
directed to this record are ignored, except for puts to the field DISP itself.
|
||||
The B<DISP> field can be set to a non-zero value to reject puts from outside of
|
||||
the IOC (i.e. via Channel Access or PV Access) to any field of the record other
|
||||
than to the DISP field itself. Field changes and record processing can still be
|
||||
instigated from inside the IOC using DB links and the IOC scan mechanisms.
|
||||
|
||||
The B<DTYP> field specifies the device type for the record. Most record types
|
||||
have their own set of device types which are specified in the IOC's database
|
||||
@@ -489,17 +492,37 @@ The B<TIME> field holds the time stamp when this record was last processed.
|
||||
The B<UTAG> field can be used to hold a site-specific 64-bit User Tag value
|
||||
that is associated with the record's time stamp.
|
||||
|
||||
The B<TSE> field indicates the mechanism to use to get the time stamp. '0' -
|
||||
call get time as before '-1' - call the time stamp driver and use the best
|
||||
source available. '-2' - the device support sets the time stamp and optional
|
||||
user tag from the hardware. Values between 1-255 request the time of the last
|
||||
occurance of a generalTime event.
|
||||
The B<TSE> field value indicates the mechanism to use to get the time stamp:
|
||||
|
||||
=over
|
||||
|
||||
=item *
|
||||
|
||||
C< 0> E<mdash> Get the current time as normal
|
||||
|
||||
=item *
|
||||
|
||||
C<-1> E<mdash> Ask the time stamp driver for its best source of the current time, if
|
||||
available.
|
||||
|
||||
=item *
|
||||
|
||||
C<-2> E<mdash> Device support sets the time stamp and the optional User Tag from the
|
||||
hardware.
|
||||
|
||||
=item *
|
||||
|
||||
Positive values (normally between 1-255) get the time of the last occurance of
|
||||
the numbered generalTime event.
|
||||
|
||||
=back
|
||||
|
||||
The B<TSEL> field contains an input link for obtaining the time stamp. If this
|
||||
link points to the TIME field of a record then the time stamp and user tag of
|
||||
link points to the TIME field of a record then the time stamp and User Tag of
|
||||
that record are copied directly into this record (Channel Access links can only
|
||||
copy the time stamp). If the link points to any other field, that field's value
|
||||
is read and stored in the TSE field which is then used to acquire a time stamp.
|
||||
copy the time stamp, not the User Tag). If the link points to any other field,
|
||||
that field's value is read and stored in the TSE field which is then used to
|
||||
provide the time stamp as described above.
|
||||
|
||||
=fields ASG, ASP, DISP, DTYP, MLOK, MLIS, PPN, PPNR, PUTF, RDES, RPRO, TIME, UTAG, TSE, TSEL
|
||||
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
#include "dbFldTypes.h"
|
||||
#include "dbLink.h"
|
||||
#include "link.h"
|
||||
#include "errlog.h"
|
||||
|
||||
/**************************** Convert functions ****************************/
|
||||
|
||||
@@ -178,17 +179,23 @@ static long dbConstLoadLS(struct link *plink, char *pbuffer, epicsUInt32 size,
|
||||
epicsUInt32 *plen)
|
||||
{
|
||||
const char *pstr = plink->value.constantStr;
|
||||
long status;
|
||||
|
||||
if (!pstr)
|
||||
return S_db_badField;
|
||||
|
||||
return dbLSConvertJSON(pstr, pbuffer, size, plen);
|
||||
status = dbLSConvertJSON(pstr, pbuffer, size, plen);
|
||||
if (status)
|
||||
errlogPrintf("... while parsing link %s.%s\n",
|
||||
plink->precord->name, dbLinkFieldName(plink));
|
||||
return status;
|
||||
}
|
||||
|
||||
static long dbConstLoadArray(struct link *plink, short dbrType, void *pbuffer,
|
||||
long *pnReq)
|
||||
{
|
||||
const char *pstr = plink->value.constantStr;
|
||||
long status;
|
||||
|
||||
if (!pstr)
|
||||
return S_db_badField;
|
||||
@@ -197,7 +204,11 @@ static long dbConstLoadArray(struct link *plink, short dbrType, void *pbuffer,
|
||||
if (dbrType == DBF_MENU || dbrType == DBF_ENUM || dbrType == DBF_DEVICE)
|
||||
dbrType = DBF_USHORT;
|
||||
|
||||
return dbPutConvertJSON(pstr, dbrType, pbuffer, pnReq);
|
||||
status = dbPutConvertJSON(pstr, dbrType, pbuffer, pnReq);
|
||||
if (status)
|
||||
errlogPrintf("... while parsing link %s.%s\n",
|
||||
plink->precord->name, dbLinkFieldName(plink));
|
||||
return status;
|
||||
}
|
||||
|
||||
static long dbConstGetNelements(const struct link *plink, long *nelements)
|
||||
|
||||
@@ -30,10 +30,12 @@ typedef struct parseContext {
|
||||
} parseContext;
|
||||
|
||||
static int dbcj_null(void *ctx) {
|
||||
errlogPrintf("dbConvertJSON: Null objects not supported\n");
|
||||
return 0; /* Illegal */
|
||||
}
|
||||
|
||||
static int dbcj_boolean(void *ctx, int val) {
|
||||
errlogPrintf("dbConvertJSON: Boolean not supported\n");
|
||||
return 0; /* Illegal */
|
||||
}
|
||||
|
||||
@@ -50,10 +52,6 @@ static int dbcj_integer(void *ctx, long long num) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int dblsj_integer(void *ctx, long long num) {
|
||||
return 0; /* Illegal */
|
||||
}
|
||||
|
||||
static int dbcj_double(void *ctx, double num) {
|
||||
parseContext *parser = (parseContext *) ctx;
|
||||
FASTCONVERT conv = dbFastPutConvertRoutine[DBF_DOUBLE][parser->dbrType];
|
||||
@@ -66,7 +64,9 @@ static int dbcj_double(void *ctx, double num) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int dblsj_double(void *ctx, double num) {
|
||||
static int dblsj_number(void *ctx, const char *val, size_t len) {
|
||||
errlogPrintf("dbLSConvertJSON: Numeric value %.*s provided, string expected\n",
|
||||
(int)len, val);
|
||||
return 0; /* Illegal */
|
||||
}
|
||||
|
||||
@@ -78,7 +78,8 @@ static int dbcj_string(void *ctx, const unsigned char *val, size_t len) {
|
||||
* metadata about the field than we have available at the moment.
|
||||
*/
|
||||
if (parser->dbrType != DBF_STRING) {
|
||||
errlogPrintf("dbConvertJSON: String provided, numeric value(s) expected\n");
|
||||
errlogPrintf("dbConvertJSON: String \"%.*s\" provided, numeric value expected\n",
|
||||
(int)len, val);
|
||||
return 0; /* Illegal */
|
||||
}
|
||||
|
||||
@@ -97,11 +98,6 @@ static int dblsj_string(void *ctx, const unsigned char *val, size_t len) {
|
||||
parseContext *parser = (parseContext *) ctx;
|
||||
char *pdest = parser->pdest;
|
||||
|
||||
if (parser->dbrType != DBF_STRING) {
|
||||
errlogPrintf("dbConvertJSON: dblsj_string dbrType error\n");
|
||||
return 0; /* Illegal */
|
||||
}
|
||||
|
||||
if (parser->elems > 0) {
|
||||
if (len > parser->dbrSize - 1)
|
||||
len = parser->dbrSize - 1;
|
||||
@@ -118,14 +114,6 @@ static int dbcj_start_map(void *ctx) {
|
||||
return 0; /* Illegal */
|
||||
}
|
||||
|
||||
static int dbcj_map_key(void *ctx, const unsigned char *key, size_t len) {
|
||||
return 0; /* Illegal */
|
||||
}
|
||||
|
||||
static int dbcj_end_map(void *ctx) {
|
||||
return 0; /* Illegal */
|
||||
}
|
||||
|
||||
static int dbcj_start_array(void *ctx) {
|
||||
parseContext *parser = (parseContext *) ctx;
|
||||
|
||||
@@ -135,32 +123,30 @@ static int dbcj_start_array(void *ctx) {
|
||||
return (parser->depth == 1);
|
||||
}
|
||||
|
||||
static int dbcj_end_array(void *ctx) {
|
||||
parseContext *parser = (parseContext *) ctx;
|
||||
|
||||
parser->depth--;
|
||||
return (parser->depth == 0);
|
||||
}
|
||||
|
||||
|
||||
static yajl_callbacks dbcj_callbacks = {
|
||||
dbcj_null, dbcj_boolean, dbcj_integer, dbcj_double, NULL, dbcj_string,
|
||||
dbcj_start_map, dbcj_map_key, dbcj_end_map,
|
||||
dbcj_start_array, dbcj_end_array
|
||||
dbcj_start_map, NULL, NULL,
|
||||
dbcj_start_array, NULL
|
||||
};
|
||||
|
||||
long dbPutConvertJSON(const char *json, short dbrType,
|
||||
void *pdest, long *pnRequest)
|
||||
{
|
||||
parseContext context, *parser = &context;
|
||||
yajl_alloc_funcs dbcj_alloc;
|
||||
yajl_handle yh;
|
||||
yajl_status ys;
|
||||
size_t jlen = strlen(json);
|
||||
long status;
|
||||
|
||||
if(INVALID_DB_REQ(dbrType))
|
||||
if (INVALID_DB_REQ(dbrType)) {
|
||||
errlogPrintf("dbConvertJSON: Invalid dbrType %d\n", dbrType);
|
||||
return S_db_badDbrtype;
|
||||
}
|
||||
|
||||
if (!jlen) {
|
||||
*pnRequest = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
parser->depth = 0;
|
||||
parser->dbrType = dbrType;
|
||||
@@ -168,10 +154,11 @@ long dbPutConvertJSON(const char *json, short dbrType,
|
||||
parser->pdest = pdest;
|
||||
parser->elems = *pnRequest;
|
||||
|
||||
yajl_set_default_alloc_funcs(&dbcj_alloc);
|
||||
yh = yajl_alloc(&dbcj_callbacks, &dbcj_alloc, parser);
|
||||
if (!yh)
|
||||
yh = yajl_alloc(&dbcj_callbacks, NULL, parser);
|
||||
if (!yh) {
|
||||
errlogPrintf("dbConvertJSON: out of memory\n");
|
||||
return S_db_noMemory;
|
||||
}
|
||||
|
||||
ys = yajl_parse(yh, (const unsigned char *) json, jlen);
|
||||
if (ys == yajl_status_ok)
|
||||
@@ -183,15 +170,13 @@ long dbPutConvertJSON(const char *json, short dbrType,
|
||||
status = 0;
|
||||
break;
|
||||
|
||||
case yajl_status_error: {
|
||||
unsigned char *err = yajl_get_error(yh, 1,
|
||||
(const unsigned char *) json, jlen);
|
||||
fprintf(stderr, "dbConvertJSON: %s\n", err);
|
||||
yajl_free_error(yh, err);
|
||||
default: {
|
||||
unsigned char *err = yajl_get_error(yh, 1,
|
||||
(const unsigned char *) json, jlen);
|
||||
errlogPrintf("dbConvertJSON: %s", err);
|
||||
yajl_free_error(yh, err);
|
||||
status = S_db_badField;
|
||||
}
|
||||
/* fall through */
|
||||
default:
|
||||
status = S_db_badField;
|
||||
}
|
||||
|
||||
yajl_free(yh);
|
||||
@@ -200,16 +185,15 @@ long dbPutConvertJSON(const char *json, short dbrType,
|
||||
|
||||
|
||||
static yajl_callbacks dblsj_callbacks = {
|
||||
dbcj_null, dbcj_boolean, dblsj_integer, dblsj_double, NULL, dblsj_string,
|
||||
dbcj_start_map, dbcj_map_key, dbcj_end_map,
|
||||
dbcj_start_array, dbcj_end_array
|
||||
dbcj_null, dbcj_boolean, NULL, NULL, dblsj_number, dblsj_string,
|
||||
dbcj_start_map, NULL, NULL,
|
||||
dbcj_start_array, NULL
|
||||
};
|
||||
|
||||
long dbLSConvertJSON(const char *json, char *pdest, epicsUInt32 size,
|
||||
epicsUInt32 *plen)
|
||||
{
|
||||
parseContext context, *parser = &context;
|
||||
yajl_alloc_funcs dbcj_alloc;
|
||||
yajl_handle yh;
|
||||
yajl_status ys;
|
||||
size_t jlen = strlen(json);
|
||||
@@ -226,10 +210,11 @@ long dbLSConvertJSON(const char *json, char *pdest, epicsUInt32 size,
|
||||
parser->pdest = pdest;
|
||||
parser->elems = 1;
|
||||
|
||||
yajl_set_default_alloc_funcs(&dbcj_alloc);
|
||||
yh = yajl_alloc(&dblsj_callbacks, &dbcj_alloc, parser);
|
||||
if (!yh)
|
||||
yh = yajl_alloc(&dblsj_callbacks, NULL, parser);
|
||||
if (!yh) {
|
||||
errlogPrintf("dbLSConvertJSON: out of memory\n");
|
||||
return S_db_noMemory;
|
||||
}
|
||||
|
||||
ys = yajl_parse(yh, (const unsigned char *) json, jlen);
|
||||
|
||||
@@ -239,15 +224,13 @@ long dbLSConvertJSON(const char *json, char *pdest, epicsUInt32 size,
|
||||
status = 0;
|
||||
break;
|
||||
|
||||
case yajl_status_error: {
|
||||
unsigned char *err = yajl_get_error(yh, 1,
|
||||
(const unsigned char *) json, jlen);
|
||||
fprintf(stderr, "dbLoadLS_JSON: %s\n", err);
|
||||
yajl_free_error(yh, err);
|
||||
default: {
|
||||
unsigned char *err = yajl_get_error(yh, 1,
|
||||
(const unsigned char *) json, jlen);
|
||||
errlogPrintf("dbLSConvertJSON: %s", err);
|
||||
yajl_free_error(yh, err);
|
||||
status = S_db_badField;
|
||||
}
|
||||
/* fall through */
|
||||
default:
|
||||
status = S_db_badField;
|
||||
}
|
||||
|
||||
yajl_free(yh);
|
||||
|
||||
@@ -731,7 +731,7 @@ static db_field_log* db_create_field_log (struct dbChannel *chan, int use_val)
|
||||
/* don't make a copy yet, just reference the field value */
|
||||
pLog->u.r.field = dbChannelField(chan);
|
||||
/* indicate field value still owned by record */
|
||||
pLog->u.r.dtor = NULL;
|
||||
pLog->dtor = NULL;
|
||||
/* no private data yet, may be set by a filter */
|
||||
pLog->u.r.pvt = NULL;
|
||||
}
|
||||
@@ -789,6 +789,18 @@ static void db_queue_event_log (evSubscrip *pevent, db_field_log *pLog)
|
||||
|
||||
LOCKEVQUE (ev_que);
|
||||
|
||||
/* if we have an event on the queue and both the last
|
||||
* event on the queue and the current event reference
|
||||
* a record field, simply ignore duplicate events.
|
||||
*/
|
||||
if (pevent->npend > 0u
|
||||
&& !dbfl_has_copy(*pevent->pLastLog)
|
||||
&& !dbfl_has_copy(pLog)) {
|
||||
db_delete_field_log(pLog);
|
||||
UNLOCKEVQUE (ev_que);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* add to task local event que
|
||||
*/
|
||||
@@ -886,6 +898,8 @@ unsigned int caEventMask
|
||||
if ( (dbChannelField(pevent->chan) == (void *)pField || pField==NULL) &&
|
||||
(caEventMask & pevent->select)) {
|
||||
db_field_log *pLog = db_create_event_log(pevent);
|
||||
if(pLog)
|
||||
pLog->mask = caEventMask & pevent->select;
|
||||
pLog = dbChannelRunPreChain(pevent->chan, pLog);
|
||||
if (pLog) db_queue_event_log(pevent, pLog);
|
||||
}
|
||||
@@ -1199,7 +1213,7 @@ void db_delete_field_log (db_field_log *pfl)
|
||||
{
|
||||
if (pfl) {
|
||||
/* Free field if reference type field log and dtor is set */
|
||||
if (pfl->type == dbfl_type_ref && pfl->u.r.dtor) pfl->u.r.dtor(pfl);
|
||||
if (pfl->type == dbfl_type_ref && pfl->dtor) pfl->dtor(pfl);
|
||||
/* Free the field log chunk */
|
||||
freeListFree(dbevFieldLogFreeList, pfl);
|
||||
}
|
||||
|
||||
@@ -1366,10 +1366,17 @@ static long cvt_device_st(
|
||||
char **papChoice;
|
||||
char *pchoice;
|
||||
|
||||
if(!paddr
|
||||
|| !(pdbFldDes = paddr->pfldDes)
|
||||
|| !(pdbDeviceMenu = (dbDeviceMenu *)pdbFldDes->ftPvt)
|
||||
|| *from>=pdbDeviceMenu->nChoice
|
||||
if (!paddr
|
||||
|| !(pdbFldDes = paddr->pfldDes)) {
|
||||
recGblDbaddrError(S_db_errArg, paddr, "dbFastLinkConv(cvt_device_st)");
|
||||
return S_db_errArg;
|
||||
}
|
||||
if (!(pdbDeviceMenu = (dbDeviceMenu *)pdbFldDes->ftPvt)) {
|
||||
/* Valid, record type has no device support */
|
||||
*to = '\0';
|
||||
return 0;
|
||||
}
|
||||
if (*from >= pdbDeviceMenu->nChoice
|
||||
|| !(papChoice= pdbDeviceMenu->papChoice)
|
||||
|| !(pchoice=papChoice[*from])) {
|
||||
recGblDbaddrError(S_db_badChoice,paddr,"dbFastLinkConv(cvt_device_st)");
|
||||
|
||||
@@ -35,8 +35,14 @@ static const iocshArg * const dbLoadDatabaseArgs[3] =
|
||||
{
|
||||
&dbLoadDatabaseArg0,&dbLoadDatabaseArg1,&dbLoadDatabaseArg2
|
||||
};
|
||||
static const iocshFuncDef dbLoadDatabaseFuncDef =
|
||||
{"dbLoadDatabase",3,dbLoadDatabaseArgs};
|
||||
static const iocshFuncDef dbLoadDatabaseFuncDef = {
|
||||
"dbLoadDatabase",
|
||||
3,
|
||||
dbLoadDatabaseArgs,
|
||||
"Load the given .dbd file, with 'path' added as a search path, with the given substitutions.\n\n"
|
||||
"Substitutions are usually not needed for .dbd files.\n\n"
|
||||
"Example: dbLoadDatabase dbd/my.dbd\n",
|
||||
};
|
||||
static void dbLoadDatabaseCallFunc(const iocshArgBuf *args)
|
||||
{
|
||||
iocshSetError(dbLoadDatabase(args[0].sval,args[1].sval,args[2].sval));
|
||||
@@ -46,7 +52,14 @@ static void dbLoadDatabaseCallFunc(const iocshArgBuf *args)
|
||||
static const iocshArg dbLoadRecordsArg0 = { "file name",iocshArgString};
|
||||
static const iocshArg dbLoadRecordsArg1 = { "substitutions",iocshArgString};
|
||||
static const iocshArg * const dbLoadRecordsArgs[2] = {&dbLoadRecordsArg0,&dbLoadRecordsArg1};
|
||||
static const iocshFuncDef dbLoadRecordsFuncDef = {"dbLoadRecords",2,dbLoadRecordsArgs};
|
||||
static const iocshFuncDef dbLoadRecordsFuncDef = {
|
||||
"dbLoadRecords",
|
||||
2,
|
||||
dbLoadRecordsArgs,
|
||||
"Load the given .db file, with the given substitutions.\n\n"
|
||||
"Substitutions should be given in the format 'var1=value1,var2=value2'.\n\n"
|
||||
"Example: dbLoadRecords db/myRecords.db 'user=myself,host=myhost'\n",
|
||||
};
|
||||
static void dbLoadRecordsCallFunc(const iocshArgBuf *args)
|
||||
{
|
||||
iocshSetError(dbLoadRecords(args[0].sval,args[1].sval));
|
||||
|
||||
@@ -817,7 +817,7 @@ static void periodicTask(void *arg)
|
||||
epicsTimeAddSeconds(&next, delay);
|
||||
if (++overruns >= 10 &&
|
||||
epicsTimeDiffInSeconds(&now, &reported) > report_delay) {
|
||||
errlogPrintf("\ndbScan warning from '%s' scan thread:\n"
|
||||
errlogPrintf("\ndbScan " ERL_WARNING " from '%s' scan thread:\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",
|
||||
|
||||
@@ -996,8 +996,12 @@ static void printBuffer(
|
||||
i = 0;
|
||||
while (len > 0) {
|
||||
int chunk = (len > MAXLINE - 5) ? MAXLINE - 5 : len;
|
||||
|
||||
sprintf(pmsg, "\"%.*s\"", chunk, (char *)pbuffer + i);
|
||||
strcpy(pmsg, "\"");
|
||||
while (epicsStrnEscapedFromRawSize((char *)pbuffer + i, chunk) >= MAXLINE - 5)
|
||||
chunk--;
|
||||
epicsStrnEscapedFromRaw(pmsg+1, MAXLINE - 5,
|
||||
(char *)pbuffer + i, chunk);
|
||||
strcat(pmsg, "\"");
|
||||
len -= chunk; i += chunk;
|
||||
if (len > 0)
|
||||
strcat(pmsg, " +");
|
||||
|
||||
@@ -97,7 +97,6 @@ struct dbfl_val {
|
||||
* data is still owned by a record. See the macro dbfl_has_copy below.
|
||||
*/
|
||||
struct dbfl_ref {
|
||||
dbfl_freeFunc *dtor; /* Callback to free filter-allocated resources */
|
||||
void *pvt; /* Private pointer */
|
||||
void *field; /* Field value */
|
||||
};
|
||||
@@ -120,6 +119,7 @@ typedef struct db_field_log {
|
||||
short field_type; /* DBF type of data */
|
||||
short field_size; /* Size of a single element */
|
||||
long no_elements; /* No of valid array elements */
|
||||
dbfl_freeFunc *dtor; /* Callback to free filter-allocated resources */
|
||||
union {
|
||||
struct dbfl_val v;
|
||||
struct dbfl_ref r;
|
||||
@@ -136,7 +136,7 @@ typedef struct db_field_log {
|
||||
* the db_field_log still owns the (empty) data.
|
||||
*/
|
||||
#define dbfl_has_copy(p)\
|
||||
((p) && ((p)->type==dbfl_type_val || (p)->u.r.dtor || (p)->no_elements==0))
|
||||
((p) && ((p)->type==dbfl_type_val || (p)->dtor || (p)->no_elements==0))
|
||||
|
||||
#define dbfl_pfield(p)\
|
||||
((p)->type==dbfl_type_val ? &p->u.v.field : p->u.r.field)
|
||||
|
||||
@@ -56,63 +56,66 @@ static void getMaxRangeValues(short field_type, double *pupper_limit,
|
||||
double *plower_limit);
|
||||
|
||||
|
||||
|
||||
void recGblDbaddrError(long status, const struct dbAddr *paddr,
|
||||
const char *pmessage)
|
||||
{
|
||||
dbCommon *precord = 0;
|
||||
dbFldDes *pdbFldDes = 0;
|
||||
|
||||
if(paddr) {
|
||||
pdbFldDes = paddr->pfldDes;
|
||||
precord = paddr->precord;
|
||||
}
|
||||
errPrintf(status,0,0,
|
||||
"PV: %s.%s "
|
||||
"error detected in routine: %s\n",
|
||||
(precord ? precord->name : "Unknown"),
|
||||
(pdbFldDes ? pdbFldDes->name : ""),
|
||||
(pmessage ? pmessage : "Unknown"));
|
||||
return;
|
||||
}
|
||||
|
||||
void recGblRecordError(long status, void *pdbc,
|
||||
const char *pmessage)
|
||||
{
|
||||
dbCommon *precord = pdbc;
|
||||
dbCommon *precord = pdbc;
|
||||
char errMsg[256] = "";
|
||||
|
||||
errPrintf(status,0,0,
|
||||
"PV: %s %s\n",
|
||||
(precord ? precord->name : "Unknown"),
|
||||
(pmessage ? pmessage : ""));
|
||||
return;
|
||||
if (status)
|
||||
errSymLookup(status, errMsg, sizeof(errMsg));
|
||||
|
||||
errlogPrintf("recGblRecordError: %s %s PV: %s\n",
|
||||
pmessage ? pmessage : "", errMsg,
|
||||
precord ? precord->name : "Unknown");
|
||||
}
|
||||
|
||||
|
||||
void recGblDbaddrError(long status, const struct dbAddr *paddr,
|
||||
const char *pmessage)
|
||||
{
|
||||
dbCommon *precord = 0;
|
||||
dbFldDes *pdbFldDes = 0;
|
||||
char errMsg[256] = "";
|
||||
|
||||
if (paddr) {
|
||||
pdbFldDes = paddr->pfldDes;
|
||||
precord = paddr->precord;
|
||||
}
|
||||
if (status)
|
||||
errSymLookup(status, errMsg, sizeof(errMsg));
|
||||
|
||||
errlogPrintf("recGblDbaddrError: %s %s PV: %s.%s\n",
|
||||
pmessage ? pmessage : "",errMsg,
|
||||
precord ? precord->name : "Unknown",
|
||||
pdbFldDes ? pdbFldDes->name : "");
|
||||
}
|
||||
|
||||
void recGblRecSupError(long status, const struct dbAddr *paddr,
|
||||
const char *pmessage, const char *psupport_name)
|
||||
{
|
||||
dbCommon *precord = 0;
|
||||
dbFldDes *pdbFldDes = 0;
|
||||
dbRecordType *pdbRecordType = 0;
|
||||
char errMsg[256] = "";
|
||||
|
||||
if(paddr) {
|
||||
if (paddr) {
|
||||
precord = paddr->precord;
|
||||
pdbFldDes = paddr->pfldDes;
|
||||
if(pdbFldDes) pdbRecordType = pdbFldDes->pdbRecordType;
|
||||
if (pdbFldDes)
|
||||
pdbRecordType = pdbFldDes->pdbRecordType;
|
||||
}
|
||||
errPrintf(status,0,0,
|
||||
"Record Support Routine (%s) "
|
||||
"Record Type %s "
|
||||
"PV %s.%s "
|
||||
" %s\n",
|
||||
(psupport_name ? psupport_name : "Unknown"),
|
||||
(pdbRecordType ? pdbRecordType->name : "Unknown"),
|
||||
(precord ? precord->name : "Unknown"),
|
||||
(pdbFldDes ? pdbFldDes->name : ""),
|
||||
(pmessage ? pmessage : ""));
|
||||
return;
|
||||
|
||||
if (status)
|
||||
errSymLookup(status, errMsg, sizeof(errMsg));
|
||||
|
||||
errlogPrintf("recGblRecSupError: %s %s %s::%s PV: %s.%s\n",
|
||||
pmessage ? pmessage : "", errMsg,
|
||||
pdbRecordType ? pdbRecordType->name : "Unknown",
|
||||
psupport_name ? psupport_name : "Unknown",
|
||||
precord ? precord->name : "Unknown",
|
||||
pdbFldDes ? pdbFldDes->name : "");
|
||||
}
|
||||
|
||||
|
||||
void recGblGetPrec(const struct dbAddr *paddr, long *precision)
|
||||
{
|
||||
dbFldDes *pdbFldDes = paddr->pfldDes;
|
||||
|
||||
@@ -37,7 +37,11 @@
|
||||
#include "special.h"
|
||||
#include "iocInit.h"
|
||||
|
||||
|
||||
/* This file is included from dbYacc.y
|
||||
* Duplicate some declarations to avoid warnings from analysis tools which don't know about this.
|
||||
*/
|
||||
static int yyerror(char *str);
|
||||
static long pvt_yy_parse(void);
|
||||
|
||||
/*global declarations*/
|
||||
char *makeDbdDepends=0;
|
||||
@@ -99,8 +103,8 @@ static char *my_buffer_ptr=NULL;
|
||||
static MAC_HANDLE *macHandle = NULL;
|
||||
typedef struct inputFile{
|
||||
ELLNODE node;
|
||||
char *path;
|
||||
char *filename;
|
||||
const char *path;
|
||||
const char *filename;
|
||||
FILE *fp;
|
||||
int line_num;
|
||||
}inputFile;
|
||||
@@ -155,7 +159,7 @@ static void *getLastTemp(void)
|
||||
return(ptempListNode->item);
|
||||
}
|
||||
|
||||
static char *dbOpenFile(DBBASE *pdbbase,const char *filename,FILE **fp)
|
||||
const char *dbOpenFile(DBBASE *pdbbase,const char *filename,FILE **fp)
|
||||
{
|
||||
ELLLIST *ppathList = (ELLLIST *)pdbbase->pathPvt;
|
||||
dbPathNode *pdbPathNode;
|
||||
@@ -223,8 +227,10 @@ static long dbReadCOM(DBBASE **ppdbbase,const char *filename, FILE *fp,
|
||||
epicsPrintf("dbReadCOM: Parser stack dirty %d\n", ellCount(&tempList));
|
||||
}
|
||||
|
||||
if (getIocState() != iocVoid)
|
||||
return -2;
|
||||
if (getIocState() != iocVoid) {
|
||||
status = -2;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if(*ppdbbase == 0) *ppdbbase = dbAllocBase();
|
||||
pdbbase = *ppdbbase;
|
||||
@@ -268,8 +274,8 @@ static long dbReadCOM(DBBASE **ppdbbase,const char *filename, FILE *fp,
|
||||
pinputFile->path = dbOpenFile(pdbbase, pinputFile->filename, &fp1);
|
||||
if (!pinputFile->filename || !fp1) {
|
||||
errPrintf(0, __FILE__, __LINE__,
|
||||
"dbRead opening file %s",pinputFile->filename);
|
||||
free(pinputFile->filename);
|
||||
"dbRead opening file %s\n",pinputFile->filename);
|
||||
free((char*)pinputFile->filename);
|
||||
free(pinputFile);
|
||||
status = -1;
|
||||
goto cleanup;
|
||||
@@ -277,6 +283,7 @@ static long dbReadCOM(DBBASE **ppdbbase,const char *filename, FILE *fp,
|
||||
pinputFile->fp = fp1;
|
||||
} else {
|
||||
pinputFile->fp = fp;
|
||||
fp = NULL;
|
||||
}
|
||||
pinputFile->line_num = 0;
|
||||
pinputFileNow = pinputFile;
|
||||
@@ -332,6 +339,8 @@ cleanup:
|
||||
if(my_buffer) free((void *)my_buffer);
|
||||
my_buffer = NULL;
|
||||
freeInputFileList();
|
||||
if(fp)
|
||||
fclose(fp);
|
||||
return(status);
|
||||
}
|
||||
|
||||
|
||||
@@ -106,7 +106,13 @@ static void dbDumpDriverCallFunc(const iocshArgBuf *args)
|
||||
|
||||
/* dbDumpLink */
|
||||
static const iocshArg * const dbDumpLinkArgs[] = { &argPdbbase};
|
||||
static const iocshFuncDef dbDumpLinkFuncDef = {"dbDumpLink",1,dbDumpLinkArgs};
|
||||
static const iocshFuncDef dbDumpLinkFuncDef = {
|
||||
"dbDumpLink",
|
||||
1,
|
||||
dbDumpLinkArgs,
|
||||
"Dump list of registered links\n"
|
||||
"Example: dbDumpLink pdbbase\n",
|
||||
};
|
||||
static void dbDumpLinkCallFunc(const iocshArgBuf *args)
|
||||
{
|
||||
dbDumpLink(*iocshPpdbbase);
|
||||
@@ -147,7 +153,13 @@ static void dbDumpVariableCallFunc(const iocshArgBuf *args)
|
||||
static const iocshArg dbDumpBreaktableArg1 = { "tableName",iocshArgString};
|
||||
static const iocshArg * const dbDumpBreaktableArgs[] =
|
||||
{&argPdbbase,&dbDumpBreaktableArg1};
|
||||
static const iocshFuncDef dbDumpBreaktableFuncDef = {"dbDumpBreaktable",2,dbDumpBreaktableArgs};
|
||||
static const iocshFuncDef dbDumpBreaktableFuncDef = {
|
||||
"dbDumpBreaktable",
|
||||
2,
|
||||
dbDumpBreaktableArgs,
|
||||
"Dump the given break table\n"
|
||||
"Example: dbDumpBreaktable pdbbase typeKdegC\n",
|
||||
};
|
||||
static void dbDumpBreaktableCallFunc(const iocshArgBuf *args)
|
||||
{
|
||||
dbDumpBreaktable(*iocshPpdbbase,args[1].sval);
|
||||
@@ -157,7 +169,14 @@ static void dbDumpBreaktableCallFunc(const iocshArgBuf *args)
|
||||
static const iocshArg dbPvdDumpArg1 = { "verbose",iocshArgInt};
|
||||
static const iocshArg * const dbPvdDumpArgs[] = {
|
||||
&argPdbbase,&dbPvdDumpArg1};
|
||||
static const iocshFuncDef dbPvdDumpFuncDef = {"dbPvdDump",2,dbPvdDumpArgs};
|
||||
static const iocshFuncDef dbPvdDumpFuncDef = {
|
||||
"dbPvdDump",
|
||||
2,
|
||||
dbPvdDumpArgs,
|
||||
"Dump the various buckets of the process variable directory.\n"
|
||||
"If verbose is greater than 0, also print the process variables in each bucket.\n"
|
||||
"Example: dbPvdDump pdbbase 1\n",
|
||||
};
|
||||
static void dbPvdDumpCallFunc(const iocshArgBuf *args)
|
||||
{
|
||||
dbPvdDump(*iocshPpdbbase,args[1].ival);
|
||||
@@ -167,7 +186,16 @@ static void dbPvdDumpCallFunc(const iocshArgBuf *args)
|
||||
static const iocshArg dbPvdTableSizeArg0 = { "size",iocshArgInt};
|
||||
static const iocshArg * const dbPvdTableSizeArgs[1] =
|
||||
{&dbPvdTableSizeArg0};
|
||||
static const iocshFuncDef dbPvdTableSizeFuncDef = {"dbPvdTableSize",1,dbPvdTableSizeArgs};
|
||||
static const iocshFuncDef dbPvdTableSizeFuncDef = {
|
||||
"dbPvdTableSize",
|
||||
1,
|
||||
dbPvdTableSizeArgs,
|
||||
"Change the number of buckets in the process variable directory.\n\n"
|
||||
"The process variable directory size should be set before loading the database.\n"
|
||||
"The size of the process variable directory can automatically grow.\n"
|
||||
"The size must be a power of 2.\n\n"
|
||||
"Example: dbPvdTableSize 1024\n",
|
||||
};
|
||||
static void dbPvdTableSizeCallFunc(const iocshArgBuf *args)
|
||||
{
|
||||
dbPvdTableSize(args[0].ival);
|
||||
@@ -175,7 +203,15 @@ static void dbPvdTableSizeCallFunc(const iocshArgBuf *args)
|
||||
|
||||
/* dbReportDeviceConfig */
|
||||
static const iocshArg * const dbReportDeviceConfigArgs[] = {&argPdbbase};
|
||||
static const iocshFuncDef dbReportDeviceConfigFuncDef = {"dbReportDeviceConfig",1,dbReportDeviceConfigArgs};
|
||||
static const iocshFuncDef dbReportDeviceConfigFuncDef = {
|
||||
"dbReportDeviceConfig",
|
||||
1,
|
||||
dbReportDeviceConfigArgs,
|
||||
"Print the link type, link value, device type, record name,\n"
|
||||
"and linearisation info (if applicable),\n"
|
||||
"for every record using a specific device type.\n\n"
|
||||
"Example: dbReportDeviceConfig pdbbase\n",
|
||||
};
|
||||
static void dbReportDeviceConfigCallFunc(const iocshArgBuf *args)
|
||||
{
|
||||
dbReportDeviceConfig(*iocshPpdbbase,stdout);
|
||||
|
||||
@@ -441,6 +441,9 @@ void dbFreeBase(dbBase *pdbbase)
|
||||
DBENTRY dbentry;
|
||||
long status;
|
||||
|
||||
if(!pdbbase)
|
||||
return;
|
||||
|
||||
dbInitEntry(pdbbase,&dbentry);
|
||||
status = dbFirstRecordType(&dbentry);
|
||||
while(!status) {
|
||||
@@ -2275,7 +2278,7 @@ long dbParseLink(const char *str, short ftype, dbLinkInfo *pinfo)
|
||||
}
|
||||
|
||||
/* generalized extraction of ID character and integer pairs (eg. "#C15 S14") */
|
||||
ret = sscanf(pinfo->target, "# %c%d %c%d %c%d %c%d %c%d %c",
|
||||
ret = sscanf(pinfo->target, "# %c%i %c%i %c%i %c%i %c%i %c",
|
||||
&pinfo->hwid[0], &pinfo->hwnums[0],
|
||||
&pinfo->hwid[1], &pinfo->hwnums[1],
|
||||
&pinfo->hwid[2], &pinfo->hwnums[2],
|
||||
@@ -3293,11 +3296,11 @@ void dbDumpRecordType(DBBASE *pdbbase,const char *recordTypeName)
|
||||
printf("name(%s) no_fields(%hd) no_prompt(%hd) no_links(%hd)\n",
|
||||
pdbRecordType->name,pdbRecordType->no_fields,
|
||||
pdbRecordType->no_prompt,pdbRecordType->no_links);
|
||||
printf("index name\tsortind sortname\n");
|
||||
printf("index offset size name\tsortind sortname\n");
|
||||
for(i=0; i<pdbRecordType->no_fields; i++) {
|
||||
pdbFldDes = pdbRecordType->papFldDes[i];
|
||||
printf("%5d %s\t%7d %s\n",
|
||||
i,pdbFldDes->name,
|
||||
printf("%5d %6u %4u %s\t%7d %s\n",
|
||||
i,pdbFldDes->offset,pdbFldDes->size, pdbFldDes->name,
|
||||
pdbRecordType->sortFldInd[i],pdbRecordType->papsortFldName[i]);
|
||||
}
|
||||
printf("link_ind ");
|
||||
|
||||
@@ -57,8 +57,26 @@ DBCORE_API void dbCopyEntryContents(DBENTRY *pfrom,
|
||||
|
||||
DBCORE_API extern int dbBptNotMonotonic;
|
||||
|
||||
/** \brief Open .dbd or .db file and read definitions.
|
||||
* \param ppdbbase The database. Typically the "pdbbase" global
|
||||
* \param filename Filename to read/search. May be absolute, or relative.
|
||||
* \param path If !NULL, search path when filename is relative, of for 'include' statements.
|
||||
* Split by ':' or ';' (cf. OSI_PATH_LIST_SEPARATOR)
|
||||
* \param substitutions If !NULL, macro definitions like "NAME=VAL,OTHER=SOME"
|
||||
* \return 0 on success
|
||||
*/
|
||||
DBCORE_API long dbReadDatabase(DBBASE **ppdbbase,
|
||||
const char *filename, const char *path, const char *substitutions);
|
||||
/** \brief Read definitions from already opened .dbd or .db file.
|
||||
* \param ppdbbase The database. Typically the "&pdbbase" global
|
||||
* \param fp FILE* from which to read definitions. Will always be fclose()'d
|
||||
* \param path If !NULL, search path when filename is relative, of for 'include' statements.
|
||||
* Split by ':' or ';' (cf. OSI_PATH_LIST_SEPARATOR)
|
||||
* \param substitutions If !NULL, macro definitions like "NAME=VAL,OTHER=SOME"
|
||||
* \return 0 on success
|
||||
*
|
||||
* \note This function will always close the provided 'fp'.
|
||||
*/
|
||||
DBCORE_API long dbReadDatabaseFP(DBBASE **ppdbbase,
|
||||
FILE *fp, const char *path, const char *substitutions);
|
||||
DBCORE_API long dbPath(DBBASE *pdbbase, const char *path);
|
||||
|
||||
@@ -16,6 +16,9 @@
|
||||
#ifndef INCdbStaticPvth
|
||||
#define INCdbStaticPvth 1
|
||||
|
||||
#include "dbCoreAPI.h"
|
||||
#include "dbStaticLib.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
@@ -40,6 +43,9 @@ void dbMsgPrint(DBENTRY *pdbentry, const char *fmt, ...) EPICS_PRINTF_STYLE(2,3)
|
||||
|
||||
void dbPutStringSuggest(DBENTRY *pdbentry, const char *pstring);
|
||||
|
||||
DBCORE_API
|
||||
const char *dbOpenFile(DBBASE *pdbbase,const char *filename,FILE **fp);
|
||||
|
||||
struct jlink;
|
||||
|
||||
typedef struct dbLinkInfo {
|
||||
|
||||
@@ -14,12 +14,21 @@
|
||||
|
||||
/* dbLoadTemplate */
|
||||
static const iocshArg dbLoadTemplateArg0 = {"filename", iocshArgString};
|
||||
static const iocshArg dbLoadTemplateArg1 = {"var=value", iocshArgString};
|
||||
static const iocshArg dbLoadTemplateArg1 = {"var1=value1,var2=value2", iocshArgString};
|
||||
static const iocshArg * const dbLoadTemplateArgs[2] = {
|
||||
&dbLoadTemplateArg0, &dbLoadTemplateArg1
|
||||
};
|
||||
static const iocshFuncDef dbLoadTemplateFuncDef =
|
||||
{"dbLoadTemplate", 2, dbLoadTemplateArgs};
|
||||
static const iocshFuncDef dbLoadTemplateFuncDef = {
|
||||
"dbLoadTemplate",
|
||||
2,
|
||||
dbLoadTemplateArgs,
|
||||
"Load the substitution file given as first argument, apply the substitutions\n"
|
||||
"for each template in the substitution file, and load them using 'dbLoadRecords'.\n\n"
|
||||
"The second argument provides extra variables to substitute in the\n"
|
||||
"template files (not the substitution file).\n\n"
|
||||
"See 'help dbLoadRecords' for more information.\n\n"
|
||||
"Example: dbLoadTemplate db/my.substitutions 'user=myself,host=myhost'\n",
|
||||
};
|
||||
static void dbLoadTemplateCallFunc(const iocshArgBuf *args)
|
||||
{
|
||||
iocshSetError(dbLoadTemplate(args[0].sval, args[1].sval));
|
||||
|
||||
@@ -19,7 +19,13 @@ IOCSH_STATIC_FUNC void dlload(const char* name)
|
||||
|
||||
static const iocshArg dlloadArg0 = { "path/library.so", iocshArgString};
|
||||
static const iocshArg * const dlloadArgs[] = {&dlloadArg0};
|
||||
static const iocshFuncDef dlloadFuncDef = {"dlload", 1, dlloadArgs};
|
||||
static const iocshFuncDef dlloadFuncDef = {
|
||||
"dlload",
|
||||
1,
|
||||
dlloadArgs,
|
||||
"Load the given shared library.\n\n"
|
||||
"Example: dlload myLibrary.so\n",
|
||||
};
|
||||
static void dlloadCallFunc(const iocshArgBuf *args)
|
||||
{
|
||||
dlload(args[0].sval);
|
||||
|
||||
@@ -23,6 +23,7 @@ DBCORE_API int coreRelease(void)
|
||||
printf ( "############################################################################\n" );
|
||||
printf ( "## %s\n", epicsReleaseVersion );
|
||||
printf ( "## %s\n", "Rev. " EPICS_VCS_VERSION );
|
||||
printf ( "## %s\n", "Rev. Date " EPICS_VCS_VERSION_DATE );
|
||||
printf ( "############################################################################\n" );
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -28,8 +28,19 @@
|
||||
/* registerAllRecordDeviceDrivers */
|
||||
static const iocshArg rrddArg0 = {"pdbbase", iocshArgPdbbase};
|
||||
static const iocshArg *rrddArgs[] = {&rrddArg0};
|
||||
static const iocshFuncDef rrddFuncDef =
|
||||
{"registerAllRecordDeviceDrivers", 1, rrddArgs};
|
||||
static const iocshFuncDef rrddFuncDef = {
|
||||
"registerAllRecordDeviceDrivers",
|
||||
1,
|
||||
rrddArgs,
|
||||
"Register all records, devices, from all DBD available.\n\n"
|
||||
"Calling this function is equivalent to calling every\n"
|
||||
"'<name>_registerRecordDeviceDriver' which has been linked\n"
|
||||
"into the process, e.g. by dynamic loading, or by linking with\n"
|
||||
"a generated '<name>_registerRecordDeviceDriver.cpp' files.\n\n"
|
||||
"These are registered into the database given as first argument,\n"
|
||||
"which should always be 'pdbbase'.\n\n"
|
||||
"Example: registerAllRecordDeviceDrivers pdbbase\n",
|
||||
};
|
||||
static void rrddCallFunc(const iocshArgBuf *args)
|
||||
{
|
||||
iocshSetError(registerAllRecordDeviceDrivers(*iocshPpdbbase));
|
||||
|
||||
@@ -21,28 +21,48 @@ static const iocshArg * const registryXxxFindArgs[1] = {®istryXxxFindArg0};
|
||||
|
||||
/* registryRecordTypeFind */
|
||||
static const iocshFuncDef registryRecordTypeFindFuncDef = {
|
||||
"registryRecordTypeFind",1,registryXxxFindArgs};
|
||||
"registryRecordTypeFind",
|
||||
1,
|
||||
registryXxxFindArgs,
|
||||
"Prints the registry address of the record type given as first argument.\n\n"
|
||||
"Example: registryRecordTypeFind ai\n",
|
||||
};
|
||||
static void registryRecordTypeFindCallFunc(const iocshArgBuf *args) {
|
||||
printf("%p\n", (void*) registryRecordTypeFind(args[0].sval));
|
||||
}
|
||||
|
||||
/* registryDeviceSupportFind */
|
||||
static const iocshFuncDef registryDeviceSupportFindFuncDef = {
|
||||
"registryDeviceSupportFind",1,registryXxxFindArgs};
|
||||
"registryDeviceSupportFind",
|
||||
1,
|
||||
registryXxxFindArgs,
|
||||
"Prints the registry address of the device support given as first argument.\n\n"
|
||||
"Example: registryDeviceSupportFind devAaiSoft\n",
|
||||
};
|
||||
static void registryDeviceSupportFindCallFunc(const iocshArgBuf *args) {
|
||||
printf("%p\n", (void*) registryDeviceSupportFind(args[0].sval));
|
||||
}
|
||||
|
||||
/* registryDriverSupportFind */
|
||||
static const iocshFuncDef registryDriverSupportFindFuncDef = {
|
||||
"registryDriverSupportFind",1,registryXxxFindArgs};
|
||||
"registryDriverSupportFind",
|
||||
1,
|
||||
registryXxxFindArgs,
|
||||
"Prints the registry address of the driver support given as first argument.\n\n"
|
||||
"Example: registryDriverSupportFind stream\n",
|
||||
};
|
||||
static void registryDriverSupportFindCallFunc(const iocshArgBuf *args) {
|
||||
printf("%p\n", (void*) registryDriverSupportFind(args[0].sval));
|
||||
}
|
||||
|
||||
/* registryFunctionFind */
|
||||
static const iocshFuncDef registryFunctionFindFuncDef = {
|
||||
"registryFunctionFind",1,registryXxxFindArgs};
|
||||
"registryFunctionFind",
|
||||
1,
|
||||
registryXxxFindArgs,
|
||||
"Prints the registry address of the registered function given as first argument.\n\n"
|
||||
"Example: registryFunctionFind registryFunctionFind\n",
|
||||
};
|
||||
static void registryFunctionFindCallFunc(const iocshArgBuf *args) {
|
||||
printf("%p\n", (void*) registryFunctionFind(args[0].sval));
|
||||
}
|
||||
|
||||
@@ -58,7 +58,7 @@ void camsgtask ( void *pParm )
|
||||
|
||||
epicsSocketConvertErrnoToString (
|
||||
sockErrBuf, sizeof ( sockErrBuf ) );
|
||||
errlogPrintf("CAS: FIONREAD error: %s\n",
|
||||
errlogPrintf("CAS: FIONREAD " ERL_ERROR ": %s\n",
|
||||
sockErrBuf);
|
||||
cas_send_bs_msg(client, TRUE);
|
||||
}
|
||||
|
||||
@@ -139,7 +139,7 @@ void cas_send_bs_msg ( struct client *pclient, int lock_needed )
|
||||
char sockErrBuf[64];
|
||||
epicsSocketConvertErrnoToString (
|
||||
sockErrBuf, sizeof ( sockErrBuf ) );
|
||||
errlogPrintf ("CAS: Socket shutdown error: %s\n",
|
||||
errlogPrintf ("CAS: Socket shutdown " ERL_ERROR ": %s\n",
|
||||
sockErrBuf );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -86,7 +86,7 @@ static void req_server (void *pParm)
|
||||
char sockErrBuf[64];
|
||||
epicsSocketConvertErrnoToString (
|
||||
sockErrBuf, sizeof ( sockErrBuf ) );
|
||||
errlogPrintf("CAS: Client accept error: %s (%d)\n",
|
||||
errlogPrintf("CAS: Client accept " ERL_ERROR ": %s (%d)\n",
|
||||
sockErrBuf, (int)addLen );
|
||||
epicsThreadSleep(15.0);
|
||||
continue;
|
||||
@@ -131,7 +131,7 @@ int tryBind(SOCKET sock, const osiSockAddr* addr, const char *name)
|
||||
{
|
||||
epicsSocketConvertErrnoToString (
|
||||
sockErrBuf, sizeof ( sockErrBuf ) );
|
||||
errlogPrintf ( "CAS: %s bind error: %s\n",
|
||||
errlogPrintf ( "CAS: %s bind " ERL_ERROR ": %s\n",
|
||||
name, sockErrBuf );
|
||||
epicsThreadSuspendSelf ();
|
||||
}
|
||||
@@ -196,7 +196,7 @@ SOCKET* rsrv_grab_tcp(unsigned short *port)
|
||||
char sockErrBuf[64];
|
||||
epicsSocketConvertErrnoToString (
|
||||
sockErrBuf, sizeof ( sockErrBuf ) );
|
||||
errlogPrintf ( "CAS: getsockname error: %s\n",
|
||||
errlogPrintf ( "CAS: getsockname " ERL_ERROR ": %s\n",
|
||||
sockErrBuf );
|
||||
epicsThreadSuspendSelf ();
|
||||
ok = 0;
|
||||
@@ -572,17 +572,22 @@ void rsrv_init (void)
|
||||
|
||||
{
|
||||
unsigned short sport = ca_server_port;
|
||||
char buf[6]; /* space for 0 - 65535 */
|
||||
socks = rsrv_grab_tcp(&sport);
|
||||
|
||||
if ( sport != ca_server_port ) {
|
||||
ca_server_port = sport;
|
||||
errlogPrintf ( "cas warning: Configured TCP port was unavailable.\n");
|
||||
errlogPrintf ( "cas warning: Using dynamically assigned TCP port %hu,\n",
|
||||
errlogPrintf ( "cas " ERL_WARNING ": Configured TCP port was unavailable.\n");
|
||||
errlogPrintf ( "cas " ERL_WARNING ": Using dynamically assigned TCP port %hu,\n",
|
||||
ca_server_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" );
|
||||
errlogPrintf ( "cas warning: reachable with UDP unicast (a host's IP in EPICS_CA_ADDR_LIST)\n" );
|
||||
errlogPrintf ( "cas " ERL_WARNING ": but now two or more servers share the same UDP port.\n");
|
||||
errlogPrintf ( "cas " ERL_WARNING ": Depending on your IP kernel this server may not be\n" );
|
||||
errlogPrintf ( "cas " ERL_WARNING ": reachable with UDP unicast (a host's IP in EPICS_CA_ADDR_LIST)\n" );
|
||||
}
|
||||
|
||||
epicsSnprintf(buf, sizeof(buf)-1u, "%u", ca_server_port);
|
||||
buf[sizeof(buf)-1u] = '\0';
|
||||
epicsEnvSet("RSRV_SERVER_PORT", buf);
|
||||
}
|
||||
|
||||
/* start servers (TCP and UDP(s) for each interface.
|
||||
|
||||
@@ -94,7 +94,7 @@ void rsrv_online_notify_task(void *pParm)
|
||||
|
||||
epicsSocketConvertErrorToString(sockErrBuf, sizeof(sockErrBuf), err);
|
||||
ipAddrToDottedIP(&pAddr->addr.ia, sockDipBuf, sizeof(sockDipBuf));
|
||||
errlogPrintf ( "CAS: CA beacon send to %s error: %s\n",
|
||||
errlogPrintf ( "CAS: CA beacon send to %s " ERL_ERROR ": %s\n",
|
||||
sockDipBuf, sockErrBuf);
|
||||
|
||||
lastError[i] = err;
|
||||
|
||||
@@ -109,7 +109,7 @@ static db_field_log* filter(void* pvt, dbChannel *chan, db_field_log *pfl)
|
||||
break;
|
||||
|
||||
case dbfl_type_ref:
|
||||
must_lock = !pfl->u.r.dtor;
|
||||
must_lock = !pfl->dtor;
|
||||
if (must_lock) {
|
||||
dbScanLock(dbChannelRecord(chan));
|
||||
dbChannelGetArrayInfo(chan, &pSource, &nSource, &offset);
|
||||
@@ -123,9 +123,9 @@ static db_field_log* filter(void* pvt, dbChannel *chan, db_field_log *pfl)
|
||||
offset = (offset + start) % pfl->no_elements;
|
||||
dbExtractArray(pSource, pTarget, pfl->field_size,
|
||||
nTarget, pfl->no_elements, offset, my->incr);
|
||||
if (pfl->u.r.dtor) pfl->u.r.dtor(pfl);
|
||||
if (pfl->dtor) pfl->dtor(pfl);
|
||||
pfl->u.r.field = pTarget;
|
||||
pfl->u.r.dtor = freeArray;
|
||||
pfl->dtor = freeArray;
|
||||
pfl->u.r.pvt = my->arrayFreeList;
|
||||
}
|
||||
/* adjust no_elements (even if zero elements remain) */
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
#include <chfPlugin.h>
|
||||
#include <recGbl.h>
|
||||
#include <epicsExit.h>
|
||||
#include <db_field_log.h>
|
||||
#include <dbAccess.h>
|
||||
#include <epicsExport.h>
|
||||
|
||||
typedef struct myStruct {
|
||||
@@ -81,8 +81,8 @@ static db_field_log* filter(void* pvt, dbChannel *chan, db_field_log *pfl) {
|
||||
status = dbFastGetConvertRoutine[pfl->field_type][DBR_DOUBLE]
|
||||
(localAddr.pfield, (void*) &val, &localAddr);
|
||||
if (!status) {
|
||||
send = 0;
|
||||
recGblCheckDeadband(&my->last, val, my->hyst, &send, 1);
|
||||
send = pfl->mask & ~(DBE_VALUE|DBE_LOG);
|
||||
recGblCheckDeadband(&my->last, val, my->hyst, &send, pfl->mask & (DBE_VALUE|DBE_LOG));
|
||||
if (send && my->mode == 1) {
|
||||
my->hyst = val * my->cval/100.;
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#include "freeList.h"
|
||||
#include "db_field_log.h"
|
||||
#include "chfPlugin.h"
|
||||
#include "epicsExit.h"
|
||||
#include "epicsExport.h"
|
||||
|
||||
typedef struct myStruct {
|
||||
@@ -102,17 +103,20 @@ static chfPluginIf pif = {
|
||||
NULL /* channel_close */
|
||||
};
|
||||
|
||||
static void decShutdown(void *ignore)
|
||||
{
|
||||
if (myStructFreeList)
|
||||
freeListCleanup(myStructFreeList);
|
||||
myStructFreeList = NULL;
|
||||
}
|
||||
|
||||
static void decInitialize(void)
|
||||
{
|
||||
static int firstTime = 1;
|
||||
|
||||
if (!firstTime) return;
|
||||
firstTime = 0;
|
||||
|
||||
if (!myStructFreeList)
|
||||
freeListInitPvt(&myStructFreeList, sizeof(myStruct), 64);
|
||||
|
||||
chfPluginRegister("dec", &pif, opts);
|
||||
epicsAtExit(decShutdown, NULL);
|
||||
}
|
||||
|
||||
epicsExportRegistrar(decInitialize);
|
||||
|
||||
@@ -35,7 +35,7 @@ static db_field_log* filter(void* pvt, dbChannel *chan, db_field_log *pfl) {
|
||||
|
||||
/* If reference and not already copied,
|
||||
must make a copy (to ensure coherence between time and data) */
|
||||
if (pfl->type == dbfl_type_ref && !pfl->u.r.dtor) {
|
||||
if (pfl->type == dbfl_type_ref && !pfl->dtor) {
|
||||
void *pTarget = calloc(pfl->no_elements, pfl->field_size);
|
||||
void *pSource = pfl->u.r.field;
|
||||
if (pTarget) {
|
||||
@@ -46,7 +46,7 @@ static db_field_log* filter(void* pvt, dbChannel *chan, db_field_log *pfl) {
|
||||
dbExtractArray(pSource, pTarget, pfl->field_size,
|
||||
nSource, pfl->no_elements, offset, 1);
|
||||
pfl->u.r.field = pTarget;
|
||||
pfl->u.r.dtor = freeArray;
|
||||
pfl->dtor = freeArray;
|
||||
pfl->u.r.pvt = pvt;
|
||||
dbScanUnlock(dbChannelRecord(chan));
|
||||
}
|
||||
|
||||
@@ -275,8 +275,11 @@ static long fetch_values(aSubRecord *prec)
|
||||
|
||||
/* Get the input link values */
|
||||
for (i = 0; i < NUM_ARGS; i++) {
|
||||
DBLINK *plink = &(&prec->inpa)[i];
|
||||
long nRequest = (&prec->noa)[i];
|
||||
status = dbGetLink(&(&prec->inpa)[i], (&prec->fta)[i], (&prec->a)[i], 0,
|
||||
if(dbLinkIsConstant(plink))
|
||||
continue;
|
||||
status = dbGetLink(plink, (&prec->fta)[i], (&prec->a)[i], 0,
|
||||
&nRequest);
|
||||
if (status)
|
||||
return status;
|
||||
|
||||
@@ -44,6 +44,7 @@
|
||||
#include "special.h"
|
||||
#include "cantProceed.h"
|
||||
#include "menuYesNo.h"
|
||||
#include "menuOmsl.h"
|
||||
|
||||
#define GEN_SIZE_OFFSET
|
||||
#include "aaoRecord.h"
|
||||
@@ -92,6 +93,7 @@ rset aaoRSET={
|
||||
epicsExportAddress(rset,aaoRSET);
|
||||
|
||||
static void monitor(aaoRecord *);
|
||||
static long fetchValue(aaoRecord *, int);
|
||||
static long writeValue(aaoRecord *);
|
||||
|
||||
static long init_record(struct dbCommon *pcommon, int pass)
|
||||
@@ -142,7 +144,7 @@ static long init_record(struct dbCommon *pcommon, int pass)
|
||||
recGblRecordError(S_dev_missingSup, prec, "aao: init_record");
|
||||
return S_dev_missingSup;
|
||||
}
|
||||
return 0;
|
||||
return fetchValue(prec, 1);
|
||||
}
|
||||
|
||||
static long process(struct dbCommon *pcommon)
|
||||
@@ -161,6 +163,9 @@ static long process(struct dbCommon *pcommon)
|
||||
if ( !pact ) {
|
||||
prec->udf = FALSE;
|
||||
|
||||
if(!!(status = fetchValue(prec, 0)))
|
||||
return status;
|
||||
|
||||
/* Update the timestamp before writing output values so it
|
||||
* will be up to date if any downstream records fetch it via TSEL */
|
||||
recGblGetTimeStampSimm(prec, prec->simm, NULL);
|
||||
@@ -339,6 +344,34 @@ static void monitor(aaoRecord *prec)
|
||||
db_post_events(prec, &prec->val, monitor_mask);
|
||||
}
|
||||
|
||||
static long fetchValue(aaoRecord *prec, int init)
|
||||
{
|
||||
int isConst;
|
||||
long status;
|
||||
long nReq = prec->nelm;
|
||||
|
||||
if(prec->omsl!=menuOmslclosed_loop)
|
||||
return 0;
|
||||
|
||||
isConst = dbLinkIsConstant(&prec->dol);
|
||||
|
||||
if(init && isConst) {
|
||||
status = dbLoadLinkArray(&prec->dol, prec->ftvl, prec->bptr, &nReq);
|
||||
|
||||
} else if(!init && !isConst) {
|
||||
status = dbGetLink(&prec->dol, prec->ftvl, prec->bptr, 0, &nReq);
|
||||
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(!status) {
|
||||
prec->nord = nReq;
|
||||
prec->udf = FALSE;
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
static long writeValue(aaoRecord *prec)
|
||||
{
|
||||
aaodset *pdset = (aaodset *) prec->dset;
|
||||
|
||||
@@ -10,9 +10,9 @@
|
||||
=title Array Analog Output (aao)
|
||||
|
||||
The array analog output record type is used to write array data. The array data
|
||||
can contain any of the supported data types. The record is in many ways similar to
|
||||
the waveform record but outputs arrays instead of reading them. It also allows the
|
||||
device support to allocate the array storage.
|
||||
can contain any of the supported data types. The record is in many ways similar
|
||||
to the waveform record but outputs arrays instead of reading them. It also
|
||||
allows the device support to allocate the array storage.
|
||||
|
||||
=recordtype aao
|
||||
|
||||
@@ -40,22 +40,22 @@ These fields are described in L<Scan Fields|dbCommonRecord/Scan Fields>.
|
||||
=head3 Write Parameters
|
||||
|
||||
These fields are configurable by the user to specify how and where to the record
|
||||
writes its data. The OUT field determines where the array analog output writes its
|
||||
output. It can be a hardware address, a channel access or database link, or a
|
||||
constant. Only in records that use soft device support can the OUT field be a
|
||||
channel access link, a database link, or a constant. Otherwise, the OUT field must
|
||||
be a hardware address. See L<Address
|
||||
writes its data. The OUT field determines where the array analog output writes
|
||||
its output. It can be a hardware address, a channel access or database link, or
|
||||
a constant. Only in records that use soft device support can the OUT field be a
|
||||
channel access link, a database link, or a constant. Otherwise, the OUT field
|
||||
must be a hardware address. See L<Address
|
||||
Specification|https://docs.epics-controls.org/en/latest/guides/EPICS_Process_Database_Concepts.html#address-specification>
|
||||
for information on the format of hardware addresses and database links.
|
||||
|
||||
=head4 Fields related to array writing
|
||||
|
||||
The DTYP field must contain the name of the appropriate device support module. The
|
||||
values in the array referenced by are written to the location specified in the OUT
|
||||
field. (If the OUT link is a constant, no data are written.) NELM specifies the
|
||||
maximum number of elements that the array can hold, while FTVL specifies the data
|
||||
type of the elements (follow the link in the table below for a list of the
|
||||
available choices).
|
||||
The DTYP field must contain the name of the appropriate device support module.
|
||||
The values in the array referenced by are written to the location specified in
|
||||
the OUT field. (If the OUT link is a constant, no data are written.) NELM
|
||||
specifies the maximum number of elements that the array can hold, while FTVL
|
||||
specifies the data type of the elements (follow the link in the table below for
|
||||
a list of the available choices).
|
||||
|
||||
=fields DTYP, OUT, NELM, FTVL
|
||||
|
||||
@@ -119,13 +119,34 @@ These parameters are used by the run-time code for processing the array analog
|
||||
output record. They are not configured using a configuration tool. Only the VAL
|
||||
field is modifiable at run-time.
|
||||
|
||||
VAL references the array where the array analog output record stores its data. The
|
||||
BPTR field holds the address of the array.
|
||||
VAL references the array where the array analog output record stores its data.
|
||||
The BPTR field holds the address of the array.
|
||||
|
||||
The NORD field holds a counter of the number of elements that have been written to
|
||||
the output,
|
||||
The NORD field holds a counter of the number of elements that have been written
|
||||
to the output,
|
||||
|
||||
=fields VAL, BPTR, NORD
|
||||
=fields VAL, BPTR, NORD, OMSL, DOL
|
||||
|
||||
The following steps are performed in order during record processing.
|
||||
|
||||
=head4 Fetch Value
|
||||
|
||||
The OMSL menu field is used to determine whether the DOL link
|
||||
field should be used during processing or not:
|
||||
|
||||
=over
|
||||
|
||||
=item *
|
||||
If OMSL is C<supervisory> the DOL field are not used.
|
||||
The new output value is taken from the VAL field, which may have been set from
|
||||
elsewhere.
|
||||
|
||||
=item *
|
||||
If OMSL is C<closed_loop> the DOL link field is read to obtain a value.
|
||||
|
||||
=back
|
||||
|
||||
Note: The OMSL and DOL fields were added to the aaoRecord in Base 7.0.7.
|
||||
|
||||
=head3 Simulation Mode Parameters
|
||||
|
||||
@@ -327,6 +348,17 @@ Scan forward link if necessary, set PACT FALSE, and return.
|
||||
promptgroup("50 - Output")
|
||||
interest(1)
|
||||
}
|
||||
field(DOL,DBF_INLINK) {
|
||||
prompt("Desired Output Link")
|
||||
promptgroup("40 - Input")
|
||||
interest(1)
|
||||
}
|
||||
field(OMSL,DBF_MENU) {
|
||||
prompt("Output Mode Select")
|
||||
promptgroup("50 - Output")
|
||||
interest(1)
|
||||
menu(menuOmsl)
|
||||
}
|
||||
field(EGU,DBF_STRING) {
|
||||
prompt("Engineering Units")
|
||||
promptgroup("80 - Display")
|
||||
@@ -440,9 +472,9 @@ Scan forward link if necessary, set PACT FALSE, and return.
|
||||
=head3 Fields Of Interest To Device Support
|
||||
|
||||
Each array analog output record record must have an associated set of device
|
||||
support routines. The primary responsibility of the device support routines is to
|
||||
write the array data value whenever C<write_aao()> is called. The device support
|
||||
routines are primarily interested in the following fields:
|
||||
support routines. The primary responsibility of the device support routines is
|
||||
to write the array data value whenever C<write_aao()> is called. The device
|
||||
support routines are primarily interested in the following fields:
|
||||
|
||||
=fields PACT, DPVT, NSEV, NSTA, OUT, NELM, FTVL, BPTR, NORD
|
||||
|
||||
@@ -492,7 +524,8 @@ provided for any device type that can use the ioEvent scanner.
|
||||
|
||||
long write_aao(dbCommon *precord)
|
||||
|
||||
This routine must write the array data to output. It returns the following values:
|
||||
This routine must write the array data to output. It returns the following
|
||||
values:
|
||||
|
||||
=over
|
||||
|
||||
|
||||
@@ -37,7 +37,7 @@
|
||||
#include "recGbl.h"
|
||||
#include "menuConvert.h"
|
||||
#include "menuOmsl.h"
|
||||
#include "menuYesNo.h"
|
||||
#include "menuSimm.h"
|
||||
#include "menuIvoa.h"
|
||||
|
||||
#define GEN_SIZE_OFFSET
|
||||
@@ -561,14 +561,20 @@ static long writeValue(aoRecord *prec)
|
||||
}
|
||||
|
||||
switch (prec->simm) {
|
||||
case menuYesNoNO:
|
||||
case menuSimmNO:
|
||||
status = pdset->write_ao(prec);
|
||||
break;
|
||||
|
||||
case menuYesNoYES: {
|
||||
case menuSimmYES:
|
||||
case menuSimmRAW:
|
||||
recGblSetSevr(prec, SIMM_ALARM, prec->sims);
|
||||
if (prec->pact || (prec->sdly < 0.)) {
|
||||
status = dbPutLink(&prec->siol, DBR_DOUBLE, &prec->oval, 1);
|
||||
if (prec->simm == menuSimmYES)
|
||||
/* don't convert */
|
||||
status = dbPutLink(&prec->siol, DBR_DOUBLE, &prec->oval, 1);
|
||||
else /* prec->simm == menuSimmRAW*/
|
||||
/* convert */
|
||||
status = dbPutLink(&prec->siol, DBR_LONG, &prec->rval, 1);
|
||||
prec->pact = FALSE;
|
||||
} else { /* !prec->pact && delay >= 0. */
|
||||
epicsCallback *pvt = prec->simpvt;
|
||||
@@ -580,7 +586,6 @@ static long writeValue(aoRecord *prec)
|
||||
prec->pact = TRUE;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
recGblSetSevr(prec, SOFT_ALARM, INVALID_ALARM);
|
||||
|
||||
@@ -263,8 +263,9 @@ processing.
|
||||
|
||||
The following fields are used to operate the record in simulation mode.
|
||||
|
||||
If SIMM (fetched through SIML) is YES, the record is put in SIMS
|
||||
If SIMM (fetched through SIML, if populated) is YES, the record is put in SIMS
|
||||
severity and the value is written through SIOL, without conversion.
|
||||
If SIMM is RAW, the value is converted and RVAL is written.
|
||||
SSCN sets a different SCAN mechanism to use in simulation mode.
|
||||
SDLY sets a delay (in sec) that is used for asynchronous simulation
|
||||
processing.
|
||||
@@ -557,7 +558,7 @@ for more information on simulation mode and its fields.
|
||||
prompt("Simulation Mode")
|
||||
special(SPC_MOD)
|
||||
interest(1)
|
||||
menu(menuYesNo)
|
||||
menu(menuSimm)
|
||||
}
|
||||
field(SIMS,DBF_MENU) {
|
||||
prompt("Simulation Mode Severity")
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
#include "special.h"
|
||||
#include "menuIvoa.h"
|
||||
#include "menuOmsl.h"
|
||||
#include "menuYesNo.h"
|
||||
#include "menuSimm.h"
|
||||
|
||||
#define GEN_SIZE_OFFSET
|
||||
#include "boRecord.h"
|
||||
@@ -427,14 +427,18 @@ static long writeValue(boRecord *prec)
|
||||
}
|
||||
|
||||
switch (prec->simm) {
|
||||
case menuYesNoNO:
|
||||
case menuSimmNO:
|
||||
status = pdset->write_bo(prec);
|
||||
break;
|
||||
|
||||
case menuYesNoYES: {
|
||||
case menuSimmYES:
|
||||
case menuSimmRAW:
|
||||
recGblSetSevr(prec, SIMM_ALARM, prec->sims);
|
||||
if (prec->pact || (prec->sdly < 0.)) {
|
||||
status = dbPutLink(&prec->siol, DBR_USHORT, &prec->val, 1);
|
||||
if (prec->simm == menuSimmYES)
|
||||
status = dbPutLink(&prec->siol, DBR_USHORT, &prec->val, 1);
|
||||
else /* prec->simm == menuSimmRAW */
|
||||
status = dbPutLink(&prec->siol, DBR_ULONG, &prec->rval, 1);
|
||||
prec->pact = FALSE;
|
||||
} else { /* !prec->pact && delay >= 0. */
|
||||
epicsCallback *pvt = prec->simpvt;
|
||||
@@ -446,7 +450,6 @@ static long writeValue(boRecord *prec)
|
||||
prec->pact = TRUE;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
recGblSetSevr(prec, SOFT_ALARM, INVALID_ALARM);
|
||||
|
||||
@@ -182,8 +182,9 @@ The WPDT field is a private field for honoring seconds to hold HIGH.
|
||||
|
||||
The following fields are used to operate the record in simulation mode.
|
||||
|
||||
If SIMM (fetched through SIML) is YES, the record is put in SIMS
|
||||
severity and the value is written through SIOL.
|
||||
If SIMM (fetched through SIML, if populated) is YES, the record is put
|
||||
in SIMS severity and the unconverted value is written through SIOL.
|
||||
If SIMM is RAW, the value is converted and RVAL is written.
|
||||
SSCN sets a different SCAN mechanism to use in simulation mode.
|
||||
SDLY sets a delay (in sec) that is used for asynchronous simulation
|
||||
processing.
|
||||
@@ -328,7 +329,7 @@ for more information on simulation mode and its fields.
|
||||
prompt("Simulation Mode")
|
||||
special(SPC_MOD)
|
||||
interest(1)
|
||||
menu(menuYesNo)
|
||||
menu(menuSimm)
|
||||
}
|
||||
field(SIMS,DBF_MENU) {
|
||||
prompt("Simulation Mode Severity")
|
||||
|
||||
@@ -212,18 +212,26 @@ described in L<Alarm Fields|dbCommonRecord/Alarm Fields>.
|
||||
|
||||
These parameters are used by the run-time code for processing the data
|
||||
compression algorithm. They are not configurable by the user, though some are
|
||||
accessible at run-time. They can represent the current state of the waveform or
|
||||
accessible at run-time. They can represent the current state of the algorithm or
|
||||
of the record whose field is referenced by the INP field.
|
||||
|
||||
=fields NUSE, OUSE, BPTR, SPTR, WPTR, CVB, INPN, INX
|
||||
|
||||
NUSE and OUSE hold the current and previous number of elements stored in VAL.
|
||||
|
||||
BPTR is a pointer that refers to the buffer referenced by VAL.
|
||||
BPTR points to the buffer referenced by VAL.
|
||||
|
||||
SPTR points to an array that is used for array averages.
|
||||
|
||||
WPTR is used by the dbGetlinks routines.
|
||||
WPTR points to the buffer containing data referenced by INP.
|
||||
|
||||
CVB stores the current compressed value for C<<< N to 1 >>> algorithms when INP
|
||||
references a scalar.
|
||||
|
||||
INPN is updated when the record processes; if INP references an array and the
|
||||
size changes, the WPTR buffer is reallocated.
|
||||
|
||||
INX counts the number of readings collected.
|
||||
|
||||
=head2 Record Support
|
||||
|
||||
@@ -482,7 +490,7 @@ Scan forward link if necessary, set PACT FALSE, and return.
|
||||
interest(3)
|
||||
}
|
||||
field(INX,DBF_ULONG) {
|
||||
prompt("Compressed Array Inx")
|
||||
prompt("Current number of readings")
|
||||
special(SPC_NOMOD)
|
||||
interest(3)
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
#include "special.h"
|
||||
#include "menuOmsl.h"
|
||||
#include "menuIvoa.h"
|
||||
#include "menuYesNo.h"
|
||||
#include "menuSimm.h"
|
||||
|
||||
#define GEN_SIZE_OFFSET
|
||||
#include "mbboDirectRecord.h"
|
||||
@@ -359,14 +359,18 @@ static long writeValue(mbboDirectRecord *prec)
|
||||
}
|
||||
|
||||
switch (prec->simm) {
|
||||
case menuYesNoNO:
|
||||
case menuSimmNO:
|
||||
status = pdset->write_mbbo(prec);
|
||||
break;
|
||||
|
||||
case menuYesNoYES: {
|
||||
case menuSimmYES:
|
||||
case menuSimmRAW:
|
||||
recGblSetSevr(prec, SIMM_ALARM, prec->sims);
|
||||
if (prec->pact || (prec->sdly < 0.)) {
|
||||
status = dbPutLink(&prec->siol, DBR_ULONG, &prec->val, 1);
|
||||
if (prec->simm == menuSimmYES)
|
||||
status = dbPutLink(&prec->siol, DBR_LONG, &prec->val, 1);
|
||||
else /* prec->simm == menuSimmRAW */
|
||||
status = dbPutLink(&prec->siol, DBR_ULONG, &prec->rval, 1);
|
||||
prec->pact = FALSE;
|
||||
} else { /* !prec->pact && delay >= 0. */
|
||||
epicsCallback *pvt = prec->simpvt;
|
||||
@@ -378,7 +382,6 @@ static long writeValue(mbboDirectRecord *prec)
|
||||
prec->pact = TRUE;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
recGblSetSevr(prec, SOFT_ALARM, INVALID_ALARM);
|
||||
|
||||
@@ -223,6 +223,7 @@ The following fields are used to operate the record in simulation mode.
|
||||
|
||||
If SIMM (fetched through SIML) is YES, the record is put in SIMS
|
||||
severity and the value is written through SIOL, without conversion.
|
||||
If SIMM is RAW, the value is converted and RVAL is written.
|
||||
SSCN sets a different SCAN mechanism to use in simulation mode.
|
||||
SDLY sets a delay (in sec) that is used for asynchronous simulation
|
||||
processing.
|
||||
@@ -248,7 +249,7 @@ for more information on simulation mode and its fields.
|
||||
prompt("Simulation Mode")
|
||||
special(SPC_MOD)
|
||||
interest(1)
|
||||
menu(menuYesNo)
|
||||
menu(menuSimm)
|
||||
}
|
||||
field(SIMS,DBF_MENU) {
|
||||
prompt("Simulation Mode Severity")
|
||||
|
||||
@@ -35,7 +35,7 @@
|
||||
#include "special.h"
|
||||
#include "menuOmsl.h"
|
||||
#include "menuIvoa.h"
|
||||
#include "menuYesNo.h"
|
||||
#include "menuSimm.h"
|
||||
|
||||
#define GEN_SIZE_OFFSET
|
||||
#include "mbboRecord.h"
|
||||
@@ -448,14 +448,18 @@ static long writeValue(mbboRecord *prec)
|
||||
}
|
||||
|
||||
switch (prec->simm) {
|
||||
case menuYesNoNO:
|
||||
case menuSimmNO:
|
||||
status = pdset->write_mbbo(prec);
|
||||
break;
|
||||
|
||||
case menuYesNoYES: {
|
||||
case menuSimmYES:
|
||||
case menuSimmRAW:
|
||||
recGblSetSevr(prec, SIMM_ALARM, prec->sims);
|
||||
if (prec->pact || (prec->sdly < 0.)) {
|
||||
status = dbPutLink(&prec->siol, DBR_USHORT, &prec->val, 1);
|
||||
if (prec->simm == menuSimmYES)
|
||||
status = dbPutLink(&prec->siol, DBR_USHORT, &prec->val, 1);
|
||||
else /* prec->simm == menuSimmRAW */
|
||||
status = dbPutLink(&prec->siol, DBR_ULONG, &prec->rval, 1);
|
||||
prec->pact = FALSE;
|
||||
} else { /* !prec->pact && delay >= 0. */
|
||||
epicsCallback *pvt = prec->simpvt;
|
||||
@@ -467,7 +471,6 @@ static long writeValue(mbboRecord *prec)
|
||||
prec->pact = TRUE;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
recGblSetSevr(prec, SOFT_ALARM, INVALID_ALARM);
|
||||
|
||||
@@ -165,8 +165,9 @@ for converting VAL to RVAL.
|
||||
|
||||
The following fields are used to operate the record in simulation mode.
|
||||
|
||||
If SIMM (fetched through SIML) is YES, the record is put in SIMS
|
||||
If SIMM (fetched through SIML, if populated) is YES, the record is put in SIMS
|
||||
severity and the value is written through SIOL, without conversion.
|
||||
If SIMM is RAW, the value is converted and RVAL is written.
|
||||
SSCN sets a different SCAN mechanism to use in simulation mode.
|
||||
SDLY sets a delay (in sec) that is used for asynchronous simulation
|
||||
processing.
|
||||
@@ -657,7 +658,7 @@ for more information on simulation mode and its fields.
|
||||
prompt("Simulation Mode")
|
||||
special(SPC_MOD)
|
||||
interest(1)
|
||||
menu(menuYesNo)
|
||||
menu(menuSimm)
|
||||
}
|
||||
field(SIMS,DBF_MENU) {
|
||||
prompt("Simulation Mode Severity")
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user