Compare commits
406 Commits
R3.15.2
...
calinkplug
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
46e74328dc | ||
|
|
cb35889a20 | ||
|
|
29afb88006 | ||
|
|
bd8977c51d | ||
|
|
32c3231f5d | ||
|
|
33033e6027 | ||
|
|
905c5e3336 | ||
|
|
47b3c68af1 | ||
|
|
4b3bdf31c2 | ||
|
|
3740082b61 | ||
|
|
7a1766279c | ||
|
|
cec0222c5a | ||
|
|
394820318c | ||
|
|
45db78981c | ||
|
|
7d94d05bb7 | ||
|
|
5c93eb6049 | ||
|
|
92bd217ade | ||
|
|
f0b5b52cef | ||
|
|
e0b578aff5 | ||
|
|
0fd07d1632 | ||
|
|
fbf6b6d3e5 | ||
|
|
82042e270e | ||
|
|
32c3f8a44f | ||
|
|
fc4f010972 | ||
|
|
bd2ada3f8c | ||
|
|
3d7e95b88a | ||
|
|
8fea0117ae | ||
|
|
8392106f59 | ||
|
|
9502841b55 | ||
|
|
19680d7869 | ||
|
|
4a0ef34771 | ||
|
|
5d42e60745 | ||
|
|
e01d4b14d6 | ||
|
|
791fa1310a | ||
|
|
b4404c8266 | ||
|
|
f73f3332fa | ||
|
|
3f4c6abb31 | ||
|
|
64cd49410e | ||
|
|
0f6f833e91 | ||
|
|
c08df82898 | ||
|
|
f0c56bcbba | ||
|
|
76c422c5fd | ||
|
|
fee62836cd | ||
|
|
2c48bcc26b | ||
|
|
a3ab342833 | ||
|
|
3304470323 | ||
|
|
4f2f910d09 | ||
|
|
217f515985 | ||
|
|
ebc59a4223 | ||
|
|
58285501de | ||
|
|
fa07bc3efe | ||
|
|
bfa8e324f7 | ||
|
|
51a69862e7 | ||
|
|
4b8c11aeef | ||
|
|
62efa2e96a | ||
|
|
c7bcb09540 | ||
|
|
5ee778b0c2 | ||
|
|
7b3a040b34 | ||
|
|
e721be4ff5 | ||
|
|
88b47c021c | ||
|
|
d0a316f7ca | ||
|
|
ba307ba6e7 | ||
|
|
d943f6966f | ||
|
|
76d7ce1f76 | ||
|
|
66b0acbe3e | ||
|
|
822c6815c5 | ||
|
|
684ae7b35c | ||
|
|
e18eca80db | ||
|
|
b45622ac5e | ||
|
|
89e6fdbca0 | ||
|
|
599e6635fb | ||
|
|
4241b4e6cb | ||
|
|
9eedf0581e | ||
|
|
ffb23a3480 | ||
|
|
2be59e985d | ||
|
|
b4fd19e7db | ||
|
|
969b1030b2 | ||
|
|
84880e876d | ||
|
|
45a741ac6f | ||
|
|
0575d3764f | ||
|
|
48da96cce5 | ||
|
|
18d863b918 | ||
|
|
61e61aab24 | ||
|
|
d3e046a466 | ||
|
|
f528f347cd | ||
|
|
ffed8910c0 | ||
|
|
99d331e50a | ||
|
|
0fb923186a | ||
|
|
ce500bc98c | ||
|
|
6376ae8b1a | ||
|
|
445c0ada8c | ||
|
|
87759d1c1d | ||
|
|
9d95528eba | ||
|
|
95a1998e0b | ||
|
|
5769db3862 | ||
|
|
00a74fbb4e | ||
|
|
03fde89dd4 | ||
|
|
8421b46398 | ||
|
|
650aaea203 | ||
|
|
fcfe7ed312 | ||
|
|
f2c4b2c81e | ||
|
|
167dea3735 | ||
|
|
1c1eb030a9 | ||
|
|
43cf5af621 | ||
|
|
235e4bd835 | ||
|
|
601d6d9fe8 | ||
|
|
b961b25151 | ||
|
|
eb9246e4c9 | ||
|
|
d729006908 | ||
|
|
0aeba281be | ||
|
|
04fee51795 | ||
|
|
cba449d53b | ||
|
|
cdffcbfbae | ||
|
|
ba5ea5da93 | ||
|
|
c884175a86 | ||
|
|
a4e9dcff00 | ||
|
|
c9323da5e3 | ||
|
|
ffe441d032 | ||
|
|
6143520680 | ||
|
|
f814398d77 | ||
|
|
d2e4fcbbac | ||
|
|
4cca4673ca | ||
|
|
840da801fb | ||
|
|
e75f44100e | ||
|
|
45c8a173f3 | ||
|
|
e227ae3590 | ||
|
|
ecedd9c362 | ||
|
|
4e7b185977 | ||
|
|
b74ecff2fc | ||
|
|
1255e75828 | ||
|
|
d6eea14fd0 | ||
|
|
98504d1cdc | ||
|
|
568ece9c27 | ||
|
|
f876bdb42c | ||
|
|
605074a580 | ||
|
|
64da181e6f | ||
|
|
823613fb4f | ||
|
|
f837add8c4 | ||
|
|
15307c4db6 | ||
|
|
8d5815ac95 | ||
|
|
25d7d46e08 | ||
|
|
5f31d9d2b1 | ||
|
|
bb67c9db2f | ||
|
|
cb965611ce | ||
|
|
e805abe971 | ||
|
|
c80f71e294 | ||
|
|
692bbafffe | ||
|
|
fd863738be | ||
|
|
2052062324 | ||
|
|
5d74216017 | ||
|
|
991ff308e0 | ||
|
|
9779d6987c | ||
|
|
b7c2815c21 | ||
|
|
77a5f0db77 | ||
|
|
8d6469957e | ||
|
|
ef90d2d3c7 | ||
|
|
29d2b7f6a2 | ||
|
|
ef39f658cd | ||
|
|
e298fb4c27 | ||
|
|
d94840ae6c | ||
|
|
c1f742e741 | ||
|
|
6f0814108a | ||
|
|
770d9ab313 | ||
|
|
7e7cca6164 | ||
|
|
03bdf4effa | ||
|
|
3258927bab | ||
|
|
a8e285067d | ||
|
|
2d9c529f5e | ||
|
|
05d3dbf453 | ||
|
|
695f516cbf | ||
|
|
fda3824ab4 | ||
|
|
42a67524e0 | ||
|
|
d4ed70154b | ||
|
|
f776f6b422 | ||
|
|
cd746a339f | ||
|
|
6438750615 | ||
|
|
b099ea2966 | ||
|
|
2ec5c80652 | ||
|
|
e1039bacfc | ||
|
|
54381b7bf9 | ||
|
|
69d3c94328 | ||
|
|
a02860728e | ||
|
|
440ccc3c4a | ||
|
|
3d19f19d81 | ||
|
|
214edd42d1 | ||
|
|
f8b0b2f5f0 | ||
|
|
8596dc41f2 | ||
|
|
88cb33a04d | ||
|
|
5af0663058 | ||
|
|
830704021d | ||
|
|
96ee2cd00c | ||
|
|
c2d2f671bb | ||
|
|
4b9958304d | ||
|
|
d0875f4f12 | ||
|
|
c4b128dfff | ||
|
|
c74eb27474 | ||
|
|
a245c3805a | ||
|
|
5aa145920d | ||
|
|
122b9d3185 | ||
|
|
4081377c4b | ||
|
|
405f50533e | ||
|
|
8f0950288a | ||
|
|
3551513297 | ||
|
|
fc775d76aa | ||
|
|
7195372caf | ||
|
|
655735fa13 | ||
|
|
8aea808b1d | ||
|
|
66e126bae8 | ||
|
|
8ce86bb061 | ||
|
|
f2ff270644 | ||
|
|
bc3cbd990f | ||
|
|
53bf7cd994 | ||
|
|
a9c4d59537 | ||
|
|
895b415968 | ||
|
|
87999c7047 | ||
|
|
9331ca3f44 | ||
|
|
1587c44d79 | ||
|
|
51f0a4509b | ||
|
|
94f00cb0c6 | ||
|
|
d32332d545 | ||
|
|
4544be090d | ||
|
|
15db30ba79 | ||
|
|
cd79a7b53d | ||
|
|
717d7ff4eb | ||
|
|
696e00eec9 | ||
|
|
a81d123ea5 | ||
|
|
f21fce0b42 | ||
|
|
6fffcf9985 | ||
|
|
d890c80961 | ||
|
|
d49fc94314 | ||
|
|
88864e949b | ||
|
|
536c6e91ff | ||
|
|
35c07a958f | ||
|
|
67aa96b957 | ||
|
|
041423092f | ||
|
|
869410d6f6 | ||
|
|
6862ef6580 | ||
|
|
b6aea68304 | ||
|
|
9d00978176 | ||
|
|
b6fbea9610 | ||
|
|
180f40c1f7 | ||
|
|
d5832354e8 | ||
|
|
44980a1dac | ||
|
|
af1daea3e7 | ||
|
|
b2716f0a19 | ||
|
|
050b9f9a48 | ||
|
|
4ab6aa79e3 | ||
|
|
1e39224836 | ||
|
|
f99fbe1964 | ||
|
|
aa04941119 | ||
|
|
46853d6862 | ||
|
|
fe4f607e63 | ||
|
|
02ac91aa2a | ||
|
|
9e82a96700 | ||
|
|
ef7399159c | ||
|
|
d7b3293ba3 | ||
|
|
9014ca899d | ||
|
|
f1b403b59a | ||
|
|
877a409de1 | ||
|
|
7395d1d88d | ||
|
|
4d3831ce58 | ||
|
|
c5130468cd | ||
|
|
7dd067e887 | ||
|
|
c56091978c | ||
|
|
2f8e6bf17e | ||
|
|
144281e0a3 | ||
|
|
83c2414ad0 | ||
|
|
1d99ad6cfc | ||
|
|
bd4d8ec26b | ||
|
|
504665bf09 | ||
|
|
1411522a11 | ||
|
|
ae3d3904d9 | ||
|
|
002bafdf07 | ||
|
|
8bfa40d858 | ||
|
|
138e2f1ad5 | ||
|
|
a5e1f367d6 | ||
|
|
fefe6fd1fc | ||
|
|
eb31fcedeb | ||
|
|
dd6edb103c | ||
|
|
50e6a305b1 | ||
|
|
5eb49ebaf0 | ||
|
|
58602cfa80 | ||
|
|
1e68f3899d | ||
|
|
8b3d37d392 | ||
|
|
ed4bcd831f | ||
|
|
837111296e | ||
|
|
9198428619 | ||
|
|
8f3fcc2787 | ||
|
|
a308fb7c1e | ||
|
|
60823bd2fb | ||
|
|
d0790c3060 | ||
|
|
9f0d34656a | ||
|
|
f6cdbe2693 | ||
|
|
625172419e | ||
|
|
80343363a4 | ||
|
|
444b89f557 | ||
|
|
3a54e97758 | ||
|
|
fa1ddeeb1b | ||
|
|
ca46bf70b7 | ||
|
|
c0e2a44365 | ||
|
|
84c3ec7c64 | ||
|
|
65e781d58a | ||
|
|
a613a96ad3 | ||
|
|
155017bf09 | ||
|
|
961dc450c4 | ||
|
|
fbd2d4dd31 | ||
|
|
53d01f6fb4 | ||
|
|
7fd4ac5a67 | ||
|
|
1240f09797 | ||
|
|
7947807e09 | ||
|
|
2c28d4b69e | ||
|
|
9946681f72 | ||
|
|
196baac7da | ||
|
|
ce93142fe1 | ||
|
|
5d61a512d1 | ||
|
|
3a7cb33234 | ||
|
|
6b6eb5ff6a | ||
|
|
e4f336de94 | ||
|
|
fa23ba8a6a | ||
|
|
ee8bf98f94 | ||
|
|
30b8872150 | ||
|
|
7a8266c419 | ||
|
|
0c16937a5a | ||
|
|
ad6f55d92f | ||
|
|
e852de64ff | ||
|
|
f72ebb9fb9 | ||
|
|
6ac1e050bd | ||
|
|
ff4c88ed05 | ||
|
|
7a5d4cf6cc | ||
|
|
765fb7c63e | ||
|
|
fa4678798c | ||
|
|
127bdfd9aa | ||
|
|
8fea2f5ae6 | ||
|
|
07bb2fbef7 | ||
|
|
58a8a07cc4 | ||
|
|
a78abd0070 | ||
|
|
c26b02c20d | ||
|
|
ee297dc558 | ||
|
|
8ce0ba1e54 | ||
|
|
b8a7da18d2 | ||
|
|
8af3ffb653 | ||
|
|
ffd188bea3 | ||
|
|
af89b716f4 | ||
|
|
adcde46e9e | ||
|
|
c9d889ef3e | ||
|
|
8344b4ed79 | ||
|
|
3a647fb38b | ||
|
|
8bf765365f | ||
|
|
e7037cc519 | ||
|
|
0d41dd0113 | ||
|
|
52fc47dec5 | ||
|
|
e73ed24631 | ||
|
|
40cb21df9f | ||
|
|
40d4d608ed | ||
|
|
77efc973da | ||
|
|
3e8ba7d7cf | ||
|
|
a7a87372aa | ||
|
|
a014314384 | ||
|
|
8ef1e0d177 | ||
|
|
b9cbf7a3ac | ||
|
|
4bd4b93649 | ||
|
|
c120027768 | ||
|
|
cc3a01d460 | ||
|
|
06beb2cafb | ||
|
|
be06b37b42 | ||
|
|
a526d0eb04 | ||
|
|
cd228ccf0e | ||
|
|
bbbf05414d | ||
|
|
d7d45beedd | ||
|
|
a7ad9a1256 | ||
|
|
92641e9160 | ||
|
|
368223f783 | ||
|
|
85e21863dc | ||
|
|
b9b8cde5f6 | ||
|
|
6d7a0327e0 | ||
|
|
d897c9b686 | ||
|
|
ff14d811a7 | ||
|
|
2ba2b90b06 | ||
|
|
7d50f62aed | ||
|
|
4518e8fa7f | ||
|
|
e06b0a87b2 | ||
|
|
c16c4590ec | ||
|
|
b3ad20dec7 | ||
|
|
0f23784b32 | ||
|
|
42a3cf8e11 | ||
|
|
79497c359c | ||
|
|
75321140ef | ||
|
|
95d0d97f6a | ||
|
|
e24d4637c9 | ||
|
|
e5b354708e | ||
|
|
bc9255ba1c | ||
|
|
3f462bd032 | ||
|
|
6a018dce35 | ||
|
|
55cf45364c | ||
|
|
3885c809a6 | ||
|
|
5847f98f89 | ||
|
|
6ed997aacc | ||
|
|
5a8915c051 | ||
|
|
a919d7e09d | ||
|
|
c5da1e3f89 | ||
|
|
33804bd7ea | ||
|
|
200355bc00 | ||
|
|
480797c0e0 | ||
|
|
470a9f9fc9 | ||
|
|
99626f03cd | ||
|
|
8c60e7e2cb |
10
README
10
README
@@ -2,14 +2,14 @@
|
||||
EPICS Base - the central core of a control system toolkit
|
||||
---------------------------------------------------------
|
||||
|
||||
Copyright (c) 1991-2003 The University of Chicago, as Operator
|
||||
of Argonne National Laboratory.
|
||||
Copyright UChicago Argonne LLC, as Operator of Argonne
|
||||
National Laboratory.
|
||||
Copyright (c) 1991-2003 The Regents of the University of
|
||||
California, as Operator of Los Alamos National Laboratory.
|
||||
|
||||
EPICS Base Versions 3.13.7 and higher are distributed
|
||||
subject to a Software License Agreement found in the
|
||||
file LICENSE that is included with this distribution.
|
||||
EPICS Base is distributed subject to a Software License
|
||||
Agreement found in the file LICENSE that is included with
|
||||
this distribution.
|
||||
|
||||
---------------------------------------------------------
|
||||
|
||||
|
||||
@@ -3,13 +3,12 @@
|
||||
# National Laboratory.
|
||||
# Copyright (c) 2002 The Regents of the University of California, as
|
||||
# Operator of Los Alamos National Laboratory.
|
||||
# EPICS BASE Versions 3.13.7
|
||||
# and higher are distributed subject to a Software License Agreement found
|
||||
# EPICS BASE is distributed subject to a Software License Agreement found
|
||||
# in file LICENSE that is included with this distribution.
|
||||
#*************************************************************************
|
||||
|
||||
#---------------------------------------------------------------
|
||||
# Epics base directories
|
||||
# EPICS Base directories
|
||||
|
||||
EPICS_BASE_HOST_BIN = $(EPICS_BASE)/bin/$(EPICS_HOST_ARCH)
|
||||
EPICS_BASE_HOST_LIB = $(EPICS_BASE)/lib/$(EPICS_HOST_ARCH)
|
||||
@@ -19,12 +18,12 @@ ifdef T_A
|
||||
endif
|
||||
|
||||
#---------------------------------------------------------------
|
||||
# Epics base Ioc libraries
|
||||
# EPICS Base Ioc libraries
|
||||
|
||||
EPICS_BASE_IOC_LIBS += dbRecStd dbCore ca Com
|
||||
|
||||
#---------------------------------------------------------------
|
||||
# Epics base Host libraries
|
||||
# EPICS Base Host libraries
|
||||
|
||||
EPICS_BASE_HOST_LIBS += cas gdd
|
||||
EPICS_BASE_HOST_LIBS += ca Com
|
||||
@@ -42,13 +41,11 @@ endif # BASE_TOP
|
||||
#---------------------------------------------------------------
|
||||
# Base c preprocessor flags
|
||||
|
||||
BASE_CPPFLAGS =
|
||||
|
||||
# osithread default stack
|
||||
OSITHREAD_USE_DEFAULT_STACK = NO
|
||||
OSITHREAD_DEFAULT_STACK_FLAGS_YES = -DOSITHREAD_USE_DEFAULT_STACK
|
||||
OSITHREAD_DEFAULT_STACK_FLAGS_NO =
|
||||
BASE_CPPFLAGS += $(OSITHREAD_DEFAULT_STACK_FLAGS_$(OSITHREAD_USE_DEFAULT_STACK))
|
||||
|
||||
BASE_CPPFLAGS = $(OSITHREAD_DEFAULT_STACK_FLAGS_$(OSITHREAD_USE_DEFAULT_STACK))
|
||||
|
||||
#---------------------------------------------------------------
|
||||
# Where to find the installed build tools
|
||||
@@ -59,7 +56,7 @@ TOOLS = $(abspath $(EPICS_BASE_HOST_BIN))
|
||||
FIND_TOOL = $(firstword $(wildcard $(TOOLS)/$(1) $(TOP)/src/tools/$(1)))
|
||||
|
||||
#---------------------------------------------------------------
|
||||
# Epics base build tools and tool flags
|
||||
# EPICS Base build tools and tool flags
|
||||
|
||||
MAKEBPT = $(TOOLS)/makeBpt$(HOSTEXE)
|
||||
DBEXPAND = $(PERL) $(TOOLS)/dbdExpand.pl
|
||||
@@ -68,11 +65,11 @@ DBTOMENUH = $(PERL) $(TOOLS)/dbdToMenuH.pl
|
||||
REGISTERRECORDDEVICEDRIVER = $(PERL) $(TOOLS)/registerRecordDeviceDriver.pl
|
||||
CONVERTRELEASE = $(PERL) $(call FIND_TOOL,convertRelease.pl)
|
||||
FULLPATHNAME = $(PERL) $(TOOLS)/fullPathName.pl
|
||||
GENVERSIONHEADER = $(PERL) $(TOOLS)/genVersionHeader.pl $(QUIET_FLAG)
|
||||
|
||||
#-------------------------------------------------------
|
||||
#---------------------------------------------------------------
|
||||
# tools for installing libraries and products
|
||||
INSTALL_QUIETLY := $(if $(findstring s,$(MAKEFLAGS)),-q,)
|
||||
INSTALL = $(PERL) $(TOOLS)/installEpics.pl $(INSTALL_QUIETLY)
|
||||
INSTALL = $(PERL) $(TOOLS)/installEpics.pl $(QUIET_FLAG)
|
||||
INSTALL_PRODUCT = $(INSTALL)
|
||||
INSTALL_LIBRARY = $(INSTALL)
|
||||
|
||||
@@ -82,7 +79,7 @@ MKMF = $(PERL) $(TOOLS)/mkmf.pl
|
||||
REPLACEVAR = $(PERL) $(TOOLS)/replaceVAR.pl
|
||||
|
||||
#---------------------------------------------------------------
|
||||
# private versions of lex/yacc from EPICS
|
||||
# Our versions of lex (flex) and yacc (antelope)
|
||||
EYACC = $(TOOLS)/antelope$(HOSTEXE)
|
||||
ELEX = $(TOOLS)/e_flex$(HOSTEXE) -S$(EPICS_BASE)/include/flex.skel.static
|
||||
|
||||
@@ -90,28 +87,6 @@ YACC = $(EYACC)
|
||||
LEX = $(ELEX)
|
||||
|
||||
#---------------------------------------------------------------
|
||||
# Our use of msi is incompatible with older versions
|
||||
# The 3.15 version of msi supports new options
|
||||
|
||||
MSI3_15 = $(EPICS_BASE_HOST_BIN)/msi
|
||||
|
||||
#---------------------------------------------------------------
|
||||
# External tools and tool flags - must be in path or defined in application
|
||||
|
||||
ADL2DL ?= adl2dl
|
||||
|
||||
# sch2edif compiler and flags
|
||||
SCH2EDIF = sch2edif
|
||||
SCH2EDIF_PATH =
|
||||
SCH2EDIF_SYSFLAGS = -n -ap -p.+..+$(SCH2EDIF_PATH)+$(CAPFAST_TEMPLATES)/sym+
|
||||
SCH2EDIF_FLAGS =
|
||||
|
||||
# e2db and flags
|
||||
# - again there is an assumption where edb.def is installed.
|
||||
E2DB ?= e2db
|
||||
E2DB_SYSFLAGS = -ate -d $(CAPFAST_TEMPLATES)/edb.def
|
||||
E2DB_FLAGS =
|
||||
|
||||
DBST ?= dbst
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -17,26 +17,37 @@
|
||||
# EPICS_SITE_VERSION is defined in CONFIG_SITE for sites that want a local
|
||||
# version number to be included in the reported version string.
|
||||
|
||||
# In 3.15 we still define BASE_3_14 so "ifdef BASE_3_14" means
|
||||
# 3.14 or later, but "ifeq ($(BASE_3_14),YES)" means 3.14 only.
|
||||
# We define BASE_3_14 and BASE_3_15 as NO and BASE_3_16 as YES, so
|
||||
# ifdef BASE_3_14
|
||||
# true for 3.14 or later
|
||||
# ifdef BASE_3_15
|
||||
# true for 3.15 or later
|
||||
# ifeq ($(BASE_3_14),YES)
|
||||
# true for 3.14.x only
|
||||
# ifeq ($(BASE_3_15),YES)
|
||||
# true for 3.15 only
|
||||
# ifeq ($(BASE_3_16),YES)
|
||||
# true for 3.16 only.
|
||||
|
||||
BASE_3_14 = NO
|
||||
BASE_3_15 = YES
|
||||
BASE_3_15 = NO
|
||||
BASE_3_16 = YES
|
||||
|
||||
# EPICS_VERSION must be a number >0 and <256
|
||||
EPICS_VERSION = 3
|
||||
|
||||
# EPICS_REVISION must be a number >=0 and <256
|
||||
EPICS_REVISION = 15
|
||||
EPICS_REVISION = 16
|
||||
|
||||
# EPICS_MODIFICATION must be a number >=0 and <256
|
||||
EPICS_MODIFICATION = 2
|
||||
EPICS_MODIFICATION = 0
|
||||
|
||||
# EPICS_PATCH_LEVEL must be a number (win32 resource file requirement)
|
||||
# Not included if zero
|
||||
EPICS_PATCH_LEVEL = 0
|
||||
EPICS_PATCH_LEVEL = 1
|
||||
|
||||
# This will end in -DEV between official releases
|
||||
#EPICS_DEV_SNAPSHOT=-DEV
|
||||
EPICS_DEV_SNAPSHOT=-DEV
|
||||
#EPICS_DEV_SNAPSHOT=-pre1
|
||||
#EPICS_DEV_SNAPSHOT=-pre1-DEV
|
||||
#EPICS_DEV_SNAPSHOT=-pre2
|
||||
@@ -45,7 +56,7 @@ EPICS_PATCH_LEVEL = 0
|
||||
#EPICS_DEV_SNAPSHOT=-rc1-DEV
|
||||
#EPICS_DEV_SNAPSHOT=-rc2
|
||||
#EPICS_DEV_SNAPSHOT=-rc2-DEV
|
||||
EPICS_DEV_SNAPSHOT=
|
||||
#EPICS_DEV_SNAPSHOT=
|
||||
|
||||
# No changes should be needed below here
|
||||
|
||||
|
||||
@@ -40,10 +40,6 @@ BUILD_ARCHS = $(EPICS_HOST_ARCH) $(CROSS1) $(CROSS2)
|
||||
# otherwise override this in os/CONFIG_SITE.<host_arch>.Common
|
||||
PERL = perl -CSD
|
||||
|
||||
#-------------------------------------------------------
|
||||
# dbst based database optimization default
|
||||
DB_OPT = NO
|
||||
|
||||
#-------------------------------------------------------
|
||||
# Check configure/RELEASE file for consistency
|
||||
CHECK_RELEASE_YES = checkRelease
|
||||
@@ -85,6 +81,7 @@ IOCS_APPL_TOP = $(shell $(FULLPATHNAME) $(INSTALL_LOCATION))
|
||||
# Make echo output - suppress echoing if make's '-s' flag is set
|
||||
NOP = :
|
||||
ECHO = @$(if $(findstring s,$(patsubst T_A=%,,$(MAKEFLAGS))),$(NOP),echo)
|
||||
QUIET_FLAG := $(if $(findstring s,$(MAKEFLAGS)),-q,)
|
||||
|
||||
#-------------------------------------------------------
|
||||
ifdef T_A
|
||||
@@ -334,6 +331,14 @@ COMPILE.cpp = $(CCC) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES)
|
||||
# C preprocessor command
|
||||
PREPROCESS.cpp = $(CPP) $(CPPFLAGS) $(INCLUDES) $< > $@
|
||||
|
||||
#--------------------------------------------------
|
||||
# genVersion header defaults
|
||||
|
||||
# C macro name
|
||||
GENVERSIONMACRO = VCSVERSION
|
||||
# C macro default value (empty to use date+time)
|
||||
GENVERSIONDEFAULT =
|
||||
|
||||
#--------------------------------------------------
|
||||
# Header dependency file generation
|
||||
|
||||
|
||||
@@ -57,8 +57,11 @@ EPICS_TS_NTP_INET=
|
||||
# Prompt string
|
||||
# IOCSH_HISTSIZE
|
||||
# Number of lines of command history to keep.
|
||||
# IOCSH_HISTEDIT_DISABLE
|
||||
# Prevents use of readline or equivalent if defined.
|
||||
IOCSH_PS1="epics> "
|
||||
IOCSH_HISTSIZE=50
|
||||
IOCSH_HISTEDIT_DISABLE=
|
||||
|
||||
# Log Server:
|
||||
# EPICS_IOC_LOG_INET
|
||||
|
||||
@@ -8,10 +8,12 @@
|
||||
#*************************************************************************
|
||||
#RULES.Db
|
||||
|
||||
# Set db substitutions file suffix
|
||||
# Set db substitutions and template file suffixes
|
||||
SUBST_SUFFIX ?= .substitutions
|
||||
TEMPL_SUFFIX ?= .template
|
||||
|
||||
##################################################### vpath
|
||||
#---------------------------------------------------------------
|
||||
# vpath
|
||||
|
||||
vpath %.pm $(USR_VPATH) $(SRC_DIRS) $(dir $(DBD))
|
||||
vpath %.pod $(USR_VPATH) $(SRC_DIRS) $(dir $(DBD))
|
||||
@@ -19,12 +21,13 @@ vpath %.dbd $(USR_VPATH) $(SRC_DIRS) $(dir $(DBD))
|
||||
vpath %.db $(USR_VPATH) $(SRC_DIRS) $(dir $(DB))
|
||||
vpath %.vdb $(USR_VPATH) $(SRC_DIRS) $(dir $(DB))
|
||||
vpath %$(SUBST_SUFFIX) $(USR_VPATH) $(SRC_DIRS) $(COMMON_DIR)
|
||||
vpath %.template $(USR_VPATH) $(SRC_DIRS) $(COMMON_DIR)
|
||||
vpath %$(TEMPL_SUFFIX) $(USR_VPATH) $(SRC_DIRS) $(COMMON_DIR)
|
||||
vpath bpt%.data $(USR_VPATH) $(SRC_DIRS) $(COMMON_DIR)
|
||||
vpath %.acf $(USR_VPATH) $(SRC_DIRS) $(COMMON_DIR)
|
||||
vpath %.acs $(USR_VPATH) $(SRC_DIRS) $(COMMON_DIR)
|
||||
|
||||
##################################################### dbflags dbdflags
|
||||
#---------------------------------------------------------------
|
||||
# dbflags dbdflags
|
||||
|
||||
DBD_SEARCH_DIRS = . .. $(COMMON_DIR) $(SRC_DIRS) $(INSTALL_DBD) $(RELEASE_DBD_DIRS)
|
||||
DB_SEARCH_DIRS = . .. $(COMMON_DIR) $(SRC_DIRS) $(INSTALL_DB) $(RELEASE_DB_DIRS)
|
||||
@@ -33,13 +36,15 @@ DBDFLAGS = $(USR_DBDFLAGS) $(CMD_DBDFLAGS) $(addprefix -I,$(DBD_SEARCH_DIRS))
|
||||
DBFLAGS = $($*_DBFLAGS) $(USR_DBFLAGS) $(CMD_DBFLAGS) $(addprefix -I,$(DB_SEARCH_DIRS))
|
||||
REGRDDFLAGS = $(DBDFLAGS) $($*_REGRDDFLAGS) $(USR_REGRDDFLAGS) $(CMD_REGRDDFLAGS)
|
||||
|
||||
##################################################### Targets
|
||||
#---------------------------------------------------------------
|
||||
# Targets
|
||||
|
||||
# ---------------------------------------------------
|
||||
# To allow os specific dbd files AND have the -j option work properly,
|
||||
|
||||
CROSS_TARGET_OS_TYPES = $(sort $(foreach target, \
|
||||
$(EPICS_HOST_ARCH) $(CROSS_COMPILER_TARGET_ARCHS),$(firstword $(subst -, ,$(target)))))
|
||||
CROSS_TARGET_OS_TYPES = $(sort $(foreach target, \
|
||||
$(EPICS_HOST_ARCH) $(CROSS_COMPILER_TARGET_ARCHS), \
|
||||
$(firstword $(subst -, ,$(target)))))
|
||||
DBD += $(foreach type, $(CROSS_TARGET_OS_TYPES), $(DBD_$(type)))
|
||||
|
||||
# Users add os specific dbd files to a Makefile as follows
|
||||
@@ -86,31 +91,28 @@ SOURCE_DB_bbb = $(foreach dir, $(GENERIC_SRC_DIRS), $(SOURCE_DB_aaa) )
|
||||
SOURCE_DB_aaa = $(addsuffix /$(file), $(dir) )
|
||||
|
||||
COMMONS = $(COMMON_DIR)/*.dbd $(COMMON_DIR)/*.db $(COMMON_DIR)/*.h \
|
||||
$(COMMON_DIR)/*$(SUBST_SUFFIX) $(COMMON_DIR)/*.template
|
||||
$(COMMON_DIR)/*$(SUBST_SUFFIX) $(COMMON_DIR)/*$(TEMPL_SUFFIX)
|
||||
|
||||
# Remove trailing numbers (to 99) on stem
|
||||
TEMPLATE1=$(patsubst %0,%,$(patsubst %1,%,$(patsubst %2,%,$(patsubst %3,%,$(patsubst %4,%, \
|
||||
$(patsubst %5,%,$(patsubst %6,%,$(patsubst %7,%,$(patsubst %8,%,$(patsubst %9,%, \
|
||||
$*))))))))))
|
||||
TEMPLATE2=$(patsubst %0,%,$(patsubst %1,%,$(patsubst %2,%,$(patsubst %3,%,$(patsubst %4,%, \
|
||||
$(patsubst %5,%,$(patsubst %6,%,$(patsubst %7,%,$(patsubst %8,%,$(patsubst %9,%, \
|
||||
$(TEMPLATE1)))))))))))
|
||||
TEMPLATE3=$(addsuffix .template,$(addprefix ../,$(TEMPLATE2)))
|
||||
TEMPLATE_FILENAME=$(firstword $(wildcard $($*_TEMPLATE) $(addprefix ../,$($*_TEMPLATE)) ../$*.template $(TEMPLATE3) ../template))
|
||||
|
||||
# dbst based database optimization
|
||||
ifeq '$(DB_OPT)' 'YES'
|
||||
RAW=.raw
|
||||
DBS = $(filter %.db,$(DB)) $(addsuffix $(RAW),$(filter %.db,$(DB)))
|
||||
COMMON_DBS = $(addprefix $(COMMON_DIR)/,$(DBS))
|
||||
endif
|
||||
TEMPLATE1 = $(patsubst %0,%,$(patsubst %1,%,$(patsubst %2,%,$(patsubst %3,%, \
|
||||
$(patsubst %4,%,$(patsubst %5,%,$(patsubst %6,%,$(patsubst %7,%, \
|
||||
$(patsubst %8,%,$(patsubst %9,%,$*))))))))))
|
||||
TEMPLATE2 = $(patsubst %0,%,$(patsubst %1,%,$(patsubst %2,%,$(patsubst %3,%, \
|
||||
$(patsubst %4,%,$(patsubst %5,%,$(patsubst %6,%,$(patsubst %7,%, \
|
||||
$(patsubst %8,%,$(patsubst %9,%,$(TEMPLATE1)))))))))))
|
||||
TEMPLATE3 = $(addsuffix $(TEMPL_SUFFIX),$(addprefix ../,$(TEMPLATE2)))
|
||||
TEMPLATE_FILENAME = $(firstword $(wildcard $($*_TEMPLATE) \
|
||||
$(addprefix ../,$($*_TEMPLATE)) ../$*$(TEMPL_SUFFIX) $(TEMPLATE3) \
|
||||
../template))
|
||||
|
||||
INSTALL_DB_INSTALLS = $(addprefix $(INSTALL_DB)/,$(notdir $(DB_INSTALLS)))
|
||||
INSTALL_DBD_INSTALLS = $(addprefix $(INSTALL_DBD)/,$(notdir $(DBD_INSTALLS)))
|
||||
|
||||
COMMONDEP_TARGET = $(COMMON_DIR)/$(basename $@)
|
||||
|
||||
##################################################### acf files
|
||||
#---------------------------------------------------------------
|
||||
# acf files
|
||||
|
||||
# An access security configuration file, *.acf, can be created from
|
||||
# an *.acs file (has format of acf file plus #include "filename" lines)
|
||||
|
||||
@@ -123,7 +125,8 @@ ACF_INCLUDES = -I. $(TARGET_INCLUDES) $(USR_INCLUDES)\
|
||||
ACFDEPENDS_CMD = $(MKMF) -m $@ $(ACF_INCLUDES) $(COMMONDEP_TARGET) $<
|
||||
ACF_CMD = $(CPP) $(ACF_CPPFLAGS) $(ACF_INCLUDES) $< > $@
|
||||
|
||||
##################################################### dependancies
|
||||
#---------------------------------------------------------------
|
||||
# dependencies
|
||||
|
||||
HINC += $(addsuffix .h,$(DBDINC_NAME))
|
||||
COMMON_DBDINC += $(addprefix $(COMMON_DIR)/,$(HINC))
|
||||
@@ -133,12 +136,12 @@ DBDDEPENDS_FILES += $(addsuffix $(DEP),$(HINC) \
|
||||
$(patsubst $(COMMON_DIR)/%,%, \
|
||||
$(filter-out $(COMMON_DIR)/bpt%.dbd,$(COMMON_DBDS))))
|
||||
|
||||
#####################################################
|
||||
#---------------------------------------------------------------
|
||||
|
||||
ifndef T_A
|
||||
|
||||
DEP = .d
|
||||
TEMPLATE3+=$(addsuffix .template, $(TEMPLATE2))
|
||||
TEMPLATE3 += $(addsuffix $(TEMPL_SUFFIX), $(TEMPLATE2))
|
||||
|
||||
COMMON_DIR = .
|
||||
INSTALL_DBDS =
|
||||
@@ -153,18 +156,13 @@ ACTIONS += install
|
||||
ACTIONS += buildInstall
|
||||
ACTIONS += runtests tapfiles
|
||||
|
||||
actionArchTargets = $(foreach x, $(ACTIONS),\ $(foreach arch,$(BUILD_ARCHS), $(x)$(DIVIDER)$(arch)))
|
||||
actionArchTargets = $(foreach action, $(ACTIONS), \
|
||||
$(foreach arch, $(BUILD_ARCHS), $(action)$(DIVIDER)$(arch)))
|
||||
cleanArchTargets = $(foreach arch, $(BUILD_ARCHS), clean$(DIVIDER)$(arch))
|
||||
|
||||
cleanArchTargets = $(foreach arch,$(BUILD_ARCHS), clean$(DIVIDER)$(arch))
|
||||
-include $(TOP)/configure/CONFIG_APP_INCLUDE
|
||||
|
||||
all: install
|
||||
ifeq ($(EPICS_HOST_ARCH),$T_A)
|
||||
host: install
|
||||
else
|
||||
# Do nothing
|
||||
host:
|
||||
endif
|
||||
|
||||
install: buildInstall
|
||||
|
||||
@@ -172,20 +170,30 @@ buildInstall : build
|
||||
|
||||
rebuild: clean install
|
||||
|
||||
.PHONY: all host $(ACTIONS)
|
||||
.PHONY: all $(ACTIONS)
|
||||
|
||||
$(actionArchTargets) $(BUILD_ARCHS): install
|
||||
$(cleanArchTargets): clean
|
||||
|
||||
.PHONY: $(BUILD_ARCHS) $(actionArchTargets) $(cleanArchTargets)
|
||||
|
||||
endif # T_A defined
|
||||
else
|
||||
# T_A is defined
|
||||
ifeq ($(EPICS_HOST_ARCH),$(T_A))
|
||||
host: install
|
||||
else
|
||||
host:
|
||||
endif
|
||||
|
||||
.PHONY: host
|
||||
endif # T_A
|
||||
|
||||
ifneq (,$(strip $(DBDDEPENDS_FILES)))
|
||||
-include $(DBDDEPENDS_FILES)
|
||||
endif
|
||||
|
||||
##################################################### build dependancies, clean rule
|
||||
#---------------------------------------------------------------
|
||||
# build dependancies, clean rule
|
||||
|
||||
inc : $(COMMON_INC) $(INSTALL_INC)
|
||||
|
||||
@@ -205,7 +213,8 @@ db_clean :
|
||||
|
||||
realclean: clean
|
||||
|
||||
##################################################### Dependency files
|
||||
#---------------------------------------------------------------
|
||||
# Dependency files
|
||||
|
||||
%Record.h$(DEP): $(COMMON_DIR)/%Record.dbd
|
||||
@$(RM) $@
|
||||
@@ -256,19 +265,19 @@ menu%.h$(DEP): ../menu%.dbd
|
||||
@$(DBEXPAND) -D $(DBDFLAGS) -o $(COMMONDEP_TARGET) $($*_DBD) > $@
|
||||
@echo "$(COMMONDEP_TARGET): ../Makefile" >> $@
|
||||
|
||||
%.db$(RAW)$(DEP): %$(SUBST_SUFFIX)
|
||||
%.db$(DEP): %$(SUBST_SUFFIX)
|
||||
@$(RM) $@
|
||||
$(MSI3_15) -D $(DBFLAGS) -o $(COMMONDEP_TARGET) -S$< $(TEMPLATE_FILENAME) > $@
|
||||
|
||||
%.db$(RAW)$(DEP): ../%$(SUBST_SUFFIX)
|
||||
%.db$(DEP): ../%$(SUBST_SUFFIX)
|
||||
@$(RM) $@
|
||||
$(MSI3_15) -D $(DBFLAGS) -o $(COMMONDEP_TARGET) -S$< $(TEMPLATE_FILENAME) > $@
|
||||
|
||||
%.db$(RAW)$(DEP): %.template
|
||||
%.db$(DEP): %$(TEMPL_SUFFIX)
|
||||
@$(RM) $@
|
||||
$(MSI3_15) -D $(DBFLAGS) -o $(COMMONDEP_TARGET) $< > $@
|
||||
|
||||
%.db$(RAW)$(DEP): ../%.template
|
||||
%.db$(DEP): ../%$(TEMPL_SUFFIX)
|
||||
@$(RM) $@
|
||||
$(MSI3_15) -D $(DBFLAGS) -o $(COMMONDEP_TARGET) $< > $@
|
||||
|
||||
@@ -282,14 +291,8 @@ menu%.h$(DEP): ../menu%.dbd
|
||||
|
||||
.PRECIOUS: %$(DEP)
|
||||
|
||||
##################################################### CapFast filter
|
||||
|
||||
$(COMMON_DIR)/%.edf: ../%.sch $(DEPSCHS)
|
||||
@$(RM) $@
|
||||
@if [ ! -f cad.rc -a -r ../cad.rc ] ; then ln -s ../cad.rc ; fi
|
||||
$(SCH2EDIF) $(SCH2EDIF_SYSFLAGS) $(SCH2EDIF_FLAGS) -o $@ $<
|
||||
|
||||
##################################################### Substitution files
|
||||
#---------------------------------------------------------------
|
||||
# Substitution files
|
||||
|
||||
# WARNING: CREATESUBSTITUTIONS script needs output dir on command line
|
||||
|
||||
@@ -310,25 +313,21 @@ $(INSTALL_DB)/%$(SUBST_SUFFIX): ../%$(SUBST_SUFFIX)
|
||||
|
||||
.PRECIOUS: $(COMMON_DIR)/%$(SUBST_SUFFIX)
|
||||
|
||||
##################################################### Template files
|
||||
#---------------------------------------------------------------
|
||||
# Template files
|
||||
|
||||
$(COMMON_DIR)/%.template: $(COMMON_DIR)/%.edf
|
||||
@$(RM) $@
|
||||
$(E2DB) $(E2DB_SYSFLAGS) $(E2DB_FLAGS) -n $@.VAR $<
|
||||
@$(REPLACEVAR) < $@.VAR > $@
|
||||
@$(RM) $@.VAR
|
||||
|
||||
$(INSTALL_DB)/%.template: %.template
|
||||
$(INSTALL_DB)/%$(TEMPL_SUFFIX): %$(TEMPL_SUFFIX)
|
||||
$(ECHO) "Installing template file $@"
|
||||
@$(INSTALL) -d -m $(INSTALL_PERMISSIONS) $< $(@D)
|
||||
|
||||
$(INSTALL_DB)/%.template: ../%.template
|
||||
$(INSTALL_DB)/%$(TEMPL_SUFFIX): ../%$(TEMPL_SUFFIX)
|
||||
$(ECHO) "Installing template file $@"
|
||||
@$(INSTALL) -d -m $(INSTALL_PERMISSIONS) $< $(@D)
|
||||
|
||||
.PRECIOUS: $(COMMON_DIR)/%.template
|
||||
.PRECIOUS: $(COMMON_DIR)/%$(TEMPL_SUFFIX)
|
||||
|
||||
##################################################### INC files
|
||||
#---------------------------------------------------------------
|
||||
# INC files
|
||||
|
||||
$(COMMON_DIR)/%Record.h: $(COMMON_DIR)/%Record.dbd
|
||||
@$(RM) $(notdir $@)
|
||||
@@ -362,7 +361,8 @@ $(COMMON_DIR)/menu%.h: ../menu%.dbd
|
||||
|
||||
.PRECIOUS: $(COMMON_DIR)/%.h
|
||||
|
||||
##################################################### DBD files
|
||||
#---------------------------------------------------------------
|
||||
# DBD files
|
||||
|
||||
$(COMMON_DIR)/bpt%.dbd: bpt%.data
|
||||
@$(RM) $(notdir $@)
|
||||
@@ -425,7 +425,8 @@ $(foreach file, $(DBD_INSTALLS), $(eval $(call DBD_INSTALLS_template, $(file))))
|
||||
|
||||
.PRECIOUS: $(COMMON_DBDS) $(COMMON_DIR)/%.dbd
|
||||
|
||||
##################################################### HTML files
|
||||
#---------------------------------------------------------------
|
||||
# HTML files
|
||||
|
||||
$(COMMON_DIR)/%.html: %.dbd.pod $(TOOLS)/dbdToHtml.pl
|
||||
@$(RM) $(notdir $@)
|
||||
@@ -444,37 +445,44 @@ $(COMMON_DIR)/%.html: %.pm $(TOOLS)/podToHtml.pl
|
||||
|
||||
$(COMMON_DIR)/%.html: ../%.pm $(TOOLS)/podToHtml.pl
|
||||
@$(RM) $(notdir $@)
|
||||
$(PERL) $(TOOLS)/podToHtml.pl -o $(notdir $@) $<
|
||||
$(PERL) $(TOOLS)/podToHtml.pl -s -o $(notdir $@) $<
|
||||
@$(MKDIR) $(dir $@)
|
||||
@$(MV) $(notdir $@) $@
|
||||
|
||||
$(COMMON_DIR)/%.html: ../%.pl $(TOOLS)/podToHtml.pl
|
||||
@$(RM) $(notdir $@)
|
||||
$(PERL) $(TOOLS)/podToHtml.pl -s -o $(notdir $@) $<
|
||||
@$(MV) $(notdir $@) $@
|
||||
|
||||
.PRECIOUS: $(COMMON_DIR)/%.html %.html
|
||||
|
||||
##################################################### DB files
|
||||
#---------------------------------------------------------------
|
||||
# DB files
|
||||
|
||||
$(COMMON_DIR)/%.db$(RAW): $(COMMON_DIR)/%.edf
|
||||
$(COMMON_DIR)/%.db: $(COMMON_DIR)/%.edf
|
||||
$(E2DB) $(E2DB_SYSFLAGS) $(E2DB_FLAGS) -n $*.VAR $<
|
||||
@$(REPLACEVAR) < $*.VAR > $@
|
||||
@$(RM) $*.VAR
|
||||
|
||||
$(COMMON_DIR)/%.db$(RAW): %$(SUBST_SUFFIX)
|
||||
$(COMMON_DIR)/%.db: %$(SUBST_SUFFIX)
|
||||
$(ECHO) "Inflating database from $< $(TEMPLATE_FILENAME)"
|
||||
@$(RM) $(notdir $@)
|
||||
$(MSI3_15) $(DBFLAGS) -o $(notdir $@) -S$< $(TEMPLATE_FILENAME)
|
||||
@$(MV) $(notdir $@) $@
|
||||
|
||||
$(COMMON_DIR)/%.db$(RAW): ../%$(SUBST_SUFFIX)
|
||||
$(COMMON_DIR)/%.db: ../%$(SUBST_SUFFIX)
|
||||
$(ECHO) "Inflating database from $< $(TEMPLATE_FILENAME)"
|
||||
@$(RM) $(notdir $@)
|
||||
$(MSI3_15) $(DBFLAGS) -o $(notdir $@) -S$< $(TEMPLATE_FILENAME)
|
||||
@$(MV) $(notdir $@) $@
|
||||
|
||||
$(COMMON_DIR)/%.db$(RAW): %.template
|
||||
$(COMMON_DIR)/%.db: %$(TEMPL_SUFFIX)
|
||||
$(ECHO) "Inflating database from $<"
|
||||
@$(RM) $(notdir $@)
|
||||
$(MSI3_15) $(DBFLAGS) -o $(notdir $@) $<
|
||||
@$(MV) $(notdir $@) $@
|
||||
|
||||
$(COMMON_DIR)/%.db$(RAW): ../%.template
|
||||
$(COMMON_DIR)/%.db: ../%$(TEMPL_SUFFIX)
|
||||
$(ECHO) "Inflating database from $<"
|
||||
@$(RM) $(notdir $@)
|
||||
$(MSI3_15) $(DBFLAGS) -o $(notdir $@) $<
|
||||
@@ -492,22 +500,6 @@ $(COMMON_DIR)/%.acf: ../%.acs
|
||||
|
||||
.PRECIOUS: $(COMMON_DIR)/%.acf
|
||||
|
||||
# dbst based database optimization
|
||||
ifeq '$(DB_OPT)' 'YES'
|
||||
|
||||
$(COMMON_DIR)/%.db$(RAW): ../%.db
|
||||
@$(RM) $@
|
||||
$(CP) $< $@
|
||||
|
||||
$(COMMON_DIR)/%.db: $(COMMON_DIR)/%.db$(RAW)
|
||||
$(ECHO) "Optimizing database $@"
|
||||
@$(RM) $@
|
||||
$(DBST) . $< -d > $@
|
||||
|
||||
.PRECIOUS: $(COMMON_DIR)/%.db
|
||||
.PRECIOUS: $(DB:%=$(COMMON_DIR)/%$(RAW))
|
||||
else
|
||||
|
||||
$(INSTALL_DB)/%: %
|
||||
$(ECHO) "Installing $@"
|
||||
@$(INSTALL) -d -m $(INSTALL_PERMISSIONS) $< $(@D)
|
||||
@@ -515,7 +507,6 @@ $(INSTALL_DB)/%: %
|
||||
$(INSTALL_DB)/%: ../%
|
||||
$(ECHO) "Installing $@"
|
||||
@$(INSTALL) -d -m $(INSTALL_PERMISSIONS) $< $(@D)
|
||||
endif
|
||||
|
||||
$(INSTALL_DB)/%.db: $(COMMON_DIR)/%.db
|
||||
$(ECHO) "Installing created db file $@"
|
||||
@@ -531,8 +522,8 @@ $(foreach file, $(DB_INSTALLS), $(eval $(call DB_INSTALLS_template, $(file))))
|
||||
.PRECIOUS: $(COMMON_DIR)/%.edf
|
||||
.PRECIOUS: $(COMMON_DBS)
|
||||
|
||||
##################################################### register record,device,driver support
|
||||
|
||||
#---------------------------------------------------------------
|
||||
# register record,device,driver support
|
||||
|
||||
%_registerRecordDeviceDriver.cpp: $(COMMON_DIR)/%.dbd
|
||||
@$(RM) $@
|
||||
@@ -548,4 +539,3 @@ $(foreach file, $(DB_INSTALLS), $(eval $(call DB_INSTALLS_template, $(file))))
|
||||
|
||||
.PRECIOUS: %_registerRecordDeviceDriver.cpp
|
||||
|
||||
##################################################### END OF FILE
|
||||
|
||||
@@ -81,6 +81,8 @@ else
|
||||
host:
|
||||
endif
|
||||
|
||||
-include $(CONFIG)/RULES_FILE_TYPE
|
||||
|
||||
-include $(CONFIG)/RULES.Db
|
||||
|
||||
#---------------------------------------------------------------
|
||||
@@ -122,8 +124,6 @@ LIBTARGETS += $(LIBNAME) $(INSTALL_LIBS) $(TESTLIBNAME) \
|
||||
$(LOADABLE_SHRLIBNAME) $(INSTALL_LOADABLE_SHRLIBS)
|
||||
|
||||
|
||||
-include $(CONFIG)/RULES_FILE_TYPE
|
||||
|
||||
# Main targets
|
||||
|
||||
install: buildInstall
|
||||
@@ -168,7 +168,7 @@ ifdef RES
|
||||
endif
|
||||
|
||||
$(DIRECTORY_TARGETS) :
|
||||
$(MKDIR) -p $@
|
||||
$(MKDIR) $@
|
||||
|
||||
$(PRODNAME): $(INSTALL_LIB_INSTALLS)
|
||||
|
||||
@@ -346,6 +346,14 @@ tapfiles: $(TESTSCRIPTS) $(TAPFILES)
|
||||
@$(RM) $@
|
||||
$(PERL) $(TOOLS)/makeTestfile.pl $@ $<
|
||||
|
||||
#---------------------------------------------------------------
|
||||
# Generate header with version number from VCS
|
||||
|
||||
ifneq ($(GENVERSION),)
|
||||
$(COMMON_DIR)/$(GENVERSION): FORCE
|
||||
$(GENVERSIONHEADER) -t $(TOP) -N $(GENVERSIONMACRO) -V "$(GENVERSIONDEFAULT)" $@
|
||||
endif
|
||||
|
||||
#---------------------------------------------------------------
|
||||
# Install rules for BIN_INSTALLS and LIB_INSTALLS
|
||||
|
||||
@@ -479,7 +487,7 @@ $(INSTALL_TEMPLATES_SUBDIR)/%: %
|
||||
.PRECIOUS: $(COMMON_INC)
|
||||
|
||||
.PHONY: all host inc build install clean rebuild buildInstall build_clean
|
||||
.PHONY: runtests tapfiles checkRelease warnRelease noCheckRelease
|
||||
.PHONY: runtests tapfiles checkRelease warnRelease noCheckRelease FORCE
|
||||
|
||||
endif # BASE_RULES_BUILD
|
||||
# EOF RULES_BUILD
|
||||
|
||||
@@ -2,6 +2,9 @@
|
||||
|
||||
vpath %@ $(USR_VPATH) $(ALL_SRC_DIRS)
|
||||
|
||||
#---------------------------------------------------------------
|
||||
# Variable expansion
|
||||
|
||||
# Default settings
|
||||
EXPAND_TOOL ?= $(PERL) $(TOOLS)/expandVars.pl
|
||||
|
||||
@@ -24,3 +27,39 @@ expand_clean:
|
||||
@$(RM) $(EXPANDED)
|
||||
|
||||
.PHONY : expand_clean
|
||||
|
||||
#---------------------------------------------------------------
|
||||
# Assemblies (files assembled from snippets)
|
||||
|
||||
ASSEMBLE_TOOL ?= $(PERL) $(TOOLS)/assembleSnippets.pl
|
||||
|
||||
define COMMON_ASSEMBLY_template
|
||||
$1_SNIPPETS += $$(foreach dir, .. $$(SRC_DIRS), \
|
||||
$$(wildcard $$(dir)/$$($1_PATTERN)))
|
||||
$(COMMON_DIR)/$1: $$($1_SNIPPETS)
|
||||
$(ECHO) "Assembling common file $$@ from snippets"
|
||||
@$(RM) $1
|
||||
$(ASSEMBLE_TOOL) -o $1 $$^
|
||||
@$(MV) $1 $$@
|
||||
endef
|
||||
$(foreach asy, $(COMMON_ASSEMBLIES), \
|
||||
$(eval $(call COMMON_ASSEMBLY_template,$(strip $(asy)))))
|
||||
|
||||
define ASSEMBLY_template
|
||||
$1_SNIPPETS += $$(foreach dir, .. $$(SRC_DIRS), \
|
||||
$$(wildcard $$(dir)/$$($1_PATTERN)))
|
||||
$1: $$($1_SNIPPETS)
|
||||
$(ECHO) "Assembling file $$@ from snippets"
|
||||
@$(RM) $$@
|
||||
$(ASSEMBLE_TOOL) -o $$@ $$^
|
||||
endef
|
||||
$(foreach asy, $(ASSEMBLIES), \
|
||||
$(eval $(call ASSEMBLY_template,$(strip $(asy)))))
|
||||
|
||||
define ASSEMBLY_DEP_template
|
||||
$1$(DEP):
|
||||
@echo $1: > $$@
|
||||
endef
|
||||
$(foreach asy, $(sort $(COMMON_ASSEMBLIES) $(ASSEMBLIES)), \
|
||||
$(eval $(call ASSEMBLY_DEP_template,$(strip $(asy)))))
|
||||
|
||||
|
||||
@@ -26,14 +26,14 @@ POSIX_LDLIBS += -lpthread
|
||||
ARCH_DEP_CFLAGS += -m32
|
||||
ARCH_DEP_LDFLAGS += -m32
|
||||
|
||||
# Compiler defines _X86_ 1
|
||||
# Compiler defines __MSVCRT__ 1
|
||||
# 32-bit compiler defines _X86_ 1
|
||||
# Compiler defines __CYGWIN__ 1
|
||||
# Compiler defines __CYGWIN32__ 1
|
||||
# 32-bit compiler defines __CYGWIN32__ 1
|
||||
# Compiler defines __unix__ 1
|
||||
# Compiler defines __unix 1
|
||||
# Compiler defines unix 1
|
||||
|
||||
# This macro now deprecated, use __CYGWIN__ in the future
|
||||
OP_SYS_CPPFLAGS += -DCYGWIN32
|
||||
|
||||
EXE = .exe
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
# CONFIG.Common.vxWorksCommon
|
||||
#
|
||||
# $Revision-Id$
|
||||
# This file is maintained by the build community.
|
||||
#
|
||||
# Definitions for vxWorks target archs
|
||||
# Sites may override these definitions in CONFIG_SITE.Common.vxWorksCommon
|
||||
# Override these definitions in CONFIG_SITE.Common.vxWorksCommon
|
||||
# or CONFIG_SITE.<host>.vxWorksCommon
|
||||
#-------------------------------------------------------
|
||||
|
||||
@@ -66,8 +65,6 @@ VXWORKS_MAJOR_VERSION = $(basename $(basename $(VXWORKS_VERSION)))
|
||||
|
||||
# These are needed for vxWorks 6.x; the GNU toolset version number
|
||||
# is in the path to the compiler tools:
|
||||
VX_GNU_VERSION_5.4 = 2.95
|
||||
VX_GNU_VERSION_5.5 = 2.96
|
||||
VX_GNU_VERSION_6.0 = 3.3.2
|
||||
VX_GNU_VERSION_6.1 = 3.3.2
|
||||
VX_GNU_VERSION_6.2 = 3.3.2
|
||||
@@ -83,30 +80,21 @@ VX_GNU_VERSION = $(VX_GNU_VERSION_$(VXWORKS_VERSION))
|
||||
VX_GNU_MAJOR_VERSION = $(basename $(basename $(VX_GNU_VERSION)))
|
||||
|
||||
#--------------------------------------------------
|
||||
# Fix WIND_BASE for vxWorks 6.x on linux
|
||||
# NB: We know the value of WIND_HOST_TYPE here, but not VXWORKS_VERSION
|
||||
# Fix old Linux WIND_HOST_TYPE
|
||||
ifeq ($(WIND_HOST_TYPE),x86-linux)
|
||||
WIND_HOST_TYPE_5 = x86-linux
|
||||
WIND_HOST_TYPE_6 = x86-linux2
|
||||
WIND_HOST_TYPE = $(WIND_HOST_TYPE_$(VXWORKS_MAJOR_VERSION))
|
||||
WIND_HOST_TYPE = x86-linux2
|
||||
endif
|
||||
|
||||
#--------------------------------------------------
|
||||
# vxWorks directory definitions
|
||||
VX_DIR_5 = $(WIND_BASE)
|
||||
VX_DIR_6 = $(WIND_BASE)/vxworks-$(VXWORKS_VERSION)
|
||||
VX_DIR = $(VX_DIR_$(VXWORKS_MAJOR_VERSION))
|
||||
VX_DIR = $(WIND_BASE)/vxworks-$(VXWORKS_VERSION)
|
||||
|
||||
VX_INCLUDE_DIRS_5 = $(VX_DIR)/target/h
|
||||
VX_INCLUDE_DIRS_6 = $(VX_DIR)/target/h $(VX_DIR)/target/h/wrn/coreip
|
||||
GNU_TARGET_INCLUDE_DIR = $(VX_INCLUDE_DIRS_$(VXWORKS_MAJOR_VERSION))
|
||||
GNU_TARGET_INCLUDE_DIR = $(VX_DIR)/target/h $(VX_DIR)/target/h/wrn/coreip
|
||||
|
||||
#--------------------------------------------------
|
||||
# vxWorks GNU directories
|
||||
|
||||
GNU_DIR_5 = $(WIND_BASE)/host/$(WIND_HOST_TYPE)
|
||||
GNU_DIR_6 = $(WIND_BASE)/gnu/$(VX_GNU_VERSION)-vxworks-$(VXWORKS_VERSION)/$(WIND_HOST_TYPE)
|
||||
GNU_DIR = $(GNU_DIR_$(VXWORKS_MAJOR_VERSION))
|
||||
GNU_DIR = $(WIND_BASE)/gnu/$(VX_GNU_VERSION)-vxworks-$(VXWORKS_VERSION)/$(WIND_HOST_TYPE)
|
||||
|
||||
#--------------------------------------------------
|
||||
# Wind River moved nm out of GNU_BIN in some versions
|
||||
@@ -126,9 +114,7 @@ NM = $(NM_DIR)/$(CMPLR_PREFIX)nm$(CMPLR_SUFFIX)$(HOSTEXE)
|
||||
|
||||
#--------------------------------------------------
|
||||
# A linker script is essential for munching from vxWorks 6.6 onwards
|
||||
# (i.e. with versions that use gcc 4.1.2 or later). It can be used
|
||||
# with any vxWorks 5 or 6 version, but apparently should not be used
|
||||
# when compiling for 68K (which isn't supported in vxWorks 6 anyway)
|
||||
# (i.e. with versions that use gcc 4.1.2 or later).
|
||||
MUNCH_LDFLAGS_6 = -T $(VX_DIR)/target/h/tool/gnu/ldscripts/link.OUT
|
||||
MUNCH_LDFLAGS = $(MUNCH_LDFLAGS_$(VXWORKS_MAJOR_VERSION))
|
||||
|
||||
@@ -143,11 +129,10 @@ export TOOL_FAMILY = GNU
|
||||
OP_SYS_CPPFLAGS += -DvxWorks=vxWorks
|
||||
OP_SYS_CFLAGS += -fno-builtin
|
||||
|
||||
# Fix for vxWorks 5 headers that use macros defined in vxWorks.h but
|
||||
# Fix for vxWorks headers that use macros defined in vxWorks.h but
|
||||
# which don't actually include vxWorks.h themselves, for example the
|
||||
# target/h/sys/stat.h file which uses ULONG. This also stops dbDefs.h
|
||||
# from defining the OFFSET macro, which generates lots of warnings in
|
||||
# both vxWorks 5 and 6.
|
||||
# from defining the OFFSET macro, which generates lots of warnings.
|
||||
OP_SYS_INCLUDE_CPPFLAGS += -include $(VX_DIR)/target/h/vxWorks.h
|
||||
|
||||
#--------------------------------------------------
|
||||
@@ -160,7 +145,6 @@ OPT_CXXFLAGS_YES = -O2
|
||||
CODE_CFLAGS =
|
||||
#
|
||||
# For vxWorks versions before 6.3 we need this g++ compiler flag
|
||||
CODE_CXXFLAGS_5 = -fno-implicit-templates
|
||||
CODE_CXXFLAGS_6.0 = -fno-implicit-templates
|
||||
CODE_CXXFLAGS_6.1 = -fno-implicit-templates
|
||||
CODE_CXXFLAGS_6.2 = -fno-implicit-templates
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
CP = cp
|
||||
MV = mv
|
||||
RM = rm -f
|
||||
MKDIR = mkdir
|
||||
MKDIR = mkdir -p
|
||||
RMDIR = rm -rf
|
||||
CAT = cat
|
||||
|
||||
|
||||
33
configure/os/CONFIG.cygwin-x86.linux-arm
Normal file
33
configure/os/CONFIG.cygwin-x86.linux-arm
Normal file
@@ -0,0 +1,33 @@
|
||||
# CONFIG.cygwin-x86.linux-arm
|
||||
#
|
||||
# Definitions for cywgin-x86 host - linux-arm target builds
|
||||
# Override these settings in CONFIG_SITE.cygwin-x86.linux-arm
|
||||
#-------------------------------------------------------
|
||||
|
||||
VALID_BUILDS = Ioc
|
||||
GNU_TARGET = arm-linux
|
||||
|
||||
# prefix of compiler tools
|
||||
CMPLR_SUFFIX =
|
||||
CMPLR_PREFIX = $(addsuffix -,$(GNU_TARGET))
|
||||
|
||||
# Provide a link-time path for shared libraries
|
||||
SHRLIBDIR_RPATH_LDFLAGS_YES += $(SHRLIB_DEPLIB_DIRS:%=-Wl,-rpath-link,%)
|
||||
SHRLIBDIR_LDFLAGS += $(SHRLIBDIR_RPATH_LDFLAGS_$(LINKER_USE_RPATH))
|
||||
|
||||
# Provide a link-time path for products
|
||||
PRODDIR_RPATH_LDFLAGS_YES += $(PROD_DEPLIB_DIRS:%=-Wl,-rpath-link,%)
|
||||
PRODDIR_LDFLAGS += $(PRODDIR_RPATH_LDFLAGS_$(LINKER_USE_RPATH))
|
||||
|
||||
# Provide a link-time path for readline
|
||||
RUNTIME_LDFLAGS_READLINE_YES = -Wl,-rpath-link,$(GNU_DIR)/lib
|
||||
RUNTIME_LDFLAGS_READLINE = $(RUNTIME_LDFLAGS_READLINE_$(LINKER_USE_RPATH))
|
||||
RUNTIME_LDFLAGS_READLINE_CURSES = $(RUNTIME_LDFLAGS_READLINE_$(LINKER_USE_RPATH))
|
||||
RUNTIME_LDFLAGS_READLINE_NCURSES = $(RUNTIME_LDFLAGS_READLINE_$(LINKER_USE_RPATH))
|
||||
|
||||
# Library flags
|
||||
STATIC_LDFLAGS_YES= -Wl,-Bstatic
|
||||
STATIC_LDFLAGS_NO=
|
||||
STATIC_LDLIBS_YES= -Wl,-Bdynamic
|
||||
STATIC_LDLIBS_NO=
|
||||
|
||||
7
configure/os/CONFIG.cygwin-x86_64.linux-arm
Normal file
7
configure/os/CONFIG.cygwin-x86_64.linux-arm
Normal file
@@ -0,0 +1,7 @@
|
||||
# CONFIG.cygwin-x86_64.linux-arm
|
||||
#
|
||||
# Definitions for cygwin-x86_64 host - linux-arkm targets
|
||||
# Override these settings in CONFIG_SITE.cygwin-x86_64.linux-arm
|
||||
#-------------------------------------------------------
|
||||
|
||||
include $(CONFIG)/os/CONFIG.cygwin-x86.linux-arm
|
||||
@@ -10,4 +10,4 @@
|
||||
#Include definitions common to unix hosts
|
||||
include $(CONFIG)/os/CONFIG.UnixCommon.Common
|
||||
|
||||
WIND_HOST_TYPE = x86-linux
|
||||
WIND_HOST_TYPE = x86-linux2
|
||||
|
||||
@@ -10,4 +10,4 @@
|
||||
#Include definitions common to unix hosts
|
||||
include $(CONFIG)/os/CONFIG.UnixCommon.Common
|
||||
|
||||
WIND_HOST_TYPE = x86-linux
|
||||
WIND_HOST_TYPE = x86-linux2
|
||||
|
||||
@@ -10,4 +10,4 @@
|
||||
#Include definitions common to unix hosts
|
||||
include $(CONFIG)/os/CONFIG.UnixCommon.Common
|
||||
|
||||
WIND_HOST_TYPE = x86-linux
|
||||
WIND_HOST_TYPE = x86-linux2
|
||||
|
||||
@@ -10,4 +10,4 @@
|
||||
#Include definitions common to unix hosts
|
||||
include $(CONFIG)/os/CONFIG.UnixCommon.Common
|
||||
|
||||
WIND_HOST_TYPE = x86-linux
|
||||
WIND_HOST_TYPE = x86-linux2
|
||||
|
||||
@@ -10,4 +10,4 @@
|
||||
#Include definitions common to unix hosts
|
||||
include $(CONFIG)/os/CONFIG.UnixCommon.Common
|
||||
|
||||
WIND_HOST_TYPE = x86-linux
|
||||
WIND_HOST_TYPE = x86-linux2
|
||||
|
||||
28
configure/os/CONFIG.linux-x86.windows-x64-mingw
Normal file
28
configure/os/CONFIG.linux-x86.windows-x64-mingw
Normal file
@@ -0,0 +1,28 @@
|
||||
# CONFIG.linux-x86.windows-x64-mingw
|
||||
#
|
||||
# Definitions for linux-x86 host windows-x64-mingw target builds
|
||||
# Override these definitions in CONFIG_SITE.linux-x86.windows-x64-mingw
|
||||
#-------------------------------------------------------
|
||||
|
||||
# Include common gnu compiler definitions
|
||||
include $(CONFIG)/CONFIG.gnuCommon
|
||||
|
||||
# Add resource compiler
|
||||
RCCMD = $(GNU_BIN)/$(CMPLR_PREFIX)windres$(CMPLR_SUFFIX) $(INCLUDES) $< $@
|
||||
|
||||
# Remove -fPIC flags, add out-implib
|
||||
SHRLIB_CFLAGS =
|
||||
SHRLIB_LDFLAGS = -shared \
|
||||
-Wl,--out-implib,$(DLLSTUB_PREFIX)$*$(DLLSTUB_SUFFIX)
|
||||
LOADABLE_SHRLIB_LDFLAGS = -shared \
|
||||
-Wl,--out-implib,$(DLLSTUB_PREFIX)$*$(DLLSTUB_SUFFIX)
|
||||
|
||||
# No need to explicitly link with gcc library
|
||||
GNU_LDLIBS_YES =
|
||||
|
||||
# Link with winsock2
|
||||
OP_SYS_LDLIBS = -lws2_32
|
||||
|
||||
# Use static compiler-support libraries
|
||||
OP_SYS_LDFLAGS += -static-libgcc -static-libstdc++
|
||||
# There is no compiler flag for static libwinpthread
|
||||
@@ -10,4 +10,4 @@
|
||||
#Include definitions common to unix hosts
|
||||
include $(CONFIG)/os/CONFIG.UnixCommon.Common
|
||||
|
||||
WIND_HOST_TYPE = x86-linux
|
||||
WIND_HOST_TYPE = x86-linux2
|
||||
|
||||
9
configure/os/CONFIG.linux-x86_64.windows-x64-mingw
Normal file
9
configure/os/CONFIG.linux-x86_64.windows-x64-mingw
Normal file
@@ -0,0 +1,9 @@
|
||||
# CONFIG.linux-x86_64.windows-x64-mingw
|
||||
#
|
||||
# Definitions for linux-x86_64 host windows-x64-mingw target builds
|
||||
# Override these definitions in CONFIG_SITE.linux-x86_64.windows-x64-mingw
|
||||
#-------------------------------------------------------
|
||||
|
||||
# Settings as for the linux-x86 host architecture
|
||||
include $(CONFIG)/os/CONFIG.linux-x86.windows-x64-mingw
|
||||
|
||||
@@ -1,25 +1,17 @@
|
||||
# CONFIG_SITE.Common.vxWorksCommon
|
||||
#
|
||||
# Site specific definitions for vxWorks target builds.
|
||||
# Only the local epics system manager should modify this file
|
||||
|
||||
# Compiler options can vary with the vxWorks version number, so we
|
||||
# need to know that. However don't include any third-level digits
|
||||
# (e.g. the .2 in 5.5.2) because we don't need them.
|
||||
# need to know that. Do not include any third-level digits.
|
||||
|
||||
# Note: vxWorks 5.4.x (Tornado 2.0.x) is not supported
|
||||
# Note: vxWorks 5.4.x and 5.5.x (Tornado 2.x) are not supported.
|
||||
# VxWorks 6.0 through 6.5 use older, untested versions of GCC.
|
||||
|
||||
VXWORKS_VERSION = 5.5
|
||||
#VXWORKS_VERSION = 6.0
|
||||
#VXWORKS_VERSION = 6.1
|
||||
#VXWORKS_VERSION = 6.2
|
||||
#VXWORKS_VERSION = 6.3
|
||||
#VXWORKS_VERSION = 6.4
|
||||
#VXWORKS_VERSION = 6.5
|
||||
#VXWORKS_VERSION = 6.6
|
||||
#VXWORKS_VERSION = 6.7
|
||||
#VXWORKS_VERSION = 6.8
|
||||
#VXWORKS_VERSION = 6.9
|
||||
VXWORKS_VERSION = 6.9
|
||||
|
||||
|
||||
# Sites may override the following path for a particular host
|
||||
@@ -27,10 +19,8 @@ VXWORKS_VERSION = 5.5
|
||||
# CONFIG_SITE.$(EPICS_HOST_ARCH).vxWorksCommon file.
|
||||
|
||||
# WIND_BASE is where you installed the Wind River software.
|
||||
# Under vxWorks 6.x this is *not* the same as the old VX_DIR setting
|
||||
|
||||
WIND_BASE = /usr/local/vw/tornado22-$(ARCH_CLASS)
|
||||
#WIND_BASE = /usr/local/vw/vxWorks-$(VXWORKS_VERSION)
|
||||
WIND_BASE = /usr/local/vw/vxWorks-$(VXWORKS_VERSION)
|
||||
#WIND_BASE = /ade/vxWorks/$(VXWORKS_VERSION)
|
||||
|
||||
|
||||
@@ -39,9 +29,9 @@ WIND_BASE = /usr/local/vw/tornado22-$(ARCH_CLASS)
|
||||
#WORKBENCH_VERSION = 2.6
|
||||
#WORKBENCH_VERSION = 3.0
|
||||
#WORKBENCH_VERSION = 3.2
|
||||
#WORKBENCH_VERSION = 3.3
|
||||
WORKBENCH_VERSION = 3.3
|
||||
|
||||
|
||||
# Utilities Version number, required from vxWorks 6.8 and later
|
||||
|
||||
#UTILITIES_VERSION = 1.0
|
||||
UTILITIES_VERSION = 1.0
|
||||
|
||||
@@ -6,5 +6,5 @@
|
||||
# Site override definitions for cygwin-x86 host builds
|
||||
#-------------------------------------------------------
|
||||
|
||||
#CROSS_COMPILER_TARGET_ARCHS =
|
||||
#CROSS_COMPILER_TARGET_ARCHS = linux-arm
|
||||
|
||||
|
||||
16
configure/os/CONFIG_SITE.cygwin-x86.linux-arm
Normal file
16
configure/os/CONFIG_SITE.cygwin-x86.linux-arm
Normal file
@@ -0,0 +1,16 @@
|
||||
# CONFIG_SITE.cygwin-x86.linux-arm
|
||||
#
|
||||
# $Revision-Id$
|
||||
#
|
||||
# Site specific definitions for cygwin-x86 host - linux-arm target builds
|
||||
#-------------------------------------------------------
|
||||
|
||||
# Tools install path
|
||||
GNU_DIR = /usr/local/arm-linux
|
||||
|
||||
# GNU crosscompiler target name
|
||||
GNU_TARGET = arm-linux
|
||||
|
||||
STATIC_BUILD = YES
|
||||
SHARED_LIBRARIES = NO
|
||||
|
||||
16
configure/os/CONFIG_SITE.cygwin-x86_64.linux-arm
Normal file
16
configure/os/CONFIG_SITE.cygwin-x86_64.linux-arm
Normal file
@@ -0,0 +1,16 @@
|
||||
# CONFIG_SITE.cygwin-x86_64.linux-arm
|
||||
#
|
||||
# $Revision-Id$
|
||||
#
|
||||
# Site specific definitions for cygwin-x86_64 host - linux-arm target builds
|
||||
#-------------------------------------------------------
|
||||
|
||||
# Tools install path
|
||||
GNU_DIR = /usr/local/arm-linux
|
||||
|
||||
# GNU crosscompiler target name
|
||||
GNU_TARGET = arm-linux
|
||||
|
||||
STATIC_BUILD = YES
|
||||
SHARED_LIBRARIES = NO
|
||||
|
||||
20
configure/os/CONFIG_SITE.linux-x86.windows-x64-mingw
Normal file
20
configure/os/CONFIG_SITE.linux-x86.windows-x64-mingw
Normal file
@@ -0,0 +1,20 @@
|
||||
# CONFIG_SITE.linux-x86.windows-x64-mingw
|
||||
#
|
||||
# Configuration for linux-x86 host windows-x64-mingw target builds
|
||||
#-------------------------------------------------------
|
||||
|
||||
# Early versions of the MinGW cross-build tools can only build
|
||||
# static (non-DLL) libraries. For example RHEL's cross-gcc 4.4.6
|
||||
# needs these uncommented, cross-gcc 4.6.3 for Ubuntu does not:
|
||||
#SHARED_LIBRARIES = NO
|
||||
#STATIC_BUILD = YES
|
||||
|
||||
# The cross-build tools are in $(GNU_DIR)/bin
|
||||
# Default is /usr
|
||||
#GNU_DIR = /usr/local
|
||||
|
||||
# Different distribution cross-build packages use different prefixes:
|
||||
# Ubuntu:
|
||||
#CMPLR_PREFIX = i686-w64-mingw32-
|
||||
# RHEL:
|
||||
CMPLR_PREFIX = x86_64-w64-mingw32-
|
||||
8
configure/os/CONFIG_SITE.linux-x86_64.windows-x64-mingw
Normal file
8
configure/os/CONFIG_SITE.linux-x86_64.windows-x64-mingw
Normal file
@@ -0,0 +1,8 @@
|
||||
# CONFIG_SITE.linux-x86_64.windows-x64-mingw
|
||||
#
|
||||
# Configuration for linux-x86_64 host windows-x64-mingw target builds
|
||||
#-------------------------------------------------------
|
||||
|
||||
# Inherit from the linux-x86 host architecture
|
||||
include $(CONFIG)/os/CONFIG_SITE.linux-x86.windows-x64-mingw
|
||||
|
||||
@@ -4,32 +4,36 @@
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
|
||||
<title>Known Problems in R3.15.2</title>
|
||||
<title>Known Problems in R3.16.0.1</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h1 style="text-align: center">EPICS Base R3.15.2: Known Problems</h1>
|
||||
<h1 style="text-align: center">EPICS Base R3.16.0.1: Known Problems</h1>
|
||||
|
||||
<p>Any patch files linked below should be applied at the root of the
|
||||
base-3.15.2 tree. Download them, then use the GNU Patch program as
|
||||
base-3.16.0.1 tree. Download them, then use the GNU Patch program as
|
||||
follows:</p>
|
||||
|
||||
<blockquote><pre>% <b>cd <i>/path/to/</i>base-3.15.2</b>
|
||||
<blockquote><pre>% <b>cd <i>/path/to/</i>base-3.16.0.1</b>
|
||||
% <b>patch -p0 < <i>/path/to/</i>file.patch</b></pre></blockquote>
|
||||
|
||||
<p>The following significant problems have been reported with this
|
||||
version of EPICS Base:</p>
|
||||
<p>The following problems were known by the developers at the time of this
|
||||
release:</p>
|
||||
|
||||
<ul>
|
||||
|
||||
<li>Parallel builds ("make -j") on native Windows are not working properly.
|
||||
Builds tend to hang (make saturating one core) - interrupting and calling make
|
||||
again usually finishes the build. Sequential builds work and are recommended
|
||||
for automated build environments.</li>
|
||||
<!-- Items added after release should be formatted thusly:
|
||||
<li>YYYY-MM-DD: Description of problem
|
||||
...</li>
|
||||
-->
|
||||
|
||||
<li>IOCs running on some versions of Cygwin may display warnings at iocInit
|
||||
about duplicate EPICS CA Address list entries. These warnings might be due
|
||||
to a bug in Cygwin; they are benign and can be ignored.</li>
|
||||
|
||||
<li>64-bit Windows builds of the CAS library may not work with some compilers.
|
||||
The code in <tt>src/legacy/gdd</tt> is currently incompatible with the LLP64
|
||||
model that Windows uses for its 64-bit ABI.</li>
|
||||
The code in <tt>src/legacy/gdd</tt> is incompatible with the LLP64 model
|
||||
that Windows uses for its 64-bit ABI.</li>
|
||||
|
||||
</ul>
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
Installation Instructions
|
||||
|
||||
EPICS Base Release 3.15.2
|
||||
EPICS Base Release 3.16.0
|
||||
|
||||
--------------------------------------------------------------------------
|
||||
|
||||
@@ -85,17 +85,17 @@
|
||||
as processes on the host platform.
|
||||
|
||||
vxWorks
|
||||
You must have vxWorks 5.5.x or 6.x installed if any of your target
|
||||
systems are vxWorks systems; the C++ compiler for vxWorks 5.4 is now too
|
||||
old to support. The vxWorks installation provides the cross-compiler and
|
||||
header files needed to build for these targets. The absolute path to and
|
||||
the version number of the vxWorks installation must be set in the
|
||||
You must have vxWorks 6 installed if any of your target systems are
|
||||
vxWorks systems; the C++ compilers for vxWorks 5.x are now too old to
|
||||
support. The vxWorks installation provides the cross-compiler and header
|
||||
files needed to build for these targets. The absolute path to and the
|
||||
version number of the vxWorks installation must be set in the
|
||||
base/configure/os/CONFIG_SITE.Common.vxWorksCommon file or in one of its
|
||||
target-specific overrides.
|
||||
|
||||
Consult the vxWorks 5.x or vxWorks 6.x EPICS web pages about and the
|
||||
vxWorks documentation for information about configuring your vxWorks
|
||||
operating system for use with EPICS.
|
||||
Consult the vxWorks 6.x EPICS web pages and the vxWorks documentation
|
||||
for information about configuring your vxWorks operating system for use
|
||||
with EPICS.
|
||||
|
||||
RTEMS
|
||||
For RTEMS targets, you need RTEMS core and toolset version 4.9.2 or
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
<BODY>
|
||||
<CENTER>
|
||||
<H1>Installation Instructions</H1>
|
||||
<H2>EPICS Base Release 3.15.2</H2><BR>
|
||||
<H2>EPICS Base Release 3.16.0</H2><BR>
|
||||
</CENTER>
|
||||
<HR>
|
||||
<H3> Table of Contents</H3>
|
||||
@@ -90,17 +90,16 @@
|
||||
as processes on the host platform.</P>
|
||||
|
||||
<P><B>vxWorks</B><BR>
|
||||
You must have vxWorks 5.5.x or 6.x installed if any of your target systems are
|
||||
vxWorks systems; the C++ compiler for vxWorks 5.4 is now too old to support.
|
||||
The vxWorks installation provides the cross-compiler and header files needed to
|
||||
You must have vxWorks 6 installed if any of your target systems are vxWorks
|
||||
systems; the C++ compilers for vxWorks 5.x are now too old to support. The
|
||||
vxWorks installation provides the cross-compiler and header files needed to
|
||||
build for these targets. The absolute path to and the version number of the
|
||||
vxWorks installation must be set in the
|
||||
base/configure/os/CONFIG_SITE.Common.vxWorksCommon file or in one of its
|
||||
target-specific overrides.</P>
|
||||
|
||||
<P>Consult the <a href="http://www.aps.anl.gov/epics/base/tornado.php">vxWorks
|
||||
5.x</a> or <a href="http://www.aps.anl.gov/epics/base/vxWorks6.php">vxWorks
|
||||
6.x</a> EPICS web pages about and the vxWorks documentation for information
|
||||
<P>Consult the <a href="http://www.aps.anl.gov/epics/base/vxWorks6.php">vxWorks
|
||||
6.x</a> EPICS web pages and the vxWorks documentation for information
|
||||
about configuring your vxWorks operating system for use with EPICS.</P>
|
||||
|
||||
<P><B>RTEMS</B><BR>
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -39,7 +39,7 @@
|
||||
</blockquote>
|
||||
|
||||
<p>This document describes the procedures and provides a checklist of tasks
|
||||
that should be performed when creating new releases of EPICS Base.</p>
|
||||
that should be performed when creating production releases of EPICS Base.</p>
|
||||
|
||||
<h3>The Release Process</h3>
|
||||
|
||||
@@ -60,11 +60,8 @@ responsible for these tasks in the past and are expected to continue in the
|
||||
relevent roles unless the Release Manager designates otherwise:</p>
|
||||
|
||||
<dl>
|
||||
<dt><strong>Release Manager</strong> (Andrew Johnson)</dt>
|
||||
<dd>Responsible for the release</dd>
|
||||
<dt><strong>Configuration Manager</strong> (Janet Anderson)</dt>
|
||||
<dd>Responsible for committing version number updates and for
|
||||
creating tarfiles</dd>
|
||||
<dt><strong>Release Manager</strong> (Ralph Lange)</dt>
|
||||
<dd>Responsible for managing and tagging the release</dd>
|
||||
<dt><strong>Platform Developers</strong></dt>
|
||||
<dd>Responsible for individual operating system platforms</dd>
|
||||
<dt><strong>Application Developers</strong></dt>
|
||||
@@ -134,42 +131,42 @@ relevent roles unless the Release Manager designates otherwise:</p>
|
||||
</tr>
|
||||
<tr>
|
||||
<td> </td>
|
||||
<td>Configuration Manager</td>
|
||||
<td>Release Manager</td>
|
||||
<td>Edit and commit changes to the EPICS version number file
|
||||
configure/CONFIG_BASE_VERSION.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td> </td>
|
||||
<td>Configuration Manager</td>
|
||||
<td>Release Manager</td>
|
||||
<td>Tag the module in Bazaar, using these tag conventions:
|
||||
<ul>
|
||||
<li>
|
||||
<tt>R3.15.2-pre<i>n</i></tt>
|
||||
<tt>R3.16.1-pre<i>n</i></tt>
|
||||
— pre-release tag
|
||||
</li>
|
||||
<li>
|
||||
<tt>R3.15.2-rc<i>n</i></tt>
|
||||
<tt>R3.16.1-rc<i>n</i></tt>
|
||||
— release candidate tag, note the <tt>rc</tt> is now
|
||||
lower-case</li>
|
||||
</ul>
|
||||
<blockquote><tt>
|
||||
cd ~/base/mirror-3.15<br />
|
||||
bzr tag R3.15.2-rc<i>n</i>
|
||||
cd ~/base/mirror-3.16<br />
|
||||
bzr tag R3.16.1-rc<i>n</i>
|
||||
</tt></blockquote>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td> </td>
|
||||
<td>Configuration Manager</td>
|
||||
<td>Release Manager</td>
|
||||
<td>Export the tagged version into a tarfile. Note that this command
|
||||
generates a gzipped tarfile directly from the repository:
|
||||
<blockquote><tt>
|
||||
cd ~/base<br />
|
||||
bzr export --keywords=publish
|
||||
--root=base-3.15.2-rc<i>n</i>
|
||||
-r tag:R3.15.2-rc<i>n</i>
|
||||
baseR3.15.2-rc<i>n</i>.tar.gz
|
||||
mirror-3.15
|
||||
--root=base-3.16.1-rc<i>n</i>
|
||||
-r tag:R3.16.1-rc<i>n</i>
|
||||
base-3.16.1-rc<i>n</i>.tar.gz
|
||||
mirror-3.16
|
||||
</tt></blockquote>
|
||||
This requires that the Bazaar keywords plugin is installed and
|
||||
configured properly.
|
||||
@@ -177,14 +174,15 @@ relevent roles unless the Release Manager designates otherwise:</p>
|
||||
</tr>
|
||||
<tr>
|
||||
<td> </td>
|
||||
<td>Configuration Manager</td>
|
||||
<td>Release Manager</td>
|
||||
<td>Test the tarfile by extracting its contents and building it on at
|
||||
least one supported platform.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td> </td>
|
||||
<td>Configuration Manager</td>
|
||||
<td>Copy the tar file to the Base download area of the website.</td>
|
||||
<td>Website Manager</td>
|
||||
<td>If necessary recreate the tarfile following the instructions above.
|
||||
Copy the tar file to the Base download area of the website</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td> </td>
|
||||
@@ -223,13 +221,6 @@ relevent roles unless the Release Manager designates otherwise:</p>
|
||||
<td>Run the CA client side regression tests on all available host
|
||||
platforms.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td> </td>
|
||||
<td>Platform Developers</td>
|
||||
<td>Build and run the
|
||||
<a href="https://launchpad.net/epics-base-tests">epics-base-tests</a>
|
||||
suite on all available platforms.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td> </td>
|
||||
<td>Platform Developers</td>
|
||||
@@ -279,32 +270,32 @@ relevent roles unless the Release Manager designates otherwise:</p>
|
||||
</tr>
|
||||
<tr>
|
||||
<td> </td>
|
||||
<td>Configuration Manager</td>
|
||||
<td>Release Manager</td>
|
||||
<td>Edit and commit changes to the EPICS version number file
|
||||
configure/CONFIG_BASE_VERSION.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td> </td>
|
||||
<td>Configuration Manager</td>
|
||||
<td>Release Manager</td>
|
||||
<td>Tag the module in Bazaar:
|
||||
<blockquote><tt>
|
||||
cd ~/base/mirror-3.15<br />
|
||||
bzr tag R3.15.2</i>
|
||||
cd ~/base/mirror-3.16<br />
|
||||
bzr tag R3.16.1</i>
|
||||
</tt></blockquote>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td> </td>
|
||||
<td>Configuration Manager</td>
|
||||
<td>Release Manager</td>
|
||||
<td>Export the tagged version into a tarfile. Note that this command
|
||||
generates a gzipped tarfile directly from the repository:
|
||||
<blockquote><tt>
|
||||
cd ~/base<br />
|
||||
bzr export --keywords=publish
|
||||
--root=base-3.15.2
|
||||
-r tag:R3.15.2
|
||||
baseR3.15.2.tar.gz
|
||||
mirror-3.15
|
||||
--root=base-3.16.1
|
||||
-r tag:R3.16.1
|
||||
base-3.16.1.tar.gz
|
||||
mirror-3.16
|
||||
</tt></blockquote>
|
||||
This requires that the Bazaar keywords plugin is installed and
|
||||
configured properly.
|
||||
@@ -312,7 +303,7 @@ relevent roles unless the Release Manager designates otherwise:</p>
|
||||
</tr>
|
||||
<tr>
|
||||
<td> </td>
|
||||
<td>Configuration Manager</td>
|
||||
<td>Release Manager</td>
|
||||
<td>Test the tar file by extracting its contents and building it on at
|
||||
least one supported platform</td>
|
||||
</tr>
|
||||
@@ -321,8 +312,9 @@ relevent roles unless the Release Manager designates otherwise:</p>
|
||||
</tr>
|
||||
<tr>
|
||||
<td> </td>
|
||||
<td>Configuration Manager</td>
|
||||
<td>Copy the tar file to the Base download area of the website</td>
|
||||
<td>Website Manager</td>
|
||||
<td>If necessary recreate the tarfile following the instructions above.
|
||||
Copy the tar file to the Base download area of the website</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td> </td>
|
||||
|
||||
@@ -209,6 +209,7 @@ $Date$</span></small></p>
|
||||
<li><a href="#ca_pend_event">ca_poll</a></li>
|
||||
<li><a href="#ca_puser">ca_puser</a></li>
|
||||
<li><a href="#ca_put">ca_put</a></li>
|
||||
<li><a href="#ca_put">ca_put_callback</a></li>
|
||||
<li><a href="#ca_set_puser">ca_set_puser</a></li>
|
||||
<li><a href="#ca_signal">ca_signal</a></li>
|
||||
<li><a href="#ca_sg_block">ca_sg_block</a></li>
|
||||
@@ -223,7 +224,7 @@ $Date$</span></small></p>
|
||||
<li><a href="#ca_test_io">ca_test_io</a></li>
|
||||
<li><a href="#ca_write_access">ca_write_access</a></li>
|
||||
<li><a href="#ca_state">channel_state</a></li>
|
||||
<li><a href="#dbr_size[]">dbr_size[]</a></li>
|
||||
<li><a href="#dbr_size">dbr_size[]</a></li>
|
||||
<li><a href="#dbr_size_n">dbr_size_n</a></li>
|
||||
<li><a href="#dbr_value_size">dbr_value_size[]</a></li>
|
||||
<li><a href="#dbr_type_t">dbr_type_to_text</a></li>
|
||||
@@ -840,23 +841,18 @@ seconds. See also <a href="#Disconnect">EPICS_CA_CONN_TMO</a> and
|
||||
Interval</a>.</p>
|
||||
|
||||
<p>CA servers build a list of addresses to send beacons to during
|
||||
initialization. If EPICS_CAS_AUTO_BEACON_ADDR_LIST has the value "YES" then the
|
||||
beacon address list will be automatically configured to contain the broadcast
|
||||
addresses of all LAN interfaces found in the host and the destination address
|
||||
of all point-to-point interfaces found in the host. However, if the user also
|
||||
initialization. If EPICS_CAS_AUTO_BEACON_ADDR_LIST has the value "YES"
|
||||
(the default) this list will be automatically populated with the broadcast
|
||||
addresses of all network interfaces. However, if the user also
|
||||
defines EPICS_CAS_INTF_ADDR_LIST then beacon address list automatic
|
||||
configuration is constrained to the network interfaces specified therein, and
|
||||
therefore only the broadcast addresses of the specified LAN interfaces, and the
|
||||
destination addresses of all specified point-to-point links, will be
|
||||
therefore only the broadcast addresses of the specified LAN interfaces, will be
|
||||
automatically configured.</p>
|
||||
|
||||
<p>If EPICS_CAS_BEACON_ADDR_LIST is defined then its contents will be used to
|
||||
augment any automatic configuration of the beacon address list. Individual
|
||||
entries in EPICS_CAS_BEACON_ADDR_LIST may override the destination port number
|
||||
if ":nnn" follows the host name or IP address there. Alternatively, when both
|
||||
EPICS_CAS_BEACON_ADDR_LIST and EPICS_CAS_INTF_ADDR_LIST are not defined then
|
||||
the contents of EPICS_CA_ADDR_LIST is used to augment the list. Otherwise, the
|
||||
list is not augmented.</p>
|
||||
if ":nnn" follows the host name or IP address there.</p>
|
||||
|
||||
<p>The EPICS_CAS_BEACON_PORT parameter specifies the destination port for
|
||||
server beacons. The only exception to this occurs when ports are specified in
|
||||
@@ -867,20 +863,18 @@ specified in EPICS_CA_REPEATER_PORT.</p>
|
||||
<h4>Binding a Server to a Limited Set of Network Interfaces</h4>
|
||||
|
||||
<p>The parameter EPICS_CAS_INTF_ADDR_LIST allows a ca server to bind itself to,
|
||||
and therefore accept messages only over, a limited set of the local host's
|
||||
and therefore accept messages received by, a limited set of the local host's
|
||||
network interfaces (each specified by its IP address). On UNIX systems type
|
||||
"netstat -i" (type "ipconfig" on windows) to see a list of the local host's
|
||||
network interfaces. Specifically, UDP search messages addressed to both the IP
|
||||
addresses in EPICS_CAS_INTF_ADDR_LIST and also to the broadcast addresses of
|
||||
the corresponding LAN interfaces will be accepted by the server. By default,
|
||||
"netstat -ie" (type "ipconfig" on windows) to see a list of the local host's
|
||||
network interfaces. By default,
|
||||
the CA server is accessible from all network interfaces configured into its
|
||||
host.</p>
|
||||
|
||||
<p>In R3.14 and previous releases the CA server employed by iocCore did not
|
||||
implement the EPICS_CAS_INTF_ADDR_LIST feature. In this release the iocCore
|
||||
server will read the first IP address from the parameter variable and use that
|
||||
to select which interface to bind to. Any additional IP addresses will be
|
||||
ignored and a warning message displayed during IOC initialization.</p>
|
||||
<p>Until R3.15.4 the CA server employed by iocCore did not
|
||||
implement the EPICS_CAS_INTF_ADDR_LIST feature.</p>
|
||||
|
||||
<p>Prior to R3.15.4 CA servers would build the beacon address list
|
||||
using EPICS_CA_ADDR_LIST if EPICS_CAS_BEACON_ADDR_LIST was no set.</p>
|
||||
|
||||
<h4>Ignoring Process Variable Name Resolution Requests From Certain Hosts</h4>
|
||||
|
||||
@@ -3831,7 +3825,7 @@ specified channel and boolean false otherwise.</p>
|
||||
channel and boolean false otherwise</dd>
|
||||
</dl>
|
||||
|
||||
<h3><code><a name="dbr_size[]">dbr_size[]</a></code></h3>
|
||||
<h3><code><a name="dbr_size">dbr_size[]</a></code></h3>
|
||||
<pre>#include <db_access.h>
|
||||
extern unsigned dbr_size[/* TYPE */];</pre>
|
||||
|
||||
@@ -3953,11 +3947,12 @@ int ca_sg_create ( CA_SYNC_GID *PGID );</pre>
|
||||
|
||||
<p>A synchronous group can be used to guarantee that a set of channel access
|
||||
requests have completed. Once a synchronous group has been created then channel
|
||||
access get and put requests may be issued within it using <code>ca_sg_get()</code> and
|
||||
<code>ca_sg_put()</code> respectively. The routines <code>ca_sg_block()</code> and <code>ca_sg_test()</code> can be
|
||||
access get and put requests may be issued within it using
|
||||
<code>ca_sg_array_get()</code> and <code>ca_sg_array_put()</code> respectively.
|
||||
The routines <code>ca_sg_block()</code> and <code>ca_sg_test()</code> can be
|
||||
used to block for and test for completion respectively. The routine
|
||||
<code>ca_sg_reset()</code> is used to discard knowledge of old requests which have timed out
|
||||
and in all likelihood will never be satisfied.</p>
|
||||
<code>ca_sg_reset()</code> is used to discard knowledge of old requests which
|
||||
have timed out and in all likelihood will never be satisfied.</p>
|
||||
|
||||
<p>Any number of asynchronous groups can have application requested operations
|
||||
outstanding within them at any given time.</p>
|
||||
@@ -4131,16 +4126,20 @@ status = ca_sg_reset(gid);</pre>
|
||||
|
||||
<h3><code><a name="ca_sg_put">ca_sg_array_put()</a></code></h3>
|
||||
<pre>#include <cadef.h>
|
||||
int ca_sg_put ( CA_SYNC_GID GID, chtype TYPE,
|
||||
chid CHID, void *PVALUE );
|
||||
int ca_sg_array_put ( CA_SYNC_GID GID, chtype TYPE,
|
||||
unsigned long COUNT, chid CHID, void *PVALUE );</pre>
|
||||
|
||||
<p>Write a value, or array of values, to a channel and increment the
|
||||
outstanding request count of a synchronous group. The <code>ca_sg_array_put()</code>
|
||||
functionality is implemented using <code>ca_array_put_callback()</code>.</p>
|
||||
<p>Write a value, or array of values, to a channel and increment the outstanding
|
||||
request count of a synchronous group. The <code>ca_sg_put()</code> and
|
||||
<code>ca_sg_array_put()</code> functionality is implemented using
|
||||
<code>ca_array_put_callback()</code>.</p>
|
||||
|
||||
<p>All remote operation requests such as the above are accumulated (buffered)
|
||||
and not forwarded to the server until one of <code>ca_flush_io()</code>, <code>ca_pend_io()</code>,
|
||||
<code>ca_pend_event()</code>, or <code>ca_sg_block()</code> are called. This allows several requests to be
|
||||
and not forwarded to the server until one of <code>ca_flush_io()</code>,
|
||||
<code>ca_pend_io()</code>, <code>ca_pend_event()</code>, or
|
||||
<code>ca_sg_block()</code> are called. This allows several requests to be
|
||||
efficiently sent in one message.</p>
|
||||
|
||||
<p>If a connection is lost and then resumed outstanding puts are not
|
||||
@@ -4193,6 +4192,8 @@ reissued.</p>
|
||||
|
||||
<h3><code><a name="ca_sg_get">ca_sg_array_get()</a></code></h3>
|
||||
<pre>#include <cadef.h>
|
||||
int ca_sg_get ( CA_SYNC_GID GID, chtype TYPE,
|
||||
chid CHID, void *PVALUE );
|
||||
int ca_sg_array_get ( CA_SYNC_GID GID,
|
||||
chtype TYPE, unsigned long COUNT,
|
||||
chid CHID, void *PVALUE );</pre>
|
||||
@@ -4200,16 +4201,19 @@ int ca_sg_array_get ( CA_SYNC_GID GID,
|
||||
<h4>Description</h4>
|
||||
|
||||
<p>Read a value from a channel and increment the outstanding request count of a
|
||||
synchronous group. The <code>ca_sg_array_get()</code> functionality is implemented using
|
||||
synchronous group. The <code>ca_sg_get()</code> and
|
||||
<code>ca_sg_array_get()</code> functionality is implemented using
|
||||
<code>ca_array_get_callback()</code>.</p>
|
||||
|
||||
<p>The values written into your program's variables by <code>ca_sg_get()</code> should not be
|
||||
referenced by your program until ECA_NORMAL has been received from <code>ca_sg_block()</code>,
|
||||
or until <code>ca_sg_test()</code> returns ECA_IODONE.</p>
|
||||
<p>The values written into your program's variables by <code>ca_sg_get()</code>
|
||||
or <code>ca_sg_array_get()</code> should not be referenced by your program until
|
||||
ECA_NORMAL has been received from <code>ca_sg_block()</code>, or until
|
||||
<code>ca_sg_test()</code> returns ECA_IODONE.</p>
|
||||
|
||||
<p>All remote operation requests such as the above are accumulated (buffered)
|
||||
and not forwarded to the server until one of <code>ca_flush_io()</code>, <code>ca_pend_io()</code>,
|
||||
<code>ca_pend_event()</code>, or <code>ca_sg_block()</code> are called. This allows several requests to be
|
||||
and not forwarded to the server until one of <code>ca_flush_io()</code>,
|
||||
<code>ca_pend_io()</code>, <code>ca_pend_event()</code>, or
|
||||
<code>ca_sg_block()</code> are called. This allows several requests to be
|
||||
efficiently sent in one message.</p>
|
||||
|
||||
<p>If a connection is lost and then resumed outstanding gets are not
|
||||
|
||||
@@ -22,7 +22,7 @@ extern "C" {
|
||||
epicsShareFunc void epicsShareAPI configureChannelAccessAddressList
|
||||
( struct ELLLIST *pList, SOCKET sock, unsigned short port );
|
||||
|
||||
epicsShareFunc void epicsShareAPI addAddrToChannelAccessAddressList
|
||||
epicsShareFunc int epicsShareAPI addAddrToChannelAccessAddressList
|
||||
( struct ELLLIST *pList, const ENV_PARAM *pEnv,
|
||||
unsigned short port, int ignoreNonDefaultPort );
|
||||
|
||||
|
||||
@@ -73,7 +73,7 @@ static char *getToken ( const char **ppString, char *pBuf, unsigned bufSIze )
|
||||
/*
|
||||
* addAddrToChannelAccessAddressList ()
|
||||
*/
|
||||
extern "C" void epicsShareAPI addAddrToChannelAccessAddressList
|
||||
extern "C" int epicsShareAPI addAddrToChannelAccessAddressList
|
||||
( ELLLIST *pList, const ENV_PARAM *pEnv,
|
||||
unsigned short port, int ignoreNonDefaultPort )
|
||||
{
|
||||
@@ -82,11 +82,11 @@ extern "C" void epicsShareAPI addAddrToChannelAccessAddressList
|
||||
const char *pToken;
|
||||
struct sockaddr_in addr;
|
||||
char buf[32u]; /* large enough to hold an IP address */
|
||||
int status;
|
||||
int status, ret = -1;
|
||||
|
||||
pStr = envGetConfigParamPtr (pEnv);
|
||||
if (!pStr) {
|
||||
return;
|
||||
return ret;
|
||||
}
|
||||
|
||||
while ( ( pToken = getToken (&pStr, buf, sizeof (buf) ) ) ) {
|
||||
@@ -104,7 +104,7 @@ extern "C" void epicsShareAPI addAddrToChannelAccessAddressList
|
||||
pNewNode = (osiSockAddrNode *) calloc (1, sizeof(*pNewNode));
|
||||
if (pNewNode==NULL) {
|
||||
fprintf ( stderr, "addAddrToChannelAccessAddressList(): no memory available for configuration\n");
|
||||
return;
|
||||
break;
|
||||
}
|
||||
|
||||
pNewNode->addr.ia = addr;
|
||||
@@ -113,9 +113,10 @@ extern "C" void epicsShareAPI addAddrToChannelAccessAddressList
|
||||
* LOCK applied externally
|
||||
*/
|
||||
ellAdd (pList, &pNewNode->node);
|
||||
ret = 0; /* success if anything is added to the list */
|
||||
}
|
||||
|
||||
return;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -66,6 +66,8 @@ void repeaterSubscribeTimer::shutdown (
|
||||
epicsTimerNotify::expireStatus repeaterSubscribeTimer::
|
||||
expire ( const epicsTime & /* currentTime */ )
|
||||
{
|
||||
epicsGuard < epicsMutex > guard ( this->stateMutex );
|
||||
|
||||
static const unsigned nTriesToMsg = 50;
|
||||
if ( this->attempts > nTriesToMsg && ! this->once ) {
|
||||
callbackManager mgr ( this->ctxNotify, this->cbMutex );
|
||||
@@ -92,12 +94,15 @@ epicsTimerNotify::expireStatus repeaterSubscribeTimer::
|
||||
|
||||
void repeaterSubscribeTimer::show ( unsigned /* level */ ) const
|
||||
{
|
||||
epicsGuard < epicsMutex > guard ( this->stateMutex );
|
||||
|
||||
::printf ( "repeater subscribe timer: attempts=%u registered=%u once=%u\n",
|
||||
this->attempts, this->registered, this->once );
|
||||
}
|
||||
|
||||
void repeaterSubscribeTimer::confirmNotify ()
|
||||
{
|
||||
epicsGuard < epicsMutex > guard ( this->stateMutex );
|
||||
this->registered = true;
|
||||
}
|
||||
|
||||
|
||||
@@ -70,6 +70,7 @@ private:
|
||||
repeaterTimerNotify & iiu;
|
||||
epicsMutex & cbMutex;
|
||||
cacContextNotify & ctxNotify;
|
||||
mutable epicsMutex stateMutex;
|
||||
unsigned attempts;
|
||||
bool registered;
|
||||
bool once;
|
||||
|
||||
@@ -163,6 +163,19 @@ udpiiu::udpiiu (
|
||||
throwWithLocation ( noSocket () );
|
||||
}
|
||||
|
||||
#ifdef IP_ADD_MEMBERSHIP
|
||||
{
|
||||
int flag = 1;
|
||||
if ( setsockopt ( this->sock, IPPROTO_IP, IP_MULTICAST_LOOP,
|
||||
(char *) &flag, sizeof ( flag ) ) == -1 ) {
|
||||
char sockErrBuf[64];
|
||||
epicsSocketConvertErrnoToString (
|
||||
sockErrBuf, sizeof ( sockErrBuf ) );
|
||||
errlogPrintf("CAC: failed to set mcast loopback\n");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
int boolValue = true;
|
||||
int status = setsockopt ( this->sock, SOL_SOCKET, SO_BROADCAST,
|
||||
(char *) &boolValue, sizeof ( boolValue ) );
|
||||
|
||||
@@ -141,6 +141,39 @@ caStatus caServerI::attachInterface ( const caNetAddr & addrIn,
|
||||
return S_cas_success;
|
||||
}
|
||||
|
||||
void caServerI::addMCast(const osiSockAddr& addr)
|
||||
{
|
||||
#ifdef IP_ADD_MEMBERSHIP
|
||||
epicsGuard < epicsMutex > locker ( this->mutex );
|
||||
tsDLIter < casIntfOS > iter = this->intfList.firstIter ();
|
||||
while ( iter.valid () ) {
|
||||
struct ip_mreq mreq;
|
||||
|
||||
memset(&mreq, 0, sizeof(mreq));
|
||||
mreq.imr_interface = iter->serverAddress().getSockIP().sin_addr;
|
||||
mreq.imr_multiaddr = addr.ia.sin_addr;
|
||||
|
||||
if ( setsockopt ( iter->casDGIntfIO::getFD (), IPPROTO_IP,
|
||||
IP_ADD_MEMBERSHIP, (char *) &mreq,
|
||||
sizeof ( mreq ) ) < 0) {
|
||||
struct sockaddr_in temp;
|
||||
char name[40];
|
||||
char sockErrBuf[64];
|
||||
temp.sin_family = AF_INET;
|
||||
temp.sin_addr = mreq.imr_multiaddr;
|
||||
temp.sin_port = addr.ia.sin_port;
|
||||
epicsSocketConvertErrnoToString (
|
||||
sockErrBuf, sizeof ( sockErrBuf ) );
|
||||
ipAddrToDottedIP (&temp, name, sizeof(name));
|
||||
fprintf(stderr, "CAS: Socket mcast join %s failed with \"%s\"\n",
|
||||
name, sockErrBuf );
|
||||
}
|
||||
|
||||
iter++;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void caServerI::sendBeacon ( ca_uint32_t beaconNo )
|
||||
{
|
||||
epicsGuard < epicsMutex > locker ( this->mutex );
|
||||
|
||||
@@ -103,6 +103,8 @@ private:
|
||||
caStatus attachInterface ( const caNetAddr & addr, bool autoBeaconAddr,
|
||||
bool addConfigAddr );
|
||||
|
||||
virtual void addMCast(const osiSockAddr&);
|
||||
|
||||
void sendBeacon ( ca_uint32_t beaconNo );
|
||||
|
||||
caServerI ( const caServerI & );
|
||||
|
||||
@@ -354,9 +354,11 @@ caStatus casDGClient::searchFailResponse ( const caHdrLargeArray * mp )
|
||||
status = this->out.copyInHeader ( CA_PROTO_NOT_FOUND, 0,
|
||||
mp->m_dataType, mp->m_count, mp->m_cid, mp->m_available, 0 );
|
||||
|
||||
this->out.commitMsg ();
|
||||
if ( status == S_cas_success ) {
|
||||
this->out.commitMsg ();
|
||||
}
|
||||
|
||||
return S_cas_success;
|
||||
return S_cas_success;
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
//
|
||||
|
||||
#include <ctype.h>
|
||||
#include <list>
|
||||
|
||||
#include "epicsSignal.h"
|
||||
#include "envDefs.h"
|
||||
@@ -92,6 +93,9 @@ void caServerIO::locateInterfaces ()
|
||||
autoBeaconAddr = true;
|
||||
}
|
||||
|
||||
typedef std::list<osiSockAddr> mcastAddrs_t;
|
||||
mcastAddrs_t mcastAddrs;
|
||||
|
||||
//
|
||||
// bind to the the interfaces specified - otherwise wildcard
|
||||
// with INADDR_ANY and allow clients to attach from any interface
|
||||
@@ -114,6 +118,15 @@ void caServerIO::locateInterfaces ()
|
||||
pToken);
|
||||
continue;
|
||||
}
|
||||
|
||||
epicsUInt32 top = ntohl(saddr.sin_addr.s_addr)>>24;
|
||||
if (saddr.sin_family==AF_INET && top>=224 && top<=239) {
|
||||
osiSockAddr oaddr;
|
||||
oaddr.ia = saddr;
|
||||
mcastAddrs.push_back(oaddr);
|
||||
continue;
|
||||
}
|
||||
|
||||
stat = this->attachInterface (caNetAddr(saddr), autoBeaconAddr, configAddrOnceFlag);
|
||||
if (stat) {
|
||||
errMessage(stat, "unable to attach explicit interface");
|
||||
@@ -131,6 +144,10 @@ void caServerIO::locateInterfaces ()
|
||||
errMessage(stat, "unable to attach any interface");
|
||||
}
|
||||
}
|
||||
|
||||
for (mcastAddrs_t::const_iterator it = mcastAddrs.begin(); it!=mcastAddrs.end(); ++it) {
|
||||
this->addMCast(*it);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
@@ -47,6 +47,8 @@ private:
|
||||
virtual caStatus attachInterface (
|
||||
const caNetAddr & addr, bool autoBeaconAddr,
|
||||
bool addConfigAddr ) = 0;
|
||||
|
||||
virtual void addMCast(const osiSockAddr&) = 0;
|
||||
};
|
||||
|
||||
#endif // caServerIOh
|
||||
|
||||
@@ -19,6 +19,11 @@
|
||||
#include "addrList.h"
|
||||
#include "errlog.h"
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
# include <BaseTsd.h>
|
||||
typedef SSIZE_T ssize_t;
|
||||
#endif
|
||||
|
||||
#define epicsExportSharedSymbols
|
||||
#include "casDGIntfIO.h"
|
||||
#include "ipIgnoreEntry.h"
|
||||
@@ -148,26 +153,8 @@ casDGIntfIO::casDGIntfIO ( caServerI & serverIn, clientBufMemoryManager & memMgr
|
||||
}
|
||||
|
||||
if ( addConfigBeaconAddr ) {
|
||||
|
||||
//
|
||||
// by default use EPICS_CA_ADDR_LIST for the
|
||||
// beacon address list
|
||||
//
|
||||
const ENV_PARAM *pParam;
|
||||
|
||||
if ( envGetConfigParamPtr ( & EPICS_CAS_INTF_ADDR_LIST ) ||
|
||||
envGetConfigParamPtr ( & EPICS_CAS_BEACON_ADDR_LIST ) ) {
|
||||
pParam = & EPICS_CAS_BEACON_ADDR_LIST;
|
||||
}
|
||||
else {
|
||||
pParam = & EPICS_CA_ADDR_LIST;
|
||||
}
|
||||
|
||||
//
|
||||
// add in the configured addresses
|
||||
//
|
||||
addAddrToChannelAccessAddressList (
|
||||
& BCastAddrList, pParam, beaconPort, pParam == & EPICS_CA_ADDR_LIST );
|
||||
& BCastAddrList, &EPICS_CAS_BEACON_ADDR_LIST, beaconPort, 0 );
|
||||
}
|
||||
|
||||
removeDuplicateAddresses ( & this->beaconAddrList, & BCastAddrList, 0 );
|
||||
@@ -267,7 +254,7 @@ casDGIntfIO::~casDGIntfIO()
|
||||
|
||||
// avoid use of ellFree because problems on windows occur if the
|
||||
// free is in a different DLL than the malloc
|
||||
ELLNODE * nnode = this->beaconAddrList.node.next;
|
||||
ELLNODE * nnode = ellFirst(&this->beaconAddrList);
|
||||
while ( nnode )
|
||||
{
|
||||
ELLNODE * pnode = nnode;
|
||||
@@ -418,52 +405,24 @@ bufSizeT casDGIntfIO ::
|
||||
}
|
||||
|
||||
void casDGIntfIO::sendBeaconIO ( char & msg, unsigned length,
|
||||
aitUint16 & portField, aitUint32 & addrField )
|
||||
aitUint16 & portField, aitUint32 & )
|
||||
{
|
||||
caNetAddr addr = this->serverAddress ();
|
||||
struct sockaddr_in inetAddr = addr.getSockIP();
|
||||
osiSockAddrNode *pAddr;
|
||||
int status;
|
||||
char buf[64];
|
||||
|
||||
portField = inetAddr.sin_port; // the TCP port
|
||||
|
||||
for (pAddr = reinterpret_cast <osiSockAddrNode *> ( ellFirst ( & this->beaconAddrList ) );
|
||||
pAddr; pAddr = reinterpret_cast <osiSockAddrNode *> ( ellNext ( & pAddr->node ) ) ) {
|
||||
status = connect ( this->beaconSock, &pAddr->addr.sa, sizeof ( pAddr->addr.sa ) );
|
||||
if (status<0) {
|
||||
char sockErrBuf[64];
|
||||
epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) );
|
||||
ipAddrToDottedIP ( & pAddr->addr.ia, buf, sizeof ( buf ) );
|
||||
errlogPrintf ( "%s: CA beacon routing (connect to \"%s\") error was \"%s\"\n",
|
||||
__FILE__, buf, sockErrBuf );
|
||||
}
|
||||
else {
|
||||
osiSockAddr sockAddr;
|
||||
osiSocklen_t size = ( osiSocklen_t ) sizeof ( sockAddr.sa );
|
||||
status = getsockname ( this->beaconSock, &sockAddr.sa, &size );
|
||||
if ( status < 0 ) {
|
||||
char sockErrBuf[64];
|
||||
epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) );
|
||||
errlogPrintf ( "%s: CA beacon routing (getsockname) error was \"%s\"\n",
|
||||
__FILE__, sockErrBuf );
|
||||
}
|
||||
else if ( sockAddr.sa.sa_family == AF_INET ) {
|
||||
addrField = sockAddr.ia.sin_addr.s_addr;
|
||||
for (ELLNODE *pNode = ellFirst(&this->beaconAddrList); pNode; pNode = ellNext(pNode))
|
||||
{
|
||||
osiSockAddrNode *pAddr = reinterpret_cast<osiSockAddrNode *>(pNode);
|
||||
|
||||
status = send ( this->beaconSock, &msg, length, 0 );
|
||||
if ( status < 0 ) {
|
||||
char sockErrBuf[64];
|
||||
epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) );
|
||||
ipAddrToA ( &pAddr->addr.ia, buf, sizeof(buf) );
|
||||
errlogPrintf ( "%s: CA beacon (send to \"%s\") error was \"%s\"\n",
|
||||
__FILE__, buf, sockErrBuf );
|
||||
}
|
||||
else {
|
||||
unsigned statusAsLength = static_cast < unsigned > ( status );
|
||||
assert ( statusAsLength == length );
|
||||
}
|
||||
}
|
||||
ssize_t status = sendto(this->beaconSock, &msg, length, 0, &pAddr->addr.sa, sizeof(pAddr->addr.ia));
|
||||
if ( status != length ) {
|
||||
char sockErrBuf[64], buf[64];
|
||||
epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) );
|
||||
ipAddrToA ( &pAddr->addr.ia, buf, sizeof(buf) );
|
||||
errlogPrintf ( "%s: CA beacon (send to \"%s\") error was \"%s\" (%u)\n",
|
||||
__FILE__, buf, sockErrBuf, (unsigned)status );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,7 +24,6 @@
|
||||
#include "dbDefs.h"
|
||||
#include "epicsAtomic.h"
|
||||
#include "epicsEvent.h"
|
||||
#include "epicsExit.h"
|
||||
#include "epicsInterrupt.h"
|
||||
#include "epicsRingPointer.h"
|
||||
#include "epicsString.h"
|
||||
@@ -54,6 +53,7 @@ typedef struct cbQueueSet {
|
||||
epicsEventId semWakeUp;
|
||||
epicsRingPointerId queue;
|
||||
int queueOverflow;
|
||||
int shutdown;
|
||||
int threadsConfigured;
|
||||
int threadsRunning;
|
||||
} cbQueueSet;
|
||||
@@ -73,7 +73,8 @@ static epicsTimerQueueId timerQueue;
|
||||
enum ctl {ctlInit, ctlRun, ctlPause, ctlExit};
|
||||
static volatile enum ctl cbCtl;
|
||||
static epicsEventId startStopEvent;
|
||||
static void *exitCallback;
|
||||
|
||||
static int callbackIsInit;
|
||||
|
||||
/* Static data */
|
||||
static char *threadNamePrefix[NUM_CALLBACK_PRIORITIES] = {
|
||||
@@ -89,8 +90,8 @@ static int priorityValue[NUM_CALLBACK_PRIORITIES] = {0, 1, 2};
|
||||
|
||||
int callbackSetQueueSize(int size)
|
||||
{
|
||||
if (startStopEvent) {
|
||||
errlogPrintf("Callback system already initialized\n");
|
||||
if (callbackIsInit) {
|
||||
fprintf(stderr, "Callback system already initialized\n");
|
||||
return -1;
|
||||
}
|
||||
callbackQueueSize = size;
|
||||
@@ -99,8 +100,8 @@ int callbackSetQueueSize(int size)
|
||||
|
||||
int callbackParallelThreads(int count, const char *prio)
|
||||
{
|
||||
if (startStopEvent) {
|
||||
errlogPrintf("Callback system already initialized\n");
|
||||
if (callbackIsInit) {
|
||||
fprintf(stderr, "Callback system already initialized\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -110,7 +111,7 @@ int callbackParallelThreads(int count, const char *prio)
|
||||
count = callbackParallelThreadsDefault;
|
||||
if (count < 1) count = 1;
|
||||
|
||||
if (!prio || strcmp(prio, "") == 0 || strcmp(prio, "*") == 0) {
|
||||
if (!prio || *prio == 0 || strcmp(prio, "*") == 0) {
|
||||
int i;
|
||||
|
||||
for (i = 0; i < NUM_CALLBACK_PRIORITIES; i++) {
|
||||
@@ -119,42 +120,43 @@ int callbackParallelThreads(int count, const char *prio)
|
||||
}
|
||||
else {
|
||||
dbMenu *pdbMenu;
|
||||
int i;
|
||||
|
||||
if (!pdbbase) {
|
||||
errlogPrintf("callbackParallelThreads: pdbbase not set\n");
|
||||
fprintf(stderr, "callbackParallelThreads: pdbbase not set\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Find prio in menuPriority */
|
||||
pdbMenu = dbFindMenu(pdbbase, "menuPriority");
|
||||
if (pdbMenu) {
|
||||
int i, gotMatch = 0;
|
||||
|
||||
for (i = 0; i < pdbMenu->nChoice; i++) {
|
||||
gotMatch = (epicsStrCaseCmp(prio, pdbMenu->papChoiceValue[i])==0) ? TRUE : FALSE;
|
||||
if (gotMatch)
|
||||
break;
|
||||
}
|
||||
if (gotMatch) {
|
||||
callbackQueue[i].threadsConfigured = count;
|
||||
return 0;
|
||||
}
|
||||
else {
|
||||
errlogPrintf("Unknown priority \"%s\"\n", prio);
|
||||
return -1;
|
||||
}
|
||||
if (!pdbMenu) {
|
||||
fprintf(stderr, "callbackParallelThreads: No Priority menu\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (i = 0; i < pdbMenu->nChoice; i++) {
|
||||
if (epicsStrCaseCmp(prio, pdbMenu->papChoiceValue[i]) == 0)
|
||||
goto found;
|
||||
}
|
||||
fprintf(stderr, "callbackParallelThreads: "
|
||||
"Unknown priority \"%s\"\n", prio);
|
||||
return -1;
|
||||
|
||||
found:
|
||||
callbackQueue[i].threadsConfigured = count;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void callbackTask(void *arg)
|
||||
{
|
||||
cbQueueSet *mySet = &callbackQueue[*(int*)arg];
|
||||
int prio = *(int*)arg;
|
||||
cbQueueSet *mySet = &callbackQueue[prio];
|
||||
|
||||
taskwdInsert(0, NULL, NULL);
|
||||
epicsEventSignal(startStopEvent);
|
||||
|
||||
while(TRUE) {
|
||||
while(!mySet->shutdown) {
|
||||
void *ptr;
|
||||
if (epicsRingPointerIsEmpty(mySet->queue))
|
||||
epicsEventMustWait(mySet->semWakeUp);
|
||||
@@ -163,42 +165,52 @@ static void callbackTask(void *arg)
|
||||
CALLBACK *pcallback = (CALLBACK *)ptr;
|
||||
if(!epicsRingPointerIsEmpty(mySet->queue))
|
||||
epicsEventMustTrigger(mySet->semWakeUp);
|
||||
if (ptr == &exitCallback) goto shutdown;
|
||||
mySet->queueOverflow = FALSE;
|
||||
(*pcallback->callback)(pcallback);
|
||||
}
|
||||
}
|
||||
|
||||
shutdown:
|
||||
mySet->threadsRunning--;
|
||||
if(!epicsAtomicDecrIntT(&mySet->threadsRunning))
|
||||
epicsEventSignal(startStopEvent);
|
||||
taskwdRemove(0);
|
||||
epicsEventSignal(startStopEvent);
|
||||
}
|
||||
|
||||
void callbackShutdown(void)
|
||||
void callbackStop(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (cbCtl == ctlExit) return;
|
||||
cbCtl = ctlExit;
|
||||
|
||||
/* sequential shutdown of workers */
|
||||
for (i = 0; i < NUM_CALLBACK_PRIORITIES; i++) {
|
||||
while (callbackQueue[i].threadsRunning) {
|
||||
if(epicsRingPointerPush(callbackQueue[i].queue, &exitCallback)) {
|
||||
epicsEventSignal(callbackQueue[i].semWakeUp);
|
||||
epicsEventWait(startStopEvent);
|
||||
} else {
|
||||
epicsThreadSleep(0.05);
|
||||
}
|
||||
}
|
||||
assert(callbackQueue[i].threadsRunning==0);
|
||||
epicsEventDestroy(callbackQueue[i].semWakeUp);
|
||||
epicsRingPointerDelete(callbackQueue[i].queue);
|
||||
callbackQueue[i].shutdown = 1;
|
||||
epicsEventSignal(callbackQueue[i].semWakeUp);
|
||||
}
|
||||
|
||||
for (i = 0; i < NUM_CALLBACK_PRIORITIES; i++) {
|
||||
cbQueueSet *mySet = &callbackQueue[i];
|
||||
|
||||
while (epicsAtomicGetIntT(&mySet->threadsRunning)) {
|
||||
epicsEventSignal(mySet->semWakeUp);
|
||||
epicsEventWaitWithTimeout(startStopEvent, 0.1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void callbackCleanup(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < NUM_CALLBACK_PRIORITIES; i++) {
|
||||
cbQueueSet *mySet = &callbackQueue[i];
|
||||
|
||||
assert(epicsAtomicGetIntT(&mySet->threadsRunning)==0);
|
||||
epicsEventDestroy(mySet->semWakeUp);
|
||||
epicsRingPointerDelete(mySet->queue);
|
||||
}
|
||||
|
||||
epicsTimerQueueRelease(timerQueue);
|
||||
epicsEventDestroy(startStopEvent);
|
||||
startStopEvent = NULL;
|
||||
callbackIsInit = 0;
|
||||
memset(callbackQueue, 0, sizeof(callbackQueue));
|
||||
}
|
||||
|
||||
@@ -208,10 +220,14 @@ void callbackInit(void)
|
||||
int j;
|
||||
char threadName[32];
|
||||
|
||||
if (startStopEvent)
|
||||
if (callbackIsInit) {
|
||||
errlogMessage("Warning: callbackInit called again before callbackCleanup\n");
|
||||
return;
|
||||
}
|
||||
callbackIsInit = 1;
|
||||
|
||||
startStopEvent = epicsEventMustCreate(epicsEventEmpty);
|
||||
if(!startStopEvent)
|
||||
startStopEvent = epicsEventMustCreate(epicsEventEmpty);
|
||||
cbCtl = ctlRun;
|
||||
timerQueue = epicsTimerQueueAllocate(0, epicsThreadPriorityScanHigh);
|
||||
|
||||
@@ -239,7 +255,7 @@ void callbackInit(void)
|
||||
cantProceed("Failed to spawn callback thread %s\n", threadName);
|
||||
} else {
|
||||
epicsEventWait(startStopEvent);
|
||||
callbackQueue[i].threadsRunning++;
|
||||
epicsAtomicIncrIntT(&callbackQueue[i].threadsRunning);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,17 +47,20 @@ typedef struct callbackPvt {
|
||||
|
||||
typedef void (*CALLBACKFUNC)(struct callbackPvt*);
|
||||
|
||||
#define callbackSetCallback(PFUN,PCALLBACK)\
|
||||
( (PCALLBACK)->callback = (PFUN) )
|
||||
#define callbackSetPriority(PRIORITY,PCALLBACK)\
|
||||
( (PCALLBACK)->priority = (PRIORITY) )
|
||||
#define callbackSetUser(USER,PCALLBACK)\
|
||||
( (PCALLBACK)->user = (void *)(USER) )
|
||||
#define callbackGetUser(USER,PCALLBACK)\
|
||||
( (USER) = (void *)((CALLBACK *)(PCALLBACK))->user )
|
||||
#define callbackSetCallback(PFUN, PCALLBACK) \
|
||||
( (PCALLBACK)->callback = (PFUN) )
|
||||
#define callbackSetPriority(PRIORITY, PCALLBACK) \
|
||||
( (PCALLBACK)->priority = (PRIORITY) )
|
||||
#define callbackGetPriority(PRIORITY, PCALLBACK) \
|
||||
( (PRIORITY) = (PCALLBACK)->priority )
|
||||
#define callbackSetUser(USER, PCALLBACK) \
|
||||
( (PCALLBACK)->user = (void *) (USER) )
|
||||
#define callbackGetUser(USER, PCALLBACK) \
|
||||
( (USER) = (PCALLBACK)->user )
|
||||
|
||||
epicsShareFunc void callbackInit(void);
|
||||
epicsShareFunc void callbackShutdown(void);
|
||||
epicsShareFunc void callbackStop(void);
|
||||
epicsShareFunc void callbackCleanup(void);
|
||||
epicsShareFunc int callbackRequest(CALLBACK *pCallback);
|
||||
epicsShareFunc void callbackSetProcess(
|
||||
CALLBACK *pcallback, int Priority, void *pRec);
|
||||
|
||||
@@ -50,11 +50,12 @@
|
||||
#include "dbFldTypes.h"
|
||||
#include "dbFldTypes.h"
|
||||
#include "dbLink.h"
|
||||
#include "dbLock.h"
|
||||
#include "dbLockPvt.h"
|
||||
#include "dbNotify.h"
|
||||
#include "dbScan.h"
|
||||
#include "dbServer.h"
|
||||
#include "dbStaticLib.h"
|
||||
#include "dbStaticPvt.h"
|
||||
#include "devSup.h"
|
||||
#include "epicsEvent.h"
|
||||
#include "link.h"
|
||||
@@ -812,11 +813,10 @@ long dbGet(DBADDR *paddr, short dbrType,
|
||||
void *pbuffer, long *options, long *nRequest, void *pflin)
|
||||
{
|
||||
char *pbuf = pbuffer;
|
||||
void *pfieldsave;
|
||||
void *pfieldsave = paddr->pfield;
|
||||
db_field_log *pfl = (db_field_log *)pflin;
|
||||
short field_type;
|
||||
long no_elements;
|
||||
long offset;
|
||||
long capacity, no_elements, offset;
|
||||
struct rset *prset;
|
||||
long status = 0;
|
||||
|
||||
@@ -826,18 +826,33 @@ long dbGet(DBADDR *paddr, short dbrType,
|
||||
return 0;
|
||||
|
||||
if (!pfl || pfl->type == dbfl_type_rec) {
|
||||
field_type = paddr->field_type;
|
||||
no_elements = paddr->no_elements;
|
||||
field_type = paddr->field_type;
|
||||
no_elements = capacity = paddr->no_elements;
|
||||
|
||||
/* Update field info from record
|
||||
* may modify paddr->pfield
|
||||
*/
|
||||
if (paddr->pfldDes->special == SPC_DBADDR &&
|
||||
(prset = dbGetRset(paddr)) &&
|
||||
prset->get_array_info) {
|
||||
status = prset->get_array_info(paddr, &no_elements, &offset);
|
||||
} else
|
||||
offset = 0;
|
||||
} else {
|
||||
field_type = pfl->field_type;
|
||||
no_elements = pfl->no_elements;
|
||||
field_type = pfl->field_type;
|
||||
no_elements = capacity = pfl->no_elements;
|
||||
offset = 0;
|
||||
}
|
||||
|
||||
if (field_type >= DBF_INLINK && field_type <= DBF_FWDLINK)
|
||||
return getLinkValue(paddr, dbrType, pbuf, nRequest);
|
||||
if (field_type >= DBF_INLINK && field_type <= DBF_FWDLINK) {
|
||||
status = getLinkValue(paddr, dbrType, pbuf, nRequest);
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (paddr->special == SPC_ATTRIBUTE)
|
||||
return getAttrValue(paddr, dbrType, pbuf, nRequest);
|
||||
if (paddr->special == SPC_ATTRIBUTE) {
|
||||
status = getAttrValue(paddr, dbrType, pbuf, nRequest);
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* Check for valid request */
|
||||
if (INVALID_DB_REQ(dbrType) || field_type > DBF_DEVICE) {
|
||||
@@ -845,26 +860,13 @@ long dbGet(DBADDR *paddr, short dbrType,
|
||||
|
||||
sprintf(message, "dbGet: Request type is %d\n", dbrType);
|
||||
recGblDbaddrError(S_db_badDbrtype, paddr, message);
|
||||
return S_db_badDbrtype;
|
||||
status = S_db_badDbrtype;
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* For SPC_DBADDR fields, the rset function
|
||||
* get_array_info() is allowed to modify
|
||||
* paddr->pfield. So we store the original
|
||||
* value and restore it later.
|
||||
*/
|
||||
pfieldsave = paddr->pfield;
|
||||
|
||||
/* Update field info */
|
||||
if (paddr->pfldDes->special == SPC_DBADDR &&
|
||||
(prset = dbGetRset(paddr)) &&
|
||||
prset->get_array_info) {
|
||||
status = prset->get_array_info(paddr, &no_elements, &offset);
|
||||
} else
|
||||
offset = 0;
|
||||
|
||||
if (offset == 0 && (!nRequest || no_elements == 1)) {
|
||||
if (nRequest) *nRequest = 1;
|
||||
if (nRequest)
|
||||
*nRequest = 1;
|
||||
if (!pfl || pfl->type == dbfl_type_rec) {
|
||||
status = dbFastGetConvertRoutine[field_type][dbrType]
|
||||
(paddr->pfield, pbuf, paddr);
|
||||
@@ -883,10 +885,11 @@ long dbGet(DBADDR *paddr, short dbrType,
|
||||
}
|
||||
} else {
|
||||
long n;
|
||||
long (*convert)();
|
||||
GETCONVERTFUNC convert;
|
||||
|
||||
if (nRequest) {
|
||||
if (no_elements<(*nRequest)) *nRequest = no_elements;
|
||||
if (no_elements < *nRequest)
|
||||
*nRequest = no_elements;
|
||||
n = *nRequest;
|
||||
} else {
|
||||
n = 1;
|
||||
@@ -901,11 +904,11 @@ long dbGet(DBADDR *paddr, short dbrType,
|
||||
status = S_db_badDbrtype;
|
||||
goto done;
|
||||
}
|
||||
/* convert database field and place it in the buffer */
|
||||
/* convert data into the caller's buffer */
|
||||
if (n <= 0) {
|
||||
;/*do nothing*/
|
||||
} else if (!pfl || pfl->type == dbfl_type_rec) {
|
||||
status = convert(paddr, pbuf, n, no_elements, offset);
|
||||
status = convert(paddr, pbuf, n, capacity, offset);
|
||||
} else {
|
||||
DBADDR localAddr = *paddr; /* Structure copy */
|
||||
|
||||
@@ -916,7 +919,7 @@ long dbGet(DBADDR *paddr, short dbrType,
|
||||
localAddr.pfield = (char *) &pfl->u.v.field;
|
||||
else
|
||||
localAddr.pfield = (char *) pfl->u.r.field;
|
||||
status = convert(&localAddr, pbuf, n, no_elements, offset);
|
||||
status = convert(&localAddr, pbuf, n, capacity, offset);
|
||||
}
|
||||
}
|
||||
done:
|
||||
@@ -940,19 +943,25 @@ devSup* dbDSETtoDevSup(dbRecordType *prdes, struct dset *pdset) {
|
||||
static long dbPutFieldLink(DBADDR *paddr,
|
||||
short dbrType, const void *pbuffer, long nRequest)
|
||||
{
|
||||
dbLinkInfo link_info;
|
||||
DBADDR *pdbaddr = NULL;
|
||||
dbCommon *precord = paddr->precord;
|
||||
dbCommon *lockrecs[2];
|
||||
dbLocker locker;
|
||||
dbFldDes *pfldDes = paddr->pfldDes;
|
||||
long special = paddr->special;
|
||||
struct link *plink = (struct link *)paddr->pfield;
|
||||
const char *pstring = (const char *)pbuffer;
|
||||
DBENTRY dbEntry;
|
||||
struct dsxt *old_dsxt = NULL;
|
||||
struct dset *new_dset = NULL;
|
||||
struct dsxt *new_dsxt = NULL;
|
||||
devSup *new_devsup = NULL;
|
||||
long status;
|
||||
int isDevLink;
|
||||
short scan;
|
||||
|
||||
STATIC_ASSERT(DBLOCKER_NALLOC>=2);
|
||||
|
||||
switch (dbrType) {
|
||||
case DBR_CHAR:
|
||||
case DBR_UCHAR:
|
||||
@@ -967,31 +976,57 @@ static long dbPutFieldLink(DBADDR *paddr,
|
||||
return S_db_badDbrtype;
|
||||
}
|
||||
|
||||
dbInitEntry(pdbbase, &dbEntry);
|
||||
status = dbFindRecord(&dbEntry, precord->name);
|
||||
if (!status) status = dbFindField(&dbEntry, pfldDes->name);
|
||||
if (status) goto finish;
|
||||
status = dbParseLink(pstring, pfldDes->field_type, &link_info);
|
||||
if (status)
|
||||
return status;
|
||||
|
||||
if (link_info.ltype == PV_LINK &&
|
||||
(link_info.modifiers & (pvlOptCA | pvlOptCP | pvlOptCPP)) == 0) {
|
||||
DBADDR tempaddr;
|
||||
|
||||
if (dbNameToAddr(link_info.target, &tempaddr)==0) {
|
||||
/* This will become a DB link. */
|
||||
pdbaddr = malloc(sizeof(*pdbaddr));
|
||||
if (!pdbaddr) {
|
||||
status = S_db_noMemory;
|
||||
goto cleanup;
|
||||
}
|
||||
*pdbaddr = tempaddr; /* struct copy */
|
||||
}
|
||||
}
|
||||
|
||||
isDevLink = ellCount(&precord->rdes->devList) > 0 &&
|
||||
(strcmp(pfldDes->name, "INP") == 0 ||
|
||||
strcmp(pfldDes->name, "OUT") == 0);
|
||||
pfldDes->isDevLink;
|
||||
|
||||
dbLockSetGblLock();
|
||||
dbLockSetRecordLock(precord);
|
||||
memset(&locker, 0, sizeof(locker));
|
||||
lockrecs[0] = precord;
|
||||
lockrecs[1] = pdbaddr ? pdbaddr->precord : NULL;
|
||||
dbLockerPrepare(&locker, lockrecs, 2);
|
||||
|
||||
dbScanLockMany(&locker);
|
||||
|
||||
scan = precord->scan;
|
||||
|
||||
if (isDevLink) {
|
||||
devSup *pdevSup = dbDTYPtoDevSup(precord->rdes, precord->dtyp);
|
||||
if (pdevSup) {
|
||||
new_dset = pdevSup->pdset;
|
||||
new_dsxt = pdevSup->pdsxt;
|
||||
new_devsup = dbDTYPtoDevSup(precord->rdes, precord->dtyp);
|
||||
if (new_devsup) {
|
||||
new_dset = new_devsup->pdset;
|
||||
new_dsxt = new_devsup->pdsxt;
|
||||
}
|
||||
}
|
||||
|
||||
if (dbCanSetLink(plink, &link_info, new_devsup)) {
|
||||
/* link type mis-match prevents assignment */
|
||||
status = S_dbLib_badField;
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
if (isDevLink) {
|
||||
if (precord->dset) {
|
||||
pdevSup = dbDSETtoDevSup(precord->rdes, precord->dset);
|
||||
if (pdevSup)
|
||||
old_dsxt = pdevSup->pdsxt;
|
||||
devSup *old_devsup = dbDSETtoDevSup(precord->rdes, precord->dset);
|
||||
|
||||
if (old_devsup)
|
||||
old_dsxt = old_devsup->pdsxt;
|
||||
}
|
||||
|
||||
if (new_dsxt == NULL ||
|
||||
@@ -1017,13 +1052,11 @@ static long dbPutFieldLink(DBADDR *paddr,
|
||||
switch (plink->type) { /* Old link type */
|
||||
case DB_LINK:
|
||||
case CA_LINK:
|
||||
dbRemoveLink(plink);
|
||||
case CONSTANT:
|
||||
dbRemoveLink(&locker, plink); /* link type becomes PV_LINK */
|
||||
break;
|
||||
|
||||
case PV_LINK:
|
||||
case CONSTANT:
|
||||
break; /* do nothing */
|
||||
|
||||
case MACRO_LINK:
|
||||
break; /* should never get here */
|
||||
|
||||
@@ -1037,7 +1070,7 @@ static long dbPutFieldLink(DBADDR *paddr,
|
||||
|
||||
if (special) status = dbPutSpecial(paddr, 0);
|
||||
|
||||
if (!status) status = dbPutString(&dbEntry, pstring);
|
||||
if (!status) status = dbSetLink(plink, &link_info, new_devsup);
|
||||
|
||||
if (!status && special) status = dbPutSpecial(paddr, 1);
|
||||
|
||||
@@ -1064,11 +1097,9 @@ static long dbPutFieldLink(DBADDR *paddr,
|
||||
|
||||
switch (plink->type) { /* New link type */
|
||||
case PV_LINK:
|
||||
dbAddLink(precord, plink, pfldDes->field_type);
|
||||
break;
|
||||
|
||||
case CONSTANT:
|
||||
break; /* do nothing */
|
||||
dbAddLink(&locker, plink, pfldDes->field_type, pdbaddr);
|
||||
break;
|
||||
|
||||
case DB_LINK:
|
||||
case CA_LINK:
|
||||
@@ -1094,9 +1125,10 @@ postScanEvent:
|
||||
if (scan != precord->scan)
|
||||
db_post_events(precord, &precord->scan, DBE_VALUE | DBE_LOG);
|
||||
unlock:
|
||||
dbLockSetGblUnlock();
|
||||
finish:
|
||||
dbFinishEntry(&dbEntry);
|
||||
dbScanUnlockMany(&locker);
|
||||
dbLockerFinalize(&locker);
|
||||
cleanup:
|
||||
free(link_info.target);
|
||||
return status;
|
||||
}
|
||||
|
||||
@@ -1228,20 +1260,20 @@ long dbPut(DBADDR *paddr, short dbrType,
|
||||
status = dbPutConvertRoutine[dbrType][field_type](paddr, pbuffer,
|
||||
nRequest, no_elements, offset);
|
||||
}
|
||||
if (status) goto done;
|
||||
|
||||
/* update array info */
|
||||
if (paddr->pfldDes->special == SPC_DBADDR &&
|
||||
if (!status &&
|
||||
paddr->pfldDes->special == SPC_DBADDR &&
|
||||
prset && prset->put_array_info) {
|
||||
status = prset->put_array_info(paddr, nRequest);
|
||||
}
|
||||
if (status) goto done;
|
||||
|
||||
/* check if special processing is required */
|
||||
/* Always do special processing if needed */
|
||||
if (special) {
|
||||
status = dbPutSpecial(paddr,1);
|
||||
if (status) goto done;
|
||||
long status2 = dbPutSpecial(paddr, 1);
|
||||
if (status2) goto done;
|
||||
}
|
||||
if (status) goto done;
|
||||
|
||||
/* Propagate monitor events for this field, */
|
||||
/* unless the field is VAL and PP is true. */
|
||||
|
||||
@@ -205,6 +205,7 @@ private:
|
||||
cacContextNotify & notify;
|
||||
epics_auto_ptr < cacContext > pNetContext;
|
||||
char * pStateNotifyCache;
|
||||
bool isolated;
|
||||
|
||||
cacChannel & createChannel (
|
||||
epicsGuard < epicsMutex > &,
|
||||
|
||||
@@ -6,14 +6,13 @@
|
||||
* EPICS BASE is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
\*************************************************************************/
|
||||
|
||||
/* $Revision-Id$
|
||||
/* dbCa.c
|
||||
*
|
||||
* Original Authors: Bob Dalesio and Marty Kraimer
|
||||
* Date: 26MAR96
|
||||
*
|
||||
*/
|
||||
|
||||
#define EPICS_DBCA_PRIVATE_API
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
@@ -30,6 +29,7 @@
|
||||
#include "epicsPrint.h"
|
||||
#include "epicsString.h"
|
||||
#include "epicsThread.h"
|
||||
#include "epicsAtomic.h"
|
||||
#include "epicsTime.h"
|
||||
#include "errlog.h"
|
||||
#include "errMdef.h"
|
||||
@@ -46,13 +46,17 @@
|
||||
#include "dbCaPvt.h"
|
||||
#include "dbCommon.h"
|
||||
#include "db_convert.h"
|
||||
#include "dbLink.h"
|
||||
#include "dbLock.h"
|
||||
#include "dbScan.h"
|
||||
#include "link.h"
|
||||
#include "recSup.h"
|
||||
|
||||
/* defined in dbContext.cpp
|
||||
* Setup local CA access
|
||||
*/
|
||||
extern void dbServiceIOInit();
|
||||
|
||||
extern int dbServiceIsolate;
|
||||
|
||||
static ELLLIST workList = ELLLIST_INIT; /* Work list for dbCaTask */
|
||||
static epicsMutexId workListLock; /*Mutual exclusions semaphores for workList*/
|
||||
@@ -60,7 +64,7 @@ static epicsEventId workListEvent; /*wakeup event for dbCaTask*/
|
||||
static int removesOutstanding = 0;
|
||||
#define removesOutstandingWarning 10000
|
||||
|
||||
static volatile enum {
|
||||
static volatile enum dbCaCtl_t {
|
||||
ctlInit, ctlRun, ctlPause, ctlExit
|
||||
} dbCaCtl;
|
||||
static epicsEventId startStopEvent;
|
||||
@@ -70,68 +74,55 @@ struct ca_client_context * dbCaClientContext;
|
||||
/* Forward declarations */
|
||||
static void dbCaTask(void *);
|
||||
|
||||
static lset dbCa_lset;
|
||||
|
||||
#define printLinks(pcaLink) \
|
||||
errlogPrintf("%s has DB CA link to %s\n",\
|
||||
pcaLink->plink->value.pv_link.precord->name, pcaLink->pvname)
|
||||
pcaLink->plink->precord->name, pcaLink->pvname)
|
||||
|
||||
static int dbca_chan_count;
|
||||
|
||||
/* caLink locking
|
||||
*
|
||||
* workListLock
|
||||
* This is only used to put request into and take them out of workList.
|
||||
* While this is locked no other locks are taken
|
||||
* Lock ordering:
|
||||
* dbScanLock -> caLink.lock -> workListLock
|
||||
*
|
||||
* dbScanLock
|
||||
* dbCaAddLink and dbCaRemoveLink are only called by dbAccess or iocInit
|
||||
* They are only called by dbAccess when it has a global lock on lock set.
|
||||
* It is assumed that ALL other dbCaxxx calls are made only if dbScanLock
|
||||
* is already active. These routines are intended for use by record/device
|
||||
* support.
|
||||
* workListLock:
|
||||
* Guards access to workList.
|
||||
*
|
||||
* caLink.lock
|
||||
* Any code that use a caLink takes this lock and releases it when done
|
||||
* dbScanLock:
|
||||
* All dbCa* functions operating on a single link may only be called when
|
||||
* the record containing the DBLINK is locked. Including:
|
||||
* dbCaGet*()
|
||||
* isConnected()
|
||||
* dbCaPutLink()
|
||||
* scanForward()
|
||||
* dbCaAddLinkCallback()
|
||||
* dbCaRemoveLink()
|
||||
*
|
||||
* dbCaTask and the channel access callbacks NEVER access anything in the
|
||||
* records except after locking caLink.lock and checking that caLink.plink
|
||||
* is not null. They NEVER call dbScanLock.
|
||||
* Guard the pointer plink.value.pv_link.pvt, but not the struct caLink
|
||||
* which is pointed to.
|
||||
*
|
||||
* The above is necessary to prevent deadlocks and attempts to use a caLink
|
||||
* that has been deleted.
|
||||
* caLink.lock:
|
||||
* Guards the caLink structure (but not the struct DBLINK)
|
||||
*
|
||||
* Just a few words about handling dbCaRemoveLink because this is when
|
||||
* it is essential that nothing trys to use a caLink that has been freed.
|
||||
* The dbCaTask only locks caLink, and must not lock the record (a violation of lock order).
|
||||
*
|
||||
* dbCaRemoveLink is called when links are being modified. This is only
|
||||
* done with the dbScan mechanism guranteeing that nothing from
|
||||
* database access trys to access the record containing the caLink.
|
||||
* During link modification or IOC shutdown the pca->plink pointer (guarded by caLink.lock)
|
||||
* is used as a flag to indicate that a link is no longer active.
|
||||
*
|
||||
* Thus the problem is to make sure that nothing from channel access
|
||||
* accesses a caLink that is deleted. This is done as follows.
|
||||
* References to the struct caLink are owned by the dbCaTask, and any scanOnceCallback()
|
||||
* which is in progress.
|
||||
*
|
||||
* dbCaRemoveLink does the following:
|
||||
* epicsMutexMustLock(pca->lock);
|
||||
* pca->plink = 0;
|
||||
* plink->value.pv_link.pvt = 0;
|
||||
* epicsMutexUnlock(pca->lock);
|
||||
* addAction(pca,CA_CLEAR_CHANNEL);
|
||||
*
|
||||
* dbCaTask issues a ca_clear_channel and then frees the caLink.
|
||||
*
|
||||
* If any channel access callback gets called before the ca_clear_channel
|
||||
* it finds pca->plink=0 and does nothing. Once ca_clear_channel
|
||||
* is called no other callback for this caLink will be called.
|
||||
* The libca and scanOnceCallback callbacks take no action if pca->plink==NULL.
|
||||
*
|
||||
* dbCaPutLinkCallback causes an additional complication because
|
||||
* when dbCaRemoveLink is called the callback may not have occured.
|
||||
* What is done is the following:
|
||||
* If callback has not occured dbCaRemoveLink sets plinkPutCallback=plink
|
||||
* If putCallback is called before dbCaTask calls ca_clear_channel
|
||||
* it does NOT call the users callback.
|
||||
* dbCaTask calls the users callback passing plinkPutCallback AFTER
|
||||
* it has called ca_clear_channel
|
||||
* Thus the users callback will get called exactly once.
|
||||
*/
|
||||
* If putComplete sees plink==0 it will not call the user's code.
|
||||
* If pca->putCallback is non-zero, dbCaTask will call the
|
||||
* user's callback AFTER it has called ca_clear_channel.
|
||||
* Thus the user's callback will get called exactly once.
|
||||
*/
|
||||
|
||||
static void addAction(caLink *pca, short link_action)
|
||||
{
|
||||
@@ -165,10 +156,22 @@ static void addAction(caLink *pca, short link_action)
|
||||
epicsEventSignal(workListEvent);
|
||||
}
|
||||
|
||||
static void dbCaLinkFree(caLink *pca)
|
||||
static void caLinkInc(caLink *pca)
|
||||
{
|
||||
assert(epicsAtomicGetIntT(&pca->refcount)>0);
|
||||
epicsAtomicIncrIntT(&pca->refcount);
|
||||
}
|
||||
|
||||
static void caLinkDec(caLink *pca)
|
||||
{
|
||||
int cnt;
|
||||
dbCaCallback callback;
|
||||
struct link *plinkPutCallback = 0;
|
||||
void *userPvt = 0;
|
||||
|
||||
cnt = epicsAtomicDecrIntT(&pca->refcount);
|
||||
assert(cnt>=0);
|
||||
if(cnt>0)
|
||||
return;
|
||||
|
||||
if (pca->chid) {
|
||||
ca_clear_channel(pca->chid);
|
||||
@@ -176,8 +179,7 @@ static void dbCaLinkFree(caLink *pca)
|
||||
}
|
||||
callback = pca->putCallback;
|
||||
if (callback) {
|
||||
plinkPutCallback = pca->plinkPutCallback;
|
||||
pca->plinkPutCallback = 0;
|
||||
userPvt = pca->putUserPvt;
|
||||
pca->putCallback = 0;
|
||||
pca->putType = 0;
|
||||
}
|
||||
@@ -188,13 +190,49 @@ static void dbCaLinkFree(caLink *pca)
|
||||
free(pca->pvname);
|
||||
epicsMutexDestroy(pca->lock);
|
||||
free(pca);
|
||||
if (callback) callback(plinkPutCallback);
|
||||
if (callback) callback(userPvt);
|
||||
}
|
||||
|
||||
void dbCaCallbackProcess(void *usrPvt)
|
||||
/* Block until worker thread has processed all previously queued actions.
|
||||
* Does not prevent additional actions from being queued.
|
||||
*/
|
||||
void dbCaSync(void)
|
||||
{
|
||||
struct link *plink = (struct link *)usrPvt;
|
||||
dbCommon *pdbCommon = plink->value.pv_link.precord;
|
||||
epicsEventId wake;
|
||||
caLink templink;
|
||||
|
||||
/* we only partially initialize templink.
|
||||
* It has no link field and no subscription
|
||||
* so the worker must handle it early
|
||||
*/
|
||||
memset(&templink, 0, sizeof(templink));
|
||||
templink.refcount = 1;
|
||||
|
||||
wake = epicsEventMustCreate(epicsEventEmpty);
|
||||
templink.lock = epicsMutexMustCreate();
|
||||
|
||||
templink.userPvt = wake;
|
||||
|
||||
addAction(&templink, CA_SYNC);
|
||||
|
||||
epicsEventMustWait(wake);
|
||||
/* Worker holds workListLock when calling epicsEventMustTrigger()
|
||||
* we cycle through workListLock to ensure worker call to
|
||||
* epicsEventMustTrigger() returns before we destroy the event.
|
||||
*/
|
||||
epicsMutexMustLock(workListLock);
|
||||
epicsMutexUnlock(workListLock);
|
||||
|
||||
assert(templink.refcount==1);
|
||||
|
||||
epicsMutexDestroy(templink.lock);
|
||||
epicsEventDestroy(wake);
|
||||
}
|
||||
|
||||
void dbCaCallbackProcess(void *userPvt)
|
||||
{
|
||||
struct link *plink = (struct link *)userPvt;
|
||||
dbCommon *pdbCommon = plink->precord;
|
||||
|
||||
dbScanLock(pdbCommon);
|
||||
pdbCommon->rset->process(pdbCommon);
|
||||
@@ -203,46 +241,25 @@ void dbCaCallbackProcess(void *usrPvt)
|
||||
|
||||
void dbCaShutdown(void)
|
||||
{
|
||||
if (dbCaCtl == ctlRun || dbCaCtl == ctlPause) {
|
||||
dbCaCtl = ctlExit;
|
||||
epicsEventSignal(workListEvent);
|
||||
epicsEventMustWait(startStopEvent);
|
||||
epicsEventDestroy(startStopEvent);
|
||||
} else {
|
||||
/* manually cleanup queue since dbCa thread isn't running
|
||||
* which only happens in unit tests
|
||||
*/
|
||||
caLink *pca;
|
||||
epicsMutexMustLock(workListLock);
|
||||
while((pca=(caLink*)ellGet(&workList))!=NULL) {
|
||||
if(pca->link_action&CA_CLEAR_CHANNEL) {
|
||||
dbCaLinkFree(pca);
|
||||
}
|
||||
}
|
||||
epicsMutexUnlock(workListLock);
|
||||
}
|
||||
enum dbCaCtl_t cur = dbCaCtl;
|
||||
assert(cur == ctlRun || cur == ctlPause);
|
||||
dbCaCtl = ctlExit;
|
||||
epicsEventSignal(workListEvent);
|
||||
epicsEventMustWait(startStopEvent);
|
||||
}
|
||||
|
||||
static void dbCaExit(void *arg)
|
||||
static void dbCaLinkInitImpl(int isolate)
|
||||
{
|
||||
dbCaShutdown();
|
||||
}
|
||||
dbServiceIsolate = isolate;
|
||||
dbServiceIOInit();
|
||||
|
||||
void dbCaLinkInitIsolated(void)
|
||||
{
|
||||
if (!workListLock)
|
||||
workListLock = epicsMutexMustCreate();
|
||||
if (!workListEvent)
|
||||
workListEvent = epicsEventMustCreate(epicsEventEmpty);
|
||||
dbCaCtl = ctlExit;
|
||||
epicsAtExit(dbCaExit, NULL);
|
||||
}
|
||||
|
||||
void dbCaLinkInit(void)
|
||||
{
|
||||
dbServiceIOInit();
|
||||
dbCaLinkInitIsolated();
|
||||
startStopEvent = epicsEventMustCreate(epicsEventEmpty);
|
||||
if(!startStopEvent)
|
||||
startStopEvent = epicsEventMustCreate(epicsEventEmpty);
|
||||
dbCaCtl = ctlPause;
|
||||
|
||||
epicsThreadCreate("dbCaLink", epicsThreadPriorityMedium,
|
||||
@@ -251,6 +268,16 @@ void dbCaLinkInit(void)
|
||||
epicsEventMustWait(startStopEvent);
|
||||
}
|
||||
|
||||
void dbCaLinkInitIsolated(void)
|
||||
{
|
||||
dbCaLinkInitImpl(1);
|
||||
}
|
||||
|
||||
void dbCaLinkInit(void)
|
||||
{
|
||||
dbCaLinkInitImpl(0);
|
||||
}
|
||||
|
||||
void dbCaRun(void)
|
||||
{
|
||||
if (dbCaCtl == ctlPause) {
|
||||
@@ -275,6 +302,7 @@ void dbCaAddLinkCallback(struct link *plink,
|
||||
assert(!plink->value.pv_link.pvt);
|
||||
|
||||
pca = (caLink *)dbCalloc(1, sizeof(caLink));
|
||||
pca->refcount = 1;
|
||||
pca->lock = epicsMutexMustCreate();
|
||||
pca->plink = plink;
|
||||
pca->pvname = epicsStrDup(plink->value.pv_link.pvname);
|
||||
@@ -283,13 +311,21 @@ void dbCaAddLinkCallback(struct link *plink,
|
||||
pca->userPvt = userPvt;
|
||||
|
||||
epicsMutexMustLock(pca->lock);
|
||||
plink->lset = &dbCa_lset;
|
||||
plink->type = CA_LINK;
|
||||
plink->value.pv_link.pvt = pca;
|
||||
plink->value.pv_link.backend = "ca";
|
||||
addAction(pca, CA_CONNECT);
|
||||
epicsMutexUnlock(pca->lock);
|
||||
}
|
||||
|
||||
void dbCaRemoveLink(struct link *plink)
|
||||
long dbCaAddLink(struct dbLocker *locker, struct link *plink, short dbfType)
|
||||
{
|
||||
dbCaAddLinkCallback(plink, 0, 0, NULL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void dbCaRemoveLink(struct dbLocker *locker, struct link *plink)
|
||||
{
|
||||
caLink *pca = (caLink *)plink->value.pv_link.pvt;
|
||||
|
||||
@@ -297,8 +333,10 @@ void dbCaRemoveLink(struct link *plink)
|
||||
epicsMutexMustLock(pca->lock);
|
||||
pca->plink = 0;
|
||||
plink->value.pv_link.pvt = 0;
|
||||
if (pca->putCallback)
|
||||
pca->plinkPutCallback = plink;
|
||||
plink->value.pv_link.backend = NULL;
|
||||
plink->value.pv_link.pvlMask = 0;
|
||||
plink->type = PV_LINK;
|
||||
plink->lset = NULL;
|
||||
/* Unlock before addAction or dbCaTask might free first */
|
||||
epicsMutexUnlock(pca->lock);
|
||||
addAction(pca, CA_CLEAR_CHANNEL);
|
||||
@@ -358,15 +396,15 @@ long dbCaGetLink(struct link *plink,short dbrType, void *pdest,
|
||||
assert(pca->pgetNative);
|
||||
status = fConvert(pca->pgetNative, pdest, 0);
|
||||
} else {
|
||||
long ntoget = *nelements;
|
||||
unsigned long ntoget = *nelements;
|
||||
struct dbAddr dbAddr;
|
||||
long (*aConvert)(struct dbAddr *paddr, void *to, long nreq, long nto, long off);
|
||||
|
||||
aConvert = dbGetConvertRoutine[newType][dbrType];
|
||||
assert(pca->pgetNative);
|
||||
|
||||
if (ntoget > pca->nelements)
|
||||
ntoget = pca->nelements;
|
||||
if (ntoget > pca->usedelements)
|
||||
ntoget = pca->usedelements;
|
||||
*nelements = ntoget;
|
||||
|
||||
memset((void *)&dbAddr, 0, sizeof(dbAddr));
|
||||
@@ -421,6 +459,7 @@ long dbCaPutLinkCallback(struct link *plink,short dbrType,
|
||||
if (!pca->pputNative) {
|
||||
pca->pputNative = dbCalloc(pca->nelements,
|
||||
dbr_value_size[ca_field_type(pca->chid)]);
|
||||
pca->putnelements = 0;
|
||||
/* Fixed and disabled by ANJ, see comment above.
|
||||
plink->value.pv_link.pvlMask |= pvlOptOutNative;
|
||||
*/
|
||||
@@ -430,6 +469,7 @@ long dbCaPutLinkCallback(struct link *plink,short dbrType,
|
||||
|
||||
fConvert = dbFastPutConvertRoutine[dbrType][newType];
|
||||
status = fConvert(pbuffer, pca->pputNative, 0);
|
||||
pca->putnelements = 1;
|
||||
} else {
|
||||
struct dbAddr dbAddr;
|
||||
long (*aConvert)(struct dbAddr *paddr, const void *from, long nreq, long nfrom, long off);
|
||||
@@ -442,10 +482,7 @@ long dbCaPutLinkCallback(struct link *plink,short dbrType,
|
||||
if(nRequest>pca->nelements)
|
||||
nRequest = pca->nelements;
|
||||
status = aConvert(&dbAddr, pbuffer, nRequest, pca->nelements, 0);
|
||||
if(nRequest<pca->nelements) {
|
||||
long elemsize = dbr_value_size[ca_field_type(pca->chid)];
|
||||
memset(nRequest*elemsize+(char*)pca->pputNative, 0, (pca->nelements-nRequest)*elemsize);
|
||||
}
|
||||
pca->putnelements = nRequest;
|
||||
}
|
||||
link_action |= CA_WRITE_NATIVE;
|
||||
pca->gotOutNative = TRUE;
|
||||
@@ -465,7 +502,13 @@ long dbCaPutLinkCallback(struct link *plink,short dbrType,
|
||||
return status;
|
||||
}
|
||||
|
||||
int dbCaIsLinkConnected(const struct link *plink)
|
||||
long dbCaPutLink(struct link *plink, short dbrType,
|
||||
const void *pbuffer, long nRequest)
|
||||
{
|
||||
return dbCaPutLinkCallback(plink, dbrType, pbuffer, nRequest, 0, NULL);
|
||||
}
|
||||
|
||||
static int isConnected(const struct link *plink)
|
||||
{
|
||||
caLink *pca;
|
||||
|
||||
@@ -475,7 +518,7 @@ int dbCaIsLinkConnected(const struct link *plink)
|
||||
return pca->isConnected;
|
||||
}
|
||||
|
||||
void dbCaScanFwdLink(struct link *plink) {
|
||||
static void scanForward(struct link *plink) {
|
||||
short fwdLinkValue = 1;
|
||||
|
||||
if (plink->value.pv_link.pvlMask & pvlOptFWD)
|
||||
@@ -494,7 +537,7 @@ void dbCaScanFwdLink(struct link *plink) {
|
||||
return -1; \
|
||||
}
|
||||
|
||||
long dbCaGetNelements(const struct link *plink, long *nelements)
|
||||
static long getElements(const struct link *plink, long *nelements)
|
||||
{
|
||||
caLink *pca;
|
||||
|
||||
@@ -504,7 +547,7 @@ long dbCaGetNelements(const struct link *plink, long *nelements)
|
||||
return 0;
|
||||
}
|
||||
|
||||
long dbCaGetAlarm(const struct link *plink,
|
||||
static long getAlarm(const struct link *plink,
|
||||
epicsEnum16 *pstat, epicsEnum16 *psevr)
|
||||
{
|
||||
caLink *pca;
|
||||
@@ -516,7 +559,7 @@ long dbCaGetAlarm(const struct link *plink,
|
||||
return 0;
|
||||
}
|
||||
|
||||
long dbCaGetTimeStamp(const struct link *plink,
|
||||
static long getTimeStamp(const struct link *plink,
|
||||
epicsTimeStamp *pstamp)
|
||||
{
|
||||
caLink *pca;
|
||||
@@ -527,7 +570,7 @@ long dbCaGetTimeStamp(const struct link *plink,
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dbCaGetLinkDBFtype(const struct link *plink)
|
||||
static int getDBFtype(const struct link *plink)
|
||||
{
|
||||
caLink *pca;
|
||||
int type;
|
||||
@@ -558,7 +601,7 @@ long dbCaGetAttributes(const struct link *plink,
|
||||
return 0;
|
||||
}
|
||||
|
||||
long dbCaGetControlLimits(const struct link *plink,
|
||||
static long getControlLimits(const struct link *plink,
|
||||
double *low, double *high)
|
||||
{
|
||||
caLink *pca;
|
||||
@@ -574,7 +617,7 @@ long dbCaGetControlLimits(const struct link *plink,
|
||||
return gotAttributes ? 0 : -1;
|
||||
}
|
||||
|
||||
long dbCaGetGraphicLimits(const struct link *plink,
|
||||
static long getGraphicLimits(const struct link *plink,
|
||||
double *low, double *high)
|
||||
{
|
||||
caLink *pca;
|
||||
@@ -590,7 +633,7 @@ long dbCaGetGraphicLimits(const struct link *plink,
|
||||
return gotAttributes ? 0 : -1;
|
||||
}
|
||||
|
||||
long dbCaGetAlarmLimits(const struct link *plink,
|
||||
static long getAlarmLimits(const struct link *plink,
|
||||
double *lolo, double *low, double *high, double *hihi)
|
||||
{
|
||||
caLink *pca;
|
||||
@@ -608,7 +651,7 @@ long dbCaGetAlarmLimits(const struct link *plink,
|
||||
return gotAttributes ? 0 : -1;
|
||||
}
|
||||
|
||||
long dbCaGetPrecision(const struct link *plink, short *precision)
|
||||
static long getPrecision(const struct link *plink, short *precision)
|
||||
{
|
||||
caLink *pca;
|
||||
int gotAttributes;
|
||||
@@ -620,7 +663,7 @@ long dbCaGetPrecision(const struct link *plink, short *precision)
|
||||
return gotAttributes ? 0 : -1;
|
||||
}
|
||||
|
||||
long dbCaGetUnits(const struct link *plink,
|
||||
static long getUnits(const struct link *plink,
|
||||
char *units, int unitsSize)
|
||||
{
|
||||
caLink *pca;
|
||||
@@ -635,6 +678,52 @@ long dbCaGetUnits(const struct link *plink,
|
||||
return gotAttributes ? 0 : -1;
|
||||
}
|
||||
|
||||
static void scanComplete(void *raw, dbCommon *prec)
|
||||
{
|
||||
caLink *pca = raw;
|
||||
epicsMutexMustLock(pca->lock);
|
||||
if(!pca->plink) {
|
||||
/* IOC shutdown or link re-targeted. Do nothing. */
|
||||
} else if(pca->scanningOnce==0) {
|
||||
errlogPrintf("dbCa.c complete callback w/ scanningOnce==0\n");
|
||||
} else if(--pca->scanningOnce){
|
||||
/* another scan is queued */
|
||||
if(scanOnceCallback(prec, scanComplete, raw)) {
|
||||
errlogPrintf("dbCa.c failed to re-queue scanOnce\n");
|
||||
} else
|
||||
caLinkInc(pca);
|
||||
}
|
||||
epicsMutexUnlock(pca->lock);
|
||||
caLinkDec(pca);
|
||||
}
|
||||
|
||||
/* must be called with pca->lock held */
|
||||
static void scanLinkOnce(dbCommon *prec, caLink *pca) {
|
||||
if(pca->scanningOnce==0) {
|
||||
if(scanOnceCallback(prec, scanComplete, pca)) {
|
||||
errlogPrintf("dbCa.c failed to queue scanOnce\n");
|
||||
} else
|
||||
caLinkInc(pca);
|
||||
}
|
||||
if(pca->scanningOnce<5)
|
||||
pca->scanningOnce++;
|
||||
/* else too many scans queued */
|
||||
}
|
||||
|
||||
static lset dbCa_lset = {
|
||||
LSET_API_VERSION,
|
||||
dbCaReportLink,
|
||||
dbCaRemoveLink,
|
||||
isConnected,
|
||||
getDBFtype, getElements,
|
||||
dbCaGetLink,
|
||||
getControlLimits, getGraphicLimits, getAlarmLimits,
|
||||
getPrecision, getUnits,
|
||||
getAlarm, getTimeStamp,
|
||||
dbCaPutLink,
|
||||
scanForward
|
||||
};
|
||||
|
||||
static void connectionCallback(struct connection_handler_args arg)
|
||||
{
|
||||
caLink *pca;
|
||||
@@ -649,13 +738,13 @@ static void connectionCallback(struct connection_handler_args arg)
|
||||
pca->isConnected = (ca_state(arg.chid) == cs_conn);
|
||||
if (!pca->isConnected) {
|
||||
struct pv_link *ppv_link = &plink->value.pv_link;
|
||||
dbCommon *precord = ppv_link->precord;
|
||||
dbCommon *precord = plink->precord;
|
||||
|
||||
pca->nDisconnect++;
|
||||
if (precord &&
|
||||
((ppv_link->pvlMask & pvlOptCP) ||
|
||||
((ppv_link->pvlMask & pvlOptCPP) && precord->scan == 0)))
|
||||
scanOnce(precord);
|
||||
scanLinkOnce(precord, pca);
|
||||
goto done;
|
||||
}
|
||||
pca->hasReadAccess = ca_read_access(arg.chid);
|
||||
@@ -685,6 +774,7 @@ static void connectionCallback(struct connection_handler_args arg)
|
||||
}
|
||||
pca->gotFirstConnection = TRUE;
|
||||
pca->nelements = ca_element_count(arg.chid);
|
||||
pca->usedelements = 0;
|
||||
pca->dbrType = ca_field_type(arg.chid);
|
||||
if ((plink->value.pv_link.pvlMask & pvlOptInpNative) && !pca->pgetNative) {
|
||||
link_action |= CA_MONITOR_NATIVE;
|
||||
@@ -723,7 +813,7 @@ static void eventCallback(struct event_handler_args arg)
|
||||
if (!plink) goto done;
|
||||
monitor = pca->monitor;
|
||||
userPvt = pca->userPvt;
|
||||
precord = plink->value.pv_link.precord;
|
||||
precord = plink->precord;
|
||||
if (arg.status != ECA_NORMAL) {
|
||||
if (precord) {
|
||||
if (arg.status != ECA_NORDACCESS &&
|
||||
@@ -737,6 +827,7 @@ static void eventCallback(struct event_handler_args arg)
|
||||
goto done;
|
||||
}
|
||||
assert(arg.dbr);
|
||||
assert(arg.count<=pca->nelements);
|
||||
size = arg.count * dbr_value_size[arg.type];
|
||||
if (arg.type == DBR_TIME_STRING &&
|
||||
ca_field_type(pca->chid) == DBR_ENUM) {
|
||||
@@ -753,10 +844,12 @@ static void eventCallback(struct event_handler_args arg)
|
||||
case DBR_TIME_DOUBLE:
|
||||
assert(pca->pgetNative);
|
||||
memcpy(pca->pgetNative, dbr_value_ptr(arg.dbr, arg.type), size);
|
||||
pca->usedelements = arg.count;
|
||||
pca->gotInNative = TRUE;
|
||||
break;
|
||||
default:
|
||||
errMessage(-1, "dbCa: eventCallback Logic Error\n");
|
||||
errlogPrintf("dbCa: eventCallback Logic Error. dbr=%ld dbf=%d\n",
|
||||
arg.type, ca_field_type(pca->chid));
|
||||
break;
|
||||
}
|
||||
pdbr_time_double = (struct dbr_time_double *)arg.dbr;
|
||||
@@ -768,7 +861,7 @@ static void eventCallback(struct event_handler_args arg)
|
||||
|
||||
if ((ppv_link->pvlMask & pvlOptCP) ||
|
||||
((ppv_link->pvlMask & pvlOptCPP) && precord->scan == 0))
|
||||
scanOnce(precord);
|
||||
scanLinkOnce(precord, pca);
|
||||
}
|
||||
done:
|
||||
epicsMutexUnlock(pca->lock);
|
||||
@@ -800,7 +893,7 @@ static void exceptionCallback(struct exception_handler_args args)
|
||||
}
|
||||
}
|
||||
|
||||
static void putCallback(struct event_handler_args arg)
|
||||
static void putComplete(struct event_handler_args arg)
|
||||
{
|
||||
caLink *pca = (caLink *)arg.usr;
|
||||
struct link *plink;
|
||||
@@ -837,11 +930,11 @@ static void accessRightsCallback(struct access_rights_handler_args arg)
|
||||
pca->hasWriteAccess = ca_write_access(arg.chid);
|
||||
if (pca->hasReadAccess && pca->hasWriteAccess) goto done;
|
||||
ppv_link = &plink->value.pv_link;
|
||||
precord = ppv_link->precord;
|
||||
precord = plink->precord;
|
||||
if (precord &&
|
||||
((ppv_link->pvlMask & pvlOptCP) ||
|
||||
((ppv_link->pvlMask & pvlOptCPP) && precord->scan == 0)))
|
||||
scanOnce(precord);
|
||||
scanLinkOnce(precord, pca);
|
||||
done:
|
||||
epicsMutexUnlock(pca->lock);
|
||||
}
|
||||
@@ -868,7 +961,7 @@ static void getAttribEventCallback(struct event_handler_args arg)
|
||||
getAttributes = pca->getAttributes;
|
||||
getAttributesPvt = pca->getAttributesPvt;
|
||||
if (arg.status != ECA_NORMAL) {
|
||||
dbCommon *precord = plink->value.pv_link.precord;
|
||||
dbCommon *precord = plink->precord;
|
||||
if (precord) {
|
||||
errlogPrintf("dbCa: getAttribEventCallback record %s error %s\n",
|
||||
precord->name, ca_message(arg.status));
|
||||
@@ -924,11 +1017,15 @@ static void dbCaTask(void *arg)
|
||||
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 */
|
||||
pca->link_action = 0;
|
||||
if (link_action & CA_CLEAR_CHANNEL) --removesOutstanding;
|
||||
epicsMutexUnlock(workListLock); /* Give back immediately */
|
||||
if (link_action&CA_SYNC)
|
||||
continue;
|
||||
if (link_action & CA_CLEAR_CHANNEL) { /* This must be first */
|
||||
dbCaLinkFree(pca);
|
||||
caLinkDec(pca);
|
||||
/* No alarm is raised. Since link is changing so what? */
|
||||
continue; /* No other link_action makes sense */
|
||||
}
|
||||
@@ -957,13 +1054,13 @@ static void dbCaTask(void *arg)
|
||||
assert(pca->pputNative);
|
||||
if (pca->putType == CA_PUT) {
|
||||
status = ca_array_put(
|
||||
pca->dbrType, pca->nelements,
|
||||
pca->dbrType, pca->putnelements,
|
||||
pca->chid, pca->pputNative);
|
||||
} else if (pca->putType==CA_PUT_CALLBACK) {
|
||||
status = ca_array_put_callback(
|
||||
pca->dbrType, pca->nelements,
|
||||
pca->dbrType, pca->putnelements,
|
||||
pca->chid, pca->pputNative,
|
||||
putCallback, pca);
|
||||
putComplete, pca);
|
||||
} else {
|
||||
status = ECA_PUTFAIL;
|
||||
}
|
||||
@@ -986,7 +1083,7 @@ static void dbCaTask(void *arg)
|
||||
status = ca_array_put_callback(
|
||||
DBR_STRING, 1,
|
||||
pca->chid, pca->pputString,
|
||||
putCallback, pca);
|
||||
putComplete, pca);
|
||||
} else {
|
||||
status = ECA_PUTFAIL;
|
||||
}
|
||||
@@ -1011,15 +1108,15 @@ static void dbCaTask(void *arg)
|
||||
}
|
||||
}
|
||||
if (link_action & CA_MONITOR_NATIVE) {
|
||||
size_t element_size;
|
||||
|
||||
element_size = dbr_value_size[ca_field_type(pca->chid)];
|
||||
|
||||
epicsMutexMustLock(pca->lock);
|
||||
pca->pgetNative = dbCalloc(pca->nelements, element_size);
|
||||
pca->elementSize = dbr_value_size[ca_field_type(pca->chid)];
|
||||
pca->pgetNative = dbCalloc(pca->nelements, pca->elementSize);
|
||||
epicsMutexUnlock(pca->lock);
|
||||
|
||||
status = ca_add_array_event(
|
||||
ca_field_type(pca->chid)+DBR_TIME_STRING,
|
||||
ca_element_count(pca->chid),
|
||||
dbf_type_to_DBR_TIME(ca_field_type(pca->chid)),
|
||||
0, /* dynamic size */
|
||||
pca->chid, eventCallback, pca, 0.0, 0.0, 0.0, 0);
|
||||
if (status != ECA_NORMAL) {
|
||||
errlogPrintf("dbCaTask ca_add_array_event %s\n",
|
||||
|
||||
@@ -1,20 +1,17 @@
|
||||
/*************************************************************************\
|
||||
* Copyright (c) 2002 The University of Chicago, as Operator of Argonne
|
||||
* Copyright (c) 2015 The University of Chicago, as Operator of Argonne
|
||||
* National Laboratory.
|
||||
* Copyright (c) 2002 The Regents of the University of California, as
|
||||
* Operator of Los Alamos National Laboratory.
|
||||
* EPICS BASE Versions 3.13.7
|
||||
* and higher are distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
* EPICS BASE is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
\*************************************************************************/
|
||||
/* dbCa.h */
|
||||
/* dbCa.h */
|
||||
|
||||
#ifndef INCdbCah
|
||||
#define INCdbCah
|
||||
|
||||
#include "shareLib.h"
|
||||
#include "epicsTime.h"
|
||||
#include "link.h"
|
||||
#include "dbLink.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
@@ -23,54 +20,70 @@ extern "C" {
|
||||
typedef void (*dbCaCallback)(void *userPvt);
|
||||
epicsShareFunc void dbCaCallbackProcess(void *usrPvt);
|
||||
|
||||
#ifdef EPICS_DBCA_PRIVATE_API
|
||||
|
||||
epicsShareFunc void dbCaLinkInit(void); /* internal initialization for iocBuild() */
|
||||
epicsShareFunc void dbCaLinkInitIsolated(void); /* internal initialization for iocBuildIsolated() */
|
||||
epicsShareFunc void dbCaRun(void);
|
||||
epicsShareFunc void dbCaPause(void);
|
||||
epicsShareFunc void dbCaShutdown(void);
|
||||
|
||||
struct dbLocker;
|
||||
epicsShareFunc void dbCaAddLinkCallback(struct link *plink,
|
||||
dbCaCallback connect, dbCaCallback monitor, void *userPvt);
|
||||
#define dbCaAddLink(plink) dbCaAddLinkCallback((plink), 0, 0, 0)
|
||||
epicsShareFunc void dbCaRemoveLink(struct link *plink);
|
||||
epicsShareFunc long dbCaAddLink(struct dbLocker *locker, struct link *plink, short dbfType);
|
||||
epicsShareFunc void dbCaRemoveLink(struct dbLocker *locker, struct link *plink);
|
||||
|
||||
epicsShareFunc long dbCaGetLink(struct link *plink,
|
||||
short dbrType, void *pbuffer, epicsEnum16 *pstat, epicsEnum16 *psevr,
|
||||
long *nRequest);
|
||||
|
||||
epicsShareFunc long dbCaGetAttributes(const struct link *plink,
|
||||
dbCaCallback callback, void *userPvt);
|
||||
|
||||
epicsShareFunc long dbCaPutLinkCallback(struct link *plink,
|
||||
short dbrType, const void *pbuffer,long nRequest,
|
||||
dbCaCallback callback, void *userPvt);
|
||||
#define dbCaPutLink(plink, dbrType, pbuffer, nRequest) \
|
||||
dbCaPutLinkCallback((plink), (dbrType), (pbuffer), (nRequest), 0, 0)
|
||||
epicsShareFunc int dbCaIsLinkConnected(const struct link *plink);
|
||||
epicsShareFunc void dbCaScanFwdLink(struct link *plink);
|
||||
|
||||
/* The following are available after the link is connected*/
|
||||
epicsShareFunc long dbCaGetNelements(const struct link *plink,
|
||||
long *nelements);
|
||||
#define dbCaGetSevr(plink, severity) \
|
||||
dbCaGetAlarm((plink), NULL, (severity))
|
||||
epicsShareFunc long dbCaGetAlarm(const struct link *plink,
|
||||
epicsEnum16 *status, epicsEnum16 *severity);
|
||||
epicsShareFunc long dbCaGetTimeStamp(const struct link *plink,
|
||||
epicsTimeStamp *pstamp);
|
||||
epicsShareFunc int dbCaGetLinkDBFtype(const struct link *plink);
|
||||
|
||||
/*The following are available after attribute request is complete*/
|
||||
epicsShareFunc long dbCaGetAttributes(const struct link *plink,
|
||||
dbCaCallback callback, void *userPvt);
|
||||
epicsShareFunc long dbCaGetControlLimits(const struct link *plink,
|
||||
double *low, double *high);
|
||||
epicsShareFunc long dbCaGetGraphicLimits(const struct link *plink,
|
||||
double *low, double *high);
|
||||
epicsShareFunc long dbCaGetAlarmLimits(const struct link *plink,
|
||||
double *lolo, double *low, double *high, double *hihi);
|
||||
epicsShareFunc long dbCaGetPrecision(const struct link *plink,
|
||||
short *precision);
|
||||
epicsShareFunc long dbCaGetUnits(const struct link *plink,
|
||||
char *units, int unitsSize);
|
||||
epicsShareFunc long dbCaPutLink(struct link *plink,short dbrType,
|
||||
const void *pbuffer,long nRequest);
|
||||
|
||||
extern struct ca_client_context * dbCaClientContext;
|
||||
|
||||
epicsShareFunc void dbCaSync(void);
|
||||
epicsShareFunc void dbCaReportLink(const struct link *plink, dbLinkReportInfo *pinfo);
|
||||
epicsShareExtern void (*dbAddLinkHook)(struct link *link, short dbfType);
|
||||
epicsShareFunc void dbSetBrokenLink(struct link *link, short dbfType);
|
||||
#endif
|
||||
|
||||
/* These macros are for backwards compatibility */
|
||||
|
||||
#define dbCaIsLinkConnected(link) \
|
||||
dbIsLinkConnected(link)
|
||||
|
||||
#define dbCaGetLinkDBFtype(link) \
|
||||
dbGetLinkDBFtype(link)
|
||||
#define dbCaGetNelements(link, nelements) \
|
||||
dbGetNelements(link, nelements)
|
||||
#define dbCaGetSevr(link, sevr) \
|
||||
dbGetAlarm(link, NULL, sevr)
|
||||
#define dbCaGetAlarm(link, stat, sevr) \
|
||||
dbGetAlarm(link, stat, sevr)
|
||||
#define dbCaGetTimeStamp(link, pstamp) \
|
||||
dbGetTimeStamp(link, pstamp)
|
||||
#define dbCaGetControlLimits(link, low, high) \
|
||||
dbGetControlLimits(link, low, high)
|
||||
#define dbCaGetGraphicLimits(link, low, high) \
|
||||
dbGetGraphicLimits(link, low, high)
|
||||
#define dbCaGetAlarmLimits(link, lolo, low, high, hihi) \
|
||||
dbGetAlarmLimits(link, lolo, low, high, hihi)
|
||||
#define dbCaGetPrecision(link, prec) \
|
||||
dbGetPrecision(link, prec)
|
||||
#define dbCaGetUnits(link, units, unitSize) \
|
||||
dbGetUnits(link, units, unitSize)
|
||||
|
||||
#define dbCaScanFwdLink(link) \
|
||||
dbScanFwdLink(link)
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -6,14 +6,11 @@
|
||||
* EPICS BASE is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
\*************************************************************************/
|
||||
/* dbCaPvt.h */
|
||||
/****************************************************************
|
||||
*
|
||||
* Current Author: Bob Dalesio
|
||||
* Contributing Author: Marty Kraimer
|
||||
* Date: 08APR96
|
||||
*
|
||||
****************************************************************/
|
||||
/* dbCaPvt.h
|
||||
*
|
||||
* Original Authors: Bob Dalesio, Marty Kraimer
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef INC_dbCaPvt_H
|
||||
#define INC_dbCaPvt_H
|
||||
@@ -32,62 +29,67 @@
|
||||
#define CA_MONITOR_NATIVE 0x10
|
||||
#define CA_MONITOR_STRING 0x20
|
||||
#define CA_GET_ATTRIBUTES 0x40
|
||||
#define CA_SYNC 0x1000
|
||||
/* write type */
|
||||
#define CA_PUT 0x1
|
||||
#define CA_PUT_CALLBACK 0x2
|
||||
|
||||
|
||||
typedef struct caLink
|
||||
{
|
||||
ELLNODE node;
|
||||
epicsMutexId lock;
|
||||
struct link *plink;
|
||||
char *pvname;
|
||||
chid chid;
|
||||
short link_action;
|
||||
/* The following have new values after each data event*/
|
||||
epicsEnum16 sevr;
|
||||
epicsEnum16 stat;
|
||||
epicsTimeStamp timeStamp;
|
||||
/* The following have values after connection*/
|
||||
short dbrType;
|
||||
long nelements;
|
||||
char hasReadAccess;
|
||||
char hasWriteAccess;
|
||||
char isConnected;
|
||||
char gotFirstConnection;
|
||||
/* The following are for dbCaAddLinkCallback */
|
||||
dbCaCallback connect;
|
||||
dbCaCallback monitor;
|
||||
void *userPvt;
|
||||
/* The following are for write request */
|
||||
short putType;
|
||||
dbCaCallback putCallback;
|
||||
void *putUserPvt;
|
||||
struct link *plinkPutCallback;
|
||||
/* The following are for access to additional attributes*/
|
||||
char gotAttributes;
|
||||
dbCaCallback getAttributes;
|
||||
void *getAttributesPvt;
|
||||
/* The following have values after getAttribEventCallback*/
|
||||
double controlLimits[2];
|
||||
double displayLimits[2];
|
||||
double alarmLimits[4];
|
||||
short precision;
|
||||
char units[MAX_UNITS_SIZE]; /* units of value */
|
||||
/* The following are for handling data*/
|
||||
void *pgetNative;
|
||||
char *pgetString;
|
||||
void *pputNative;
|
||||
char *pputString;
|
||||
char gotInNative;
|
||||
char gotInString;
|
||||
char gotOutNative;
|
||||
char gotOutString;
|
||||
char newOutNative;
|
||||
char newOutString;
|
||||
/* The following are for dbcar*/
|
||||
unsigned long nDisconnect;
|
||||
unsigned long nNoWrite; /*only modified by dbCaPutLink*/
|
||||
ELLNODE node;
|
||||
int refcount;
|
||||
epicsMutexId lock;
|
||||
struct link *plink;
|
||||
char *pvname;
|
||||
chid chid;
|
||||
short link_action;
|
||||
/* The following have new values after each data event*/
|
||||
epicsEnum16 sevr;
|
||||
epicsEnum16 stat;
|
||||
epicsTimeStamp timeStamp;
|
||||
/* The following have values after connection*/
|
||||
short dbrType;
|
||||
size_t elementSize; /* size of one element in pgetNative */
|
||||
unsigned long nelements; /* PVs max array size */
|
||||
unsigned long usedelements; /* currently used in pgetNative */
|
||||
unsigned long putnelements; /* currently used in pputNative */
|
||||
char hasReadAccess;
|
||||
char hasWriteAccess;
|
||||
char isConnected;
|
||||
char gotFirstConnection;
|
||||
/* The following are for dbCaAddLinkCallback */
|
||||
dbCaCallback connect;
|
||||
dbCaCallback monitor;
|
||||
void *userPvt;
|
||||
/* The following are for write request */
|
||||
short putType;
|
||||
dbCaCallback putCallback;
|
||||
void *putUserPvt;
|
||||
/* The following are for access to additional attributes*/
|
||||
char gotAttributes;
|
||||
dbCaCallback getAttributes;
|
||||
void *getAttributesPvt;
|
||||
/* The following have values after getAttribEventCallback*/
|
||||
double controlLimits[2];
|
||||
double displayLimits[2];
|
||||
double alarmLimits[4];
|
||||
short precision;
|
||||
char units[MAX_UNITS_SIZE]; /* units of value */
|
||||
/* The following are for handling data*/
|
||||
void *pgetNative;
|
||||
char *pgetString;
|
||||
void *pputNative;
|
||||
char *pputString;
|
||||
char gotInNative;
|
||||
char gotInString;
|
||||
char gotOutNative;
|
||||
char gotOutString;
|
||||
char newOutNative;
|
||||
char newOutString;
|
||||
unsigned char scanningOnce;
|
||||
/* The following are for dbcar*/
|
||||
unsigned long nDisconnect;
|
||||
unsigned long nNoWrite; /*only modified by dbCaPutLink*/
|
||||
}caLink;
|
||||
|
||||
#endif /* INC_dbCaPvt_H */
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
|
||||
#define EPICS_DBCA_PRIVATE_API
|
||||
#include "dbDefs.h"
|
||||
#include "epicsEvent.h"
|
||||
#include "epicsPrint.h"
|
||||
@@ -52,7 +53,61 @@
|
||||
#include "dbLock.h"
|
||||
#include "link.h"
|
||||
|
||||
|
||||
void dbCaReportLink(const struct link *plink, dbLinkReportInfo *pinfo)
|
||||
{
|
||||
caLink * const pca = (caLink *)plink->value.pv_link.pvt;
|
||||
const char * fname = dbGetFieldName(pinfo->pentry),
|
||||
* rname = dbGetRecordName(pinfo->pentry);
|
||||
|
||||
assert(pca);
|
||||
epicsMutexLock(pca->lock);
|
||||
assert(pca->plink==plink);
|
||||
|
||||
pinfo->connected = ca_field_type(pca->chid) != TYPENOTCONN;
|
||||
pinfo->nWriteFail = pca->nNoWrite;
|
||||
pinfo->nDisconnect = pca->nDisconnect;
|
||||
|
||||
if (pinfo->connected) {
|
||||
pinfo->readable = ca_read_access(pca->chid);
|
||||
pinfo->writable = ca_write_access(pca->chid);
|
||||
|
||||
if (pinfo->filter==dbLinkReportAll || pinfo->filter==dbLinkReportConnected) {
|
||||
int rw = pinfo->readable |
|
||||
pinfo->writable << 1;
|
||||
static const char *rights[4] = {
|
||||
"No Access", "Read Only",
|
||||
"Write Only", "Read/Write"
|
||||
};
|
||||
int mask = plink->value.pv_link.pvlMask;
|
||||
printf(LSET_REPORT_INDENT "%28s.%-4s ==> %-28s (%lu, %lu)\n",
|
||||
rname,
|
||||
fname,
|
||||
plink->value.pv_link.pvname,
|
||||
pca->nDisconnect,
|
||||
pca->nNoWrite);
|
||||
printf(LSET_REPORT_INDENT "%21s [%s%s%s%s] host %s, %s\n", "",
|
||||
mask & pvlOptInpNative ? "IN" : " ",
|
||||
mask & pvlOptInpString ? "IS" : " ",
|
||||
mask & pvlOptOutNative ? "ON" : " ",
|
||||
mask & pvlOptOutString ? "OS" : " ",
|
||||
ca_host_name(pca->chid),
|
||||
rights[rw]);
|
||||
}
|
||||
} else {
|
||||
if (pinfo->filter==dbLinkReportAll || pinfo->filter==dbLinkReportDisconnected) {
|
||||
printf(LSET_REPORT_INDENT "%28s.%-4s --> %-28s (%lu, %lu)\n",
|
||||
rname,
|
||||
fname,
|
||||
plink->value.pv_link.pvname,
|
||||
pca->nDisconnect,
|
||||
pca->nNoWrite);
|
||||
}
|
||||
}
|
||||
|
||||
epicsMutexUnlock(pca->lock);
|
||||
}
|
||||
|
||||
|
||||
long dbcar(char *precordname, int level)
|
||||
{
|
||||
DBENTRY dbentry;
|
||||
@@ -68,7 +123,7 @@ long dbcar(char *precordname, int level)
|
||||
int noWriteAccess=0;
|
||||
unsigned long nDisconnect=0;
|
||||
unsigned long nNoWrite=0;
|
||||
caLink *pca;
|
||||
|
||||
int j;
|
||||
|
||||
if (!precordname || precordname[0] == '\0' || !strcmp(precordname, "*")) {
|
||||
@@ -87,56 +142,32 @@ long dbcar(char *precordname, int level)
|
||||
!dbIsAlias(pdbentry)) {
|
||||
pdbRecordType = pdbentry->precordType;
|
||||
precord = (dbCommon *)pdbentry->precnode->precord;
|
||||
dbScanLock(precord);
|
||||
for (j=0; j<pdbRecordType->no_links; j++) {
|
||||
pdbFldDes = pdbRecordType->papFldDes[pdbRecordType->link_ind[j]];
|
||||
pdbentry->pflddes = pdbFldDes;
|
||||
plink = (DBLINK *)((char *)precord + pdbFldDes->offset);
|
||||
dbLockSetGblLock();
|
||||
if (plink->type == CA_LINK) {
|
||||
ncalinks++;
|
||||
pca = (caLink *)plink->value.pv_link.pvt;
|
||||
if (pca
|
||||
&& pca->chid
|
||||
&& (ca_field_type(pca->chid) != TYPENOTCONN)) {
|
||||
nconnected++;
|
||||
nDisconnect += pca->nDisconnect;
|
||||
nNoWrite += pca->nNoWrite;
|
||||
if (!ca_read_access(pca->chid)) noReadAccess++;
|
||||
if (!ca_write_access(pca->chid)) noWriteAccess++;
|
||||
if (level>1) {
|
||||
int rw = ca_read_access(pca->chid) |
|
||||
ca_write_access(pca->chid) << 1;
|
||||
static const char *rights[4] = {
|
||||
"No Access", "Read Only",
|
||||
"Write Only", "Read/Write"
|
||||
};
|
||||
int mask = plink->value.pv_link.pvlMask;
|
||||
printf("%28s.%-4s ==> %-28s (%lu, %lu)\n",
|
||||
precord->name,
|
||||
pdbFldDes->name,
|
||||
plink->value.pv_link.pvname,
|
||||
pca->nDisconnect,
|
||||
pca->nNoWrite);
|
||||
printf("%21s [%s%s%s%s] host %s, %s\n", "",
|
||||
mask & pvlOptInpNative ? "IN" : " ",
|
||||
mask & pvlOptInpString ? "IS" : " ",
|
||||
mask & pvlOptOutNative ? "ON" : " ",
|
||||
mask & pvlOptOutString ? "OS" : " ",
|
||||
ca_host_name(pca->chid),
|
||||
rights[rw]);
|
||||
}
|
||||
} else {
|
||||
if (level>0) {
|
||||
printf("%28s.%-4s --> %-28s (%lu, %lu)\n",
|
||||
precord->name,
|
||||
pdbFldDes->name,
|
||||
plink->value.pv_link.pvname,
|
||||
pca->nDisconnect,
|
||||
pca->nNoWrite);
|
||||
}
|
||||
}
|
||||
dbLinkReportInfo linfo;
|
||||
memset(&linfo, 0, sizeof(linfo));
|
||||
linfo.pentry = pdbentry;
|
||||
if(level==0)
|
||||
linfo.filter = dbLinkReportNone;
|
||||
else if(level==1)
|
||||
linfo.filter = dbLinkReportDisconnected;
|
||||
else
|
||||
linfo.filter = dbLinkReportAll;
|
||||
if(level>2)
|
||||
linfo.detailLevel = level-2;
|
||||
dbReportLink(plink, &linfo);
|
||||
nconnected += linfo.connected;
|
||||
nDisconnect += linfo.nDisconnect;
|
||||
noReadAccess += !linfo.readable;
|
||||
noWriteAccess += !linfo.writable;
|
||||
}
|
||||
dbLockSetGblUnlock();
|
||||
}
|
||||
dbScanUnlock(precord);
|
||||
if (precordname) goto done;
|
||||
}
|
||||
status = dbNextRecord(pdbentry);
|
||||
@@ -189,10 +220,8 @@ void dbcaStats(int *pchans, int *pdiscon)
|
||||
dbFldDes *pdbFldDes = pdbRecordType->papFldDes[i];
|
||||
plink = (DBLINK *)((char *)precord + pdbFldDes->offset);
|
||||
if (plink->type == CA_LINK) {
|
||||
caLink *pca = (caLink *)plink->value.pv_link.pvt;
|
||||
|
||||
ncalinks++;
|
||||
if (pca && pca->chid && ca_state(pca->chid) == cs_conn) {
|
||||
if (dbIsLinkConnected(plink)) {
|
||||
nconnected++;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,7 +19,6 @@
|
||||
|
||||
#include "cantProceed.h"
|
||||
#include "epicsAssert.h"
|
||||
#include "epicsExit.h"
|
||||
#include "epicsString.h"
|
||||
#include "errlog.h"
|
||||
#include "freeList.h"
|
||||
@@ -704,21 +703,27 @@ void dbChannelShow(dbChannel *chan, int level, const unsigned short indent)
|
||||
int pre = ellCount(&chan->pre_chain);
|
||||
int post = ellCount(&chan->post_chain);
|
||||
|
||||
printf("%*schannel name: %s\n", indent, "", chan->name);
|
||||
printf("%*s field_type=%s (%d bytes), dbr_type=%s, %ld element%s, %d filter%s", indent, "",
|
||||
dbGetFieldTypeString(chan->addr.field_type), chan->addr.field_size,
|
||||
dbGetFieldTypeString(chan->addr.dbr_field_type), elems, elems == 1 ? "" : "s",
|
||||
count, count == 1 ? "" : "s");
|
||||
if (count)
|
||||
printf(" (%d pre eventq, %d post eventq)\n", pre, post);
|
||||
else
|
||||
printf("\n");
|
||||
if (level > 0)
|
||||
dbChannelFilterShow(chan, level - 1, indent + 2);
|
||||
if (count) {
|
||||
printf("%*s final field_type=%s (%dB), %ld element%s\n", indent, "",
|
||||
dbGetFieldTypeString(chan->final_type), chan->final_field_size,
|
||||
felems, felems == 1 ? "" : "s");
|
||||
printf("%*sChannel: '%s'\n", indent, "", chan->name);
|
||||
if (level > 0) {
|
||||
printf("%*sfield_type=%s (%d bytes), dbr_type=%s, %ld element%s",
|
||||
indent + 4, "",
|
||||
dbGetFieldTypeString(chan->addr.field_type),
|
||||
chan->addr.field_size,
|
||||
dbGetFieldTypeString(chan->addr.dbr_field_type),
|
||||
elems, elems == 1 ? "" : "s");
|
||||
if (count)
|
||||
printf("\n%*s%d filter%s (%d pre eventq, %d post eventq)\n",
|
||||
indent + 4, "", count, count == 1 ? "" : "s", pre, post);
|
||||
else
|
||||
printf(", no filters\n");
|
||||
if (level > 1)
|
||||
dbChannelFilterShow(chan, level - 2, indent + 8);
|
||||
if (count) {
|
||||
printf("%*sfinal field_type=%s (%dB), %ld element%s\n", indent + 4, "",
|
||||
dbGetFieldTypeString(chan->final_type),
|
||||
chan->final_field_size,
|
||||
felems, felems == 1 ? "" : "s");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
118
src/ioc/db/dbChannelNOOP.h
Normal file
118
src/ioc/db/dbChannelNOOP.h
Normal file
@@ -0,0 +1,118 @@
|
||||
#ifndef DBCHANNELNOOP_H
|
||||
#define DBCHANNELNOOP_H
|
||||
|
||||
#include <string.h>
|
||||
#include <string>
|
||||
|
||||
#include "cacIO.h"
|
||||
#include "caerr.h"
|
||||
|
||||
/** @brief A channel which never connects
|
||||
*
|
||||
* Used when dbCa is placed in isolated mode for unittests
|
||||
*/
|
||||
class dbChannelNOOP : public cacChannel
|
||||
{
|
||||
std::string myname;
|
||||
public:
|
||||
dbChannelNOOP(const char *name, cacChannelNotify ¬ify)
|
||||
:cacChannel(notify)
|
||||
,myname(name)
|
||||
{}
|
||||
|
||||
virtual void destroy (
|
||||
CallbackGuard & /*callbackGuard*/,
|
||||
epicsGuard < epicsMutex > & /*mutualExclusionGuard*/ )
|
||||
{
|
||||
delete this; // goodbye cruel world
|
||||
}
|
||||
|
||||
virtual unsigned getName (
|
||||
epicsGuard < epicsMutex > &,
|
||||
char * pBuf, unsigned bufLen ) const throw ()
|
||||
{
|
||||
const char* name = myname.c_str();
|
||||
if(bufLen>myname.size()+1) {
|
||||
bufLen=myname.size()+1;
|
||||
}
|
||||
memcpy(pBuf, name, bufLen);
|
||||
pBuf[--bufLen] = '\0';
|
||||
return bufLen;
|
||||
}
|
||||
|
||||
// !! deprecated, avoid use !!
|
||||
virtual const char * pName (
|
||||
epicsGuard < epicsMutex > & guard ) const throw ()
|
||||
{return myname.c_str();}
|
||||
|
||||
virtual void show (
|
||||
epicsGuard < epicsMutex > &,
|
||||
unsigned level ) const
|
||||
{}
|
||||
|
||||
virtual void initiateConnect (
|
||||
epicsGuard < epicsMutex > & )
|
||||
{}
|
||||
|
||||
virtual unsigned requestMessageBytesPending (
|
||||
epicsGuard < epicsMutex > & /*mutualExclusionGuard*/ )
|
||||
{return 0;}
|
||||
|
||||
virtual void flush (
|
||||
epicsGuard < epicsMutex > & /*mutualExclusionGuard*/ )
|
||||
{}
|
||||
|
||||
virtual ioStatus read (
|
||||
epicsGuard < epicsMutex > &mut,
|
||||
unsigned type, arrayElementCount count,
|
||||
cacReadNotify ¬ify, ioid * = 0 )
|
||||
{
|
||||
notify.exception(mut, ECA_NORDACCESS, "dbChannelNOOP", type, count);
|
||||
return iosSynch;
|
||||
}
|
||||
|
||||
virtual void write (
|
||||
epicsGuard < epicsMutex > &,
|
||||
unsigned type, arrayElementCount count,
|
||||
const void *pValue )
|
||||
{}
|
||||
|
||||
virtual ioStatus write (
|
||||
epicsGuard < epicsMutex > &mut,
|
||||
unsigned type, arrayElementCount count,
|
||||
const void */*pValue*/, cacWriteNotify & notify, ioid * = 0 )
|
||||
{
|
||||
notify.exception(mut, ECA_NOWTACCESS, "dbChannelNOOP", type, count);
|
||||
return iosSynch;
|
||||
}
|
||||
|
||||
virtual void subscribe (
|
||||
epicsGuard < epicsMutex > &mut, unsigned type,
|
||||
arrayElementCount count, unsigned /*mask*/, cacStateNotify & notify,
|
||||
ioid * = 0 )
|
||||
{
|
||||
// should never subscribe
|
||||
notify.exception(mut, ECA_BADMASK, "dbChannelNOOP", type, count);
|
||||
}
|
||||
|
||||
virtual void ioCancel (
|
||||
CallbackGuard & callbackGuard,
|
||||
epicsGuard < epicsMutex > & mutualExclusionGuard,
|
||||
const ioid & )
|
||||
{}
|
||||
|
||||
virtual void ioShow (
|
||||
epicsGuard < epicsMutex > &,
|
||||
const ioid &, unsigned level ) const
|
||||
{}
|
||||
|
||||
virtual short nativeType (
|
||||
epicsGuard < epicsMutex > & ) const
|
||||
{return 0;} // DBR_STRING
|
||||
|
||||
virtual arrayElementCount nativeElementCount (
|
||||
epicsGuard < epicsMutex > & ) const
|
||||
{return 1;}
|
||||
};
|
||||
|
||||
#endif // DBCHANNELNOOP_H
|
||||
@@ -92,6 +92,12 @@
|
||||
interest(4)
|
||||
extra("ELLLIST mlis")
|
||||
}
|
||||
field(BKLNK,DBF_NOACCESS) {
|
||||
prompt("Backwards link tracking")
|
||||
special(SPC_NOMOD)
|
||||
interest(4)
|
||||
extra("ELLLIST bklnk")
|
||||
}
|
||||
field(DISP,DBF_UCHAR) {
|
||||
prompt("Disable putField")
|
||||
}
|
||||
|
||||
@@ -39,6 +39,7 @@
|
||||
#include "dbCAC.h"
|
||||
#include "dbChannel.h"
|
||||
#include "dbChannelIO.h"
|
||||
#include "dbChannelNOOP.h"
|
||||
#include "dbPutNotifyBlocker.h"
|
||||
|
||||
class dbService : public cacService {
|
||||
@@ -61,9 +62,16 @@ cacContext & dbService::contextCreate (
|
||||
mutualExclusion, notify );
|
||||
}
|
||||
|
||||
extern "C" int dbServiceIsolate;
|
||||
int dbServiceIsolate = 0;
|
||||
|
||||
extern "C" void dbServiceIOInit ()
|
||||
{
|
||||
caInstallDefaultService ( dbs );
|
||||
static int init=0;
|
||||
if(!init) {
|
||||
caInstallDefaultService ( dbs );
|
||||
init=1;
|
||||
}
|
||||
}
|
||||
|
||||
dbBaseIO::dbBaseIO () {}
|
||||
@@ -72,7 +80,8 @@ dbContext::dbContext ( epicsMutex & cbMutexIn,
|
||||
epicsMutex & mutexIn, cacContextNotify & notifyIn ) :
|
||||
readNotifyCache ( mutexIn ), ctx ( 0 ),
|
||||
stateNotifyCacheSize ( 0 ), mutex ( mutexIn ), cbMutex ( cbMutexIn ),
|
||||
notify ( notifyIn ), pNetContext ( 0 ), pStateNotifyCache ( 0 )
|
||||
notify ( notifyIn ), pNetContext ( 0 ), pStateNotifyCache ( 0 ),
|
||||
isolated(dbServiceIsolate)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -92,7 +101,10 @@ cacChannel & dbContext::createChannel (
|
||||
|
||||
dbChannel *dbch = dbChannel_create ( pName );
|
||||
if ( ! dbch ) {
|
||||
if ( ! this->pNetContext.get() ) {
|
||||
if ( isolated ) {
|
||||
return *new dbChannelNOOP(pName, notifyIn);
|
||||
|
||||
} else if ( ! this->pNetContext.get() ) {
|
||||
this->pNetContext.reset (
|
||||
& this->notify.createNetworkContext (
|
||||
this->mutex, this->cbMutex ) );
|
||||
@@ -142,7 +154,8 @@ void dbContext::callStateNotify ( struct dbChannel * dbch,
|
||||
const struct db_field_log * pfl,
|
||||
cacStateNotify & notifyIn )
|
||||
{
|
||||
unsigned long size = dbr_size_n ( type, count );
|
||||
long realcount = (count==0)?dbChannelElements(dbch):count;
|
||||
unsigned long size = dbr_size_n ( type, realcount );
|
||||
|
||||
if ( type > INT_MAX ) {
|
||||
epicsGuard < epicsMutex > guard ( this->mutex );
|
||||
@@ -169,8 +182,13 @@ void dbContext::callStateNotify ( struct dbChannel * dbch,
|
||||
this->stateNotifyCacheSize = size;
|
||||
}
|
||||
void *pvfl = (void *) pfl;
|
||||
int status = dbChannel_get ( dbch, static_cast <int> ( type ),
|
||||
this->pStateNotifyCache, static_cast <int> ( count ), pvfl );
|
||||
int status;
|
||||
if(count==0) /* fetch actual number of elements (dynamic array) */
|
||||
status = dbChannel_get_count( dbch, static_cast <int> ( type ),
|
||||
this->pStateNotifyCache, &realcount, pvfl );
|
||||
else /* fetch requested number of elements, truncated or zero padded */
|
||||
status = dbChannel_get( dbch, static_cast <int> ( type ),
|
||||
this->pStateNotifyCache, realcount, pvfl );
|
||||
if ( status ) {
|
||||
epicsGuard < epicsMutex > guard ( this->mutex );
|
||||
notifyIn.exception ( guard, ECA_GETFAIL,
|
||||
@@ -178,7 +196,7 @@ void dbContext::callStateNotify ( struct dbChannel * dbch,
|
||||
}
|
||||
else {
|
||||
epicsGuard < epicsMutex > guard ( this->mutex );
|
||||
notifyIn.current ( guard, type, count, this->pStateNotifyCache );
|
||||
notifyIn.current ( guard, type, realcount, this->pStateNotifyCache );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -61,27 +61,32 @@ void dbContextReadNotifyCache::callReadNotify (
|
||||
return;
|
||||
}
|
||||
|
||||
if ( dbChannelElements(dbch) < 0 ) {
|
||||
const long maxcount = dbChannelElements(dbch);
|
||||
|
||||
if ( maxcount < 0 ) {
|
||||
notify.exception ( guard, ECA_BADCOUNT,
|
||||
"database has negetive element count",
|
||||
type, count);
|
||||
return;
|
||||
}
|
||||
|
||||
if ( count > static_cast < unsigned long > ( dbChannelElements(dbch) ) ) {
|
||||
} else if ( count > (unsigned long)maxcount ) {
|
||||
notify.exception ( guard, ECA_BADCOUNT,
|
||||
"element count out of range (high side)",
|
||||
type, count);
|
||||
return;
|
||||
}
|
||||
|
||||
unsigned long size = dbr_size_n ( type, count );
|
||||
long realcount = (count==0)?maxcount:count;
|
||||
unsigned long size = dbr_size_n ( type, realcount );
|
||||
|
||||
privateAutoDestroyPtr ptr ( _allocator, size );
|
||||
int status;
|
||||
{
|
||||
epicsGuardRelease < epicsMutex > unguard ( guard );
|
||||
status = dbChannel_get ( dbch, static_cast <int> ( type ),
|
||||
ptr.get (), static_cast <long> ( count ), 0 );
|
||||
if ( count==0 )
|
||||
status = dbChannel_get_count ( dbch, (int)type, ptr.get(), &realcount, 0);
|
||||
else
|
||||
status = dbChannel_get ( dbch, (int)type, ptr.get (), realcount, 0 );
|
||||
}
|
||||
if ( status ) {
|
||||
notify.exception ( guard, ECA_GETFAIL,
|
||||
@@ -90,7 +95,7 @@ void dbContextReadNotifyCache::callReadNotify (
|
||||
}
|
||||
else {
|
||||
notify.completion (
|
||||
guard, type, count, ptr.get () );
|
||||
guard, type, realcount, ptr.get () );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -979,6 +979,7 @@ static void event_task (void *pParm)
|
||||
{
|
||||
struct event_user * const evUser = (struct event_user *) pParm;
|
||||
struct event_que * ev_que;
|
||||
unsigned char pendexit;
|
||||
|
||||
/* init hook */
|
||||
if (evUser->init_func) {
|
||||
@@ -1020,9 +1021,10 @@ static void event_task (void *pParm)
|
||||
event_read (ev_que);
|
||||
epicsMutexMustLock ( evUser->lock );
|
||||
}
|
||||
pendexit = evUser->pendexit;
|
||||
epicsMutexUnlock ( evUser->lock );
|
||||
|
||||
} while ( ! evUser->pendexit );
|
||||
} while( ! pendexit );
|
||||
|
||||
epicsMutexDestroy(evUser->firstque.writelock);
|
||||
|
||||
|
||||
@@ -233,6 +233,17 @@ static void dbtpnCallFunc(const iocshArgBuf *args)
|
||||
static const iocshFuncDef dbNotifyDumpFuncDef = {"dbNotifyDump",0,0};
|
||||
static void dbNotifyDumpCallFunc(const iocshArgBuf *args) { dbNotifyDump();}
|
||||
|
||||
/* dbPutAttribute */
|
||||
static const iocshArg dbPutAttrArg0 = { "record type",iocshArgString};
|
||||
static const iocshArg dbPutAttrArg1 = { "attribute name",iocshArgString};
|
||||
static const iocshArg dbPutAttrArg2 = { "value",iocshArgString};
|
||||
static const iocshArg * const dbPutAttrArgs[] =
|
||||
{&dbPutAttrArg0, &dbPutAttrArg1, &dbPutAttrArg2};
|
||||
static const iocshFuncDef dbPutAttrFuncDef =
|
||||
{"dbPutAttribute",3,dbPutAttrArgs};
|
||||
static void dbPutAttrCallFunc(const iocshArgBuf *args)
|
||||
{ dbPutAttribute(args[0].sval,args[1].sval,args[2].sval);}
|
||||
|
||||
/* tpn */
|
||||
static const iocshArg tpnArg0 = { "record name",iocshArgString};
|
||||
static const iocshArg tpnArg1 = { "value",iocshArgString};
|
||||
@@ -405,6 +416,7 @@ void dbIocRegister(void)
|
||||
iocshRegister(&pftFuncDef,pftCallFunc);
|
||||
iocshRegister(&dbtpnFuncDef,dbtpnCallFunc);
|
||||
iocshRegister(&dbNotifyDumpFuncDef,dbNotifyDumpCallFunc);
|
||||
iocshRegister(&dbPutAttrFuncDef,dbPutAttrCallFunc);
|
||||
iocshRegister(&tpnFuncDef,tpnCallFunc);
|
||||
iocshRegister(&dblsrFuncDef,dblsrCallFunc);
|
||||
iocshRegister(&dbLockShowLockedFuncDef,dbLockShowLockedCallFunc);
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
/*************************************************************************\
|
||||
* Copyright (c) 2010 UChicago Argonne LLC, as Operator of Argonne
|
||||
* National Laboratory.
|
||||
* Copyright (c) 2002 The Regents of the University of California, as
|
||||
* Operator of Los Alamos National Laboratory.
|
||||
* EPICS BASE is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
\*************************************************************************/
|
||||
/* dbLink.c */
|
||||
/* $Id$ */
|
||||
/*
|
||||
* National Laboratory.
|
||||
* Copyright (c) 2002 The Regents of the University of California, as
|
||||
* Operator of Los Alamos National Laboratory.
|
||||
* EPICS BASE is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
\*************************************************************************/
|
||||
/* dbLink.c
|
||||
*
|
||||
* Original Authors: Bob Dalesio, Marty Kraimer
|
||||
* Current Author: Andrew Johnson
|
||||
*/
|
||||
@@ -19,6 +18,7 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#define EPICS_DBCA_PRIVATE_API
|
||||
#include "alarm.h"
|
||||
#include "cantProceed.h"
|
||||
#include "cvtFast.h"
|
||||
@@ -45,7 +45,7 @@
|
||||
#include "dbFldTypes.h"
|
||||
#include "dbFldTypes.h"
|
||||
#include "dbLink.h"
|
||||
#include "dbLock.h"
|
||||
#include "dbLockPvt.h"
|
||||
#include "dbNotify.h"
|
||||
#include "dbScan.h"
|
||||
#include "dbStaticLib.h"
|
||||
@@ -79,7 +79,7 @@ static void inherit_severity(const struct pv_link *ppv_link, dbCommon *pdest,
|
||||
/* How to identify links in error messages */
|
||||
static const char * link_field_name(const struct link *plink)
|
||||
{
|
||||
const struct dbCommon *precord = plink->value.pv_link.precord;
|
||||
const struct dbCommon *precord = plink->precord;
|
||||
const dbRecordType *pdbRecordType = precord->rdes;
|
||||
dbFldDes * const *papFldDes = pdbRecordType->papFldDes;
|
||||
const short *link_ind = pdbRecordType->link_ind;
|
||||
@@ -97,13 +97,28 @@ static const char * link_field_name(const struct link *plink)
|
||||
|
||||
/***************************** Constant Links *****************************/
|
||||
|
||||
/* Forward definition */
|
||||
static lset dbConst_lset;
|
||||
|
||||
static void dbConstInitLink(struct link *plink)
|
||||
{
|
||||
plink->lset = &dbConst_lset;
|
||||
}
|
||||
|
||||
static void dbConstAddLink(struct link *plink)
|
||||
{
|
||||
plink->lset = &dbConst_lset;
|
||||
}
|
||||
|
||||
static long dbConstLoadLink(struct link *plink, short dbrType, void *pbuffer)
|
||||
{
|
||||
if (!plink->value.constantStr)
|
||||
return S_db_badField;
|
||||
|
||||
plink->lset = &dbConst_lset;
|
||||
|
||||
/* Constant strings are always numeric */
|
||||
if (dbrType== DBF_MENU || dbrType == DBF_ENUM || dbrType == DBF_DEVICE)
|
||||
if (dbrType == DBF_MENU || dbrType == DBF_ENUM || dbrType == DBF_DEVICE)
|
||||
dbrType = DBF_USHORT;
|
||||
|
||||
return dbFastPutConvertRoutine[DBR_STRING][dbrType]
|
||||
@@ -116,7 +131,7 @@ static long dbConstGetNelements(const struct link *plink, long *nelements)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long dbConstGetLink(struct link *plink, short dbrType, void *pbuffer,
|
||||
static long dbConstGetValue(struct link *plink, short dbrType, void *pbuffer,
|
||||
epicsEnum16 *pstat, epicsEnum16 *psevr, long *pnRequest)
|
||||
{
|
||||
if (pnRequest)
|
||||
@@ -124,8 +139,25 @@ static long dbConstGetLink(struct link *plink, short dbrType, void *pbuffer,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static lset dbConst_lset = {
|
||||
LSET_API_VERSION,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL, dbConstGetNelements,
|
||||
dbConstGetValue,
|
||||
NULL, NULL, NULL,
|
||||
NULL, NULL,
|
||||
NULL, NULL,
|
||||
NULL,
|
||||
NULL
|
||||
};
|
||||
|
||||
/***************************** Database Links *****************************/
|
||||
|
||||
/* Forward definition */
|
||||
static lset dbDb_lset;
|
||||
|
||||
static long dbDbInitLink(struct link *plink, short dbfType)
|
||||
{
|
||||
DBADDR dbaddr;
|
||||
@@ -136,45 +168,49 @@ static long dbDbInitLink(struct link *plink, short dbfType)
|
||||
if (status)
|
||||
return status;
|
||||
|
||||
plink->lset = &dbDb_lset;
|
||||
plink->type = DB_LINK;
|
||||
pdbAddr = dbCalloc(1, sizeof(struct dbAddr));
|
||||
*pdbAddr = dbaddr; /* structure copy */
|
||||
plink->value.pv_link.pvt = pdbAddr;
|
||||
dbLockSetMerge(plink->value.pv_link.precord, pdbAddr->precord);
|
||||
plink->value.pv_link.backend = "db";
|
||||
ellAdd(&dbaddr.precord->bklnk, &plink->value.pv_link.backlinknode);
|
||||
/* merging into the same lockset is deferred to the caller.
|
||||
* cf. initPVLinks()
|
||||
*/
|
||||
dbLockSetMerge(NULL, plink->precord, dbaddr.precord);
|
||||
assert(plink->precord->lset->plockSet == dbaddr.precord->lset->plockSet);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long dbDbAddLink(struct link *plink, short dbfType)
|
||||
static void dbDbAddLink(dbLocker *locker, struct link *plink, short dbfType, DBADDR *ptarget)
|
||||
{
|
||||
DBADDR dbaddr;
|
||||
long status;
|
||||
DBADDR *pdbAddr;
|
||||
|
||||
status = dbNameToAddr(plink->value.pv_link.pvname, &dbaddr);
|
||||
if (status)
|
||||
return status;
|
||||
|
||||
plink->lset = &dbDb_lset;
|
||||
plink->type = DB_LINK;
|
||||
pdbAddr = dbCalloc(1, sizeof(struct dbAddr));
|
||||
*pdbAddr = dbaddr; /* structure copy */
|
||||
plink->value.pv_link.pvt = pdbAddr;
|
||||
plink->value.pv_link.pvt = ptarget;
|
||||
plink->value.pv_link.backend = "db";
|
||||
ellAdd(&ptarget->precord->bklnk, &plink->value.pv_link.backlinknode);
|
||||
|
||||
dbLockSetRecordLock(pdbAddr->precord);
|
||||
dbLockSetMerge(plink->value.pv_link.precord, pdbAddr->precord);
|
||||
return 0;
|
||||
/* target record is already locked in dbPutFieldLink() */
|
||||
dbLockSetMerge(locker, plink->precord, ptarget->precord);
|
||||
}
|
||||
|
||||
static void dbDbRemoveLink(struct link *plink)
|
||||
static void dbDbRemoveLink(dbLocker *locker, struct link *plink)
|
||||
{
|
||||
free(plink->value.pv_link.pvt);
|
||||
DBADDR *pdbAddr = (DBADDR *) plink->value.pv_link.pvt;
|
||||
plink->value.pv_link.pvt = 0;
|
||||
plink->value.pv_link.backend = NULL;
|
||||
plink->value.pv_link.getCvt = 0;
|
||||
plink->value.pv_link.pvlMask = 0;
|
||||
plink->value.pv_link.lastGetdbrType = 0;
|
||||
plink->type = PV_LINK;
|
||||
dbLockSetSplit(plink->value.pv_link.precord);
|
||||
plink->lset = NULL;
|
||||
ellDelete(&pdbAddr->precord->bklnk, &plink->value.pv_link.backlinknode);
|
||||
dbLockSetSplit(locker, plink->precord, pdbAddr->precord);
|
||||
free(pdbAddr);
|
||||
}
|
||||
|
||||
static int dbDbIsLinkConnected(const struct link *plink)
|
||||
static int dbDbIsConnected(const struct link *plink)
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
@@ -199,7 +235,7 @@ static long dbDbGetValue(struct link *plink, short dbrType, void *pbuffer,
|
||||
{
|
||||
struct pv_link *ppv_link = &plink->value.pv_link;
|
||||
DBADDR *paddr = ppv_link->pvt;
|
||||
dbCommon *precord = plink->value.pv_link.precord;
|
||||
dbCommon *precord = plink->precord;
|
||||
long status;
|
||||
|
||||
/* scan passive records if link is process passive */
|
||||
@@ -363,7 +399,7 @@ static long dbDbPutValue(struct link *plink, short dbrType,
|
||||
const void *pbuffer, long nRequest)
|
||||
{
|
||||
struct pv_link *ppv_link = &plink->value.pv_link;
|
||||
struct dbCommon *psrce = ppv_link->precord;
|
||||
struct dbCommon *psrce = plink->precord;
|
||||
DBADDR *paddr = (DBADDR *) ppv_link->pvt;
|
||||
dbCommon *pdest = paddr->precord;
|
||||
long status = dbPut(paddr, dbrType, pbuffer, nRequest);
|
||||
@@ -394,23 +430,118 @@ static long dbDbPutValue(struct link *plink, short dbrType,
|
||||
|
||||
static void dbDbScanFwdLink(struct link *plink)
|
||||
{
|
||||
dbCommon *precord = plink->value.pv_link.precord;
|
||||
dbCommon *precord = plink->precord;
|
||||
dbAddr *paddr = (dbAddr *) plink->value.pv_link.pvt;
|
||||
|
||||
dbScanPassive(precord, paddr->precord);
|
||||
}
|
||||
|
||||
lset dbDb_lset = { dbDbInitLink, dbDbAddLink, NULL, dbDbRemoveLink,
|
||||
dbDbIsLinkConnected, dbDbGetDBFtype, dbDbGetElements, dbDbGetValue,
|
||||
dbDbGetControlLimits, dbDbGetGraphicLimits, dbDbGetAlarmLimits,
|
||||
dbDbGetPrecision, dbDbGetUnits, dbDbGetAlarm, dbDbGetTimeStamp,
|
||||
dbDbPutValue, dbDbScanFwdLink };
|
||||
static lset dbDb_lset = {
|
||||
LSET_API_VERSION,
|
||||
NULL,
|
||||
dbDbRemoveLink,
|
||||
dbDbIsConnected,
|
||||
dbDbGetDBFtype, dbDbGetElements,
|
||||
dbDbGetValue,
|
||||
dbDbGetControlLimits, dbDbGetGraphicLimits, dbDbGetAlarmLimits,
|
||||
dbDbGetPrecision, dbDbGetUnits,
|
||||
dbDbGetAlarm, dbDbGetTimeStamp,
|
||||
dbDbPutValue,
|
||||
dbDbScanFwdLink
|
||||
};
|
||||
|
||||
static void dbBrokenReportLink(const struct link *plink, dbLinkReportInfo *pinfo)
|
||||
{
|
||||
const char * fname = dbGetFieldName(pinfo->pentry),
|
||||
* rname = dbGetRecordName(pinfo->pentry);
|
||||
|
||||
if (pinfo->filter==dbLinkReportAll || pinfo->filter==dbLinkReportDisconnected) {
|
||||
printf("%28s.%-4s --> %-28s <invalid link type>\n",
|
||||
rname,
|
||||
fname,
|
||||
plink->value.pv_link.pvname);
|
||||
}
|
||||
}
|
||||
|
||||
static void dbBrokenRemoveLink(dbLocker *locker, struct link *plink)
|
||||
{
|
||||
assert(!plink->value.pv_link.pvt);
|
||||
plink->value.pv_link.pvt = 0;
|
||||
plink->value.pv_link.backend = NULL;
|
||||
plink->value.pv_link.getCvt = 0;
|
||||
plink->value.pv_link.pvlMask = 0;
|
||||
plink->value.pv_link.lastGetdbrType = 0;
|
||||
plink->type = PV_LINK;
|
||||
plink->lset = NULL;
|
||||
}
|
||||
|
||||
static lset broken_lset = {
|
||||
LSET_API_VERSION,
|
||||
dbBrokenReportLink,
|
||||
dbBrokenRemoveLink
|
||||
};
|
||||
|
||||
/***************************** Generic Link API *****************************/
|
||||
|
||||
void dbInitLink(struct dbCommon *precord, struct link *plink, short dbfType)
|
||||
void dbSetBrokenLink(struct link *plink, short dbfType)
|
||||
{
|
||||
plink->value.pv_link.precord = precord;
|
||||
plink->lset = &broken_lset;
|
||||
plink->type = CA_LINK;
|
||||
plink->value.pv_link.pvt = NULL;
|
||||
plink->value.pv_link.backend = "invalid";
|
||||
}
|
||||
|
||||
static
|
||||
void dbCaAddLinkHook(struct link *plink, short dbfType)
|
||||
{
|
||||
dbCaAddLink(NULL, plink, dbfType);
|
||||
}
|
||||
|
||||
void (*dbAddLinkHook)(struct link *link, short dbfType) = &dbCaAddLinkHook;
|
||||
|
||||
/* initialize CA_LINK with possibly custom lset */
|
||||
static
|
||||
void customlset(struct link *plink, short dbfType)
|
||||
{
|
||||
int oops = 0;
|
||||
plink->lset = NULL;
|
||||
if(dbAddLinkHook)
|
||||
(*dbAddLinkHook)(plink, dbfType);
|
||||
|
||||
if((plink->lset==NULL) ^ (plink->type==PV_LINK)) {
|
||||
oops = 1;
|
||||
errlogPrintf("custom link types must set both type and lset.\n");
|
||||
}
|
||||
if(plink->lset && plink->lset->version!=LSET_API_VERSION) {
|
||||
oops = 1;
|
||||
errlogPrintf("custom link types must set .version to LSET_API_VERSION (%u) not %u\n",
|
||||
LSET_API_VERSION, plink->lset->version);
|
||||
}
|
||||
if(oops)
|
||||
{
|
||||
plink->lset = NULL;
|
||||
plink->type = PV_LINK;
|
||||
plink->value.pv_link.pvt = NULL; /* leaking */
|
||||
}
|
||||
|
||||
if(!plink->lset)
|
||||
dbSetBrokenLink(plink, dbfType); /* install "invalid" lset as fallback */
|
||||
assert(plink->lset);
|
||||
assert(plink->type==CA_LINK);
|
||||
assert(plink->lset->version==LSET_API_VERSION);
|
||||
}
|
||||
|
||||
void dbInitLink(struct link *plink, short dbfType)
|
||||
{
|
||||
struct dbCommon *precord = plink->precord;
|
||||
|
||||
if (plink->type == CONSTANT) {
|
||||
dbConstInitLink(plink);
|
||||
return;
|
||||
}
|
||||
|
||||
if (plink->type != PV_LINK)
|
||||
return;
|
||||
|
||||
if (plink == &precord->tsel)
|
||||
recGblTSELwasModified(plink);
|
||||
@@ -425,7 +556,7 @@ void dbInitLink(struct dbCommon *precord, struct link *plink, short dbfType)
|
||||
if (dbfType == DBF_INLINK)
|
||||
plink->value.pv_link.pvlMask |= pvlOptInpNative;
|
||||
|
||||
dbCaAddLink(plink);
|
||||
customlset(plink, dbfType);
|
||||
if (dbfType == DBF_FWDLINK) {
|
||||
char *pperiod = strrchr(plink->value.pv_link.pvname, '.');
|
||||
|
||||
@@ -442,24 +573,29 @@ void dbInitLink(struct dbCommon *precord, struct link *plink, short dbfType)
|
||||
}
|
||||
}
|
||||
|
||||
void dbAddLink(struct dbCommon *precord, struct link *plink, short dbfType)
|
||||
void dbAddLink(dbLocker *locker, struct link *plink, short dbfType, DBADDR *ptarget)
|
||||
{
|
||||
plink->value.pv_link.precord = precord;
|
||||
struct dbCommon *precord = plink->precord;
|
||||
|
||||
if (plink->type == CONSTANT) {
|
||||
dbConstAddLink(plink);
|
||||
return;
|
||||
}
|
||||
|
||||
if (plink == &precord->tsel)
|
||||
recGblTSELwasModified(plink);
|
||||
|
||||
if (!(plink->value.pv_link.pvlMask & (pvlOptCA | pvlOptCP | pvlOptCPP))) {
|
||||
/* Can we make it a DB link? */
|
||||
if (!dbDbAddLink(plink, dbfType))
|
||||
return;
|
||||
if (ptarget) {
|
||||
/* It's a DB link */
|
||||
dbDbAddLink(locker, plink, dbfType, ptarget);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Make it a CA link */
|
||||
if (dbfType == DBF_INLINK)
|
||||
plink->value.pv_link.pvlMask |= pvlOptInpNative;
|
||||
|
||||
dbCaAddLink(plink);
|
||||
customlset(plink, dbfType);
|
||||
if (dbfType == DBF_FWDLINK) {
|
||||
char *pperiod = strrchr(plink->value.pv_link.pvname, '.');
|
||||
|
||||
@@ -470,69 +606,69 @@ void dbAddLink(struct dbCommon *precord, struct link *plink, short dbfType)
|
||||
|
||||
long dbLoadLink(struct link *plink, short dbrType, void *pbuffer)
|
||||
{
|
||||
switch (plink->type) {
|
||||
case CONSTANT:
|
||||
if (plink->type == CONSTANT)
|
||||
return dbConstLoadLink(plink, dbrType, pbuffer);
|
||||
}
|
||||
|
||||
/* Could pass a type hint to the other link types here */
|
||||
return S_db_notFound;
|
||||
}
|
||||
|
||||
void dbRemoveLink(struct link *plink)
|
||||
void dbRemoveLink(dbLocker *locker, struct link *plink)
|
||||
{
|
||||
switch (plink->type) {
|
||||
case DB_LINK:
|
||||
dbDbRemoveLink(plink);
|
||||
break;
|
||||
case CA_LINK:
|
||||
dbCaRemoveLink(plink);
|
||||
break;
|
||||
default:
|
||||
cantProceed("dbRemoveLink: Unexpected link type %d\n", plink->type);
|
||||
lset *plset = plink->lset;
|
||||
|
||||
if (plset) {
|
||||
if (plset->removeLink)
|
||||
plset->removeLink(locker, plink);
|
||||
plink->lset = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void dbReportLink(const struct link *plink, dbLinkReportInfo *pinfo)
|
||||
{
|
||||
lset *plset = plink->lset;
|
||||
|
||||
if (plset && plset->reportLink) {
|
||||
plset->reportLink(plink, pinfo);
|
||||
}
|
||||
plink->type = PV_LINK;
|
||||
plink->value.pv_link.pvlMask = 0;
|
||||
}
|
||||
|
||||
int dbIsLinkConnected(const struct link *plink)
|
||||
{
|
||||
switch (plink->type) {
|
||||
case DB_LINK:
|
||||
return dbDbIsLinkConnected(plink);
|
||||
case CA_LINK:
|
||||
return dbCaIsLinkConnected(plink);
|
||||
}
|
||||
return FALSE;
|
||||
lset *plset = plink->lset;
|
||||
|
||||
if (!plset || !plset->isConnected)
|
||||
return FALSE;
|
||||
|
||||
return plset->isConnected(plink);
|
||||
}
|
||||
|
||||
int dbGetLinkDBFtype(const struct link *plink)
|
||||
{
|
||||
switch (plink->type) {
|
||||
case DB_LINK:
|
||||
return dbDbGetDBFtype(plink);
|
||||
case CA_LINK:
|
||||
return dbCaGetLinkDBFtype(plink);
|
||||
}
|
||||
return -1;
|
||||
lset *plset = plink->lset;
|
||||
|
||||
if (!plset || !plset->getDBFtype)
|
||||
return -1;
|
||||
|
||||
return plset->getDBFtype(plink);
|
||||
}
|
||||
|
||||
long dbGetNelements(const struct link *plink, long *nelements)
|
||||
{
|
||||
switch (plink->type) {
|
||||
case CONSTANT:
|
||||
return dbConstGetNelements(plink, nelements);
|
||||
case DB_LINK:
|
||||
return dbDbGetElements(plink, nelements);
|
||||
case CA_LINK:
|
||||
return dbCaGetNelements(plink, nelements);
|
||||
}
|
||||
return S_db_badField;
|
||||
lset *plset = plink->lset;
|
||||
|
||||
if (!plset || !plset->getElements)
|
||||
return S_db_badField;
|
||||
|
||||
return plset->getElements(plink, nelements);
|
||||
}
|
||||
|
||||
long dbGetLink(struct link *plink, short dbrType, void *pbuffer,
|
||||
long *poptions, long *pnRequest)
|
||||
{
|
||||
struct dbCommon *precord = plink->value.pv_link.precord;
|
||||
struct dbCommon *precord = plink->precord;
|
||||
epicsEnum16 sevr = 0, stat = 0;
|
||||
lset *plset = plink->lset;
|
||||
long status;
|
||||
|
||||
if (poptions && *poptions) {
|
||||
@@ -540,21 +676,10 @@ long dbGetLink(struct link *plink, short dbrType, void *pbuffer,
|
||||
*poptions = 0;
|
||||
}
|
||||
|
||||
switch (plink->type) {
|
||||
case CONSTANT:
|
||||
status = dbConstGetLink(plink, dbrType, pbuffer, &stat, &sevr,
|
||||
pnRequest);
|
||||
break;
|
||||
case DB_LINK:
|
||||
status = dbDbGetValue(plink, dbrType, pbuffer, &stat, &sevr, pnRequest);
|
||||
break;
|
||||
case CA_LINK:
|
||||
status = dbCaGetLink(plink, dbrType, pbuffer, &stat, &sevr, pnRequest);
|
||||
break;
|
||||
default:
|
||||
cantProceed("dbGetLinkValue: Illegal link type %d\n", plink->type);
|
||||
status = -1;
|
||||
}
|
||||
if (!plset || !plset->getValue)
|
||||
return -1;
|
||||
|
||||
status = plset->getValue(plink, dbrType, pbuffer, &stat, &sevr, pnRequest);
|
||||
if (status) {
|
||||
recGblSetSevr(precord, LINK_ALARM, INVALID_ALARM);
|
||||
} else {
|
||||
@@ -565,104 +690,88 @@ long dbGetLink(struct link *plink, short dbrType, void *pbuffer,
|
||||
|
||||
long dbGetControlLimits(const struct link *plink, double *low, double *high)
|
||||
{
|
||||
switch (plink->type) {
|
||||
case DB_LINK:
|
||||
return dbDbGetControlLimits(plink, low, high);
|
||||
case CA_LINK:
|
||||
return dbCaGetControlLimits(plink, low, high);
|
||||
}
|
||||
return S_db_notFound;
|
||||
lset *plset = plink->lset;
|
||||
|
||||
if (!plset || !plset->getControlLimits)
|
||||
return S_db_notFound;
|
||||
|
||||
return plset->getControlLimits(plink, low, high);
|
||||
}
|
||||
|
||||
long dbGetGraphicLimits(const struct link *plink, double *low, double *high)
|
||||
{
|
||||
switch (plink->type) {
|
||||
case DB_LINK:
|
||||
return dbDbGetGraphicLimits(plink, low, high);
|
||||
case CA_LINK:
|
||||
return dbCaGetGraphicLimits(plink, low, high);
|
||||
}
|
||||
return S_db_notFound;
|
||||
lset *plset = plink->lset;
|
||||
|
||||
if (!plset || !plset->getGraphicLimits)
|
||||
return S_db_notFound;
|
||||
|
||||
return plset->getGraphicLimits(plink, low, high);
|
||||
}
|
||||
|
||||
long dbGetAlarmLimits(const struct link *plink, double *lolo, double *low,
|
||||
double *high, double *hihi)
|
||||
{
|
||||
switch (plink->type) {
|
||||
case DB_LINK:
|
||||
return dbDbGetAlarmLimits(plink, lolo, low, high, hihi);
|
||||
case CA_LINK:
|
||||
return dbCaGetAlarmLimits(plink, lolo, low, high, hihi);
|
||||
}
|
||||
return S_db_notFound;
|
||||
lset *plset = plink->lset;
|
||||
|
||||
if (!plset || !plset->getAlarmLimits)
|
||||
return S_db_notFound;
|
||||
|
||||
return plset->getAlarmLimits(plink, lolo, low, high, hihi);
|
||||
}
|
||||
|
||||
long dbGetPrecision(const struct link *plink, short *precision)
|
||||
{
|
||||
switch (plink->type) {
|
||||
case DB_LINK:
|
||||
return dbDbGetPrecision(plink, precision);
|
||||
case CA_LINK:
|
||||
return dbCaGetPrecision(plink, precision);
|
||||
}
|
||||
return S_db_notFound;
|
||||
lset *plset = plink->lset;
|
||||
|
||||
if (!plset || !plset->getPrecision)
|
||||
return S_db_notFound;
|
||||
|
||||
return plset->getPrecision(plink, precision);
|
||||
}
|
||||
|
||||
long dbGetUnits(const struct link *plink, char *units, int unitsSize)
|
||||
{
|
||||
switch (plink->type) {
|
||||
case DB_LINK:
|
||||
return dbDbGetUnits(plink, units, unitsSize);
|
||||
case CA_LINK:
|
||||
return dbCaGetUnits(plink, units, unitsSize);
|
||||
}
|
||||
return S_db_notFound;
|
||||
lset *plset = plink->lset;
|
||||
|
||||
if (!plset || !plset->getUnits)
|
||||
return S_db_notFound;
|
||||
|
||||
return plset->getUnits(plink, units, unitsSize);
|
||||
}
|
||||
|
||||
long dbGetAlarm(const struct link *plink, epicsEnum16 *status,
|
||||
epicsEnum16 *severity)
|
||||
{
|
||||
switch (plink->type) {
|
||||
case DB_LINK:
|
||||
return dbDbGetAlarm(plink, status, severity);
|
||||
case CA_LINK:
|
||||
return dbCaGetAlarm(plink, status, severity);
|
||||
}
|
||||
return S_db_notFound;
|
||||
lset *plset = plink->lset;
|
||||
|
||||
if (!plset || !plset->getAlarm)
|
||||
return S_db_notFound;
|
||||
|
||||
return plset->getAlarm(plink, status, severity);
|
||||
}
|
||||
|
||||
long dbGetTimeStamp(const struct link *plink, epicsTimeStamp *pstamp)
|
||||
{
|
||||
switch (plink->type) {
|
||||
case DB_LINK:
|
||||
return dbDbGetTimeStamp(plink, pstamp);
|
||||
case CA_LINK:
|
||||
return dbCaGetTimeStamp(plink, pstamp);
|
||||
}
|
||||
return S_db_notFound;
|
||||
lset *plset = plink->lset;
|
||||
|
||||
if (!plset || !plset->getTimeStamp)
|
||||
return S_db_notFound;
|
||||
|
||||
return plset->getTimeStamp(plink, pstamp);
|
||||
}
|
||||
|
||||
long dbPutLink(struct link *plink, short dbrType, const void *pbuffer,
|
||||
long nRequest)
|
||||
{
|
||||
lset *plset = plink->lset;
|
||||
long status;
|
||||
|
||||
switch (plink->type) {
|
||||
case CONSTANT:
|
||||
status = 0;
|
||||
break;
|
||||
case DB_LINK:
|
||||
status = dbDbPutValue(plink, dbrType, pbuffer, nRequest);
|
||||
break;
|
||||
case CA_LINK:
|
||||
status = dbCaPutLink(plink, dbrType, pbuffer, nRequest);
|
||||
break;
|
||||
default:
|
||||
cantProceed("dbPutLinkValue: Illegal link type %d\n", plink->type);
|
||||
status = -1;
|
||||
}
|
||||
if (!plset || !plset->putValue)
|
||||
return S_db_notFound;
|
||||
|
||||
status = plset->putValue(plink, dbrType, pbuffer, nRequest);
|
||||
if (status) {
|
||||
struct dbCommon *precord = plink->value.pv_link.precord;
|
||||
struct dbCommon *precord = plink->precord;
|
||||
|
||||
recGblSetSevr(precord, LINK_ALARM, INVALID_ALARM);
|
||||
}
|
||||
@@ -671,14 +780,10 @@ long dbPutLink(struct link *plink, short dbrType, const void *pbuffer,
|
||||
|
||||
void dbScanFwdLink(struct link *plink)
|
||||
{
|
||||
switch (plink->type) {
|
||||
case DB_LINK:
|
||||
dbDbScanFwdLink(plink);
|
||||
break;
|
||||
case CA_LINK:
|
||||
dbCaScanFwdLink(plink);
|
||||
break;
|
||||
}
|
||||
lset *plset = plink->lset;
|
||||
|
||||
if (plset && plset->scanForward)
|
||||
plset->scanForward(plink);
|
||||
}
|
||||
|
||||
/* Helper functions for long string support */
|
||||
|
||||
@@ -6,8 +6,7 @@
|
||||
* EPICS BASE is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
\*************************************************************************/
|
||||
/*
|
||||
* dbLink.h
|
||||
/* dbLink.h
|
||||
*
|
||||
* Created on: Mar 21, 2010
|
||||
* Author: Andrew Johnson
|
||||
@@ -20,21 +19,50 @@
|
||||
#include "shareLib.h"
|
||||
#include "epicsTypes.h"
|
||||
#include "epicsTime.h"
|
||||
#include "dbAddr.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct dbLocker;
|
||||
struct DBENTRY;
|
||||
|
||||
#define LSET_API_VERSION 1
|
||||
#define LSET_REPORT_INDENT ""
|
||||
|
||||
typedef enum {
|
||||
dbLinkReportNone,
|
||||
dbLinkReportAll,
|
||||
dbLinkReportConnected,
|
||||
dbLinkReportDisconnected,
|
||||
} dbLinkReportFilter;
|
||||
|
||||
typedef struct {
|
||||
/* from caller */
|
||||
dbLinkReportFilter filter;
|
||||
int detailLevel;
|
||||
struct DBENTRY *pentry;
|
||||
unsigned clearstats:1; /* after reporting, zero stat counters */
|
||||
/* callee fills in current state */
|
||||
unsigned connected:1; /* is this link connected to it's underlying data source */
|
||||
unsigned readable:1; /* would a dbGetLink() succeed at this moment */
|
||||
unsigned writable:1; /* would a dbPutLink() succeed at this moment */
|
||||
/* callee fills in statistics */
|
||||
unsigned nDisconnect; /* number of times this link has entered a not connected state */
|
||||
unsigned nEvents; /* number of times new data has been received from the underlying data source */
|
||||
unsigned nWriteFail; /* number of times dbPutLink() has failed for this link */
|
||||
} dbLinkReportInfo;
|
||||
|
||||
typedef struct lset {
|
||||
long (*initLink)(struct link *plink, short dbfType);
|
||||
long (*addLink)(struct link *plink, short dbfType);
|
||||
long (*loadLink)(struct link *plink, short dbrType, void *pbuffer);
|
||||
void (*removeLink)(struct link *plink);
|
||||
int (*isLinkConnected)(const struct link *plink);
|
||||
unsigned version; /* must be set to LSET_API_VERSION */
|
||||
void (*reportLink)(const struct link *plink, dbLinkReportInfo *pinfo);
|
||||
void (*removeLink)(struct dbLocker *locker, struct link *plink);
|
||||
int (*isConnected)(const struct link *plink);
|
||||
int (*getDBFtype)(const struct link *plink);
|
||||
long (*getElements)(const struct link *plink, long *nelements);
|
||||
long (*getValue)(struct link *plink, short dbrType, void *pbuffer,
|
||||
epicsEnum16 *pstat, epicsEnum16 *psevr, long *pnRequest);
|
||||
epicsEnum16 *pstat, epicsEnum16 *psevr, long *pnRequest);
|
||||
long (*getControlLimits)(const struct link *plink, double *lo, double *hi);
|
||||
long (*getGraphicLimits)(const struct link *plink, double *lo, double *hi);
|
||||
long (*getAlarmLimits)(const struct link *plink, double *lolo, double *lo,
|
||||
@@ -46,19 +74,21 @@ typedef struct lset {
|
||||
long (*getTimeStamp)(const struct link *plink, epicsTimeStamp *pstamp);
|
||||
long (*putValue)(struct link *plink, short dbrType,
|
||||
const void *pbuffer, long nRequest);
|
||||
void (*scanFwdLink)(struct link *plink);
|
||||
void (*scanForward)(struct link *plink);
|
||||
} lset;
|
||||
|
||||
#define dbGetSevr(PLINK, PSEVERITY) \
|
||||
dbGetAlarm((PLINK), NULL, (PSEVERITY))
|
||||
#define dbGetSevr(link, sevr) \
|
||||
dbGetAlarm(link, NULL, sevr)
|
||||
|
||||
epicsShareFunc void dbInitLink(struct dbCommon *precord, struct link *plink,
|
||||
short dbfType);
|
||||
epicsShareFunc void dbAddLink(struct dbCommon *precord, struct link *plink,
|
||||
short dbfType);
|
||||
epicsShareFunc void dbInitLink(struct link *plink, short dbfType);
|
||||
epicsShareFunc void dbAddLink(struct dbLocker *locker, struct link *plink, short dbfType,
|
||||
DBADDR *ptarget);
|
||||
epicsShareFunc long dbLoadLink(struct link *plink, short dbrType,
|
||||
void *pbuffer);
|
||||
epicsShareFunc void dbRemoveLink(struct link *plink);
|
||||
epicsShareFunc void dbRemoveLink(struct dbLocker *locker, struct link *plink);
|
||||
|
||||
epicsShareFunc void dbReportLink(const struct link *plink, dbLinkReportInfo *pinfo);
|
||||
|
||||
epicsShareFunc long dbGetNelements(const struct link *plink, long *nelements);
|
||||
epicsShareFunc int dbIsLinkConnected(const struct link *plink);
|
||||
epicsShareFunc int dbGetLinkDBFtype(const struct link *plink);
|
||||
@@ -77,7 +107,7 @@ epicsShareFunc long dbGetAlarm(const struct link *plink, epicsEnum16 *status,
|
||||
epicsEnum16 *severity);
|
||||
epicsShareFunc long dbGetTimeStamp(const struct link *plink,
|
||||
epicsTimeStamp *pstamp);
|
||||
epicsShareFunc long dbPutLink(struct link *, short dbrType,
|
||||
epicsShareFunc long dbPutLink(struct link *plink, short dbrType,
|
||||
const void *pbuffer, long nRequest);
|
||||
epicsShareFunc void dbScanFwdLink(struct link *plink);
|
||||
|
||||
|
||||
1286
src/ioc/db/dbLock.c
1286
src/ioc/db/dbLock.c
File diff suppressed because it is too large
Load Diff
@@ -13,6 +13,7 @@
|
||||
#ifndef INCdbLockh
|
||||
#define INCdbLockh
|
||||
|
||||
#include "ellLib.h"
|
||||
#include "shareLib.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
@@ -21,21 +22,26 @@ extern "C" {
|
||||
|
||||
struct dbCommon;
|
||||
struct dbBase;
|
||||
typedef struct dbLocker dbLocker;
|
||||
|
||||
epicsShareFunc void dbScanLock(struct dbCommon *precord);
|
||||
epicsShareFunc void dbScanUnlock(struct dbCommon *precord);
|
||||
|
||||
epicsShareFunc dbLocker *dbLockerAlloc(struct dbCommon * const *precs,
|
||||
size_t nrecs,
|
||||
unsigned int flags);
|
||||
|
||||
epicsShareFunc void dbLockerFree(dbLocker *);
|
||||
|
||||
epicsShareFunc void dbScanLockMany(dbLocker*);
|
||||
epicsShareFunc void dbScanUnlockMany(dbLocker*);
|
||||
|
||||
epicsShareFunc unsigned long dbLockGetLockId(
|
||||
struct dbCommon *precord);
|
||||
|
||||
epicsShareFunc void dbLockInitRecords(struct dbBase *pdbbase);
|
||||
epicsShareFunc void dbLockCleanupRecords(struct dbBase *pdbbase);
|
||||
epicsShareFunc void dbLockSetMerge(
|
||||
struct dbCommon *pfirst,struct dbCommon *psecond);
|
||||
epicsShareFunc void dbLockSetSplit(struct dbCommon *psource);
|
||||
/*The following are for code that modifies lock sets*/
|
||||
epicsShareFunc void dbLockSetGblLock(void);
|
||||
epicsShareFunc void dbLockSetGblUnlock(void);
|
||||
epicsShareFunc void dbLockSetRecordLock(struct dbCommon *precord);
|
||||
|
||||
|
||||
/* Lock Set Report */
|
||||
epicsShareFunc long dblsr(char *recordname,int level);
|
||||
@@ -47,6 +53,10 @@ epicsShareFunc long dbLockShowLocked(int level);
|
||||
/*KLUDGE to support field TPRO*/
|
||||
epicsShareFunc int * dbLockSetAddrTrace(struct dbCommon *precord);
|
||||
|
||||
/* debugging */
|
||||
epicsShareFunc unsigned long dbLockGetRefs(struct dbCommon*);
|
||||
epicsShareFunc unsigned long dbLockCountSets(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
110
src/ioc/db/dbLockPvt.h
Normal file
110
src/ioc/db/dbLockPvt.h
Normal file
@@ -0,0 +1,110 @@
|
||||
/*************************************************************************\
|
||||
* Copyright (c) 2014 Brookhaven Science Assoc., as Operator of Brookhaven
|
||||
* National Laboratory.
|
||||
* EPICS BASE is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
\*************************************************************************/
|
||||
|
||||
#ifndef DBLOCKPVT_H
|
||||
#define DBLOCKPVT_H
|
||||
|
||||
#include "dbLock.h"
|
||||
#include "epicsSpin.h"
|
||||
|
||||
/* Define to enable additional error checking */
|
||||
#undef LOCKSET_DEBUG
|
||||
|
||||
/* Define to disable the free list for lockSets */
|
||||
#undef LOCKSET_NOFREE
|
||||
|
||||
/* Define to disable use of recomputeCnt optimization */
|
||||
#undef LOCKSET_NOCNT
|
||||
|
||||
/* except for refcount (and lock), all members of dbLockSet
|
||||
* are guarded by its lock.
|
||||
*/
|
||||
typedef struct dbLockSet {
|
||||
ELLNODE node;
|
||||
ELLLIST lockRecordList; /* holds lockRecord::node */
|
||||
epicsMutexId lock;
|
||||
unsigned long id;
|
||||
|
||||
int refcount;
|
||||
#ifdef LOCKSET_DEBUG
|
||||
int ownercount;
|
||||
epicsThreadId owner;
|
||||
#endif
|
||||
dbLocker *ownerlocker;
|
||||
ELLNODE lockernode;
|
||||
|
||||
int trace; /*For field TPRO*/
|
||||
} lockSet;
|
||||
|
||||
struct lockRecord;
|
||||
|
||||
/* dbCommon.LSET is a plockRecord.
|
||||
* Except for spin and plockSet, all members of lockRecord are guarded
|
||||
* by the present lockset lock.
|
||||
* plockSet is guarded by spin.
|
||||
*/
|
||||
typedef struct lockRecord {
|
||||
ELLNODE node; /* in lockSet::lockRecordList */
|
||||
/* The association between lockRecord and lockSet
|
||||
* can only be changed while the lockSet is held,
|
||||
* and the lockRecord's spinlock is held.
|
||||
* It may be read which either lock is held.
|
||||
*/
|
||||
lockSet *plockSet;
|
||||
/* the association between lockRecord and dbCommon never changes */
|
||||
dbCommon *precord;
|
||||
epicsSpinId spin;
|
||||
|
||||
/* temp used during lockset split.
|
||||
* lockSet must be locked for access
|
||||
*/
|
||||
ELLNODE compnode;
|
||||
unsigned int compflag;
|
||||
} lockRecord;
|
||||
|
||||
typedef struct {
|
||||
lockRecord *plr;
|
||||
/* the last lock found associated with the ref.
|
||||
* not stable unless lock is locked, or ref spin
|
||||
* is locked.
|
||||
*/
|
||||
lockSet *plockSet;
|
||||
} lockRecordRef;
|
||||
|
||||
#define DBLOCKER_NALLOC 2
|
||||
/* a dbLocker can only be used by a single thread. */
|
||||
struct dbLocker {
|
||||
ELLLIST locked;
|
||||
#ifndef LOCKSET_NOCNT
|
||||
size_t recomp; /* snapshot of recomputeCnt when refs[] cache updated */
|
||||
#endif
|
||||
size_t maxrefs;
|
||||
lockRecordRef refs[DBLOCKER_NALLOC]; /* actual length is maxrefs */
|
||||
};
|
||||
|
||||
/* These are exported for testing only */
|
||||
epicsShareFunc lockSet* dbLockGetRef(lockRecord *lr); /* lookup lockset and increment ref count */
|
||||
epicsShareFunc void dbLockIncRef(lockSet* ls);
|
||||
epicsShareFunc void dbLockDecRef(lockSet *ls);
|
||||
|
||||
/* Calling dbLockerPrepare directly is an internal
|
||||
* optimization used when dbLocker on the stack.
|
||||
* nrecs must be <=DBLOCKER_NALLOC.
|
||||
*/
|
||||
void dbLockerPrepare(struct dbLocker *locker,
|
||||
struct dbCommon * const *precs,
|
||||
size_t nrecs);
|
||||
void dbLockerFinalize(dbLocker *);
|
||||
|
||||
void dbLockSetMerge(struct dbLocker *locker,
|
||||
struct dbCommon *pfirst,
|
||||
struct dbCommon *psecond);
|
||||
void dbLockSetSplit(struct dbLocker *locker,
|
||||
struct dbCommon *psource,
|
||||
struct dbCommon *psecond);
|
||||
|
||||
#endif /* DBLOCKPVT_H */
|
||||
@@ -25,7 +25,6 @@
|
||||
#include "ellLib.h"
|
||||
#include "epicsAssert.h"
|
||||
#include "epicsEvent.h"
|
||||
#include "epicsExit.h"
|
||||
#include "epicsMutex.h"
|
||||
#include "epicsString.h"
|
||||
#include "epicsThread.h"
|
||||
@@ -109,6 +108,15 @@ static void notifyCallback(CALLBACK *pcallback);
|
||||
(listnode)->isOnList=0; \
|
||||
}
|
||||
|
||||
static void notifyFree(void *raw)
|
||||
{
|
||||
notifyPvt *pnotifyPvt = raw;
|
||||
assert(pnotifyPvt->magic==MAGIC);
|
||||
epicsEventDestroy(pnotifyPvt->cancelEvent);
|
||||
epicsEventDestroy(pnotifyPvt->userCallbackEvent);
|
||||
free(pnotifyPvt);
|
||||
}
|
||||
|
||||
static void notifyInit(processNotify *ppn)
|
||||
{
|
||||
notifyPvt *pnotifyPvt;
|
||||
@@ -301,7 +309,7 @@ static void notifyCallback(CALLBACK *pcallback)
|
||||
|
||||
void dbProcessNotifyExit(void)
|
||||
{
|
||||
assert(ellCount(&pnotifyGlobal->freeList)==0);
|
||||
ellFree2(&pnotifyGlobal->freeList, ¬ifyFree);
|
||||
epicsMutexDestroy(pnotifyGlobal->lock);
|
||||
free(pnotifyGlobal);
|
||||
pnotifyGlobal = NULL;
|
||||
|
||||
@@ -25,11 +25,9 @@
|
||||
#include "dbDefs.h"
|
||||
#include "ellLib.h"
|
||||
#include "epicsEvent.h"
|
||||
#include "epicsExit.h"
|
||||
#include "epicsInterrupt.h"
|
||||
#include "epicsMutex.h"
|
||||
#include "epicsPrint.h"
|
||||
#include "epicsRingPointer.h"
|
||||
#include "epicsRingBytes.h"
|
||||
#include "epicsStdio.h"
|
||||
#include "epicsStdlib.h"
|
||||
#include "epicsString.h"
|
||||
@@ -64,7 +62,7 @@ static volatile enum ctl scanCtl;
|
||||
|
||||
static int onceQueueSize = 1000;
|
||||
static epicsEventId onceSem;
|
||||
static epicsRingPointerId onceQ;
|
||||
static epicsRingBytesId onceQ;
|
||||
static epicsThreadId onceTaskId;
|
||||
static void *exitOnce;
|
||||
|
||||
@@ -151,7 +149,7 @@ static void buildScanLists(void);
|
||||
static void addToList(struct dbCommon *precord, scan_list *psl);
|
||||
static void deleteFromList(struct dbCommon *precord, scan_list *psl);
|
||||
|
||||
void scanShutdown(void)
|
||||
void scanStop(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
@@ -168,15 +166,15 @@ void scanShutdown(void)
|
||||
|
||||
scanOnce((dbCommon *)&exitOnce);
|
||||
epicsEventWait(startStopEvent);
|
||||
}
|
||||
|
||||
void scanCleanup(void)
|
||||
{
|
||||
|
||||
deletePeriodic();
|
||||
ioscanDestroy();
|
||||
|
||||
epicsRingPointerDelete(onceQ);
|
||||
|
||||
epicsEventDestroy(startStopEvent);
|
||||
epicsEventDestroy(onceSem);
|
||||
onceSem = startStopEvent = NULL;
|
||||
epicsRingBytesDelete(onceQ);
|
||||
|
||||
free(periodicTaskId);
|
||||
papPeriodic = NULL;
|
||||
@@ -187,7 +185,8 @@ long scanInit(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
startStopEvent = epicsEventMustCreate(epicsEventEmpty);
|
||||
if(!startStopEvent)
|
||||
startStopEvent = epicsEventMustCreate(epicsEventEmpty);
|
||||
scanCtl = ctlPause;
|
||||
|
||||
initPeriodic();
|
||||
@@ -456,7 +455,9 @@ event_list *eventNameToHandle(const char *eventname)
|
||||
if (strcmp(pel->event_name, eventname) == 0) break;
|
||||
}
|
||||
if (pel == NULL) {
|
||||
pel = dbCalloc(1, sizeof(event_list));
|
||||
pel = calloc(1, sizeof(event_list));
|
||||
if (!pel)
|
||||
goto done;
|
||||
strcpy(pel->event_name, eventname);
|
||||
for (prio = 0; prio < NUM_CALLBACK_PRIORITIES; prio++) {
|
||||
callbackSetUser(&pel->scan_list[prio], &pel->callback[prio]);
|
||||
@@ -474,6 +475,7 @@ event_list *eventNameToHandle(const char *eventname)
|
||||
pevent_list[e] = pel;
|
||||
}
|
||||
}
|
||||
done:
|
||||
epicsMutexUnlock(event_lock);
|
||||
return pel;
|
||||
}
|
||||
@@ -580,6 +582,28 @@ unsigned int scanIoRequest(IOSCANPVT piosh)
|
||||
return queued;
|
||||
}
|
||||
|
||||
unsigned int scanIoImmediate(IOSCANPVT piosh, int prio)
|
||||
{
|
||||
io_scan_list *piosl;
|
||||
|
||||
if (prio<0 || prio>=NUM_CALLBACK_PRIORITIES)
|
||||
return S_db_errArg;
|
||||
else if (scanCtl != ctlRun)
|
||||
return 0;
|
||||
|
||||
piosl = &piosh->iosl[prio];
|
||||
|
||||
if (ellCount(&piosl->scan_list.list) == 0)
|
||||
return 0;
|
||||
|
||||
scanList(&piosl->scan_list);
|
||||
|
||||
if (piosh->cb)
|
||||
piosh->cb(piosh->arg, piosh, prio);
|
||||
|
||||
return 1 << prio;
|
||||
}
|
||||
|
||||
/* May not be called while a scan request is queued or running */
|
||||
void scanIoSetComplete(IOSCANPVT piosh, io_scan_complete cb, void *arg)
|
||||
{
|
||||
@@ -587,15 +611,27 @@ void scanIoSetComplete(IOSCANPVT piosh, io_scan_complete cb, void *arg)
|
||||
piosh->arg = arg;
|
||||
}
|
||||
|
||||
void scanOnce(struct dbCommon *precord)
|
||||
int scanOnce(struct dbCommon *precord) {
|
||||
return scanOnceCallback(precord, NULL, NULL);
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
struct dbCommon *prec;
|
||||
once_complete cb;
|
||||
void *usr;
|
||||
} onceEntry;
|
||||
|
||||
int scanOnceCallback(struct dbCommon *precord, once_complete cb, void *usr)
|
||||
{
|
||||
static int newOverflow = TRUE;
|
||||
int lockKey;
|
||||
onceEntry ent;
|
||||
int pushOK;
|
||||
|
||||
lockKey = epicsInterruptLock();
|
||||
pushOK = epicsRingPointerPush(onceQ, precord);
|
||||
epicsInterruptUnlock(lockKey);
|
||||
ent.prec = precord;
|
||||
ent.cb = cb;
|
||||
ent.usr = usr;
|
||||
|
||||
pushOK = epicsRingBytesPut(onceQ, (void*)&ent, sizeof(ent));
|
||||
|
||||
if (!pushOK) {
|
||||
if (newOverflow) errlogPrintf("scanOnce: Ring buffer overflow\n");
|
||||
@@ -604,6 +640,8 @@ void scanOnce(struct dbCommon *precord)
|
||||
newOverflow = TRUE;
|
||||
}
|
||||
epicsEventSignal(onceSem);
|
||||
|
||||
return !pushOK;
|
||||
}
|
||||
|
||||
static void onceTask(void *arg)
|
||||
@@ -612,14 +650,24 @@ static void onceTask(void *arg)
|
||||
epicsEventSignal(startStopEvent);
|
||||
|
||||
while (TRUE) {
|
||||
void *precord;
|
||||
|
||||
epicsEventMustWait(onceSem);
|
||||
while ((precord = epicsRingPointerPop(onceQ))) {
|
||||
if (precord == &exitOnce) goto shutdown;
|
||||
dbScanLock(precord);
|
||||
dbProcess(precord);
|
||||
dbScanUnlock(precord);
|
||||
while(1) {
|
||||
onceEntry ent;
|
||||
int bytes = epicsRingBytesGet(onceQ, (void*)&ent, sizeof(ent));
|
||||
if(bytes==0)
|
||||
break;
|
||||
if(bytes!=sizeof(ent)) {
|
||||
errlogPrintf("onceTask: received incomplete %d of %u\n",
|
||||
bytes, (unsigned)sizeof(ent));
|
||||
continue; /* what to do? */
|
||||
} else if (ent.prec == (void*)&exitOnce) goto shutdown;
|
||||
|
||||
dbScanLock(ent.prec);
|
||||
dbProcess(ent.prec);
|
||||
dbScanUnlock(ent.prec);
|
||||
if(ent.cb)
|
||||
ent.cb(ent.usr, ent.prec);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -636,10 +684,11 @@ int scanOnceSetQueueSize(int size)
|
||||
|
||||
static void initOnce(void)
|
||||
{
|
||||
if ((onceQ = epicsRingPointerCreate(onceQueueSize)) == NULL) {
|
||||
if ((onceQ = epicsRingBytesLockedCreate(sizeof(onceEntry)*onceQueueSize)) == NULL) {
|
||||
cantProceed("initOnce: Ring buffer create failed\n");
|
||||
}
|
||||
onceSem = epicsEventMustCreate(epicsEventEmpty);
|
||||
if(!onceSem)
|
||||
onceSem = epicsEventMustCreate(epicsEventEmpty);
|
||||
onceTaskId = epicsThreadCreate("scanOnce",
|
||||
epicsThreadPriorityScanLow + nPeriodic,
|
||||
epicsThreadGetStackSize(epicsThreadStackBig), onceTask, 0);
|
||||
@@ -813,9 +862,11 @@ static void spawnPeriodic(int ind)
|
||||
|
||||
static void ioscanCallback(CALLBACK *pcallback)
|
||||
{
|
||||
ioscan_head *piosh = (ioscan_head *) pcallback->user;
|
||||
int prio = pcallback->priority;
|
||||
ioscan_head *piosh;
|
||||
int prio;
|
||||
|
||||
callbackGetUser(piosh, pcallback);
|
||||
callbackGetPriority(prio, pcallback);
|
||||
scanList(&piosh->iosl[prio].scan_list);
|
||||
if (piosh->cb)
|
||||
piosh->cb(piosh->arg, piosh, prio);
|
||||
|
||||
@@ -39,14 +39,16 @@ struct ioscan_head;
|
||||
typedef struct ioscan_head *IOSCANPVT;
|
||||
typedef struct event_list *EVENTPVT;
|
||||
|
||||
typedef void (*io_scan_complete)(void *usr, IOSCANPVT, int prio);
|
||||
|
||||
struct dbCommon;
|
||||
|
||||
typedef void (*io_scan_complete)(void *usr, IOSCANPVT, int prio);
|
||||
typedef void (*once_complete)(void *usr, struct dbCommon*);
|
||||
|
||||
epicsShareFunc long scanInit(void);
|
||||
epicsShareFunc void scanRun(void);
|
||||
epicsShareFunc void scanPause(void);
|
||||
epicsShareFunc void scanShutdown(void);
|
||||
epicsShareFunc void scanStop(void);
|
||||
epicsShareFunc void scanCleanup(void);
|
||||
|
||||
epicsShareFunc EVENTPVT eventNameToHandle(const char* event);
|
||||
epicsShareFunc void postEvent(EVENTPVT epvt);
|
||||
@@ -54,7 +56,8 @@ epicsShareFunc void post_event(int event) EPICS_DEPRECATED;
|
||||
epicsShareFunc void scanAdd(struct dbCommon *);
|
||||
epicsShareFunc void scanDelete(struct dbCommon *);
|
||||
epicsShareFunc double scanPeriod(int scan);
|
||||
epicsShareFunc void scanOnce(struct dbCommon *);
|
||||
epicsShareFunc int scanOnce(struct dbCommon *);
|
||||
epicsShareFunc int scanOnceCallback(struct dbCommon *, once_complete cb, void *usr);
|
||||
epicsShareFunc int scanOnceSetQueueSize(int size);
|
||||
|
||||
/*print periodic lists*/
|
||||
@@ -68,6 +71,7 @@ epicsShareFunc int scanpiol(void);
|
||||
|
||||
epicsShareFunc void scanIoInit(IOSCANPVT *ppios);
|
||||
epicsShareFunc unsigned int scanIoRequest(IOSCANPVT pios);
|
||||
epicsShareFunc unsigned int scanIoImmediate(IOSCANPVT pios, int prio);
|
||||
epicsShareFunc void scanIoSetComplete(IOSCANPVT, io_scan_complete, void *usr);
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
@@ -334,7 +334,6 @@ long dbpf(const char *pname,const char *pvalue)
|
||||
{
|
||||
DBADDR addr;
|
||||
long status;
|
||||
epicsUInt16 value;
|
||||
short dbrType;
|
||||
size_t n = 1;
|
||||
|
||||
@@ -346,16 +345,7 @@ long dbpf(const char *pname,const char *pvalue)
|
||||
if (nameToAddr(pname, &addr))
|
||||
return -1;
|
||||
|
||||
/* For enumerated types must allow for ENUM rather than string */
|
||||
/* If entire field is digits then use DBR_ENUM else DBR_STRING */
|
||||
if (addr.dbr_field_type == DBR_ENUM && *pvalue &&
|
||||
strspn(pvalue,"0123456789") == strlen(pvalue)) {
|
||||
|
||||
sscanf(pvalue, "%hu", &value);
|
||||
pvalue = (char *) &value;
|
||||
dbrType = DBR_ENUM;
|
||||
}
|
||||
else if (addr.no_elements > 1 &&
|
||||
if (addr.no_elements > 1 &&
|
||||
(addr.dbr_field_type == DBR_CHAR || addr.dbr_field_type == DBR_UCHAR)) {
|
||||
dbrType = addr.dbr_field_type;
|
||||
n = strlen(pvalue) + 1;
|
||||
|
||||
@@ -15,19 +15,35 @@
|
||||
#include "dbmf.h"
|
||||
#include "epicsUnitTest.h"
|
||||
#include "osiFileName.h"
|
||||
#include "osiUnistd.h"
|
||||
#include "registry.h"
|
||||
#include "epicsEvent.h"
|
||||
|
||||
#define epicsExportSharedSymbols
|
||||
#include "dbAccess.h"
|
||||
#include "dbBase.h"
|
||||
#include "dbChannel.h"
|
||||
#include "dbEvent.h"
|
||||
#include "dbStaticLib.h"
|
||||
#include "dbUnitTest.h"
|
||||
#include "initHooks.h"
|
||||
#include "iocInit.h"
|
||||
|
||||
static dbEventCtx testEvtCtx;
|
||||
static epicsMutexId testEvtLock;
|
||||
static ELLLIST testEvtList; /* holds testMonitor::node */
|
||||
|
||||
struct testMonitor {
|
||||
ELLNODE node;
|
||||
dbEventSubscription sub;
|
||||
epicsEventId event;
|
||||
unsigned count;
|
||||
};
|
||||
|
||||
void testdbPrepare(void)
|
||||
{
|
||||
/* No-op at the moment */
|
||||
if(!testEvtLock)
|
||||
testEvtLock = epicsMutexMustCreate();
|
||||
}
|
||||
|
||||
void testdbReadDatabase(const char* file,
|
||||
@@ -37,19 +53,35 @@ void testdbReadDatabase(const char* file,
|
||||
if(!path)
|
||||
path = "." OSI_PATH_LIST_SEPARATOR ".." OSI_PATH_LIST_SEPARATOR
|
||||
"../O.Common" OSI_PATH_LIST_SEPARATOR "O.Common";
|
||||
if(dbReadDatabase(&pdbbase, file, path, substitutions))
|
||||
testAbort("Failed to load test database\ndbReadDatabase(%s,%s,%s)",
|
||||
file, path, substitutions);
|
||||
if(dbReadDatabase(&pdbbase, file, path, substitutions)) {
|
||||
char buf[100];
|
||||
const char *cwd = getcwd(buf, sizeof(buf));
|
||||
if(!cwd)
|
||||
cwd = "<directory too long>";
|
||||
testAbort("Failed to load test database\ndbReadDatabase(%s,%s,%s)\n from: \"%s\"",
|
||||
file, path, substitutions, cwd);
|
||||
}
|
||||
}
|
||||
|
||||
void testIocInitOk(void)
|
||||
{
|
||||
if(iocBuildIsolated() || iocRun())
|
||||
testAbort("Failed to start up test database");
|
||||
if(!(testEvtCtx=db_init_events()))
|
||||
testAbort("Failed to initialize test dbEvent context");
|
||||
if(DB_EVENT_OK!=db_start_events(testEvtCtx, "CAS-test", NULL, NULL, epicsThreadPriorityCAServerLow))
|
||||
testAbort("Failed to start test dbEvent context");
|
||||
}
|
||||
|
||||
void testIocShutdownOk(void)
|
||||
{
|
||||
epicsMutexMustLock(testEvtLock);
|
||||
if(ellCount(&testEvtList))
|
||||
testDiag("Warning, testing monitors still active at testIocShutdownOk()");
|
||||
epicsMutexUnlock(testEvtLock);
|
||||
|
||||
db_close_events(testEvtCtx);
|
||||
testEvtCtx = NULL;
|
||||
if(iocShutdown())
|
||||
testAbort("Failed to shutdown test database");
|
||||
}
|
||||
@@ -199,3 +231,90 @@ dbCommon* testdbRecordPtr(const char* pv)
|
||||
|
||||
return addr.precord;
|
||||
}
|
||||
|
||||
static
|
||||
void testmonupdate(void *user_arg, struct dbChannel *chan,
|
||||
int eventsRemaining, struct db_field_log *pfl)
|
||||
{
|
||||
testMonitor *mon = user_arg;
|
||||
|
||||
epicsMutexMustLock(testEvtLock);
|
||||
mon->count++;
|
||||
epicsMutexUnlock(testEvtLock);
|
||||
epicsEventMustTrigger(mon->event);
|
||||
}
|
||||
|
||||
testMonitor* testMonitorCreate(const char* pvname, unsigned mask, unsigned opt)
|
||||
{
|
||||
long status;
|
||||
testMonitor *mon;
|
||||
dbChannel *chan;
|
||||
assert(testEvtCtx);
|
||||
|
||||
mon = callocMustSucceed(1, sizeof(*mon), "testMonitorCreate");
|
||||
|
||||
mon->event = epicsEventMustCreate(epicsEventEmpty);
|
||||
|
||||
chan = dbChannelCreate(pvname);
|
||||
if(!chan)
|
||||
testAbort("testMonitorCreate - dbChannelCreate(\"%s\") fails", pvname);
|
||||
if(!!(status=dbChannelOpen(chan)))
|
||||
testAbort("testMonitorCreate - dbChannelOpen(\"%s\") fails w/ %ld", pvname, status);
|
||||
|
||||
mon->sub = db_add_event(testEvtCtx, chan, &testmonupdate, mon, mask);
|
||||
if(!mon->sub)
|
||||
testAbort("testMonitorCreate - db_add_event(\"%s\") fails", pvname);
|
||||
|
||||
db_event_enable(mon->sub);
|
||||
|
||||
epicsMutexMustLock(testEvtLock);
|
||||
ellAdd(&testEvtList, &mon->node);
|
||||
epicsMutexUnlock(testEvtLock);
|
||||
|
||||
return mon;
|
||||
}
|
||||
|
||||
void testMonitorDestroy(testMonitor *mon)
|
||||
{
|
||||
if(!mon) return;
|
||||
|
||||
db_event_disable(mon->sub);
|
||||
|
||||
epicsMutexMustLock(testEvtLock);
|
||||
ellDelete(&testEvtList, &mon->node);
|
||||
epicsMutexUnlock(testEvtLock);
|
||||
|
||||
db_cancel_event(mon->sub);
|
||||
|
||||
epicsEventDestroy(mon->event);
|
||||
|
||||
free(mon);
|
||||
}
|
||||
|
||||
void testMonitorWait(testMonitor *mon)
|
||||
{
|
||||
static const double delay = 60.0;
|
||||
|
||||
switch(epicsEventWaitWithTimeout(mon->event, delay))
|
||||
{
|
||||
case epicsEventOK:
|
||||
return;
|
||||
case epicsEventWaitTimeout:
|
||||
default:
|
||||
testAbort("testMonitorWait() exceeded %g second timeout", delay);
|
||||
}
|
||||
}
|
||||
|
||||
unsigned testMonitorCount(testMonitor *mon, unsigned reset)
|
||||
{
|
||||
unsigned count;
|
||||
epicsMutexMustLock(testEvtLock);
|
||||
count = mon->count;
|
||||
if(reset) {
|
||||
mon->count = 0;
|
||||
epicsEventWaitWithTimeout(mon->event, 0); /* clear the event */
|
||||
}
|
||||
epicsMutexUnlock(testEvtLock);
|
||||
return count;
|
||||
}
|
||||
|
||||
|
||||
@@ -57,6 +57,26 @@ epicsShareFunc void testdbVGetFieldEqual(const char* pv, short dbrType, va_list
|
||||
|
||||
epicsShareFunc dbCommon* testdbRecordPtr(const char* pv);
|
||||
|
||||
typedef struct testMonitor testMonitor;
|
||||
|
||||
/* Begin monitoring the named PV for changes */
|
||||
epicsShareFunc testMonitor* testMonitorCreate(const char* pvname, unsigned dbe_mask, unsigned opt);
|
||||
/* End monitoring */
|
||||
epicsShareFunc void testMonitorDestroy(testMonitor*);
|
||||
/* Return immediately if it has been updated since create, last wait,
|
||||
* or reset (count w/ reset=1).
|
||||
* Otherwise, block until the value of the target PV is updated.
|
||||
*/
|
||||
epicsShareFunc void testMonitorWait(testMonitor*);
|
||||
/* Return the number of monitor events which have occured since create,
|
||||
* or a pervious reset (called reset=1).
|
||||
* Calling w/ reset=0 only returns the count.
|
||||
* Calling w/ reset=1 resets the count to zero and ensures that the next
|
||||
* wait will block unless subsequent events occur. Returns the previous
|
||||
* count.
|
||||
*/
|
||||
epicsShareFunc unsigned testMonitorCount(testMonitor*, unsigned reset);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -132,7 +132,7 @@ const char *initHookName(int state)
|
||||
"initHookAfterInterruptAccept",
|
||||
"initHookAtEnd"
|
||||
};
|
||||
if (state < 0 || state > NELEMENTS(stateName)) {
|
||||
if (state < 0 || state >= NELEMENTS(stateName)) {
|
||||
return "Not an initHookState";
|
||||
}
|
||||
return stateName[state];
|
||||
|
||||
@@ -48,6 +48,14 @@ typedef enum {
|
||||
initHookAfterDatabasePaused,
|
||||
initHookAfterIocPaused, /* End of iocPause command */
|
||||
|
||||
initHookAtIocShutdown, /* Start of iocShutdown */
|
||||
initHookAfterCaLinkClose,
|
||||
initHookAfterScanShutdown,
|
||||
initHookAfterCallbackShutdown,
|
||||
initHookAfterCaServerStopped,
|
||||
initHookAfterDatabaseStopped,
|
||||
initHookAfterIocShutdown, /* End of iocShutdown */
|
||||
|
||||
/* Deprecated states, provided for backwards compatibility.
|
||||
* These states are announced at the same point they were before,
|
||||
* but will not be repeated if the IOC gets paused and restarted.
|
||||
|
||||
@@ -161,14 +161,9 @@ void recGblGetControlDouble(const struct dbAddr *paddr,
|
||||
&pcd->upper_ctrl_limit, &pcd->lower_ctrl_limit);
|
||||
}
|
||||
|
||||
int recGblInitConstantLink(
|
||||
struct link *plink,short dbftype,void *pdest)
|
||||
int recGblInitConstantLink(struct link *plink, short dbftype, void *pdest)
|
||||
{
|
||||
long status = dbLoadLink(plink, dbftype, pdest);
|
||||
|
||||
if (status)
|
||||
return FALSE;
|
||||
return TRUE;
|
||||
return !dbLoadLink(plink, dbftype, pdest);
|
||||
}
|
||||
|
||||
unsigned short recGblResetAlarms(void *precord)
|
||||
@@ -309,11 +304,11 @@ static void getMaxRangeValues(short field_type, double *pupper_limit,
|
||||
{
|
||||
switch(field_type){
|
||||
case DBF_CHAR:
|
||||
*pupper_limit = -128.0;
|
||||
*plower_limit = 127.0;
|
||||
*pupper_limit = (double) CHAR_MAX;
|
||||
*plower_limit = (double) CHAR_MIN;
|
||||
break;
|
||||
case DBF_UCHAR:
|
||||
*pupper_limit = 255.0;
|
||||
*pupper_limit = (double) UCHAR_MAX;
|
||||
*plower_limit = 0.0;
|
||||
break;
|
||||
case DBF_SHORT:
|
||||
|
||||
@@ -10,17 +10,25 @@ TOP=../../../..
|
||||
|
||||
include $(TOP)/configure/CONFIG
|
||||
|
||||
# Allow access to private headers in db/
|
||||
USR_CPPFLAGS = -I ../..
|
||||
|
||||
TESTLIBRARY = dbTestIoc
|
||||
|
||||
dbTestIoc_SRCS += arrRecord.c
|
||||
dbTestIoc_SRCS += xRecord.c
|
||||
dbTestIoc_SRCS += dbLinkdset.c
|
||||
dbTestIoc_SRCS += devx.c
|
||||
dbTestIoc_LIBS = dbCore ca Com
|
||||
|
||||
TARGETS += $(COMMON_DIR)/dbTestIoc.dbd
|
||||
dbTestIoc_DBD += menuGlobal.dbd
|
||||
dbTestIoc_DBD += menuConvert.dbd
|
||||
dbTestIoc_DBD += menuScan.dbd
|
||||
#dbTestIoc_DBD += arrRecord.dbd
|
||||
dbTestIoc_DBD += xRecord.dbd
|
||||
dbTestIoc_DBD += arrRecord.dbd
|
||||
dbTestIoc_DBD += devx.dbd
|
||||
dbTestIoc_DBD += dbLinkdset.dbd
|
||||
TESTFILES += $(COMMON_DIR)/dbTestIoc.dbd ../xRecord.db
|
||||
|
||||
@@ -28,6 +36,12 @@ testHarness_SRCS += dbTestIoc_registerRecordDeviceDriver.cpp
|
||||
|
||||
PROD_LIBS = dbTestIoc dbCore ca Com
|
||||
|
||||
TESTPROD_HOST += dbScanTest
|
||||
dbScanTest_SRCS += dbScanTest.c
|
||||
dbScanTest_SRCS += dbTestIoc_registerRecordDeviceDriver.cpp
|
||||
testHarness_SRCS += dbScanTest.c
|
||||
TESTS += dbScanTest
|
||||
|
||||
TESTPROD_HOST += dbShutdownTest
|
||||
dbShutdownTest_SRCS += dbShutdownTest.c
|
||||
dbShutdownTest_SRCS += dbTestIoc_registerRecordDeviceDriver.cpp
|
||||
@@ -39,7 +53,7 @@ dbPutLinkTest_SRCS += dbPutLinkTest.c
|
||||
dbPutLinkTest_SRCS += dbTestIoc_registerRecordDeviceDriver.cpp
|
||||
testHarness_SRCS += dbPutLinkTest.c
|
||||
TESTS += dbPutLinkTest
|
||||
TESTFILES += ../dbPutLinkTest.db
|
||||
TESTFILES += ../dbPutLinkTest.db ../dbBadLink.db
|
||||
|
||||
TESTPROD_HOST += dbLockTest
|
||||
dbLockTest_SRCS += dbLockTest.c
|
||||
@@ -48,6 +62,14 @@ testHarness_SRCS += dbLockTest.c
|
||||
TESTS += dbLockTest
|
||||
TESTFILES += ../dbLockTest.db
|
||||
|
||||
TESTPROD_HOST += dbStressTest
|
||||
dbStressTest_SRCS += dbStressLock.c
|
||||
dbStressTest_SRCS += dbTestIoc_registerRecordDeviceDriver.cpp
|
||||
dbStressTest_SYS_LIBS_solaris += rt
|
||||
dbStressTest_SYS_LIBS_Linux += rt
|
||||
TESTS += dbStressTest
|
||||
TESTFILES += ../dbStressLock.db
|
||||
|
||||
TESTPROD_HOST += testdbConvert
|
||||
testdbConvert_SRCS += testdbConvert.c
|
||||
testHarness_SRCS += testdbConvert.c
|
||||
@@ -73,20 +95,23 @@ dbCaStatsTest_SRCS += dbCaStatsTest.c
|
||||
dbCaStatsTest_SRCS += dbTestIoc_registerRecordDeviceDriver.cpp
|
||||
testHarness_SRCS += dbCaStatsTest.c
|
||||
TESTS += dbCaStatsTest
|
||||
TESTFILES += ../dbCaStatsTest.db
|
||||
TESTFILES += ../dbCaStats.db
|
||||
|
||||
TESTPROD_HOST += dbCaLinkTest
|
||||
dbCaLinkTest_SRCS += dbCaLinkTest.c
|
||||
dbCaLinkTest_SRCS += dbCACTest.cpp
|
||||
dbCaLinkTest_SRCS += dbTestIoc_registerRecordDeviceDriver.cpp
|
||||
testHarness_SRCS += dbCaLinkTest.c
|
||||
testHarness_SRCS += dbCACTest.cpp
|
||||
TESTS += dbCaLinkTest
|
||||
TESTFILES += ../dbCaLinkTest1.db ../dbCaLinkTest2.db ../dbCaLinkTest3.db
|
||||
|
||||
TARGETS += $(COMMON_DIR)/scanIoTest.dbd
|
||||
scanIoTest_DBD += menuGlobal.dbd
|
||||
scanIoTest_DBD += menuConvert.dbd
|
||||
scanIoTest_DBD += menuScan.dbd
|
||||
scanIoTest_DBD += yRecord.dbd
|
||||
TESTPROD_HOST += scanIoTest
|
||||
scanIoTest_SRCS += scanIoTest.c
|
||||
scanIoTest_REGRDDFLAGS = -l
|
||||
scanIoTest_SRCS += scanIoTest_registerRecordDeviceDriver.cpp
|
||||
scanIoTest_SRCS += dbTestIoc_registerRecordDeviceDriver.cpp
|
||||
testHarness_SRCS += scanIoTest.c
|
||||
testHarness_SRCS += scanIoTest_registerRecordDeviceDriver.cpp
|
||||
TESTFILES += $(COMMON_DIR)/scanIoTest.dbd ../scanIoTest.db
|
||||
TESTFILES += ../scanIoTest.db
|
||||
TESTS += scanIoTest
|
||||
|
||||
TESTPROD_HOST += dbChannelTest
|
||||
@@ -95,6 +120,16 @@ dbChannelTest_SRCS += dbTestIoc_registerRecordDeviceDriver.cpp
|
||||
testHarness_SRCS += dbChannelTest.c
|
||||
TESTS += dbChannelTest
|
||||
|
||||
TARGETS += $(COMMON_DIR)/dbChArrTest.dbd
|
||||
dbChArrTest_DBD += arrRecord.dbd
|
||||
TESTPROD_HOST += dbChArrTest
|
||||
dbChArrTest_SRCS += dbChArrTest.cpp
|
||||
dbChArrTest_SRCS += dbChArrTest_registerRecordDeviceDriver.cpp
|
||||
testHarness_SRCS += dbChArrTest.cpp
|
||||
testHarness_SRCS += dbChArrTest_registerRecordDeviceDriver.cpp
|
||||
TESTFILES += $(COMMON_DIR)/dbChArrTest.dbd ../dbChArrTest.db
|
||||
TESTS += dbChArrTest
|
||||
|
||||
TESTPROD_HOST += chfPluginTest
|
||||
chfPluginTest_SRCS += chfPluginTest.c
|
||||
chfPluginTest_SRCS += dbTestIoc_registerRecordDeviceDriver.cpp
|
||||
@@ -132,7 +167,11 @@ TESTSCRIPTS_HOST += $(TESTS:%=%.t)
|
||||
|
||||
include $(TOP)/configure/RULES
|
||||
|
||||
xRecord$(DEP): $(COMMON_DIR)/xRecord.h
|
||||
arrRecord$(DEP): $(COMMON_DIR)/arrRecord.h
|
||||
dbCaLinkTest$(DEP): $(COMMON_DIR)/xRecord.h $(COMMON_DIR)/arrRecord.h
|
||||
dbPutLinkTest$(DEP): $(COMMON_DIR)/xRecord.h
|
||||
scanIoTest$(DEP): $(COMMON_DIR)/yRecord.h
|
||||
dbStressLock$(DEP): $(COMMON_DIR)/xRecord.h
|
||||
devx$(DEP): $(COMMON_DIR)/xRecord.h
|
||||
scanIoTest$(DEP): $(COMMON_DIR)/xRecord.h
|
||||
xRecord$(DEP): $(COMMON_DIR)/xRecord.h
|
||||
|
||||
|
||||
141
src/ioc/db/test/arrRecord.c
Normal file
141
src/ioc/db/test/arrRecord.c
Normal file
@@ -0,0 +1,141 @@
|
||||
/*************************************************************************\
|
||||
* Copyright (c) 2010 Brookhaven National Laboratory.
|
||||
* Copyright (c) 2010 Helmholtz-Zentrum Berlin
|
||||
* fuer Materialien und Energie GmbH.
|
||||
* Copyright (c) 2008 UChicago Argonne LLC, as Operator of Argonne
|
||||
* National Laboratory.
|
||||
* Copyright (c) 2002 The Regents of the University of California, as
|
||||
* Operator of Los Alamos National Laboratory.
|
||||
* EPICS BASE is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
\*************************************************************************/
|
||||
|
||||
/* arrRecord.c - minimal array record for test purposes: no processing */
|
||||
|
||||
/*
|
||||
* Author: Ralph Lange <Ralph.Lange@bessy.de>
|
||||
*
|
||||
* vaguely implemented like parts of recWaveform.c by Bob Dalesio
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include "dbDefs.h"
|
||||
#include "epicsPrint.h"
|
||||
#include "dbAccess.h"
|
||||
#include "dbEvent.h"
|
||||
#include "dbFldTypes.h"
|
||||
#include "recSup.h"
|
||||
#include "recGbl.h"
|
||||
#include "cantProceed.h"
|
||||
#define GEN_SIZE_OFFSET
|
||||
#include "arrRecord.h"
|
||||
#undef GEN_SIZE_OFFSET
|
||||
#include "epicsExport.h"
|
||||
|
||||
/* Create RSET - Record Support Entry Table*/
|
||||
#define report NULL
|
||||
#define initialize NULL
|
||||
static long init_record(arrRecord *, int);
|
||||
static long process(arrRecord *);
|
||||
#define special NULL
|
||||
#define get_value NULL
|
||||
static long cvt_dbaddr(DBADDR *);
|
||||
static long get_array_info(DBADDR *, long *, long *);
|
||||
static long put_array_info(DBADDR *, long);
|
||||
#define get_units NULL
|
||||
#define get_precision NULL
|
||||
#define get_enum_str NULL
|
||||
#define get_enum_strs NULL
|
||||
#define put_enum_str NULL
|
||||
#define get_graphic_double NULL
|
||||
#define get_control_double NULL
|
||||
#define get_alarm_double NULL
|
||||
|
||||
rset arrRSET = {
|
||||
RSETNUMBER,
|
||||
report,
|
||||
initialize,
|
||||
init_record,
|
||||
process,
|
||||
special,
|
||||
get_value,
|
||||
cvt_dbaddr,
|
||||
get_array_info,
|
||||
put_array_info,
|
||||
get_units,
|
||||
get_precision,
|
||||
get_enum_str,
|
||||
get_enum_strs,
|
||||
put_enum_str,
|
||||
get_graphic_double,
|
||||
get_control_double,
|
||||
get_alarm_double
|
||||
};
|
||||
epicsExportAddress(rset, arrRSET);
|
||||
|
||||
static long init_record(arrRecord *prec, int pass)
|
||||
{
|
||||
if (pass == 0) {
|
||||
if (prec->nelm <= 0)
|
||||
prec->nelm = 1;
|
||||
if (prec->ftvl > DBF_ENUM)
|
||||
prec->ftvl = DBF_UCHAR;
|
||||
prec->bptr = callocMustSucceed(prec->nelm, dbValueSize(prec->ftvl),
|
||||
"arr calloc failed");
|
||||
|
||||
if (prec->nelm == 1) {
|
||||
prec->nord = 1;
|
||||
} else {
|
||||
prec->nord = 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long process(arrRecord *prec)
|
||||
{
|
||||
if(prec->clbk)
|
||||
(*prec->clbk)(prec);
|
||||
prec->pact = TRUE;
|
||||
recGblGetTimeStamp(prec);
|
||||
recGblFwdLink(prec);
|
||||
prec->pact = FALSE;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long cvt_dbaddr(DBADDR *paddr)
|
||||
{
|
||||
arrRecord *prec = (arrRecord *) paddr->precord;
|
||||
|
||||
paddr->pfield = prec->bptr;
|
||||
paddr->no_elements = prec->nelm;
|
||||
paddr->field_type = prec->ftvl;
|
||||
paddr->field_size = dbValueSize(prec->ftvl);
|
||||
paddr->dbr_field_type = prec->ftvl;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long get_array_info(DBADDR *paddr, long *no_elements, long *offset)
|
||||
{
|
||||
arrRecord *prec = (arrRecord *) paddr->precord;
|
||||
|
||||
*no_elements = prec->nord;
|
||||
*offset = prec->off;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long put_array_info(DBADDR *paddr, long nNew)
|
||||
{
|
||||
arrRecord *prec = (arrRecord *) paddr->precord;
|
||||
|
||||
prec->nord = nNew;
|
||||
if (prec->nord > prec->nelm)
|
||||
prec->nord = prec->nelm;
|
||||
|
||||
return 0;
|
||||
}
|
||||
42
src/ioc/db/test/arrRecord.dbd
Normal file
42
src/ioc/db/test/arrRecord.dbd
Normal file
@@ -0,0 +1,42 @@
|
||||
include "menuGlobal.dbd"
|
||||
include "menuConvert.dbd"
|
||||
include "menuScan.dbd"
|
||||
recordtype(arr) {
|
||||
include "dbCommon.dbd"
|
||||
field(VAL, DBF_NOACCESS) {
|
||||
prompt("Value")
|
||||
special(SPC_DBADDR)
|
||||
pp(TRUE)
|
||||
extra("void *val")
|
||||
}
|
||||
field(NELM, DBF_ULONG) {
|
||||
prompt("Number of Elements")
|
||||
special(SPC_NOMOD)
|
||||
initial("1")
|
||||
}
|
||||
field(FTVL, DBF_MENU) {
|
||||
prompt("Field Type of Value")
|
||||
special(SPC_NOMOD)
|
||||
menu(menuFtype)
|
||||
}
|
||||
field(NORD, DBF_ULONG) {
|
||||
prompt("Number elements read")
|
||||
special(SPC_NOMOD)
|
||||
}
|
||||
field(OFF, DBF_ULONG) {
|
||||
prompt("Offset into array")
|
||||
}
|
||||
field(BPTR, DBF_NOACCESS) {
|
||||
prompt("Buffer Pointer")
|
||||
special(SPC_NOMOD)
|
||||
extra("void *bptr")
|
||||
}
|
||||
field(INP, DBF_INLINK) {
|
||||
prompt("Input Link")
|
||||
}
|
||||
field(CLBK, DBF_NOACCESS) {
|
||||
prompt("Processing callback")
|
||||
special(SPC_NOMOD)
|
||||
extra("void (*clbk)(struct arrRecord*)")
|
||||
}
|
||||
}
|
||||
@@ -22,7 +22,9 @@
|
||||
#include "dbStaticLib.h"
|
||||
#include "dbAccessDefs.h"
|
||||
#include "registry.h"
|
||||
#include "epicsUnitTest.h"
|
||||
#include "errlog.h"
|
||||
#include "epicsExit.h"
|
||||
#include "dbUnitTest.h"
|
||||
#include "testMain.h"
|
||||
#include "osiFileName.h"
|
||||
|
||||
@@ -85,22 +87,20 @@ MAIN(arrShorthandTest)
|
||||
|
||||
testPlan(26);
|
||||
|
||||
db_init_events();
|
||||
dbChannelInit();
|
||||
testdbPrepare();
|
||||
|
||||
if (dbReadDatabase(&pdbbase, "dbTestIoc.dbd",
|
||||
"." OSI_PATH_LIST_SEPARATOR ".." OSI_PATH_LIST_SEPARATOR
|
||||
"../O.Common" OSI_PATH_LIST_SEPARATOR "O.Common", NULL))
|
||||
testAbort("Database description 'dbTestIoc.dbd' not found");
|
||||
testdbReadDatabase("dbTestIoc.dbd", NULL, NULL);
|
||||
|
||||
dbTestIoc_registerRecordDeviceDriver(pdbbase);
|
||||
if (dbReadDatabase(&pdbbase, "xRecord.db",
|
||||
"." OSI_PATH_LIST_SEPARATOR "..", NULL))
|
||||
testAbort("Test database 'xRecord.db' not found");
|
||||
testdbReadDatabase("xRecord.db", NULL, NULL);
|
||||
|
||||
testHead("Register plugin");
|
||||
testOk(!chfPluginRegister("arr", &myPif, opts), "register fake arr plugin");
|
||||
|
||||
eltc(0);
|
||||
testIocInitOk();
|
||||
eltc(1);
|
||||
|
||||
#define TESTBAD(Title, Expr) \
|
||||
testDiag(Title); \
|
||||
testOk(!(pch = dbChannelCreate("x." Expr)), "dbChannelCreate (" Expr ") fails"); \
|
||||
@@ -129,9 +129,8 @@ MAIN(arrShorthandTest)
|
||||
TESTGOOD("range [s::e]", "[2::4]", 2, 1, 4);
|
||||
TESTGOOD("range with incr [s:i:e]", "[2:3:4]", 2, 3, 4);
|
||||
|
||||
dbFreeBase(pdbbase);
|
||||
registryFree();
|
||||
pdbbase = NULL;
|
||||
testIocShutdownOk();
|
||||
testdbCleanup();
|
||||
|
||||
return testDone();
|
||||
}
|
||||
|
||||
@@ -184,7 +184,8 @@ MAIN(callbackParallelTest)
|
||||
free(pcbt[i]);
|
||||
}
|
||||
|
||||
callbackShutdown();
|
||||
callbackStop();
|
||||
callbackCleanup();
|
||||
|
||||
return testDone();
|
||||
}
|
||||
|
||||
@@ -181,7 +181,8 @@ MAIN(callbackTest)
|
||||
free(pcbt[i]);
|
||||
}
|
||||
|
||||
callbackShutdown();
|
||||
callbackStop();
|
||||
callbackCleanup();
|
||||
|
||||
return testDone();
|
||||
}
|
||||
|
||||
@@ -418,7 +418,7 @@ static void channelRegisterPost(dbChannel *chan, void *user,
|
||||
static void channel_report(dbChannel *chan, void *user, int level,
|
||||
const unsigned short indent)
|
||||
{
|
||||
testOk(level == R_LEVEL - 1, "channel_report: level correct");
|
||||
testOk(level == R_LEVEL - 2, "channel_report: level correct %u == %u", level, R_LEVEL-2);
|
||||
if (user == puser1) {
|
||||
testOk(e1 & e_report, "channel_report (1) called");
|
||||
c1 |= e_report;
|
||||
|
||||
84
src/ioc/db/test/dbCACTest.cpp
Normal file
84
src/ioc/db/test/dbCACTest.cpp
Normal file
@@ -0,0 +1,84 @@
|
||||
/*************************************************************************\
|
||||
* Copyright (c) 2015 Brookhaven Science Assoc. as operator of Brookhaven
|
||||
* National Laboratory.
|
||||
* EPICS BASE is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
\*************************************************************************/
|
||||
/*
|
||||
* Part of dbCaLinkTest, compiled seperately to avoid
|
||||
* dbAccess.h vs. db_access.h conflicts
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include <vector>
|
||||
#include <stdexcept>
|
||||
|
||||
#include <epicsEvent.h>
|
||||
|
||||
#include "epicsUnitTest.h"
|
||||
|
||||
#include "cadef.h"
|
||||
|
||||
#define testECA(OP) if((OP)!=ECA_NORMAL) {testAbort("%s", #OP);} else {testPass("%s", #OP);}
|
||||
|
||||
void putgetarray(chid chanid, double first, size_t count)
|
||||
{
|
||||
testDiag("putgetarray(%f,%u)", first, (unsigned)count);
|
||||
|
||||
std::vector<double> buf(count);
|
||||
for(size_t i=0; i<count ;i++)
|
||||
buf[i] = first + i*1.0;
|
||||
|
||||
testDiag("Put");
|
||||
|
||||
testECA(ca_array_put(DBR_DOUBLE, count, chanid, &buf[0]));
|
||||
|
||||
testECA(ca_pend_io(1.0));
|
||||
|
||||
testDiag("Get");
|
||||
|
||||
std::vector<double> buf2(count);
|
||||
|
||||
testECA(ca_array_get(DBR_DOUBLE, count, chanid, &buf2[0]));
|
||||
|
||||
testECA(ca_pend_io(1.0));
|
||||
|
||||
for(size_t i=0; i<count ;i++)
|
||||
testOk(buf[i]==buf2[i], "%f == %f", buf[i], buf2[i]);
|
||||
}
|
||||
|
||||
|
||||
|
||||
struct CATestContext
|
||||
{
|
||||
CATestContext()
|
||||
{
|
||||
if(ca_context_create(ca_enable_preemptive_callback)!=ECA_NORMAL)
|
||||
throw std::runtime_error("Failed to create CA context");
|
||||
}
|
||||
~CATestContext()
|
||||
{
|
||||
ca_context_destroy();
|
||||
}
|
||||
};
|
||||
|
||||
extern "C"
|
||||
void dbCaLinkTest_testCAC(void)
|
||||
{
|
||||
try {
|
||||
CATestContext ctxt;
|
||||
chid chanid = 0;
|
||||
testECA(ca_create_channel("target1", NULL, NULL, 0, &chanid));
|
||||
testECA(ca_pend_io(1.0));
|
||||
putgetarray(chanid, 1.0, 1);
|
||||
putgetarray(chanid, 2.0, 2);
|
||||
// repeat to ensure a cache hit in dbContextReadNotifyCacheAllocator
|
||||
putgetarray(chanid, 2.0, 2);
|
||||
putgetarray(chanid, 5.0, 5);
|
||||
|
||||
testECA(ca_clear_channel(chanid));
|
||||
}catch(std::exception& e){
|
||||
testAbort("Unexpected exception in testCAC: %s", e.what());
|
||||
}
|
||||
}
|
||||
613
src/ioc/db/test/dbCaLinkTest.c
Normal file
613
src/ioc/db/test/dbCaLinkTest.c
Normal file
@@ -0,0 +1,613 @@
|
||||
/*************************************************************************\
|
||||
* Copyright (c) 2015 Brookhaven Science Assoc. as operator of Brookhaven
|
||||
* National Laboratory.
|
||||
* EPICS BASE is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
\*************************************************************************/
|
||||
|
||||
/*
|
||||
* Author: Michael Davidsaver <mdavidsaver@bnl.gov>
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
|
||||
#define EPICS_DBCA_PRIVATE_API
|
||||
#include "epicsString.h"
|
||||
#include "dbUnitTest.h"
|
||||
#include "epicsThread.h"
|
||||
#include "cantProceed.h"
|
||||
#include "epicsEvent.h"
|
||||
#include "iocInit.h"
|
||||
#include "dbBase.h"
|
||||
#include "link.h"
|
||||
#include "dbAccess.h"
|
||||
#include "epicsStdio.h"
|
||||
#include "dbEvent.h"
|
||||
|
||||
/* Declarations from cadef.h and db_access.h which we can't include here */
|
||||
typedef void * chid;
|
||||
epicsShareExtern const unsigned short dbr_value_size[];
|
||||
epicsShareExtern short epicsShareAPI ca_field_type (chid chan);
|
||||
#define MAX_UNITS_SIZE 8
|
||||
|
||||
#include "dbCaPvt.h"
|
||||
#include "errlog.h"
|
||||
#include "testMain.h"
|
||||
|
||||
#include "xRecord.h"
|
||||
#include "arrRecord.h"
|
||||
|
||||
#define testOp(FMT,A,OP,B) testOk((A)OP(B), #A " ("FMT") " #OP " " #B " ("FMT")", A,B)
|
||||
|
||||
void dbTestIoc_registerRecordDeviceDriver(struct dbBase *);
|
||||
|
||||
static epicsEventId waitEvent;
|
||||
static unsigned waitCounter;
|
||||
|
||||
static
|
||||
void waitCB(void *unused)
|
||||
{
|
||||
if(waitEvent)
|
||||
epicsEventMustTrigger(waitEvent);
|
||||
waitCounter++; /* TODO: atomic */
|
||||
}
|
||||
|
||||
static
|
||||
void startWait(DBLINK *plink)
|
||||
{
|
||||
caLink *pca = plink->value.pv_link.pvt;
|
||||
|
||||
assert(!waitEvent);
|
||||
waitEvent = epicsEventMustCreate(epicsEventEmpty);
|
||||
|
||||
assert(pca);
|
||||
epicsMutexMustLock(pca->lock);
|
||||
assert(!pca->monitor && !pca->userPvt);
|
||||
pca->monitor = &waitCB;
|
||||
epicsMutexUnlock(pca->lock);
|
||||
testDiag("Preparing to wait on pca=%p", pca);
|
||||
}
|
||||
|
||||
static
|
||||
void waitForUpdate(DBLINK *plink)
|
||||
{
|
||||
caLink *pca = plink->value.pv_link.pvt;
|
||||
|
||||
assert(pca);
|
||||
|
||||
testDiag("Waiting on pca=%p", pca);
|
||||
epicsEventMustWait(waitEvent);
|
||||
|
||||
epicsMutexMustLock(pca->lock);
|
||||
pca->monitor = NULL;
|
||||
pca->userPvt = NULL;
|
||||
epicsMutexUnlock(pca->lock);
|
||||
|
||||
epicsEventDestroy(waitEvent);
|
||||
waitEvent = NULL;
|
||||
}
|
||||
|
||||
static
|
||||
void putLink(DBLINK *plink, short dbr, const void*buf, long nReq)
|
||||
{
|
||||
long ret;
|
||||
|
||||
waitEvent = epicsEventMustCreate(epicsEventEmpty);
|
||||
|
||||
ret = dbCaPutLinkCallback(plink, dbr, buf, nReq,
|
||||
&waitCB, NULL);
|
||||
if(ret) {
|
||||
testFail("putLink fails %ld\n", ret);
|
||||
} else {
|
||||
epicsEventMustWait(waitEvent);
|
||||
testPass("putLink ok\n");
|
||||
}
|
||||
epicsEventDestroy(waitEvent);
|
||||
waitEvent = NULL;
|
||||
}
|
||||
|
||||
static void testNativeLink(void)
|
||||
{
|
||||
xRecord *psrc, *ptarg;
|
||||
DBLINK *psrclnk;
|
||||
epicsInt32 temp;
|
||||
long nReq;
|
||||
|
||||
testDiag("Link to a scalar numeric field");
|
||||
testdbPrepare();
|
||||
|
||||
testdbReadDatabase("dbTestIoc.dbd", NULL, NULL);
|
||||
|
||||
dbTestIoc_registerRecordDeviceDriver(pdbbase);
|
||||
|
||||
testdbReadDatabase("dbCaLinkTest1.db", NULL, "TARGET=target CA");
|
||||
|
||||
eltc(0);
|
||||
testIocInitOk();
|
||||
eltc(1);
|
||||
|
||||
psrc = (xRecord*)testdbRecordPtr("source");
|
||||
ptarg= (xRecord*)testdbRecordPtr("target");
|
||||
psrclnk = &psrc->lnk;
|
||||
|
||||
/* ensure this is really a CA link */
|
||||
testOk1(dbLockGetLockId((dbCommon*)psrc)!=dbLockGetLockId((dbCommon*)ptarg));
|
||||
|
||||
testOk1(psrclnk->type==CA_LINK);
|
||||
|
||||
startWait(psrclnk);
|
||||
|
||||
dbScanLock((dbCommon*)ptarg);
|
||||
ptarg->val = 42;
|
||||
db_post_events(ptarg, &ptarg->val, DBE_VALUE|DBE_ALARM|DBE_ARCHIVE);
|
||||
dbScanUnlock((dbCommon*)ptarg);
|
||||
|
||||
waitForUpdate(psrclnk);
|
||||
|
||||
dbScanLock((dbCommon*)psrc);
|
||||
/* local CA_LINK connects immediately */
|
||||
testOk1(dbCaIsLinkConnected(psrclnk));
|
||||
nReq = 422;
|
||||
testOk1(dbCaGetNelements(psrclnk, &nReq)==0);
|
||||
testOp("%ld",nReq,==,1l);
|
||||
|
||||
nReq = 1;
|
||||
temp = 0x0f0f0f0f;
|
||||
testOk1(dbGetLink(psrclnk, DBR_LONG, (void*)&temp, NULL, &nReq)==0);
|
||||
testOp("%d",temp,==,42);
|
||||
dbScanUnlock((dbCommon*)psrc);
|
||||
|
||||
temp = 1010;
|
||||
nReq = 1;
|
||||
putLink(psrclnk, DBR_LONG, (void*)&temp, nReq);
|
||||
|
||||
dbScanLock((dbCommon*)ptarg);
|
||||
testOk1(ptarg->val==1010);
|
||||
dbScanUnlock((dbCommon*)ptarg);
|
||||
|
||||
testIocShutdownOk();
|
||||
|
||||
testdbCleanup();
|
||||
}
|
||||
|
||||
static void testStringLink(void)
|
||||
{
|
||||
xRecord *psrc, *ptarg;
|
||||
DBLINK *psrclnk;
|
||||
char temp[MAX_STRING_SIZE];
|
||||
long nReq;
|
||||
|
||||
testDiag("Link to a string field");
|
||||
testdbPrepare();
|
||||
|
||||
testdbReadDatabase("dbTestIoc.dbd", NULL, NULL);
|
||||
|
||||
dbTestIoc_registerRecordDeviceDriver(pdbbase);
|
||||
|
||||
testdbReadDatabase("dbCaLinkTest1.db", NULL, "TARGET=target.DESC CA");
|
||||
|
||||
eltc(0);
|
||||
testIocInitOk();
|
||||
eltc(1);
|
||||
|
||||
psrc = (xRecord*)testdbRecordPtr("source");
|
||||
ptarg= (xRecord*)testdbRecordPtr("target");
|
||||
psrclnk = &psrc->lnk;
|
||||
|
||||
/* ensure this is really a CA link */
|
||||
testOk1(dbLockGetLockId((dbCommon*)psrc)!=dbLockGetLockId((dbCommon*)ptarg));
|
||||
|
||||
testOk1(psrclnk->type==CA_LINK);
|
||||
|
||||
startWait(psrclnk);
|
||||
|
||||
dbScanLock((dbCommon*)ptarg);
|
||||
strcpy(ptarg->desc, "hello");
|
||||
db_post_events(ptarg, &ptarg->desc, DBE_VALUE|DBE_ALARM|DBE_ARCHIVE);
|
||||
dbScanUnlock((dbCommon*)ptarg);
|
||||
|
||||
waitForUpdate(psrclnk);
|
||||
|
||||
dbScanLock((dbCommon*)psrc);
|
||||
/* local CA_LINK connects immediately */
|
||||
testOk1(dbCaIsLinkConnected(psrclnk));
|
||||
|
||||
nReq = 1;
|
||||
memset(temp, '!', sizeof(temp));
|
||||
testOk1(dbGetLink(psrclnk, DBR_STRING, (void*)&temp, NULL, &nReq)==0);
|
||||
testOk(strcmp(temp, "hello")==0, "%s == hello", temp);
|
||||
dbScanUnlock((dbCommon*)psrc);
|
||||
|
||||
strcpy(temp, "world");
|
||||
putLink(psrclnk, DBR_STRING, (void*)&temp, nReq);
|
||||
|
||||
dbScanLock((dbCommon*)ptarg);
|
||||
testOk(strcmp(ptarg->desc, "world")==0, "%s == world", ptarg->desc);
|
||||
dbScanUnlock((dbCommon*)ptarg);
|
||||
|
||||
testIocShutdownOk();
|
||||
|
||||
testdbCleanup();
|
||||
}
|
||||
|
||||
static void wasproc(xRecord *prec)
|
||||
{
|
||||
waitCB(NULL);
|
||||
}
|
||||
|
||||
static void testCP(void)
|
||||
{
|
||||
xRecord *psrc, *ptarg;
|
||||
|
||||
testDiag("Link CP modifier");
|
||||
testdbPrepare();
|
||||
|
||||
testdbReadDatabase("dbTestIoc.dbd", NULL, NULL);
|
||||
|
||||
dbTestIoc_registerRecordDeviceDriver(pdbbase);
|
||||
|
||||
testdbReadDatabase("dbCaLinkTest1.db", NULL, "TARGET=target CP");
|
||||
|
||||
psrc = (xRecord*)testdbRecordPtr("source");
|
||||
ptarg= (xRecord*)testdbRecordPtr("target");
|
||||
|
||||
/* hook in before IOC init */
|
||||
waitCounter=0;
|
||||
psrc->clbk = &wasproc;
|
||||
|
||||
assert(!waitEvent);
|
||||
waitEvent = epicsEventMustCreate(epicsEventEmpty);
|
||||
|
||||
eltc(0);
|
||||
testIocInitOk();
|
||||
eltc(1);
|
||||
|
||||
epicsEventMustWait(waitEvent);
|
||||
|
||||
dbScanLock((dbCommon*)psrc);
|
||||
testOp("%u",waitCounter,==,1); /* initial processing */
|
||||
dbScanUnlock((dbCommon*)psrc);
|
||||
|
||||
dbScanLock((dbCommon*)ptarg);
|
||||
ptarg->val = 42;
|
||||
db_post_events(ptarg, &ptarg->val, DBE_VALUE|DBE_ALARM|DBE_ARCHIVE);
|
||||
dbScanUnlock((dbCommon*)ptarg);
|
||||
|
||||
epicsEventMustWait(waitEvent);
|
||||
|
||||
dbScanLock((dbCommon*)psrc);
|
||||
testOp("%u",waitCounter,==,2); /* process due to monitor update */
|
||||
dbScanUnlock((dbCommon*)psrc);
|
||||
|
||||
testIocShutdownOk();
|
||||
|
||||
testdbCleanup();
|
||||
|
||||
epicsEventDestroy(waitEvent);
|
||||
waitEvent = NULL;
|
||||
}
|
||||
|
||||
static void fillArray(epicsInt32 *buf, unsigned count, epicsInt32 first)
|
||||
{
|
||||
for(;count;count--,first++)
|
||||
*buf++ = first;
|
||||
}
|
||||
|
||||
static void fillArrayDouble(double *buf, unsigned count, double first)
|
||||
{
|
||||
for(;count;count--,first++)
|
||||
*buf++ = first;
|
||||
}
|
||||
|
||||
static void checkArray(const char *msg,
|
||||
epicsInt32 *buf, epicsInt32 first,
|
||||
unsigned used)
|
||||
{
|
||||
int match = 1;
|
||||
unsigned i;
|
||||
epicsInt32 x, *b;
|
||||
|
||||
for(b=buf,x=first,i=0;i<used;i++,x++,b++)
|
||||
match &= (*b)==x;
|
||||
testOk(match, "%s", msg);
|
||||
if(!match) {
|
||||
for(b=buf,x=first,i=0;i<used;i++,x++,b++)
|
||||
if((*b)!=x)
|
||||
testDiag("%u %u != %u", i, (unsigned)*b, (unsigned)x);
|
||||
}
|
||||
}
|
||||
|
||||
static void checkArrayDouble(const char *msg,
|
||||
double *buf, double first,
|
||||
unsigned used)
|
||||
{
|
||||
int match = 1;
|
||||
unsigned i;
|
||||
double x, *b;
|
||||
|
||||
for(b=buf,x=first,i=0;i<used;i++,x++,b++)
|
||||
match &= (*b)==x;
|
||||
testOk(match, "%s", msg);
|
||||
if(!match) {
|
||||
for(b=buf,x=first,i=0;i<used;i++,x++,b++)
|
||||
if((*b)!=x)
|
||||
testDiag("%u %u != %u", i, (unsigned)*b, (unsigned)x);
|
||||
}
|
||||
}
|
||||
|
||||
static void spoilputbuf(DBLINK *lnk)
|
||||
{
|
||||
caLink *pca = lnk->value.pv_link.pvt;
|
||||
|
||||
if(lnk->type!=CA_LINK || !pca->pputNative)
|
||||
return;
|
||||
epicsMutexMustLock(pca->lock);
|
||||
memset(pca->pputNative, '!',
|
||||
pca->nelements*dbr_value_size[ca_field_type(pca->chid)]);
|
||||
epicsMutexUnlock(pca->lock);
|
||||
}
|
||||
|
||||
static void testArrayLink(unsigned nsrc, unsigned ntarg)
|
||||
{
|
||||
char buf[100];
|
||||
arrRecord *psrc, *ptarg;
|
||||
DBLINK *psrclnk;
|
||||
epicsInt32 *bufsrc, *buftarg, *tmpbuf;
|
||||
long nReq;
|
||||
unsigned num_min, num_max;
|
||||
|
||||
testDiag("Link to a array numeric field");
|
||||
|
||||
/* source.INP = "target CA" */
|
||||
epicsSnprintf(buf, sizeof(buf), "TARGET=target CA,FTVL=LONG,SNELM=%u,TNELM=%u",
|
||||
nsrc, ntarg);
|
||||
testDiag("%s", buf);
|
||||
|
||||
testdbPrepare();
|
||||
|
||||
testdbReadDatabase("dbTestIoc.dbd", NULL, NULL);
|
||||
|
||||
dbTestIoc_registerRecordDeviceDriver(pdbbase);
|
||||
|
||||
testdbReadDatabase("dbCaLinkTest2.db", NULL, buf);
|
||||
|
||||
psrc = (arrRecord*)testdbRecordPtr("source");
|
||||
ptarg= (arrRecord*)testdbRecordPtr("target");
|
||||
psrclnk = &psrc->inp;
|
||||
|
||||
eltc(0);
|
||||
testIocInitOk();
|
||||
eltc(1);
|
||||
|
||||
bufsrc = psrc->bptr;
|
||||
buftarg= ptarg->bptr;
|
||||
|
||||
num_max=num_min=psrc->nelm;
|
||||
if(num_min>ptarg->nelm)
|
||||
num_min=ptarg->nelm;
|
||||
if(num_max<ptarg->nelm)
|
||||
num_max=ptarg->nelm;
|
||||
/* always request more than can possibly be filled */
|
||||
num_max += 2;
|
||||
|
||||
tmpbuf = callocMustSucceed(num_max, sizeof(*tmpbuf), "tmpbuf");
|
||||
|
||||
startWait(psrclnk);
|
||||
|
||||
dbScanLock((dbCommon*)ptarg);
|
||||
fillArray(buftarg, ptarg->nelm, 1);
|
||||
ptarg->nord = ptarg->nelm;
|
||||
db_post_events(ptarg, ptarg->bptr, DBE_VALUE|DBE_ALARM|DBE_ARCHIVE);
|
||||
dbScanUnlock((dbCommon*)ptarg);
|
||||
|
||||
waitForUpdate(psrclnk);
|
||||
|
||||
dbScanLock((dbCommon*)psrc);
|
||||
testDiag("fetch source.INP into source.BPTR");
|
||||
nReq = psrc->nelm;
|
||||
if(dbGetLink(psrclnk, DBR_LONG, bufsrc, NULL, &nReq)==0) {
|
||||
testPass("dbGetLink");
|
||||
testOp("%ld",nReq,==,(long)num_min);
|
||||
checkArray("array update", bufsrc, 1, nReq);
|
||||
} else {
|
||||
testFail("dbGetLink");
|
||||
testSkip(2, "dbGetLink fails");
|
||||
}
|
||||
testDiag("fetch source.INP into temp buffer w/ larger capacity");
|
||||
nReq = num_max;
|
||||
if(dbGetLink(psrclnk, DBR_LONG, tmpbuf, NULL, &nReq)==0) {
|
||||
testPass("dbGetLink");
|
||||
testOp("%ld",nReq,==,(long)ntarg);
|
||||
checkArray("array update", tmpbuf, 1, nReq);
|
||||
} else {
|
||||
testFail("dbGetLink");
|
||||
testSkip(2, "dbGetLink fails");
|
||||
}
|
||||
dbScanUnlock((dbCommon*)psrc);
|
||||
|
||||
fillArray(bufsrc, psrc->nelm, 2);
|
||||
/* write buffer allocated on first put */
|
||||
putLink(psrclnk, DBR_LONG, bufsrc, psrc->nelm);
|
||||
|
||||
dbScanLock((dbCommon*)ptarg);
|
||||
testOp("%ld",(long)ptarg->nord,==,(long)num_min);
|
||||
|
||||
dbScanUnlock((dbCommon*)ptarg);
|
||||
|
||||
/* write again to ensure that buffer is completely updated */
|
||||
spoilputbuf(psrclnk);
|
||||
fillArray(bufsrc, psrc->nelm, 3);
|
||||
putLink(psrclnk, DBR_LONG, bufsrc, psrc->nelm);
|
||||
|
||||
dbScanLock((dbCommon*)ptarg);
|
||||
testOp("%ld",(long)ptarg->nord,==,(long)num_min);
|
||||
checkArray("array update", buftarg, 3, num_min);
|
||||
dbScanUnlock((dbCommon*)ptarg);
|
||||
|
||||
testIocShutdownOk();
|
||||
|
||||
testdbCleanup();
|
||||
|
||||
free(tmpbuf);
|
||||
/* records don't cleanup after themselves
|
||||
* so do here to silence valgrind
|
||||
*/
|
||||
free(bufsrc);
|
||||
free(buftarg);
|
||||
}
|
||||
|
||||
|
||||
static void softarr(arrRecord *prec)
|
||||
{
|
||||
long nReq = prec->nelm;
|
||||
long status = dbGetLink(&prec->inp, DBR_DOUBLE, prec->bptr, NULL, &nReq);
|
||||
|
||||
if(status) {
|
||||
testFail("dbGetLink() -> %ld", status);
|
||||
} else {
|
||||
testPass("dbGetLink() succeeds");
|
||||
prec->nord = nReq;
|
||||
if(nReq>0)
|
||||
testDiag("%s.VAL[0] - %f", prec->name, *(double*)prec->bptr);
|
||||
}
|
||||
waitCB(NULL);
|
||||
}
|
||||
|
||||
static void testreTargetTypeChange(void)
|
||||
{
|
||||
arrRecord *psrc, *ptarg1, *ptarg2;
|
||||
double *bufsrc, *buftarg1;
|
||||
epicsInt32 *buftarg2;
|
||||
|
||||
testDiag("Retarget an link to a PV with a different type DOUBLE->LONG");
|
||||
testdbPrepare();
|
||||
|
||||
testdbReadDatabase("dbTestIoc.dbd", NULL, NULL);
|
||||
|
||||
dbTestIoc_registerRecordDeviceDriver(pdbbase);
|
||||
|
||||
testdbReadDatabase("dbCaLinkTest3.db", NULL, "NELM=5,TARGET=target1 CP");
|
||||
|
||||
psrc = (arrRecord*)testdbRecordPtr("source");
|
||||
ptarg1= (arrRecord*)testdbRecordPtr("target1");
|
||||
ptarg2= (arrRecord*)testdbRecordPtr("target2");
|
||||
|
||||
/* hook in before IOC init */
|
||||
waitCounter=0;
|
||||
psrc->clbk = &softarr;
|
||||
|
||||
assert(!waitEvent);
|
||||
waitEvent = epicsEventMustCreate(epicsEventEmpty);
|
||||
|
||||
eltc(0);
|
||||
testIocInitOk();
|
||||
eltc(1);
|
||||
|
||||
epicsEventMustWait(waitEvent); /* wait for initial processing */
|
||||
|
||||
bufsrc = psrc->bptr;
|
||||
buftarg1= ptarg1->bptr;
|
||||
buftarg2= ptarg2->bptr;
|
||||
|
||||
testDiag("Update one with original target");
|
||||
|
||||
dbScanLock((dbCommon*)ptarg2);
|
||||
fillArray(buftarg2, ptarg2->nelm, 2);
|
||||
ptarg2->nord = ptarg2->nelm;
|
||||
dbScanUnlock((dbCommon*)ptarg2);
|
||||
|
||||
/* initialize buffers */
|
||||
dbScanLock((dbCommon*)ptarg1);
|
||||
fillArrayDouble(buftarg1, ptarg1->nelm, 1);
|
||||
ptarg1->nord = ptarg1->nelm;
|
||||
db_post_events(ptarg1, ptarg1->bptr, DBE_VALUE|DBE_ALARM|DBE_ARCHIVE);
|
||||
dbScanUnlock((dbCommon*)ptarg1);
|
||||
|
||||
epicsEventMustWait(waitEvent); /* wait for update */
|
||||
|
||||
dbScanLock((dbCommon*)psrc);
|
||||
testOp("%ld",(long)psrc->nord,==,(long)5);
|
||||
checkArrayDouble("array update", bufsrc, 1, 5);
|
||||
dbScanUnlock((dbCommon*)psrc);
|
||||
|
||||
testDiag("Retarget");
|
||||
testdbPutFieldOk("source.INP", DBR_STRING, "target2 CP");
|
||||
|
||||
epicsEventMustWait(waitEvent); /* wait for update */
|
||||
|
||||
dbScanLock((dbCommon*)psrc);
|
||||
testOp("%ld",(long)psrc->nord,==,(long)5);
|
||||
checkArrayDouble("array update", bufsrc, 2, 5);
|
||||
dbScanUnlock((dbCommon*)psrc);
|
||||
|
||||
testIocShutdownOk();
|
||||
|
||||
testdbCleanup();
|
||||
|
||||
/* records don't cleanup after themselves
|
||||
* so do here to silence valgrind
|
||||
*/
|
||||
free(bufsrc);
|
||||
free(buftarg1);
|
||||
free(buftarg2);
|
||||
}
|
||||
|
||||
void dbCaLinkTest_testCAC(void);
|
||||
|
||||
static void testCAC(void)
|
||||
{
|
||||
arrRecord *psrc, *ptarg1, *ptarg2;
|
||||
double *bufsrc, *buftarg1;
|
||||
epicsInt32 *buftarg2;
|
||||
|
||||
testDiag("Check local CA through libca");
|
||||
testdbPrepare();
|
||||
|
||||
testdbReadDatabase("dbTestIoc.dbd", NULL, NULL);
|
||||
|
||||
dbTestIoc_registerRecordDeviceDriver(pdbbase);
|
||||
|
||||
testdbReadDatabase("dbCaLinkTest3.db", NULL, "NELM=5,TARGET=target1 CP");
|
||||
|
||||
psrc = (arrRecord*)testdbRecordPtr("source");
|
||||
ptarg1= (arrRecord*)testdbRecordPtr("target1");
|
||||
ptarg2= (arrRecord*)testdbRecordPtr("target2");
|
||||
|
||||
eltc(0);
|
||||
testIocInitOk();
|
||||
eltc(1);
|
||||
|
||||
bufsrc = psrc->bptr;
|
||||
buftarg1= ptarg1->bptr;
|
||||
buftarg2= ptarg2->bptr;
|
||||
|
||||
dbCaLinkTest_testCAC();
|
||||
|
||||
testIocShutdownOk();
|
||||
|
||||
testdbCleanup();
|
||||
|
||||
/* records don't cleanup after themselves
|
||||
* so do here to silence valgrind
|
||||
*/
|
||||
free(bufsrc);
|
||||
free(buftarg1);
|
||||
free(buftarg2);
|
||||
}
|
||||
|
||||
MAIN(dbCaLinkTest)
|
||||
{
|
||||
testPlan(99);
|
||||
testNativeLink();
|
||||
testStringLink();
|
||||
testCP();
|
||||
testArrayLink(1,1);
|
||||
testArrayLink(10,1);
|
||||
testArrayLink(1,10);
|
||||
testArrayLink(10,10);
|
||||
testreTargetTypeChange();
|
||||
testCAC();
|
||||
return testDone();
|
||||
}
|
||||
5
src/ioc/db/test/dbCaLinkTest1.db
Normal file
5
src/ioc/db/test/dbCaLinkTest1.db
Normal file
@@ -0,0 +1,5 @@
|
||||
record(x, "target") {}
|
||||
|
||||
record(x, "source") {
|
||||
field(LNK, "$(TARGET)")
|
||||
}
|
||||
10
src/ioc/db/test/dbCaLinkTest2.db
Normal file
10
src/ioc/db/test/dbCaLinkTest2.db
Normal file
@@ -0,0 +1,10 @@
|
||||
record(arr, "target") {
|
||||
field(FTVL, "$(TFTVL=$(FTVL=))")
|
||||
field(NELM, "$(TNELM=$(NELM=))")
|
||||
}
|
||||
|
||||
record(arr, "source") {
|
||||
field(INP, "$(TARGET)")
|
||||
field(FTVL, "$(SFTVL=$(FTVL=))")
|
||||
field(NELM, "$(SNELM=$(NELM=))")
|
||||
}
|
||||
14
src/ioc/db/test/dbCaLinkTest3.db
Normal file
14
src/ioc/db/test/dbCaLinkTest3.db
Normal file
@@ -0,0 +1,14 @@
|
||||
record(arr, "target1") {
|
||||
field(FTVL, "DOUBLE")
|
||||
field(NELM, "$(TNELM=$(NELM=))")
|
||||
}
|
||||
record(arr, "target2") {
|
||||
field(FTVL, "LONG")
|
||||
field(NELM, "$(TNELM=$(NELM=))")
|
||||
}
|
||||
|
||||
record(arr, "source") {
|
||||
field(INP, "$(TARGET)")
|
||||
field(FTVL, "DOUBLE")
|
||||
field(NELM, "$(SNELM=$(NELM=))")
|
||||
}
|
||||
246
src/ioc/db/test/dbChArrTest.cpp
Normal file
246
src/ioc/db/test/dbChArrTest.cpp
Normal file
@@ -0,0 +1,246 @@
|
||||
/*************************************************************************\
|
||||
* Copyright (c) 2010 Brookhaven National Laboratory.
|
||||
* Copyright (c) 2010 Helmholtz-Zentrum Berlin
|
||||
* fuer Materialien und Energie GmbH.
|
||||
* Copyright (c) 2016 UChicago Argonne LLC, as Operator of Argonne
|
||||
* National Laboratory.
|
||||
* Copyright (c) 2003 The Regents of the University of California, as
|
||||
* Operator of Los Alamos National Laboratory.
|
||||
* EPICS BASE is distributed subject to the Software License Agreement
|
||||
* found in the file LICENSE that is included with this distribution.
|
||||
\*************************************************************************/
|
||||
|
||||
/*
|
||||
* Authors: Ralph Lange <ralph.lange@gmx.de>,
|
||||
* Andrew Johnson <anj@aps.anl.gov>
|
||||
*/
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "registryFunction.h"
|
||||
#include "epicsThread.h"
|
||||
#include "epicsExit.h"
|
||||
#include "epicsStdio.h"
|
||||
#include "epicsString.h"
|
||||
#include "envDefs.h"
|
||||
#include "dbStaticLib.h"
|
||||
#include "dbmf.h"
|
||||
#include "registry.h"
|
||||
#include "dbAddr.h"
|
||||
#include "dbAccess.h"
|
||||
#include "asDbLib.h"
|
||||
#include "iocInit.h"
|
||||
#include "iocsh.h"
|
||||
#include "dbChannel.h"
|
||||
#include "epicsUnitTest.h"
|
||||
#include "testMain.h"
|
||||
#include "osiFileName.h"
|
||||
|
||||
extern "C" {
|
||||
int dbChArrTest_registerRecordDeviceDriver(struct dbBase *pdbbase);
|
||||
}
|
||||
|
||||
#define CA_SERVER_PORT "65535"
|
||||
|
||||
const char *server_port = CA_SERVER_PORT;
|
||||
|
||||
static void createAndOpen(const char *name, dbChannel**pch)
|
||||
{
|
||||
testOk(!!(*pch = dbChannelCreate(name)), "dbChannel %s created", name);
|
||||
testOk(!(dbChannelOpen(*pch)), "dbChannel opened");
|
||||
testOk((ellCount(&(*pch)->pre_chain) == 0), "no filters in pre chain");
|
||||
testOk((ellCount(&(*pch)->post_chain) == 0), "no filters in post chain");
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
static void freeArray(db_field_log *pfl) {
|
||||
if (pfl->type == dbfl_type_ref) {
|
||||
free(pfl->u.r.field);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void testHead (const char *title, const char *typ = "") {
|
||||
const char *line = "------------------------------------------------------------------------------";
|
||||
testDiag("%s", line);
|
||||
testDiag(title, typ);
|
||||
testDiag("%s", line);
|
||||
}
|
||||
|
||||
static void check(short dbr_type) {
|
||||
dbChannel *pch;
|
||||
db_field_log *pfl;
|
||||
dbAddr valaddr;
|
||||
dbAddr offaddr;
|
||||
const char *offname = NULL, *valname = NULL, *typname = NULL;
|
||||
epicsInt32 buf[26];
|
||||
long off, req;
|
||||
int i;
|
||||
|
||||
switch (dbr_type) {
|
||||
case DBR_LONG:
|
||||
offname = "i32.OFF";
|
||||
valname = "i32.VAL";
|
||||
typname = "long";
|
||||
break;
|
||||
case DBR_DOUBLE:
|
||||
offname = "f64.OFF";
|
||||
valname = "f64.VAL";
|
||||
typname = "double";
|
||||
break;
|
||||
case DBR_STRING:
|
||||
offname = "c40.OFF";
|
||||
valname = "c40.VAL";
|
||||
typname = "string";
|
||||
break;
|
||||
default:
|
||||
testDiag("Invalid data type %d", dbr_type);
|
||||
}
|
||||
|
||||
(void) dbNameToAddr(offname, &offaddr);
|
||||
(void) dbNameToAddr(valname, &valaddr);
|
||||
|
||||
testHead("Ten %s elements", typname);
|
||||
|
||||
/* Fill the record's array field with data, 10..19 */
|
||||
|
||||
epicsInt32 ar[10] = {10,11,12,13,14,15,16,17,18,19};
|
||||
(void) dbPutField(&valaddr, DBR_LONG, ar, 10);
|
||||
|
||||
/* Open a channel to it, make sure no filters present */
|
||||
|
||||
createAndOpen(valname, &pch);
|
||||
testOk(pch->final_type == valaddr.field_type,
|
||||
"final type unchanged (%d->%d)", valaddr.field_type, pch->final_type);
|
||||
testOk(pch->final_no_elements == valaddr.no_elements,
|
||||
"final no_elements unchanged (%ld->%ld)", valaddr.no_elements, pch->final_no_elements);
|
||||
|
||||
/* TEST1 sets the record's OFF field, then requests 10 elements from the channel,
|
||||
* passing in a transparent db_field_log and converting the data to LONG on the way in.
|
||||
* It checks that it got back the expected data and the right number of elements.
|
||||
*/
|
||||
|
||||
#define TEST1(Size, Offset, Text, Expected) \
|
||||
testDiag("Reading from offset = %d (%s)", Offset, Text); \
|
||||
off = Offset; req = 10; \
|
||||
memset(buf, 0, sizeof(buf)); \
|
||||
(void) dbPutField(&offaddr, DBR_LONG, &off, 1); \
|
||||
pfl = db_create_read_log(pch); \
|
||||
testOk(pfl && pfl->type == dbfl_type_rec, "Valid pfl, type = rec"); \
|
||||
testOk(!dbChannelGetField(pch, DBR_LONG, buf, NULL, &req, pfl), "Got Field value"); \
|
||||
testOk(req == Size, "Got %ld elements (expected %d)", req, Size); \
|
||||
if (!testOk(!memcmp(buf, Expected, sizeof(Expected)), "Data correct")) \
|
||||
for (i=0; i<Size; i++) \
|
||||
testDiag("Element %d expected %d got %d", i, Expected[i], buf[i]); \
|
||||
db_delete_field_log(pfl);
|
||||
|
||||
const epicsInt32 res_10_0[] = {10,11,12,13,14,15,16,17,18,19};
|
||||
TEST1(10, 0, "no offset", res_10_0);
|
||||
|
||||
const epicsInt32 res_10_4[] = {14,15,16,17,18,19,10,11,12,13};
|
||||
TEST1(10, 4, "wrapped", res_10_4);
|
||||
|
||||
/* Partial array */
|
||||
|
||||
testHead("Five %s elements", typname);
|
||||
off = 0; /* Reset offset for writing the next buffer */
|
||||
(void) dbPutField(&offaddr, DBR_LONG, &off, 1);
|
||||
(void) dbPutField(&valaddr, DBR_LONG, &ar[5], 5);
|
||||
|
||||
createAndOpen(valname, &pch);
|
||||
testOk(pch->final_type == valaddr.field_type,
|
||||
"final type unchanged (%d->%d)", valaddr.field_type, pch->final_type);
|
||||
testOk(pch->final_no_elements == valaddr.no_elements,
|
||||
"final no_elements unchanged (%ld->%ld)", valaddr.no_elements, pch->final_no_elements);
|
||||
|
||||
const epicsInt32 res_5_0[] = {15,16,17,18,19};
|
||||
TEST1(5, 0, "no offset", res_5_0);
|
||||
|
||||
const epicsInt32 res_5_3[] = {18,19,15,16,17};
|
||||
TEST1(5, 3, "wrapped", res_5_3);
|
||||
|
||||
/* TEST2 sets the record's OFF field, then requests 15 elements from the channel
|
||||
* but passes in a db_field_log with alternate data, converting that data to LONG.
|
||||
* It checks that it got back the expected data and the right number of elements.
|
||||
*/
|
||||
|
||||
#define TEST2(Size, Offset, Text, Expected) \
|
||||
testDiag("Reading from offset = %d (%s)", Offset, Text); \
|
||||
off = Offset; req = 15; \
|
||||
memset(buf, 0, sizeof(buf)); \
|
||||
(void) dbPutField(&offaddr, DBR_LONG, &off, 1); \
|
||||
pfl = db_create_read_log(pch); \
|
||||
pfl->type = dbfl_type_ref; \
|
||||
pfl->field_type = DBF_CHAR; \
|
||||
pfl->field_size = 1; \
|
||||
pfl->no_elements = 26; \
|
||||
pfl->u.r.dtor = freeArray; \
|
||||
pfl->u.r.field = epicsStrDup("abcdefghijklmnopqrsstuvwxyz"); \
|
||||
testOk(!dbChannelGetField(pch, DBR_LONG, buf, NULL, &req, pfl), "Got Field value"); \
|
||||
testOk(req == Size, "Got %ld elements (expected %d)", req, Size); \
|
||||
if (!testOk(!memcmp(buf, Expected, sizeof(Expected)), "Data correct")) \
|
||||
for (i=0; i<Size; i++) \
|
||||
testDiag("Element %d expected '%c' got '%c'", i, Expected[i], buf[i]); \
|
||||
db_delete_field_log(pfl);
|
||||
|
||||
testHead("Fifteen letters from field-log instead of %s", typname);
|
||||
|
||||
const epicsInt32 res_15[] = {'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l',
|
||||
'm', 'n', 'o'};
|
||||
TEST2(15, 0, "no offset", res_15);
|
||||
TEST2(15, 10, "ignored", res_15);
|
||||
|
||||
dbChannelDelete(pch);
|
||||
}
|
||||
|
||||
static dbEventCtx evtctx;
|
||||
|
||||
extern "C" {
|
||||
static void dbChArrTestCleanup(void* junk)
|
||||
{
|
||||
dbFreeBase(pdbbase);
|
||||
registryFree();
|
||||
pdbbase=0;
|
||||
|
||||
db_close_events(evtctx);
|
||||
|
||||
dbmfFreeChunks();
|
||||
}
|
||||
}
|
||||
|
||||
MAIN(dbChArrTest)
|
||||
{
|
||||
testPlan(102);
|
||||
|
||||
/* Prepare the IOC */
|
||||
|
||||
epicsEnvSet("EPICS_CA_SERVER_PORT", server_port);
|
||||
|
||||
if (dbReadDatabase(&pdbbase, "dbChArrTest.dbd",
|
||||
"." OSI_PATH_LIST_SEPARATOR ".." OSI_PATH_LIST_SEPARATOR
|
||||
"../O.Common" OSI_PATH_LIST_SEPARATOR "O.Common", NULL))
|
||||
testAbort("Database description not loaded");
|
||||
|
||||
dbChArrTest_registerRecordDeviceDriver(pdbbase);
|
||||
|
||||
if (dbReadDatabase(&pdbbase, "dbChArrTest.db",
|
||||
"." OSI_PATH_LIST_SEPARATOR "..", NULL))
|
||||
testAbort("Test database not loaded");
|
||||
|
||||
epicsAtExit(&dbChArrTestCleanup,NULL);
|
||||
|
||||
/* Start the IOC */
|
||||
|
||||
iocInit();
|
||||
evtctx = db_init_events();
|
||||
|
||||
check(DBR_LONG);
|
||||
check(DBR_DOUBLE);
|
||||
check(DBR_STRING);
|
||||
|
||||
return testDone();
|
||||
}
|
||||
15
src/ioc/db/test/dbChArrTest.db
Normal file
15
src/ioc/db/test/dbChArrTest.db
Normal file
@@ -0,0 +1,15 @@
|
||||
record(arr, "i32") {
|
||||
field(DESC, "test array record")
|
||||
field(NELM, "10")
|
||||
field(FTVL, "LONG")
|
||||
}
|
||||
record(arr, "f64") {
|
||||
field(DESC, "test array record")
|
||||
field(NELM, "10")
|
||||
field(FTVL, "DOUBLE")
|
||||
}
|
||||
record(arr, "c40") {
|
||||
field(DESC, "test array record")
|
||||
field(NELM, "10")
|
||||
field(FTVL, "STRING")
|
||||
}
|
||||
@@ -18,7 +18,7 @@
|
||||
#include "dbAccessDefs.h"
|
||||
#include "registry.h"
|
||||
#include "recSup.h"
|
||||
#include "epicsUnitTest.h"
|
||||
#include "dbUnitTest.h"
|
||||
#include "testMain.h"
|
||||
#include "osiFileName.h"
|
||||
#include "errlog.h"
|
||||
@@ -158,17 +158,16 @@ MAIN(testDbChannel) /* dbChannelTest is an API routine... */
|
||||
|
||||
testPlan(76);
|
||||
|
||||
if (dbReadDatabase(&pdbbase, "dbTestIoc.dbd",
|
||||
"." OSI_PATH_LIST_SEPARATOR ".." OSI_PATH_LIST_SEPARATOR
|
||||
"../O.Common" OSI_PATH_LIST_SEPARATOR "O.Common", NULL))
|
||||
testAbort("Database description 'dbTestIoc.dbd' not found");
|
||||
testdbPrepare();
|
||||
|
||||
testdbReadDatabase("dbTestIoc.dbd", NULL, NULL);
|
||||
|
||||
dbTestIoc_registerRecordDeviceDriver(pdbbase);
|
||||
if (dbReadDatabase(&pdbbase, "xRecord.db",
|
||||
"." OSI_PATH_LIST_SEPARATOR "..", NULL))
|
||||
testAbort("Test database 'xRecord.db' not found");
|
||||
testdbReadDatabase("xRecord.db", NULL, NULL);
|
||||
|
||||
dbChannelInit();
|
||||
eltc(0);
|
||||
testIocInitOk();
|
||||
eltc(1);
|
||||
|
||||
r = e = 0;
|
||||
/* dbChannelTest() checks record and field names */
|
||||
@@ -242,7 +241,7 @@ MAIN(testDbChannel) /* dbChannelTest is an API routine... */
|
||||
testOk1(!!(pch = dbChannelCreate("x.{\"scalar\":null}")));
|
||||
|
||||
e = e_report;
|
||||
dbChannelShow(pch, 1, 2);
|
||||
dbChannelShow(pch, 2, 2);
|
||||
|
||||
e = e_close;
|
||||
if (pch) dbChannelDelete(pch);
|
||||
@@ -267,9 +266,8 @@ MAIN(testDbChannel) /* dbChannelTest is an API routine... */
|
||||
e = e_start | e_start_map | e_abort;
|
||||
testOk1(!dbChannelCreate("x.{\"scalar\":{}}"));
|
||||
|
||||
dbFreeBase(pdbbase);
|
||||
registryFree();
|
||||
pdbbase = NULL;
|
||||
testIocShutdownOk();
|
||||
testdbCleanup();
|
||||
|
||||
return testDone();
|
||||
}
|
||||
|
||||
@@ -12,9 +12,10 @@ long link_test_extend(struct dbCommon *junk)
|
||||
static dsxt xrecextend = {&link_test_extend, &link_test_extend};
|
||||
|
||||
static
|
||||
long link_test_init(int junk)
|
||||
long link_test_init(int pass)
|
||||
{
|
||||
devExtend(&xrecextend);
|
||||
if (pass == 0)
|
||||
devExtend(&xrecextend);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -28,7 +29,6 @@ long link_test_noop(void *junk)
|
||||
static dset devxLTest ## LTYPE = {4, NULL, &link_test_init, &link_test_noop, &link_test_noop}; \
|
||||
epicsExportAddress(dset, devxLTest ## LTYPE);
|
||||
|
||||
DEFDSET(Soft)
|
||||
DEFDSET(VME_IO)
|
||||
DEFDSET(CAMAC_IO)
|
||||
DEFDSET(AB_IO)
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
device(x, CONSTANT,devxLTestSoft,"Soft Channel")
|
||||
|
||||
device(x, VME_IO, devxLTestVME_IO, "Unit Test VME_IO")
|
||||
device(x, CAMAC_IO, devxLTestCAMAC_IO, "Unit Test CAMAC_IO")
|
||||
device(x, AB_IO, devxLTestAB_IO, "Unit Test AB_IO")
|
||||
|
||||
@@ -9,7 +9,15 @@
|
||||
* Author: Michael Davidsaver <mdavidsaver@bnl.gov>
|
||||
*/
|
||||
|
||||
#include "dbLock.h"
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "epicsSpin.h"
|
||||
#include "epicsMutex.h"
|
||||
#include "dbCommon.h"
|
||||
#include "epicsThread.h"
|
||||
|
||||
#include "dbLockPvt.h"
|
||||
#include "dbStaticLib.h"
|
||||
|
||||
#include "dbUnitTest.h"
|
||||
#include "testMain.h"
|
||||
@@ -28,15 +36,19 @@ void compareSets(int match, const char *A, const char *B)
|
||||
rA = testdbRecordPtr(A);
|
||||
rB = testdbRecordPtr(B);
|
||||
|
||||
actual = dbLockGetLockId(rA)==dbLockGetLockId(rB);
|
||||
actual = rA->lset->plockSet==rB->lset->plockSet;
|
||||
|
||||
testOk(match==actual, "dbLockGetLockId(\"%s\")%c=dbLockGetLockId(\"%s\")",
|
||||
A, match?'=':'!', B);
|
||||
}
|
||||
|
||||
#define testIntOk1(A, OP, B) testOk((A) OP (B), "%s (%d) %s %s (%d)", #A, A, #OP, #B, B);
|
||||
#define testPtrOk1(A, OP, B) testOk((A) OP (B), "%s (%p) %s %s (%p)", #A, A, #OP, #B, B);
|
||||
|
||||
static
|
||||
void testSets(void) {
|
||||
testDiag("Check initial creation of DB links");
|
||||
DBENTRY entry;
|
||||
long status;
|
||||
|
||||
testdbPrepare();
|
||||
|
||||
@@ -48,6 +60,27 @@ void testSets(void) {
|
||||
testIocInitOk();
|
||||
eltc(1);
|
||||
|
||||
testDiag("Check that all records have initialized lockRecord and lockSet");
|
||||
|
||||
dbInitEntry(pdbbase, &entry);
|
||||
for(status = dbFirstRecordType(&entry);
|
||||
!status;
|
||||
status = dbNextRecordType(&entry)) {
|
||||
for(status = dbFirstRecord(&entry);
|
||||
!status;
|
||||
status = dbNextRecord(&entry)) {
|
||||
dbCommon *prec = entry.precnode->precord;
|
||||
testOk(prec->lset!=NULL, "%s.LSET != NULL", prec->name);
|
||||
if(prec->lset!=NULL)
|
||||
testOk(prec->lset->plockSet!=NULL, "%s.LSET.plockSet != NULL", prec->name);
|
||||
else
|
||||
testSkip(1, "lockRecord missing");
|
||||
}
|
||||
}
|
||||
dbFinishEntry(&entry);
|
||||
|
||||
testDiag("Check initial creation of DB links");
|
||||
|
||||
/* reca is by itself */
|
||||
compareSets(0, "reca", "recb");
|
||||
compareSets(0, "reca", "recc");
|
||||
@@ -71,6 +104,310 @@ void testSets(void) {
|
||||
|
||||
compareSets(1, "rece", "recf");
|
||||
|
||||
testOk1(testdbRecordPtr("reca")->lset->plockSet->refcount==1);
|
||||
testOk1(testdbRecordPtr("recb")->lset->plockSet->refcount==2);
|
||||
testOk1(testdbRecordPtr("recd")->lset->plockSet->refcount==3);
|
||||
|
||||
testIocShutdownOk();
|
||||
|
||||
testdbCleanup();
|
||||
}
|
||||
|
||||
static void testSingleLock(void)
|
||||
{
|
||||
dbCommon *prec;
|
||||
testDiag("testing dbScanLock()/dbScanUnlock()");
|
||||
|
||||
testdbPrepare();
|
||||
|
||||
testdbReadDatabase("dbTestIoc.dbd", NULL, NULL);
|
||||
dbTestIoc_registerRecordDeviceDriver(pdbbase);
|
||||
testdbReadDatabase("dbLockTest.db", NULL, NULL);
|
||||
|
||||
eltc(0);
|
||||
testIocInitOk();
|
||||
eltc(1);
|
||||
|
||||
prec = testdbRecordPtr("reca");
|
||||
testOk1(prec->lset->plockSet->refcount==1);
|
||||
dbScanLock(prec);
|
||||
/* scan lock does not keep a reference to the lockSet */
|
||||
testOk1(prec->lset->plockSet->refcount==1);
|
||||
dbScanUnlock(prec);
|
||||
|
||||
dbScanLock(prec);
|
||||
dbScanLock(prec);
|
||||
/* scan lock can be recursive */
|
||||
testOk1(prec->lset->plockSet->refcount==1);
|
||||
dbScanUnlock(prec);
|
||||
dbScanUnlock(prec);
|
||||
|
||||
testIocShutdownOk();
|
||||
|
||||
testdbCleanup();
|
||||
}
|
||||
|
||||
static void testMultiLock(void)
|
||||
{
|
||||
dbCommon *prec[8];
|
||||
dbLocker *plockA;
|
||||
#ifdef LOCKSET_DEBUG
|
||||
epicsThreadId myself = epicsThreadGetIdSelf();
|
||||
#endif
|
||||
|
||||
testDiag("Test multi-locker function (lock everything)");
|
||||
|
||||
testdbPrepare();
|
||||
|
||||
testdbReadDatabase("dbTestIoc.dbd", NULL, NULL);
|
||||
dbTestIoc_registerRecordDeviceDriver(pdbbase);
|
||||
testdbReadDatabase("dbLockTest.db", NULL, NULL);
|
||||
|
||||
eltc(0);
|
||||
testIocInitOk();
|
||||
eltc(1);
|
||||
|
||||
prec[0] = testdbRecordPtr("reca");
|
||||
prec[1] = testdbRecordPtr("recb");
|
||||
prec[2] = testdbRecordPtr("recc");
|
||||
prec[3] = NULL;
|
||||
prec[4] = testdbRecordPtr("recd");
|
||||
prec[5] = testdbRecordPtr("rece");
|
||||
prec[6] = testdbRecordPtr("recf");
|
||||
prec[7] = testdbRecordPtr("recg");
|
||||
|
||||
testDiag("Test init refcounts");
|
||||
testIntOk1(testdbRecordPtr("reca")->lset->plockSet->refcount,==,1);
|
||||
testIntOk1(testdbRecordPtr("recb")->lset->plockSet->refcount,==,2);
|
||||
testIntOk1(testdbRecordPtr("recd")->lset->plockSet->refcount,==,3);
|
||||
testIntOk1(testdbRecordPtr("recg")->lset->plockSet->refcount,==,1);
|
||||
|
||||
plockA = dbLockerAlloc(prec, 8, 0);
|
||||
if(!plockA)
|
||||
testAbort("dbLockerAlloc() failed");
|
||||
|
||||
|
||||
testDiag("After locker created");
|
||||
/* locker takes 7 references, one for each lockRecord. */
|
||||
testIntOk1(testdbRecordPtr("reca")->lset->plockSet->refcount,==,2);
|
||||
testIntOk1(testdbRecordPtr("recb")->lset->plockSet->refcount,==,4);
|
||||
testIntOk1(testdbRecordPtr("recd")->lset->plockSet->refcount,==,6);
|
||||
testIntOk1(testdbRecordPtr("recg")->lset->plockSet->refcount,==,2);
|
||||
#ifdef LOCKSET_DEBUG
|
||||
testPtrOk1(testdbRecordPtr("reca")->lset->plockSet->owner,==,NULL);
|
||||
testPtrOk1(testdbRecordPtr("recb")->lset->plockSet->owner,==,NULL);
|
||||
testPtrOk1(testdbRecordPtr("recd")->lset->plockSet->owner,==,NULL);
|
||||
testPtrOk1(testdbRecordPtr("recg")->lset->plockSet->owner,==,NULL);
|
||||
#endif
|
||||
|
||||
dbScanLockMany(plockA);
|
||||
|
||||
testDiag("After locker locked");
|
||||
/* locker takes 4 references, one for each lockSet. */
|
||||
testIntOk1(ellCount(&plockA->locked),==,4);
|
||||
testIntOk1(testdbRecordPtr("reca")->lset->plockSet->refcount,==,3);
|
||||
testIntOk1(testdbRecordPtr("recb")->lset->plockSet->refcount,==,5);
|
||||
testIntOk1(testdbRecordPtr("recd")->lset->plockSet->refcount,==,7);
|
||||
testIntOk1(testdbRecordPtr("recg")->lset->plockSet->refcount,==,3);
|
||||
#ifdef LOCKSET_DEBUG
|
||||
testPtrOk1(testdbRecordPtr("reca")->lset->plockSet->owner,==,myself);
|
||||
testPtrOk1(testdbRecordPtr("recb")->lset->plockSet->owner,==,myself);
|
||||
testPtrOk1(testdbRecordPtr("recd")->lset->plockSet->owner,==,myself);
|
||||
testPtrOk1(testdbRecordPtr("recg")->lset->plockSet->owner,==,myself);
|
||||
#endif
|
||||
|
||||
/* recursive locking of individual records is allowed */
|
||||
dbScanLock(prec[0]);
|
||||
testIntOk1(testdbRecordPtr("reca")->lset->plockSet->refcount,==,3);
|
||||
dbScanUnlock(prec[0]);
|
||||
|
||||
/* recursive locking with dbScanLockMany() isn't
|
||||
* dbScanLockMany(plockA); <-- would fail
|
||||
*/
|
||||
|
||||
dbScanUnlockMany(plockA);
|
||||
|
||||
testDiag("After locker unlocked");
|
||||
testIntOk1(testdbRecordPtr("reca")->lset->plockSet->refcount,==,2);
|
||||
testIntOk1(testdbRecordPtr("recb")->lset->plockSet->refcount,==,4);
|
||||
testIntOk1(testdbRecordPtr("recd")->lset->plockSet->refcount,==,6);
|
||||
testIntOk1(testdbRecordPtr("recg")->lset->plockSet->refcount,==,2);
|
||||
#ifdef LOCKSET_DEBUG
|
||||
testPtrOk1(testdbRecordPtr("reca")->lset->plockSet->owner,==,NULL);
|
||||
testPtrOk1(testdbRecordPtr("recb")->lset->plockSet->owner,==,NULL);
|
||||
testPtrOk1(testdbRecordPtr("recd")->lset->plockSet->owner,==,NULL);
|
||||
testPtrOk1(testdbRecordPtr("recg")->lset->plockSet->owner,==,NULL);
|
||||
#endif
|
||||
|
||||
dbLockerFree(plockA);
|
||||
|
||||
testDiag("After locker free'd");
|
||||
testIntOk1(testdbRecordPtr("reca")->lset->plockSet->refcount,==,1);
|
||||
testIntOk1(testdbRecordPtr("recb")->lset->plockSet->refcount,==,2);
|
||||
testIntOk1(testdbRecordPtr("recd")->lset->plockSet->refcount,==,3);
|
||||
testIntOk1(testdbRecordPtr("recg")->lset->plockSet->refcount,==,1);
|
||||
|
||||
testIocShutdownOk();
|
||||
|
||||
testdbCleanup();
|
||||
}
|
||||
|
||||
static void testLinkBreak(void)
|
||||
{
|
||||
dbCommon *precB, *precC;
|
||||
testDiag("Test break link");
|
||||
|
||||
testdbPrepare();
|
||||
|
||||
testdbReadDatabase("dbTestIoc.dbd", NULL, NULL);
|
||||
dbTestIoc_registerRecordDeviceDriver(pdbbase);
|
||||
testdbReadDatabase("dbLockTest.db", NULL, NULL);
|
||||
|
||||
eltc(0);
|
||||
testIocInitOk();
|
||||
eltc(1);
|
||||
|
||||
precB = testdbRecordPtr("recb");
|
||||
precC = testdbRecordPtr("recc");
|
||||
|
||||
testOk1(precB->lset->plockSet==precC->lset->plockSet);
|
||||
testOk1(precB->lset->plockSet->refcount==2);
|
||||
|
||||
/* break the link between B and C */
|
||||
testdbPutFieldOk("recb.SDIS", DBR_STRING, "");
|
||||
|
||||
testOk1(precB->lset->plockSet!=precC->lset->plockSet);
|
||||
testIntOk1(precB->lset->plockSet->refcount, ==, 1);
|
||||
testIntOk1(precC->lset->plockSet->refcount, ==, 1);
|
||||
|
||||
testIocShutdownOk();
|
||||
|
||||
testdbCleanup();
|
||||
}
|
||||
|
||||
static void testLinkMake(void)
|
||||
{
|
||||
dbCommon *precA, *precG;
|
||||
lockSet *lA, *lG;
|
||||
testDiag("Test make link");
|
||||
|
||||
testdbPrepare();
|
||||
|
||||
testdbReadDatabase("dbTestIoc.dbd", NULL, NULL);
|
||||
dbTestIoc_registerRecordDeviceDriver(pdbbase);
|
||||
testdbReadDatabase("dbLockTest.db", NULL, NULL);
|
||||
|
||||
eltc(0);
|
||||
testIocInitOk();
|
||||
eltc(1);
|
||||
|
||||
precA = testdbRecordPtr("reca");
|
||||
lA = dbLockGetRef(precA->lset);
|
||||
precG = testdbRecordPtr("recg");
|
||||
lG = dbLockGetRef(precG->lset);
|
||||
|
||||
testPtrOk1(precA->lset->plockSet, !=, precG->lset->plockSet);
|
||||
testIntOk1(precA->lset->plockSet->refcount, ==, 2);
|
||||
testIntOk1(precG->lset->plockSet->refcount, ==, 2);
|
||||
|
||||
/* make a link between A and G */
|
||||
testdbPutFieldOk("reca.SDIS", DBR_STRING, "recg");
|
||||
|
||||
testPtrOk1(precA->lset->plockSet, ==, precG->lset->plockSet);
|
||||
testIntOk1(precA->lset->plockSet->refcount, ==, 3);
|
||||
|
||||
if(precA->lset->plockSet==lG) {
|
||||
testIntOk1(lA->refcount, ==, 1);
|
||||
testIntOk1(lG->refcount, ==, 3);
|
||||
} else {
|
||||
testIntOk1(lA->refcount, ==, 3);
|
||||
testIntOk1(lG->refcount, ==, 1);
|
||||
}
|
||||
dbLockDecRef(lG);
|
||||
dbLockDecRef(lA);
|
||||
|
||||
testIocShutdownOk();
|
||||
|
||||
testdbCleanup();
|
||||
}
|
||||
|
||||
static void testLinkChange(void)
|
||||
{
|
||||
dbCommon *precB, *precC, *precG;
|
||||
lockSet *lB, *lG;
|
||||
testDiag("Test re-target link");
|
||||
|
||||
testdbPrepare();
|
||||
|
||||
testdbReadDatabase("dbTestIoc.dbd", NULL, NULL);
|
||||
dbTestIoc_registerRecordDeviceDriver(pdbbase);
|
||||
testdbReadDatabase("dbLockTest.db", NULL, NULL);
|
||||
|
||||
eltc(0);
|
||||
testIocInitOk();
|
||||
eltc(1);
|
||||
|
||||
precB = testdbRecordPtr("recb");
|
||||
precC = testdbRecordPtr("recc");
|
||||
precG = testdbRecordPtr("recg");
|
||||
|
||||
lB = dbLockGetRef(precB->lset);
|
||||
lG = dbLockGetRef(precG->lset);
|
||||
|
||||
testPtrOk1(lB,==,precC->lset->plockSet);
|
||||
testPtrOk1(lB,!=,lG);
|
||||
testIntOk1(lB->refcount,==,3);
|
||||
testIntOk1(lG->refcount,==,2);
|
||||
|
||||
/* break the link between B and C and replace it
|
||||
* with a link between B and G
|
||||
*/
|
||||
testdbPutFieldOk("recb.SDIS", DBR_STRING, "recg");
|
||||
|
||||
testPtrOk1(precB->lset->plockSet,==,lB);
|
||||
testPtrOk1(precG->lset->plockSet,==,lB);
|
||||
testPtrOk1(precC->lset->plockSet,!=,lB);
|
||||
testPtrOk1(precC->lset->plockSet,!=,lG);
|
||||
|
||||
testIntOk1(lB->refcount,==,3);
|
||||
testIntOk1(lG->refcount,==,1);
|
||||
testIntOk1(precC->lset->plockSet->refcount,==,1);
|
||||
|
||||
dbLockDecRef(lB);
|
||||
dbLockDecRef(lG);
|
||||
|
||||
testIocShutdownOk();
|
||||
|
||||
testdbCleanup();
|
||||
}
|
||||
|
||||
static void testLinkNOP(void)
|
||||
{
|
||||
dbCommon *precB, *precC;
|
||||
testDiag("Test re-target link to the same destination");
|
||||
|
||||
testdbPrepare();
|
||||
|
||||
testdbReadDatabase("dbTestIoc.dbd", NULL, NULL);
|
||||
dbTestIoc_registerRecordDeviceDriver(pdbbase);
|
||||
testdbReadDatabase("dbLockTest.db", NULL, NULL);
|
||||
|
||||
eltc(0);
|
||||
testIocInitOk();
|
||||
eltc(1);
|
||||
|
||||
precB = testdbRecordPtr("recb");
|
||||
precC = testdbRecordPtr("recc");
|
||||
|
||||
testOk1(precB->lset->plockSet==precC->lset->plockSet);
|
||||
testOk1(precB->lset->plockSet->refcount==2);
|
||||
|
||||
/* renew link between B and C */
|
||||
testdbPutFieldOk("recb.SDIS", DBR_STRING, "recc");
|
||||
|
||||
testOk1(precB->lset->plockSet==precC->lset->plockSet);
|
||||
testOk1(precB->lset->plockSet->refcount==2);
|
||||
|
||||
testIocShutdownOk();
|
||||
|
||||
testdbCleanup();
|
||||
@@ -78,7 +415,17 @@ void testSets(void) {
|
||||
|
||||
MAIN(dbLockTest)
|
||||
{
|
||||
testPlan(15);
|
||||
#ifdef LOCKSET_DEBUG
|
||||
testPlan(100);
|
||||
#else
|
||||
testPlan(88);
|
||||
#endif
|
||||
testSets();
|
||||
testSingleLock();
|
||||
testMultiLock();
|
||||
testLinkBreak();
|
||||
testLinkMake();
|
||||
testLinkChange();
|
||||
testLinkNOP();
|
||||
return testDone();
|
||||
}
|
||||
|
||||
@@ -19,3 +19,6 @@ record(x, "rece") {
|
||||
|
||||
record(x, "recf") {
|
||||
}
|
||||
|
||||
record(x, "recg") {
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
#include "dbAccess.h"
|
||||
#include "registry.h"
|
||||
#include "dbStaticLib.h"
|
||||
#include "dbStaticPvt.h"
|
||||
#include "osiFileName.h"
|
||||
#include "dbmf.h"
|
||||
#include "errlog.h"
|
||||
@@ -28,8 +29,124 @@
|
||||
|
||||
#include "testMain.h"
|
||||
|
||||
static
|
||||
int testStrcmp(int expect, const char *A, const char *B) {
|
||||
static const char op[] = "<=>";
|
||||
int ret = strcmp(A,B);
|
||||
testOk(ret==expect, "\"%s\" %c= \"%s\"",
|
||||
A, op[expect+1], B);
|
||||
return ret==expect;
|
||||
}
|
||||
|
||||
void dbTestIoc_registerRecordDeviceDriver(struct dbBase *);
|
||||
|
||||
#define TEST_CONSTANT(SET, EXPECT) {SET, {CONSTANT, EXPECT}}
|
||||
#define TEST_PV_LINK(SET, PV, MOD) {SET, {PV_LINK, PV, MOD}}
|
||||
|
||||
static const struct testParseDataT {
|
||||
const char * const str;
|
||||
dbLinkInfo info;
|
||||
} testParseData[] = {
|
||||
TEST_CONSTANT("", ""),
|
||||
TEST_CONSTANT("0.1", "0.1"),
|
||||
TEST_CONSTANT(" 0.2\t ", "0.2"),
|
||||
|
||||
TEST_PV_LINK("0.1a", "0.1a", 0),
|
||||
TEST_PV_LINK(" hello ", "hello", 0),
|
||||
TEST_PV_LINK(" hellox MSI", "hellox", pvlOptMSI),
|
||||
TEST_PV_LINK(" world MSICP", "world", pvlOptMSI|pvlOptCP),
|
||||
|
||||
{"#C14 S145 @testing", {VME_IO, "testing", 0, "CS", {14, 145}}},
|
||||
{"#B11 C12 N13 A14 F15 @cparam", {CAMAC_IO, "cparam", 0, "BCNAF", {11, 12, 13, 14, 15}}},
|
||||
{" #B111 C112 N113 @cparam", {CAMAC_IO, "cparam", 0, "BCN", {111, 112, 113}}},
|
||||
{" @hello world ", {INST_IO, "hello world", 0, "", /*{}*/}},
|
||||
{NULL}
|
||||
};
|
||||
|
||||
static void testLinkParse(void)
|
||||
{
|
||||
const struct testParseDataT *td = testParseData;
|
||||
dbLinkInfo info;
|
||||
testDiag("link parsing");
|
||||
testdbPrepare();
|
||||
|
||||
testdbReadDatabase("dbTestIoc.dbd", NULL, NULL);
|
||||
|
||||
dbTestIoc_registerRecordDeviceDriver(pdbbase);
|
||||
|
||||
testdbReadDatabase("dbPutLinkTest.db", NULL, NULL);
|
||||
|
||||
eltc(0);
|
||||
testIocInitOk();
|
||||
eltc(1);
|
||||
|
||||
for(;td->str; td++) {
|
||||
int i, N;
|
||||
testDiag("Parse \"%s\"", td->str);
|
||||
testOk1(dbParseLink(td->str, DBF_INLINK, &info)==0);
|
||||
testOk1(info.ltype==td->info.ltype);
|
||||
if(td->info.target)
|
||||
testStrcmp(0, info.target, td->info.target);
|
||||
if(info.ltype==td->info.ltype) {
|
||||
switch(info.ltype) {
|
||||
case PV_LINK:
|
||||
testOk1(info.modifiers==td->info.modifiers);
|
||||
break;
|
||||
case VME_IO:
|
||||
testStrcmp(0, info.hwid, td->info.hwid);
|
||||
N = strlen(td->info.hwid);
|
||||
for(i=0; i<N; i++)
|
||||
testOk(info.hwnums[i]==td->info.hwnums[i], "%d == %d",
|
||||
info.hwnums[i], td->info.hwnums[i]);
|
||||
}
|
||||
}
|
||||
free(info.target);
|
||||
}
|
||||
|
||||
testIocShutdownOk();
|
||||
|
||||
testdbCleanup();
|
||||
}
|
||||
|
||||
static const char *testParseFailData[] = {
|
||||
"#",
|
||||
"#S",
|
||||
"#ABC",
|
||||
"#A0 B",
|
||||
"#A0 B @",
|
||||
"#A0 B C @",
|
||||
"#R1 M2 D3 E4 @oops", /* RF_IO has no parm */
|
||||
"#C1 S2", /* VME_IO needs parm */
|
||||
NULL
|
||||
};
|
||||
|
||||
static void testLinkFailParse(void)
|
||||
{
|
||||
const char * const *td = testParseFailData;
|
||||
dbLinkInfo info;
|
||||
testDiag("link parsing of invalid input");
|
||||
testdbPrepare();
|
||||
|
||||
testdbReadDatabase("dbTestIoc.dbd", NULL, NULL);
|
||||
|
||||
dbTestIoc_registerRecordDeviceDriver(pdbbase);
|
||||
|
||||
testdbReadDatabase("dbPutLinkTest.db", NULL, NULL);
|
||||
|
||||
eltc(0);
|
||||
testIocInitOk();
|
||||
eltc(1);
|
||||
|
||||
for(;*td; td++) {
|
||||
testDiag("Expect failure \"%s\"", *td);
|
||||
testOk1(dbParseLink(*td, DBF_INLINK, &info)==S_dbLib_badField);
|
||||
}
|
||||
|
||||
testIocShutdownOk();
|
||||
|
||||
testdbCleanup();
|
||||
}
|
||||
|
||||
static const struct testDataT {
|
||||
const char * const linkstring;
|
||||
short linkType;
|
||||
@@ -39,6 +156,7 @@ static const struct testDataT {
|
||||
{"", CONSTANT, 0},
|
||||
{"0", CONSTANT, 0},
|
||||
{"42", CONSTANT, 0},
|
||||
{"0x1", CONSTANT, 0},
|
||||
|
||||
{"x1", DB_LINK, 0, "x1 NPP NMS"},
|
||||
{"x1.VAL", DB_LINK, 0, "x1.VAL NPP NMS"},
|
||||
@@ -77,17 +195,17 @@ static void testCADBSet(void)
|
||||
prec = (xRecord*)testdbRecordPtr("x1");
|
||||
plink = &prec->lnk;
|
||||
|
||||
for(;td->linkstring;td++) {
|
||||
for (;td->linkstring;td++) {
|
||||
testDiag("x1.LNK <- \"%s\"", td->linkstring);
|
||||
|
||||
testdbPutFieldOk("x1.LNK", DBF_STRING, td->linkstring);
|
||||
if(td->linkback)
|
||||
if (td->linkback)
|
||||
testdbGetFieldEqual("x1.LNK", DBF_STRING, td->linkback);
|
||||
else
|
||||
testdbGetFieldEqual("x1.LNK", DBF_STRING, td->linkstring);
|
||||
testOk1(plink->type==td->linkType);
|
||||
|
||||
if(plink->type==td->linkType) {
|
||||
if (plink->type==td->linkType) {
|
||||
switch(td->linkType) {
|
||||
case CONSTANT:
|
||||
if(plink->value.constantStr)
|
||||
@@ -327,7 +445,7 @@ static void testLinkInitFail(void)
|
||||
testOk1(plink->type==VME_IO);
|
||||
testOk1(plink->value.vmeio.parm!=NULL);
|
||||
|
||||
testdbGetFieldEqual("eVME_IO2.INP", DBR_STRING, "#C200 S0 @");
|
||||
testdbGetFieldEqual("eVME_IO2.INP", DBR_STRING, "#C0 S0 @");
|
||||
|
||||
prec = (xRecord*)testdbRecordPtr("eVME_IO2");
|
||||
plink = &prec->inp;
|
||||
@@ -364,9 +482,8 @@ static void testLinkFail(void)
|
||||
/* INST_IO doesn't accept empty string */
|
||||
testdbPutFieldFail(S_dbLib_badField, "rINST_IO.INP", DBR_STRING, "");
|
||||
|
||||
/* INST_IO accepts invalid input as empty string */
|
||||
testdbPutFieldOk("rINST_IO.INP", DBR_STRING, "abc");
|
||||
testdbGetFieldEqual("rINST_IO.INP", DBR_STRING, "@");
|
||||
/* INST_IO doesn't accept empty string */
|
||||
testdbPutFieldFail(S_dbLib_badField, "rINST_IO.INP", DBR_STRING, "abc");
|
||||
|
||||
/* syntax errors */
|
||||
testdbPutFieldFail(S_dbLib_badField, "rVME_IO.INP", DBR_STRING, "#S201 C200 @another VME_IO");
|
||||
@@ -387,7 +504,9 @@ static void testLinkFail(void)
|
||||
|
||||
MAIN(dbPutLinkTest)
|
||||
{
|
||||
testPlan(200);
|
||||
testPlan(251);
|
||||
testLinkParse();
|
||||
testLinkFailParse();
|
||||
testCADBSet();
|
||||
testHWInitSet();
|
||||
testHWMod();
|
||||
|
||||
73
src/ioc/db/test/dbScanTest.c
Normal file
73
src/ioc/db/test/dbScanTest.c
Normal file
@@ -0,0 +1,73 @@
|
||||
/*************************************************************************\
|
||||
* Copyright (c) 2015 Brookhaven Science Assoc. as operator of Brookhaven
|
||||
* National Laboratory.
|
||||
* EPICS BASE is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
\*************************************************************************/
|
||||
|
||||
/*
|
||||
* Author: Michael Davidsaver <mdavidsaver@bnl.gov>
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "dbScan.h"
|
||||
#include "epicsEvent.h"
|
||||
|
||||
#include "dbUnitTest.h"
|
||||
#include "testMain.h"
|
||||
|
||||
#include "dbAccess.h"
|
||||
#include "errlog.h"
|
||||
|
||||
void dbTestIoc_registerRecordDeviceDriver(struct dbBase *);
|
||||
|
||||
static epicsEventId waiter;
|
||||
static int called;
|
||||
static dbCommon *prec;
|
||||
|
||||
static void onceComp(void *junk, dbCommon *prec)
|
||||
{
|
||||
testOk1(junk==(void*)&waiter);
|
||||
testOk1(strcmp(prec->name, "reca")==0);
|
||||
called = 1;
|
||||
epicsEventMustTrigger(waiter);
|
||||
}
|
||||
|
||||
static void testOnce(void)
|
||||
{
|
||||
testDiag("check scanOnceCallback() callback");
|
||||
waiter = epicsEventMustCreate(epicsEventEmpty);
|
||||
|
||||
testdbPrepare();
|
||||
|
||||
testdbReadDatabase("dbTestIoc.dbd", NULL, NULL);
|
||||
dbTestIoc_registerRecordDeviceDriver(pdbbase);
|
||||
testdbReadDatabase("dbLockTest.db", NULL, NULL);
|
||||
|
||||
eltc(0);
|
||||
testIocInitOk();
|
||||
eltc(1);
|
||||
|
||||
prec = testdbRecordPtr("reca");
|
||||
|
||||
testDiag("scanOnce %s", prec->name);
|
||||
scanOnceCallback(prec, onceComp, &waiter);
|
||||
testDiag("Waiting");
|
||||
epicsEventMustWait(waiter);
|
||||
testOk1(called==1);
|
||||
if(!called)
|
||||
testSkip(2, "callback failed to run");
|
||||
|
||||
testIocShutdownOk();
|
||||
|
||||
testdbCleanup();
|
||||
epicsEventDestroy(waiter);
|
||||
}
|
||||
|
||||
MAIN(dbScanTest)
|
||||
{
|
||||
testPlan(3);
|
||||
testOnce();
|
||||
return testDone();
|
||||
}
|
||||
358
src/ioc/db/test/dbStressLock.c
Normal file
358
src/ioc/db/test/dbStressLock.c
Normal file
@@ -0,0 +1,358 @@
|
||||
/*************************************************************************\
|
||||
* Copyright (c) 2014 Brookhaven Science Assoc. as operator of Brookhaven
|
||||
* National Laboratory.
|
||||
* EPICS BASE is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
\*************************************************************************/
|
||||
|
||||
/*
|
||||
* Lockset stress test.
|
||||
*
|
||||
* The test stratagy is for N threads to contend for M records.
|
||||
* Each thread will perform one of three operations:
|
||||
* 1) Lock a single record.
|
||||
* 2) Lock several records.
|
||||
* 3) Retarget the TSEL link of a record
|
||||
*
|
||||
* Author: Michael Davidsaver <mdavidsaver@bnl.gov>
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
|
||||
#include "envDefs.h"
|
||||
#include "epicsEvent.h"
|
||||
#include "epicsStdlib.h"
|
||||
#include "epicsSpin.h"
|
||||
#include "epicsThread.h"
|
||||
#include "epicsMutex.h"
|
||||
#include "dbCommon.h"
|
||||
|
||||
#include "dbLockPvt.h"
|
||||
#include "dbStaticLib.h"
|
||||
|
||||
#include "dbUnitTest.h"
|
||||
#include "testMain.h"
|
||||
|
||||
#include "dbAccess.h"
|
||||
#include "errlog.h"
|
||||
|
||||
#include "xRecord.h"
|
||||
|
||||
#if defined(CLOCK_REALTIME) && defined(CLOCK_MONOTONIC) && !defined(_WIN32)
|
||||
# define TIME_STATS
|
||||
#endif
|
||||
|
||||
#define testIntOk1(A, OP, B) testOk((A) OP (B), "%s (%d) %s %s (%d)", #A, A, #OP, #B, B);
|
||||
#define testPtrOk1(A, OP, B) testOk((A) OP (B), "%s (%p) %s %s (%p)", #A, A, #OP, #B, B);
|
||||
|
||||
void dbTestIoc_registerRecordDeviceDriver(struct dbBase *);
|
||||
|
||||
/* number of seconds for the test to run */
|
||||
static double runningtime = 18.0;
|
||||
|
||||
/* number of worker threads */
|
||||
static unsigned int nworkers = 5;
|
||||
|
||||
static unsigned int nrecords;
|
||||
|
||||
#define MAXLOCK 20
|
||||
|
||||
static dbCommon **precords;
|
||||
|
||||
typedef struct {
|
||||
int id;
|
||||
unsigned long N[3];
|
||||
#ifdef TIME_STATS
|
||||
double X[3];
|
||||
double X2[3];
|
||||
#endif
|
||||
|
||||
unsigned int done;
|
||||
epicsEventId donevent;
|
||||
|
||||
dbCommon *prec[MAXLOCK];
|
||||
} workerPriv;
|
||||
|
||||
/* hopefully a uniform random number in [0.0, 1.0] */
|
||||
static
|
||||
double getRand(void)
|
||||
{
|
||||
return rand()/(double)RAND_MAX;
|
||||
}
|
||||
|
||||
static
|
||||
void doSingle(workerPriv *p)
|
||||
{
|
||||
size_t recn = (size_t)(getRand()*(nrecords-1));
|
||||
dbCommon *prec = precords[recn];
|
||||
xRecord *px = (xRecord*)prec;
|
||||
|
||||
dbScanLock(prec);
|
||||
px->val++;
|
||||
dbScanUnlock(prec);
|
||||
}
|
||||
|
||||
static volatile int bitbucket;
|
||||
|
||||
static
|
||||
void doMulti(workerPriv *p)
|
||||
{
|
||||
int sum = 0;
|
||||
size_t i;
|
||||
size_t nlock = 2 + (size_t)(getRand()*(MAXLOCK-3));
|
||||
size_t nrec = (size_t)(getRand()*(nrecords-1));
|
||||
dbLocker *locker;
|
||||
|
||||
assert(nlock>=2);
|
||||
assert(nlock<nrecords);
|
||||
|
||||
for(i=0; i<nlock; i++, nrec=(nrec+1)%nrecords) {
|
||||
p->prec[i] = precords[nrec];
|
||||
}
|
||||
|
||||
locker = dbLockerAlloc(p->prec, nlock, 0);
|
||||
if(!locker)
|
||||
testAbort("locker allocation fails");
|
||||
|
||||
dbScanLockMany(locker);
|
||||
for(i=0; i<nlock; i++) {
|
||||
xRecord *px = (xRecord*)p->prec[i];
|
||||
sum += px->val;
|
||||
}
|
||||
dbScanUnlockMany(locker);
|
||||
|
||||
dbLockerFree(locker);
|
||||
}
|
||||
|
||||
static
|
||||
void doreTarget(workerPriv *p)
|
||||
{
|
||||
char scratchsrc[60];
|
||||
char scratchdst[MAX_STRING_SIZE];
|
||||
long ret;
|
||||
DBADDR dbaddr;
|
||||
double action = getRand();
|
||||
size_t nsrc = (size_t)(getRand()*(nrecords-1));
|
||||
size_t ntarg = (size_t)(getRand()*(nrecords-1));
|
||||
xRecord *psrc = (xRecord*)precords[nsrc];
|
||||
xRecord *ptarg = (xRecord*)precords[ntarg];
|
||||
|
||||
strcpy(scratchsrc, psrc->name);
|
||||
strcat(scratchsrc, ".TSEL");
|
||||
|
||||
ret = dbNameToAddr(scratchsrc, &dbaddr);
|
||||
if(ret)
|
||||
testAbort("bad record name? %ld", ret);
|
||||
|
||||
if(action<=0.6) {
|
||||
scratchdst[0] = '\0';
|
||||
} else {
|
||||
strcpy(scratchdst, ptarg->name);
|
||||
}
|
||||
|
||||
ret = dbPutField(&dbaddr, DBR_STRING, ptarg->name, 1);
|
||||
if(ret)
|
||||
testAbort("put fails with %ld", ret);
|
||||
}
|
||||
|
||||
static
|
||||
void worker(void *raw)
|
||||
{
|
||||
#ifdef TIME_STATS
|
||||
struct timespec before;
|
||||
#endif
|
||||
workerPriv *priv = raw;
|
||||
|
||||
testDiag("worker %d is %p", priv->id, epicsThreadGetIdSelf());
|
||||
|
||||
#ifdef TIME_STATS
|
||||
clock_gettime(CLOCK_MONOTONIC, &before);
|
||||
#endif
|
||||
|
||||
while(!priv->done) {
|
||||
double sel = getRand();
|
||||
#ifdef TIME_STATS
|
||||
struct timespec after;
|
||||
double duration;
|
||||
#endif
|
||||
|
||||
int act;
|
||||
if(sel<0.33) {
|
||||
doSingle(priv);
|
||||
act = 0;
|
||||
} else if(sel<0.66) {
|
||||
doMulti(priv);
|
||||
act = 1;
|
||||
} else {
|
||||
doreTarget(priv);
|
||||
act = 2;
|
||||
}
|
||||
|
||||
#ifdef TIME_STATS
|
||||
clock_gettime(CLOCK_MONOTONIC, &after);
|
||||
|
||||
duration = (double)((long)after.tv_nsec - (long)before.tv_nsec);
|
||||
duration *= 1e-9;
|
||||
duration += (double)(after.tv_sec - before.tv_sec);
|
||||
#endif
|
||||
|
||||
priv->N[act]++;
|
||||
#ifdef TIME_STATS
|
||||
priv->X[act] += duration;
|
||||
priv->X2[act] += duration*duration;
|
||||
#endif
|
||||
}
|
||||
|
||||
epicsEventMustTrigger(priv->donevent);
|
||||
}
|
||||
|
||||
MAIN(dbStressTest)
|
||||
{
|
||||
DBENTRY ent;
|
||||
long status;
|
||||
unsigned int i;
|
||||
workerPriv *priv;
|
||||
char *nwork=getenv("NWORK");
|
||||
epicsTimeStamp seed;
|
||||
|
||||
epicsTimeGetCurrent(&seed);
|
||||
|
||||
srand(seed.nsec);
|
||||
|
||||
if(nwork) {
|
||||
long val = 0;
|
||||
epicsParseLong(nwork, &val, 0, NULL);
|
||||
if(val>2)
|
||||
nworkers = val;
|
||||
}
|
||||
|
||||
testPlan(80+nworkers*3);
|
||||
|
||||
priv = callocMustSucceed(nworkers, sizeof(*priv), "no memory");
|
||||
|
||||
testDiag("lock set stress test");
|
||||
|
||||
testdbPrepare();
|
||||
|
||||
testdbReadDatabase("dbTestIoc.dbd", NULL, NULL);
|
||||
dbTestIoc_registerRecordDeviceDriver(pdbbase);
|
||||
testdbReadDatabase("dbStressLock.db", NULL, NULL);
|
||||
|
||||
eltc(0);
|
||||
testIocInitOk();
|
||||
eltc(1);
|
||||
|
||||
/* collect an array of all records */
|
||||
dbInitEntry(pdbbase, &ent);
|
||||
for(status = dbFirstRecordType(&ent);
|
||||
!status;
|
||||
status = dbNextRecordType(&ent))
|
||||
{
|
||||
for(status = dbFirstRecord(&ent);
|
||||
!status;
|
||||
status = dbNextRecord(&ent))
|
||||
{
|
||||
if(ent.precnode->flags&DBRN_FLAGS_ISALIAS)
|
||||
continue;
|
||||
nrecords++;
|
||||
}
|
||||
|
||||
}
|
||||
if(nrecords<2)
|
||||
testAbort("where are the records!");
|
||||
precords = callocMustSucceed(nrecords, sizeof(*precords), "no mem");
|
||||
for(status = dbFirstRecordType(&ent), i = 0;
|
||||
!status;
|
||||
status = dbNextRecordType(&ent))
|
||||
{
|
||||
for(status = dbFirstRecord(&ent);
|
||||
!status;
|
||||
status = dbNextRecord(&ent))
|
||||
{
|
||||
if(ent.precnode->flags&DBRN_FLAGS_ISALIAS)
|
||||
continue;
|
||||
precords[i++] = ent.precnode->precord;
|
||||
}
|
||||
|
||||
}
|
||||
dbFinishEntry(&ent);
|
||||
|
||||
testDiag("Running with %u workers and %u records",
|
||||
nworkers, nrecords);
|
||||
|
||||
for(i=0; i<nworkers; i++) {
|
||||
priv[i].id = i;
|
||||
priv[i].donevent = epicsEventMustCreate(epicsEventEmpty);
|
||||
}
|
||||
|
||||
for(i=0; i<nworkers; i++) {
|
||||
epicsThreadMustCreate("runner", epicsThreadPriorityMedium,
|
||||
epicsThreadGetStackSize(epicsThreadStackSmall),
|
||||
&worker, &priv[i]);
|
||||
}
|
||||
|
||||
testDiag("All started. Will run for %f sec", runningtime);
|
||||
|
||||
epicsThreadSleep(runningtime);
|
||||
|
||||
testDiag("Stopping");
|
||||
|
||||
for(i=0; i<nworkers; i++) {
|
||||
priv[i].done = 1;
|
||||
}
|
||||
|
||||
for(i=0; i<nworkers; i++) {
|
||||
epicsEventMustWait(priv[i].donevent);
|
||||
epicsEventDestroy(priv[i].donevent);
|
||||
}
|
||||
|
||||
testDiag("All stopped");
|
||||
|
||||
testDiag("Validate lockSet ref counts");
|
||||
dbInitEntry(pdbbase, &ent);
|
||||
for(status = dbFirstRecordType(&ent);
|
||||
!status;
|
||||
status = dbNextRecordType(&ent))
|
||||
{
|
||||
for(status = dbFirstRecord(&ent);
|
||||
!status;
|
||||
status = dbNextRecord(&ent))
|
||||
{
|
||||
dbCommon *prec = ent.precnode->precord;
|
||||
lockSet *ls;
|
||||
if(ent.precnode->flags&DBRN_FLAGS_ISALIAS)
|
||||
continue;
|
||||
ls = prec->lset->plockSet;
|
||||
testOk(ellCount(&ls->lockRecordList)==ls->refcount, "%s only lockRecords hold refs. %d == %d",
|
||||
prec->name,ellCount(&ls->lockRecordList),ls->refcount);
|
||||
testOk1(ls->ownerlocker==NULL);
|
||||
}
|
||||
|
||||
}
|
||||
dbFinishEntry(&ent);
|
||||
|
||||
testDiag("Statistics");
|
||||
for(i=0; i<nworkers; i++) {
|
||||
testDiag("Worker %u", i);
|
||||
testDiag("N = %lu %lu %lu", priv[i].N[0], priv[i].N[1], priv[i].N[2]);
|
||||
#ifdef TIME_STATS
|
||||
testDiag("X = %g %g %g", priv[i].X[0], priv[i].X[1], priv[i].X[2]);
|
||||
testDiag("X2= %g %g %g", priv[i].X2[0], priv[i].X2[1], priv[i].X2[2]);
|
||||
#endif
|
||||
|
||||
testOk1(priv[i].N[0]>0);
|
||||
testOk1(priv[i].N[1]>0);
|
||||
testOk1(priv[i].N[2]>0);
|
||||
}
|
||||
|
||||
testIocShutdownOk();
|
||||
|
||||
testdbCleanup();
|
||||
|
||||
free(priv);
|
||||
free(precords);
|
||||
|
||||
return testDone();
|
||||
}
|
||||
40
src/ioc/db/test/dbStressLock.db
Normal file
40
src/ioc/db/test/dbStressLock.db
Normal file
@@ -0,0 +1,40 @@
|
||||
record(x, "rec01") {}
|
||||
record(x, "rec02") {}
|
||||
record(x, "rec03") {}
|
||||
record(x, "rec04") {}
|
||||
record(x, "rec05") {}
|
||||
record(x, "rec06") {}
|
||||
record(x, "rec07") {}
|
||||
record(x, "rec08") {}
|
||||
record(x, "rec09") {}
|
||||
record(x, "rec10") {}
|
||||
record(x, "rec11") {}
|
||||
record(x, "rec12") {}
|
||||
record(x, "rec13") {}
|
||||
record(x, "rec14") {}
|
||||
record(x, "rec15") {}
|
||||
record(x, "rec16") {}
|
||||
record(x, "rec17") {}
|
||||
record(x, "rec18") {}
|
||||
record(x, "rec19") {}
|
||||
record(x, "rec20") {}
|
||||
record(x, "rec21") {}
|
||||
record(x, "rec22") {}
|
||||
record(x, "rec23") {}
|
||||
record(x, "rec24") {}
|
||||
record(x, "rec25") {}
|
||||
record(x, "rec26") {}
|
||||
record(x, "rec27") {}
|
||||
record(x, "rec28") {}
|
||||
record(x, "rec29") {}
|
||||
record(x, "rec30") {}
|
||||
record(x, "rec31") {}
|
||||
record(x, "rec32") {}
|
||||
record(x, "rec33") {}
|
||||
record(x, "rec34") {}
|
||||
record(x, "rec35") {}
|
||||
record(x, "rec36") {}
|
||||
record(x, "rec37") {}
|
||||
record(x, "rec38") {}
|
||||
record(x, "rec39") {}
|
||||
record(x, "rec40") {}
|
||||
163
src/ioc/db/test/devx.c
Normal file
163
src/ioc/db/test/devx.c
Normal file
@@ -0,0 +1,163 @@
|
||||
/*************************************************************************\
|
||||
* Copyright (c) 2014 Brookhaven Science Assoc. as Operator of Brookhaven
|
||||
* National Lab
|
||||
* EPICS BASE is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
\*************************************************************************/
|
||||
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include <epicsAssert.h>
|
||||
#include <cantProceed.h>
|
||||
#include <ellLib.h>
|
||||
#include <dbDefs.h>
|
||||
#include <dbAccess.h>
|
||||
#include <dbScan.h>
|
||||
#include <devSup.h>
|
||||
#include <recGbl.h>
|
||||
#include <link.h>
|
||||
#include <dbLink.h>
|
||||
#include <xRecord.h>
|
||||
|
||||
#include <epicsExport.h>
|
||||
|
||||
#include "devx.h"
|
||||
|
||||
/* xRecord DTYP="Scan I/O"
|
||||
*
|
||||
* dset to test I/O Intr scanning.
|
||||
* INP="@drvname" names a "driver" which
|
||||
* provides a IOSCANPVT.
|
||||
* The driver also defines a callback function which
|
||||
* is invoked when the record is processed.
|
||||
*/
|
||||
|
||||
struct ELLLIST xdrivers;
|
||||
|
||||
/* Add a new "driver" with the given group id
|
||||
* and processing callback
|
||||
*/
|
||||
xdrv* xdrv_add(int group, xdrvcb cb, void *arg)
|
||||
{
|
||||
xdrv *drv=callocMustSucceed(1, sizeof(*drv), "xdrv_add");
|
||||
drv->cb = cb;
|
||||
drv->arg = arg;
|
||||
drv->group = group;
|
||||
scanIoInit(&drv->scan);
|
||||
ellAdd(&xdrivers, &drv->drvnode);
|
||||
return drv;
|
||||
}
|
||||
|
||||
/* Trigger the named "driver" group to scan */
|
||||
xdrv *xdrv_get(int group)
|
||||
{
|
||||
ELLNODE *cur;
|
||||
for(cur=ellFirst(&xdrivers); cur; cur=ellNext(cur)) {
|
||||
xdrv *curd = CONTAINER(cur, xdrv, drvnode);
|
||||
if(curd->group==group) {
|
||||
return curd;
|
||||
}
|
||||
}
|
||||
cantProceed("xdrv_get() for non-existant group");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Free all "driver" groups and record private structures.
|
||||
* Call after testdbCleanup()
|
||||
*/
|
||||
void xdrv_reset()
|
||||
{
|
||||
ELLNODE *cur;
|
||||
while((cur=ellGet(&xdrivers))!=NULL) {
|
||||
ELLNODE *cur2;
|
||||
xdrv *curd = CONTAINER(cur, xdrv, drvnode);
|
||||
while((cur2=ellGet(&curd->privlist))!=NULL) {
|
||||
xpriv *priv = CONTAINER(cur2, xpriv, privnode);
|
||||
free(priv);
|
||||
}
|
||||
free(curd);
|
||||
}
|
||||
}
|
||||
|
||||
static long xscanio_init_record(xRecord *prec)
|
||||
{
|
||||
ELLNODE *cur;
|
||||
xpriv *priv;
|
||||
xdrv *drv = NULL;
|
||||
int group, member;
|
||||
assert(prec->inp.type==INST_IO);
|
||||
|
||||
if(sscanf(prec->inp.value.instio.string, "%d %d",
|
||||
&group, &member)!=2)
|
||||
cantProceed("xscanio_init_record invalid INP string");
|
||||
|
||||
for(cur=ellFirst(&xdrivers); cur; cur=ellNext(cur)) {
|
||||
xdrv *curd = CONTAINER(cur, xdrv, drvnode);
|
||||
if(curd->group==group) {
|
||||
drv = curd;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
assert(drv!=NULL);
|
||||
priv = mallocMustSucceed(sizeof(*priv), "xscanio_init_record");
|
||||
priv->prec = prec;
|
||||
priv->drv = drv;
|
||||
priv->member = member;
|
||||
ellAdd(&drv->privlist, &priv->privnode);
|
||||
prec->dpvt = priv;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long xscanio_get_ioint_info(int cmd, xRecord *prec, IOSCANPVT *ppvt)
|
||||
{
|
||||
xpriv *priv = prec->dpvt;
|
||||
if(!priv || !priv->drv)
|
||||
return 0;
|
||||
*ppvt = priv->drv->scan;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long xscanio_read(xRecord *prec)
|
||||
{
|
||||
xpriv *priv = prec->dpvt;
|
||||
if(!priv || !priv->drv)
|
||||
return 0;
|
||||
if(priv->drv->cb)
|
||||
(*priv->drv->cb)(priv, priv->drv->arg);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static xdset devxScanIO = {
|
||||
5, NULL, NULL,
|
||||
&xscanio_init_record,
|
||||
&xscanio_get_ioint_info,
|
||||
&xscanio_read
|
||||
};
|
||||
epicsExportAddress(dset, devxScanIO);
|
||||
|
||||
/* basic DTYP="Soft Channel" */
|
||||
static long xsoft_init_record(xRecord *prec)
|
||||
{
|
||||
if(prec->inp.type==CONSTANT)
|
||||
recGblInitConstantLink(&prec->inp, DBF_LONG, &prec->val);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long xsoft_read(xRecord *prec)
|
||||
{
|
||||
if(prec->inp.type==CONSTANT)
|
||||
return 0;
|
||||
dbGetLink(&prec->inp, DBR_DOUBLE, &prec->val, NULL, NULL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct xdset devxSoft = {
|
||||
5, NULL, NULL,
|
||||
&xsoft_init_record,
|
||||
NULL,
|
||||
&xsoft_read
|
||||
};
|
||||
epicsExportAddress(dset, devxSoft);
|
||||
2
src/ioc/db/test/devx.dbd
Normal file
2
src/ioc/db/test/devx.dbd
Normal file
@@ -0,0 +1,2 @@
|
||||
device(x, CONSTANT, devxSoft, "Soft Channel")
|
||||
device(x, INST_IO , devxScanIO, "Scan I/O")
|
||||
52
src/ioc/db/test/devx.h
Normal file
52
src/ioc/db/test/devx.h
Normal file
@@ -0,0 +1,52 @@
|
||||
/*************************************************************************\
|
||||
* Copyright (c) 2014 Brookhaven Science Assoc. as Operator of Brookhaven
|
||||
* National Lab
|
||||
* EPICS BASE is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
\*************************************************************************/
|
||||
|
||||
#ifndef DEVXSCANIO_H
|
||||
#define DEVXSCANIO_H
|
||||
|
||||
#include <ellLib.h>
|
||||
#include <dbScan.h>
|
||||
|
||||
#include <shareLib.h>
|
||||
|
||||
struct xRecord;
|
||||
struct xpriv;
|
||||
|
||||
epicsShareExtern struct ELLLIST xdrivers;
|
||||
|
||||
typedef void (*xdrvcb)(struct xpriv *, void *);
|
||||
|
||||
typedef struct {
|
||||
ELLNODE drvnode;
|
||||
IOSCANPVT scan;
|
||||
xdrvcb cb;
|
||||
void *arg;
|
||||
ELLLIST privlist;
|
||||
int group;
|
||||
} xdrv;
|
||||
|
||||
typedef struct xpriv {
|
||||
ELLNODE privnode;
|
||||
xdrv *drv;
|
||||
struct xRecord *prec;
|
||||
int member;
|
||||
} xpriv;
|
||||
|
||||
epicsShareFunc xdrv *xdrv_add(int group, xdrvcb cb, void *arg);
|
||||
epicsShareFunc xdrv *xdrv_get(int group);
|
||||
epicsShareFunc void xdrv_reset();
|
||||
|
||||
typedef struct xdset {
|
||||
long number;
|
||||
long (*report)(int);
|
||||
long (*init)(int);
|
||||
long (*init_record)(struct xRecord *);
|
||||
long (*get_ioint_info)(int, struct xRecord*, IOSCANPVT*);
|
||||
long (*process)(struct xRecord *);
|
||||
} xdset;
|
||||
|
||||
#endif /* DEVXSCANIO_H */
|
||||
@@ -22,9 +22,11 @@ int callbackParallelTest(void);
|
||||
int dbStateTest(void);
|
||||
int dbCaStatsTest(void);
|
||||
int dbShutdownTest(void);
|
||||
int dbScanTest(void);
|
||||
int scanIoTest(void);
|
||||
int dbLockTest(void);
|
||||
int dbPutLinkTest(void);
|
||||
int dbCaLinkTest(void);
|
||||
int testDbChannel(void);
|
||||
int chfPluginTest(void);
|
||||
int arrShorthandTest(void);
|
||||
@@ -40,9 +42,11 @@ void epicsRunDbTests(void)
|
||||
runTest(dbStateTest);
|
||||
runTest(dbCaStatsTest);
|
||||
runTest(dbShutdownTest);
|
||||
runTest(dbScanTest);
|
||||
runTest(scanIoTest);
|
||||
runTest(dbLockTest);
|
||||
runTest(dbPutLinkTest);
|
||||
runTest(dbCaLinkTest);
|
||||
runTest(testDbChannel);
|
||||
runTest(arrShorthandTest);
|
||||
runTest(recGblCheckDeadbandTest);
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user