Compare commits

...

35 Commits

Author SHA1 Message Date
Ralph Lange
65574b5971 Update CONFIG_BASE_VERSION for 3.15.4, remove "not released" from RELEASE_NOTES 2016-05-27 08:54:04 +02:00
Andrew Johnson
4284222b54 Convert GUI_ promptgroup strings to new-style 2016-05-26 16:15:48 +02:00
Ralph Lange
54bab1e2f0 pcas: fix compiler warning (gcc 5.3.1) 2016-05-26 14:14:56 +02:00
Andrew Johnson
df200de27f Fix lsi/lso puts with DBF_STRING data 2016-05-26 13:49:18 +02:00
Ralph Lange
22d6ebe7e1 Merge changes from 3.14 branch up to rev 12642 2016-05-26 13:18:38 +02:00
Ralph Lange
9c3ed1bfe2 catools: fix escape handling when sending long strings 2016-05-26 13:13:29 +02:00
Ralph Lange
23fd4e202b ca: Fix client bug (race condition) in ipAddrToAsciiEngine 2016-05-25 12:26:32 +02:00
Andrew Johnson
a5b8b0b890 Use fabs() in calcPerform 2016-05-24 11:12:25 +02:00
Andrew Johnson
988614ae8a Reset SNAPSHOT after -rc1 tagged 2016-05-22 06:06:53 +02:00
Ralph Lange
fe887b4f83 Update CONFIG_BASE_VERSION for 3.15.4-rc1 2016-05-20 16:37:06 +02:00
Andrew Johnson
7be7ad2768 Merge changes from 3.14 branch, to revno 12640 2016-05-18 10:44:16 -05:00
Andrew Johnson
6c9555310a Update EPICS_TIMEZONE with info for 2016-2021 2016-05-18 10:26:04 -05:00
Andrew Johnson
4ccc2e9d3a Merged my devEnviron branch 2016-05-18 09:48:22 -05:00
Andrew Johnson
122fb6c731 New "getenv" device support for string input types 2016-05-17 19:09:48 -05:00
Andrew Johnson
3b8fd13152 libCom: NTP Time Provider adjusts to OS tick rate changes
Allow the NTP Time provider (used on VxWorks and RTEMS only)
to adapt to changes in the OS clock tick rate after the provider
has been initialized.

Changing the tick rate after iocInit() is not advisable,
other software might still misbehave if initialized before
an OS tick rate change.

Back-ported from the 3.15 branch (commit 12468).
2016-05-17 16:03:27 -05:00
Andrew Johnson
04a9fdb4e3 Clean up warnings from gcc 5.2 2016-05-16 17:02:34 -05:00
Andrew Johnson
3c61880d79 Allow some callback slow-ups before failing tests
The tests occasionally fail on Windows because the server
is busy; this will accept up to 4 slow callbacks without
them causing the build to fail.
2016-05-16 16:55:50 -05:00
Andrew Johnson
8081d3ada4 Merged Till's fix for lp:1581212, modified 2016-05-13 14:48:08 -05:00
Andrew Johnson
6d35ee9c3c Move dbLock.h *after* epicsExportSharedSymbols 2016-05-13 13:40:37 -05:00
Andrew Johnson
b5a0657adc Applied Till's fix for lp:1581212 2016-05-13 13:30:55 -05:00
Andrew Johnson
b87f3eaaee Merged changes from 3.14 branch to revno 12636 2016-05-13 13:12:08 -05:00
Andrew Johnson
6db0e13809 Added CROSS_COMPILER_RUNTEST_ARCHS 2016-05-13 13:00:48 -05:00
Michael Davidsaver
0f6c997288 osdNetIntf: ignore 0.0.0.0 as a broadcast address
It seems in some situations Linux (at least)
sets IFF_BROADCAST bug leaves the bcast address
set to 0.0.0.0.

Fixes lp:1581505
2016-05-13 09:31:26 -04:00
Michael Davidsaver
704c748fbd Revert "libCom/osi: osiSockDiscoverBroadcastAddresses() finds 127.255.255.255"
This reverts commit e805abe971.
2016-05-13 09:14:20 -04:00
Andrew Johnson
82456f83ee Fix Menu declaration test 2016-05-12 16:35:54 -05:00
Andrew Johnson
e98a6bbafa Make <menu>_NUM_CHOICES not an enum tag
Fixes lp:1580972
2016-05-12 15:05:18 -05:00
Andrew Johnson
9b51444fb7 Fix data types and macro name 2016-05-04 18:15:54 -05:00
Andrew Johnson
3be97865b3 Warn instead of building cap5 if xsubpp is missing 2016-05-04 14:41:51 -05:00
Andrew Johnson
67097456e3 Add additional RELEASE file checks
These checks are important for mixing Debian modules with
privately-built applications.
2016-05-03 12:26:40 -05:00
Andrew Johnson
3c8af4c571 Catch bad attribute names/values to prevent a segfault 2016-05-02 11:38:51 -05:00
Michael Davidsaver
26c04844cf add test for lp:1577108 2016-05-01 13:30:45 -04:00
Michael Davidsaver
a3d981ad0a dbLink.c: fix dbGetLink for array of length 1 2016-05-01 13:30:45 -04:00
Ralph Lange
924aa2f93b Set Base version to -pre1-DEV 2016-04-29 12:20:35 +02:00
Michael Davidsaver
51dd371784 rsrv: add rsrvCS_shutdown to avoid race in destroyAllChannels()
A race can occur between rsrv and cac by way of asLib
whereby casAccessRightsCB() is called after
a channel_in_use has been removed from
chanList/chanPendingUpdateARList.
casAccessRightsCB() would then attempt to remove
the node again, causing corruption.

Create a new rsrvCS_shutdown state when the
channel is not in either list.

Attempt to resolve lp:1571224
2016-04-26 17:06:22 -04:00
Andrew Johnson
72745d7b0c Only make runtests and tapfiles if T_A = EPICS_HOST_ARCH 2016-04-29 16:20:25 -05:00
39 changed files with 684 additions and 197 deletions

View File

@@ -37,7 +37,7 @@ EPICS_PATCH_LEVEL = 0
# This will end in -DEV between official releases
#EPICS_DEV_SNAPSHOT=-DEV
EPICS_DEV_SNAPSHOT=-pre1
#EPICS_DEV_SNAPSHOT=-pre1
#EPICS_DEV_SNAPSHOT=-pre1-DEV
#EPICS_DEV_SNAPSHOT=-pre2
#EPICS_DEV_SNAPSHOT=-pre2-DEV
@@ -45,7 +45,7 @@ EPICS_DEV_SNAPSHOT=-pre1
#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

View File

@@ -118,6 +118,13 @@ CROSS_COMPILER_TARGET_ARCHS=
# configure/os/CONFIG_SITE.<host>.Common files instead.
CROSS_COMPILER_HOST_ARCHS=
# The 'make runtests' and 'make tapfiles' build targets normally only run
# self-tests for the EPICS_HOST_ARCH architecture. If the host can execute
# the self-test programs for any other cross-built architectures such as
# a -debug architecture, those architectures can be named here.
#
CROSS_COMPILER_RUNTEST_ARCHS=
# Build shared libraries (DLLs on Windows).
# Must be either YES or NO. Definitions in the target-specific
# os/CONFIG.Common.<target> and os/CONFIG_SITE.Common.<target> files may

View File

@@ -30,10 +30,8 @@
# local timezone info for vxWorks and RTEMS IOCs. The format is
# <name>::<minutesWest>:<start daylight>:<end daylight>
# where the start and end are mmddhh - that is month,day,hour
# e.g. for ANL in 2015: EPICS_TIMEZONE=CST/CDT::360:030802:110102
# e.g. for ANL in 2016: EPICS_TIMEZONE=CST/CDT::360:031302:110602
#
# DST for 2015 US: Mar 08 - Nov 01
# EU: Mar 29 - Oct 25
# DST for 2016 US: Mar 13 - Nov 06
# EU: Mar 27 - Oct 30
# DST for 2017 US: Mar 12 - Nov 05
@@ -42,11 +40,15 @@
# EU: Mar 25 - Oct 28
# DST for 2019 US: Mar 10 - Nov 03
# EU: Mar 31 - Oct 27
# (see: http://www.timeanddate.com/time/map/)
# DST for 2020 US: Mar 08 - Nov 01
# EU: Mar 29 - Oct 25
# DST for 2021 US: Mar 14 - Nov 07
# EU: Mar 28 - Oct 31
# (see: http://www.timeanddate.com/time/dst/2016.html etc. )
#
# These values are for 2015:
EPICS_TIMEZONE=CST/CDT::360:030802:110102
#EPICS_TIMEZONE=CET/CEST::-60:032902:102502
# These values are for 2016:
EPICS_TIMEZONE=CST/CDT::360:031302:110602
#EPICS_TIMEZONE=CET/CEST::-60:032702:103002
# EPICS_TS_NTP_INET
# NTP time server ip address. Uses boot host if not set.

View File

@@ -325,7 +325,9 @@ $(MODNAME): %$(MODEXT): %$(EXE)
# Automated testing
runtests: $(TESTSCRIPTS)
ifneq (,$(findstring $(T_A),$(EPICS_HOST_ARCH) $(CROSS_COMPILER_RUNTEST_ARCHS)))
-$(PERL) -MTest::Harness -e 'runtests @ARGV if @ARGV;' $^
endif
testspec: $(TESTSCRIPTS)
@$(RM) $@
@@ -339,7 +341,9 @@ tapfiles: $(TESTSCRIPTS) $(TAPFILES)
# A .tap file is the output from running the associated test script
%.tap: %.t
ifneq (,$(findstring $(T_A),$(EPICS_HOST_ARCH) $(CROSS_COMPILER_RUNTEST_ARCHS)))
-$(PERL) $< -tap > $@
endif
# If there's a perl test script (.plt) available, use it
%.t: ../%.plt

View File

@@ -1,22 +1,22 @@
# CONFIG_SITE.Common.cygwin-x86_64
#
# $Revision-Id$
#
# Site Specific definitions for cygwin-x86_64 target
# Only the local epics system manager should modify this file
# If readline is installed uncomment the following line
# to add command-line editing and history support
#COMMANDLINE_LIBRARY = READLINE
# Uncomment the following line if readline has problems
#LDLIBS_READLINE = -lreadline -lcurses
# It makes sense to include debugging symbols even in optimized builds
# in case you want to attach gdb to the process or examine a core-dump.
# This does cost disk space, but not memory as debug symbols are not
# loaded into RAM when the binary is loaded.
OPT_CFLAGS_YES += -g
OPT_CXXFLAGS_YES += -g
# CONFIG_SITE.Common.cygwin-x86_64
#
# $Revision-Id$
#
# Site Specific definitions for cygwin-x86_64 target
# Only the local epics system manager should modify this file
# If readline is installed uncomment the following line
# to add command-line editing and history support
#COMMANDLINE_LIBRARY = READLINE
# Uncomment the following line if readline has problems
#LDLIBS_READLINE = -lreadline -lcurses
# It makes sense to include debugging symbols even in optimized builds
# in case you want to attach gdb to the process or examine a core-dump.
# This does cost disk space, but not memory as debug symbols are not
# loaded into RAM when the binary is loaded.
OPT_CFLAGS_YES += -g
OPT_CXXFLAGS_YES += -g

View File

@@ -9,12 +9,15 @@
<body lang="en">
<h1 align="center">EPICS Base Release 3.15.4</h1>
<p style="color:red">This version of EPICS Base has not been released yet.</p>
<h2 align="center">Changes made on the 3.15 branch since 3.15.3</h2>
<!-- Insert new items immediately below here ... -->
<h3>New string input device support "getenv"</h3>
<p>A new "getenv" device support for both the stringin and lsi (long string
input) record types can be used to read the value of an environment variable
from the IOC at runtime. See base/db/softIocExit.db for sample usage.</p>
<h3>Build rules and DELAY_INSTALL_LIBS</h3>
<p>A new order-only prerequisite build rule has been added to ensure that
@@ -52,6 +55,12 @@ plus space-dash-space to allow proper sorting of groups.</p>
has been deprecated. Instead, use the conversion functions between index number
and group string that have been added to dbStaticLib.</p>
<p>When a DBD file containing record-type descriptions is expanded, any
old-style <tt>GUI_xxx</tt> group names will be replaced by a new-style
string for use by the IOC. This permits an older record type to be used with
the 3.15.4 release, although eventually record types should be converted by
hand with better group names used.</p>
<h3>CA server configuration changes</h3>
<p>RSRV now honors EPICS_CAS_INTF_ADDR_LIST and binds only to the provided list
@@ -109,6 +118,67 @@ dbQuietMacroWarnings=1 <i>VxWorks</i>
<h2 align="center">Changes pulled from the 3.14 branch since 3.15.3</h2>
<!-- Insert inherited items immediately below here ... -->
<h3>Making IOC ca_get operations atomic</h3>
<p>When a CA client gets data from an IOC record using a compound data type such
as <tt>DBR_TIME_DOUBLE</tt> the value field is fetched from the database in a
separate call than the other metadata, without keeping the record locked. This
allows some other thread such as a periodic scan thread a chance to interrupt
the get operation and process the record in between. CA monitors have always
been atomic as long as the value data isn't a string or an array, but this race
condition in the CA get path has now been fixed so the record will stay locked
between the two fetch operations.</p>
<p>This fixes <a href="https://bugs.launchpad.net/epics-base/+bug/1581212">
Launchpad bug #1581212</a>, thanks to Till Strauman and Dehong Zhang.</p>
<h3>New CONFIG_SITE variable for running self-tests</h3>
<p>The 'make runtests' and 'make tapfiles' build targets normally only run the
self-tests for the main <tt>EPICS_HOST_ARCH</tt> architecture. If the host is
able to execute self-test programs for other target architectures that are being
built by the host, such as when building a <tt>-debug</tt> version of the host
architecture for example, the names of those other architectures can be added to
the new <tt>CROSS_COMPILER_RUNTEST_ARCHS</tt> variable in either the
<tt>configure/CONFIG_SITE</tt> file or in an appropriate
<tt>configure/os/CONFIG_SITE.&lt;host&gt;.Common</tt> file to have the test
programs for those targets be run as well.</p>
<h3>Additional RELEASE file checks</h3>
<p>An additional check has been added at build-time for the contents of the
configure/RELEASE file(s), which will mostly only affect users of the Debian
EPICS packages published by NSLS-2. Support modules may share an install path,
but all such modules must be listed adjacent to each other in any RELEASE files
that point to them. For example the following will fail the new checks:</p>
<blockquote><pre>
AUTOSAVE = /usr/lib/epics
ASYN = /home/mdavidsaver/asyn
EPICS_BASE = /usr/lib/epics
</pre></blockquote>
<p>giving the compile-time error</p>
<blockquote><pre>
This application's RELEASE file(s) define
EPICS_BASE = /usr/lib/epics
after but not adjacent to
AUTOSAVE = /usr/lib/epics
Module definitions that share paths must be grouped together.
Either remove a definition, or move it to a line immediately
above or below the other(s).
Any non-module definitions belong in configure/CONFIG_SITE.
</pre></blockquote>
<p>In many cases such as the one above the order of the <tt>AUTOSAVE</tt> and
<tt>ASYN</tt> lines can be swapped to let the checks pass, but if the
<tt>AUTOSAVE</tt> module depended on <tt>ASYN</tt> and hence had to appear
before it in the list this error indicates that <tt>AUTOSAVE</tt> should also be
built in its own private area; a shared copy would likely be incompatible with
the version of <tt>ASYN</tt> built in the home directory.</p>
<h3>String field buffer overflows</h3>
<p>Two buffer overflow bugs that can crash the IOC have been fixed, caused by

View File

@@ -333,6 +333,12 @@ cac::~cac ()
this->ipToAEngine.release ();
// clean-up the list of un-notified msg objects
while ( msgForMultiplyDefinedPV * msg = this->msgMultiPVList.get() ) {
msg->~msgForMultiplyDefinedPV ();
this->mdpvFreeList.release ( msg );
}
errlogFlush ();
osiSockRelease ();
@@ -606,6 +612,8 @@ void cac::transferChanToVirtCircuit (
msgForMultiplyDefinedPV * pMsg = new ( this->mdpvFreeList )
msgForMultiplyDefinedPV ( this->ipToAEngine,
*this, pChan->pName ( guard ), acc );
// cac keeps a list of these objects for proper clean-up in ~cac
this->msgMultiPVList.add ( *pMsg );
// It is possible for the ioInitiate call below to
// call the callback directly if queue quota is exceeded.
// This callback takes the callback lock and therefore we
@@ -1297,6 +1305,8 @@ void cac::pvMultiplyDefinedNotify ( msgForMultiplyDefinedPV & mfmdpv,
epicsGuard < epicsMutex > guard ( this->mutex );
this->exception ( mgr.cbGuard, guard, ECA_DBLCHNL, buf, __FILE__, __LINE__ );
}
// remove from the list and delete msg object
this->msgMultiPVList.remove ( mfmdpv );
mfmdpv.~msgForMultiplyDefinedPV ();
this->mdpvFreeList.release ( & mfmdpv );
}

View File

@@ -237,6 +237,7 @@ private:
resTable < tcpiiu, caServerID > serverTable;
tsDLList < tcpiiu > circuitList;
tsDLList < SearchDest > searchDestList;
tsDLList < msgForMultiplyDefinedPV > msgMultiPVList;
tsFreeList
< class tcpiiu, 32, epicsMutexNOOP >
freeListVirtualCircuit;

View File

@@ -55,8 +55,10 @@ msgForMultiplyDefinedPV::~msgForMultiplyDefinedPV ()
void msgForMultiplyDefinedPV::transactionComplete ( const char * pHostNameRej )
{
// calls into cac for the notification
// the msg object (= this) is being deleted as part of the notification
this->cb.pvMultiplyDefinedNotify ( *this, this->channel, this->acc, pHostNameRej );
// !! dont touch this pointer after this point because object has been deleted !!
// !! dont touch 'this' pointer after this point because object has been deleted !!
}
void * msgForMultiplyDefinedPV::operator new ( size_t size,

View File

@@ -33,6 +33,7 @@
#include "ipAddrToAsciiAsynchronous.h"
#include "tsFreeList.h"
#include "tsDLList.h"
#include "compilerDependencies.h"
#ifdef msgForMultiplyDefinedPVh_epicsExportSharedSymbols
@@ -47,7 +48,9 @@ public:
const char * pAcc, const char * pRej ) = 0;
};
class msgForMultiplyDefinedPV : public ipAddrToAsciiCallBack {
class msgForMultiplyDefinedPV :
public ipAddrToAsciiCallBack,
public tsDLNode < msgForMultiplyDefinedPV > {
public:
msgForMultiplyDefinedPV ( ipAddrToAsciiEngine & engine,
callbackForMultiplyDefinedPV &, const char * pChannelName,
@@ -62,8 +65,8 @@ private:
ipAddrToAsciiTransaction & dnsTransaction;
callbackForMultiplyDefinedPV & cb;
void transactionComplete ( const char * pHostName );
msgForMultiplyDefinedPV ( const msgForMultiplyDefinedPV & );
msgForMultiplyDefinedPV & operator = ( const msgForMultiplyDefinedPV & );
msgForMultiplyDefinedPV ( const msgForMultiplyDefinedPV & );
msgForMultiplyDefinedPV & operator = ( const msgForMultiplyDefinedPV & );
void operator delete ( void * );
};

View File

@@ -20,10 +20,19 @@ ifeq ($(OS_CLASS),Darwin)
LOADABLE_SHRLIB_SUFFIX = .$(shell $(PERL) ../perlConfig.pl dlext)
endif
PERL_VERSION = $(shell $(PERL) ../perlConfig.pl version)
PERL_ARCHNAME = $(shell $(PERL) ../perlConfig.pl archname)
PERL_ARCHPATH = $(PERL_VERSION)/$(PERL_ARCHNAME)
ifdef T_A
PERL_VERSION = $(shell $(PERL) ../perlConfig.pl version)
PERL_ARCHNAME = $(shell $(PERL) ../perlConfig.pl archname)
PERL_ARCHPATH := $(PERL_VERSION)/$(PERL_ARCHNAME)
EXTUTILS := $(shell $(PERL) ../perlConfig.pl privlib)/ExtUtils
PERLBIN := $(shell $(PERL) ../perlConfig.pl bin)
XSUBPP := $(firstword $(wildcard $(PERLBIN)/xsubpp $(EXTUTILS)/xsubpp))
ifeq ($(strip $(XSUBPP)),)
$(warning Perl's xsubpp program was not found.)
$(warning The Perl CA module will not be built.)
else
ifeq ($(T_A),$(EPICS_HOST_ARCH)) # No cross-builds (wrong Perl!)
ifeq ($(findstring $(OS_CLASS),WIN32 cygwin32),) # Doesn't build on WIN32
LOADABLE_LIBRARY_HOST = Cap5
@@ -41,6 +50,8 @@ ifeq ($(findstring $(OS_CLASS),WIN32 cygwin32),) # Doesn't build on WIN32
HTMLS = CA.html
endif
endif
endif
endif
Cap5_SRCS = Cap5.xs
Cap5_LIBS = ca Com
@@ -52,10 +63,6 @@ CLEANS += Cap5.c pod2htmd.tmp pod2htmi.tmp
include $(TOP)/configure/RULES
ifdef T_A
EXTUTILS = $(shell $(PERL) ../perlConfig.pl privlib)/ExtUtils
PERLBIN = $(shell $(PERL) ../perlConfig.pl bin)
XSUBPP = $(firstword $(wildcard $(PERLBIN)/xsubpp $(EXTUTILS)/xsubpp))
%.c: ../%.xs
$(RM) $@ $@_new
$(PERL) $(XSUBPP) -typemap $(EXTUTILS)/typemap $< > $@_new && $(MV) $@_new $@

View File

@@ -492,14 +492,13 @@ int main (int argc, char *argv[])
} else { /* Not an ENUM */
if (charArrAsStr) {
count = len;
dbrType = DBR_CHAR;
ebuf = calloc(strlen(cbuf)+1, sizeof(char));
ebuf = calloc(len, sizeof(char));
if(!ebuf) {
fprintf(stderr, "Memory allocation failed\n");
return 1;
}
epicsStrnRawFromEscaped(ebuf, strlen(cbuf)+1, cbuf, strlen(cbuf));
count = epicsStrnRawFromEscaped(ebuf, len, cbuf, len-1) + 1;
} else {
for (i = 0; i < count; ++i) {
epicsStrnRawFromEscaped(sbuf[i], sizeof(EpicsStr), *(argv+optind+i), sizeof(EpicsStr));

View File

@@ -348,10 +348,8 @@ caStatus casDGClient::searchResponse ( const caHdrLargeArray & msg,
//
caStatus casDGClient::searchFailResponse ( const caHdrLargeArray * mp )
{
int status;
epicsGuard < epicsMutex > guard ( this->mutex );
status = this->out.copyInHeader ( CA_PROTO_NOT_FOUND, 0,
this->out.copyInHeader ( CA_PROTO_NOT_FOUND, 0,
mp->m_dataType, mp->m_count, mp->m_cid, mp->m_available, 0 );
this->out.commitMsg ();

View File

@@ -402,19 +402,29 @@ struct rset * dbGetRset(const struct dbAddr *paddr)
}
long dbPutAttribute(
const char *recordTypename,const char *name,const char*value)
const char *recordTypename, const char *name, const char *value)
{
DBENTRY dbEntry;
DBENTRY *pdbEntry = &dbEntry;
long status=0;
DBENTRY dbEntry;
DBENTRY *pdbEntry = &dbEntry;
long status = 0;
if(!pdbbase) return(S_db_notFound);
dbInitEntry(pdbbase,pdbEntry);
status = dbFindRecordType(pdbEntry,recordTypename);
if(!status) status = dbPutRecordAttribute(pdbEntry,name,value);
dbFinishEntry(pdbEntry);
if(status) errMessage(status,"dbPutAttribute failure");
return(status);
if (!pdbbase)
return S_db_notFound;
if (!name) {
status = S_db_badField;
goto done;
}
if (!value)
value = "";
dbInitEntry(pdbbase, pdbEntry);
status = dbFindRecordType(pdbEntry, recordTypename);
if (!status)
status = dbPutRecordAttribute(pdbEntry, name, value);
dbFinishEntry(pdbEntry);
done:
if (status)
errMessage(status, "dbPutAttribute failure");
return status;
}
int dbIsValueField(const struct dbFldDes *pdbFldDes)

View File

@@ -224,6 +224,7 @@ static long dbDbGetValue(struct link *plink, short dbrType, void *pbuffer,
return S_db_badDbrtype;
if (paddr->no_elements == 1 && (!pnRequest || *pnRequest == 1)
&& paddr->special != SPC_DBADDR
&& paddr->special != SPC_ATTRIBUTE) {
ppv_link->getCvt = dbFastGetConvertRoutine[dbfType][dbrType];
status = ppv_link->getCvt(paddr->pfield, pbuffer, paddr);

View File

@@ -39,6 +39,7 @@
#include "dbChannel.h"
#include "dbCommon.h"
#include "dbEvent.h"
#include "dbLock.h"
#include "dbNotify.h"
#include "dbStaticLib.h"
#include "recSup.h"
@@ -153,28 +154,30 @@ int dbChannel_get_count(
* in the dbAccess.c dbGet() and getOptions() routines.
*/
dbScanLock(dbChannelRecord(chan));
switch(buffer_type) {
case(oldDBR_STRING):
status = dbChannelGetField(chan, DBR_STRING, pbuffer, &zero, nRequest, pfl);
status = dbChannelGet(chan, DBR_STRING, pbuffer, &zero, nRequest, pfl);
break;
/* case(oldDBR_INT): */
case(oldDBR_SHORT):
status = dbChannelGetField(chan, DBR_SHORT, pbuffer, &zero, nRequest, pfl);
status = dbChannelGet(chan, DBR_SHORT, pbuffer, &zero, nRequest, pfl);
break;
case(oldDBR_FLOAT):
status = dbChannelGetField(chan, DBR_FLOAT, pbuffer, &zero, nRequest, pfl);
status = dbChannelGet(chan, DBR_FLOAT, pbuffer, &zero, nRequest, pfl);
break;
case(oldDBR_ENUM):
status = dbChannelGetField(chan, DBR_ENUM, pbuffer, &zero, nRequest, pfl);
status = dbChannelGet(chan, DBR_ENUM, pbuffer, &zero, nRequest, pfl);
break;
case(oldDBR_CHAR):
status = dbChannelGetField(chan, DBR_CHAR, pbuffer, &zero, nRequest, pfl);
status = dbChannelGet(chan, DBR_CHAR, pbuffer, &zero, nRequest, pfl);
break;
case(oldDBR_LONG):
status = dbChannelGetField(chan, DBR_LONG, pbuffer, &zero, nRequest, pfl);
status = dbChannelGet(chan, DBR_LONG, pbuffer, &zero, nRequest, pfl);
break;
case(oldDBR_DOUBLE):
status = dbChannelGetField(chan, DBR_DOUBLE, pbuffer, &zero, nRequest, pfl);
status = dbChannelGet(chan, DBR_DOUBLE, pbuffer, &zero, nRequest, pfl);
break;
case(oldDBR_STS_STRING):
@@ -187,10 +190,10 @@ int dbChannel_get_count(
} newSt;
options = DBR_STATUS;
status = dbChannelGetField(chan, DBR_STRING, &newSt, &options, &zero, pfl);
status = dbChannelGet(chan, DBR_STRING, &newSt, &options, &zero, pfl);
pold->status = newSt.status;
pold->severity = newSt.severity;
status = dbChannelGetField(chan, DBR_STRING, pold->value, &zero,
status = dbChannelGet(chan, DBR_STRING, pold->value, &zero,
nRequest, pfl);
}
break;
@@ -203,10 +206,10 @@ int dbChannel_get_count(
} newSt;
options = DBR_STATUS;
status = dbChannelGetField(chan, DBR_SHORT, &newSt, &options, &zero, pfl);
status = dbChannelGet(chan, DBR_SHORT, &newSt, &options, &zero, pfl);
pold->status = newSt.status;
pold->severity = newSt.severity;
status = dbChannelGetField(chan, DBR_SHORT, &pold->value, &zero,
status = dbChannelGet(chan, DBR_SHORT, &pold->value, &zero,
nRequest, pfl);
}
break;
@@ -218,10 +221,10 @@ int dbChannel_get_count(
} newSt;
options = DBR_STATUS;
status = dbChannelGetField(chan, DBR_FLOAT, &newSt, &options, &zero, pfl);
status = dbChannelGet(chan, DBR_FLOAT, &newSt, &options, &zero, pfl);
pold->status = newSt.status;
pold->severity = newSt.severity;
status = dbChannelGetField(chan, DBR_FLOAT, &pold->value, &zero,
status = dbChannelGet(chan, DBR_FLOAT, &pold->value, &zero,
nRequest, pfl);
}
break;
@@ -233,10 +236,10 @@ int dbChannel_get_count(
} newSt;
options = DBR_STATUS;
status = dbChannelGetField(chan, DBR_ENUM, &newSt, &options, &zero, pfl);
status = dbChannelGet(chan, DBR_ENUM, &newSt, &options, &zero, pfl);
pold->status = newSt.status;
pold->severity = newSt.severity;
status = dbChannelGetField(chan, DBR_ENUM, &pold->value, &zero,
status = dbChannelGet(chan, DBR_ENUM, &pold->value, &zero,
nRequest, pfl);
}
break;
@@ -248,10 +251,10 @@ int dbChannel_get_count(
} newSt;
options = DBR_STATUS;
status = dbChannelGetField(chan, DBR_UCHAR, &newSt, &options, &zero, pfl);
status = dbChannelGet(chan, DBR_UCHAR, &newSt, &options, &zero, pfl);
pold->status = newSt.status;
pold->severity = newSt.severity;
status = dbChannelGetField(chan, DBR_UCHAR, &pold->value, &zero,
status = dbChannelGet(chan, DBR_UCHAR, &pold->value, &zero,
nRequest, pfl);
}
break;
@@ -263,10 +266,10 @@ int dbChannel_get_count(
} newSt;
options = DBR_STATUS;
status = dbChannelGetField(chan, DBR_LONG, &newSt, &options, &zero, pfl);
status = dbChannelGet(chan, DBR_LONG, &newSt, &options, &zero, pfl);
pold->status = newSt.status;
pold->severity = newSt.severity;
status = dbChannelGetField(chan, DBR_LONG, &pold->value, &zero,
status = dbChannelGet(chan, DBR_LONG, &pold->value, &zero,
nRequest, pfl);
}
break;
@@ -278,11 +281,11 @@ int dbChannel_get_count(
} newSt;
options = DBR_STATUS;
status = dbChannelGetField(chan, DBR_DOUBLE, &newSt, &options, &zero, pfl);
status = dbChannelGet(chan, DBR_DOUBLE, &newSt, &options, &zero, pfl);
pold->status = newSt.status;
pold->severity = newSt.severity;
options = 0;
status = dbChannelGetField(chan, DBR_DOUBLE, &pold->value, &options,
status = dbChannelGet(chan, DBR_DOUBLE, &pold->value, &options,
nRequest, pfl);
}
break;
@@ -296,12 +299,12 @@ int dbChannel_get_count(
} newSt;
options = DBR_STATUS | DBR_TIME;
status = dbChannelGetField(chan, DBR_STRING, &newSt, &options, &zero, pfl);
status = dbChannelGet(chan, DBR_STRING, &newSt, &options, &zero, pfl);
pold->status = newSt.status;
pold->severity = newSt.severity;
pold->stamp = newSt.time; /* structure copy */
options = 0;
status = dbChannelGetField(chan, DBR_STRING, pold->value, &options,
status = dbChannelGet(chan, DBR_STRING, pold->value, &options,
nRequest, pfl);
}
break;
@@ -315,12 +318,12 @@ int dbChannel_get_count(
} newSt;
options = DBR_STATUS | DBR_TIME;
status = dbChannelGetField(chan, DBR_SHORT, &newSt, &options, &zero, pfl);
status = dbChannelGet(chan, DBR_SHORT, &newSt, &options, &zero, pfl);
pold->status = newSt.status;
pold->severity = newSt.severity;
pold->stamp = newSt.time; /* structure copy */
options = 0;
status = dbChannelGetField(chan, DBR_SHORT, &pold->value, &options,
status = dbChannelGet(chan, DBR_SHORT, &pold->value, &options,
nRequest, pfl);
}
break;
@@ -333,12 +336,12 @@ int dbChannel_get_count(
} newSt;
options = DBR_STATUS | DBR_TIME;
status = dbChannelGetField(chan, DBR_FLOAT, &newSt, &options, &zero, pfl);
status = dbChannelGet(chan, DBR_FLOAT, &newSt, &options, &zero, pfl);
pold->status = newSt.status;
pold->severity = newSt.severity;
pold->stamp = newSt.time; /* structure copy */
options = 0;
status = dbChannelGetField(chan, DBR_FLOAT, &pold->value, &options,
status = dbChannelGet(chan, DBR_FLOAT, &pold->value, &options,
nRequest, pfl);
}
break;
@@ -351,12 +354,12 @@ int dbChannel_get_count(
} newSt;
options = DBR_STATUS | DBR_TIME;
status = dbChannelGetField(chan, DBR_ENUM, &newSt, &options, &zero, pfl);
status = dbChannelGet(chan, DBR_ENUM, &newSt, &options, &zero, pfl);
pold->status = newSt.status;
pold->severity = newSt.severity;
pold->stamp = newSt.time; /* structure copy */
options = 0;
status = dbChannelGetField(chan, DBR_ENUM, &pold->value, &options,
status = dbChannelGet(chan, DBR_ENUM, &pold->value, &options,
nRequest, pfl);
}
break;
@@ -369,12 +372,12 @@ int dbChannel_get_count(
} newSt;
options = DBR_STATUS | DBR_TIME;
status = dbChannelGetField(chan, DBR_CHAR, &newSt, &options, &zero, pfl);
status = dbChannelGet(chan, DBR_CHAR, &newSt, &options, &zero, pfl);
pold->status = newSt.status;
pold->severity = newSt.severity;
pold->stamp = newSt.time; /* structure copy */
options = 0;
status = dbChannelGetField(chan, DBR_CHAR, &pold->value, &options,
status = dbChannelGet(chan, DBR_CHAR, &pold->value, &options,
nRequest, pfl);
}
break;
@@ -387,12 +390,12 @@ int dbChannel_get_count(
} newSt;
options = DBR_STATUS | DBR_TIME;
status = dbChannelGetField(chan, DBR_LONG, &newSt, &options, &zero, pfl);
status = dbChannelGet(chan, DBR_LONG, &newSt, &options, &zero, pfl);
pold->status = newSt.status;
pold->severity = newSt.severity;
pold->stamp = newSt.time; /* structure copy */
options = 0;
status = dbChannelGetField(chan, DBR_LONG, &pold->value, &options,
status = dbChannelGet(chan, DBR_LONG, &pold->value, &options,
nRequest, pfl);
}
break;
@@ -405,12 +408,12 @@ int dbChannel_get_count(
} newSt;
options = DBR_STATUS | DBR_TIME;
status = dbChannelGetField(chan, DBR_DOUBLE, &newSt, &options, &zero, pfl);
status = dbChannelGet(chan, DBR_DOUBLE, &newSt, &options, &zero, pfl);
pold->status = newSt.status;
pold->severity = newSt.severity;
pold->stamp = newSt.time; /* structure copy */
options = 0;
status = dbChannelGetField(chan, DBR_DOUBLE, &pold->value, &options,
status = dbChannelGet(chan, DBR_DOUBLE, &pold->value, &options,
nRequest, pfl);
}
break;
@@ -428,7 +431,7 @@ int dbChannel_get_count(
} newSt;
options = DBR_STATUS | DBR_UNITS | DBR_GR_LONG | DBR_AL_LONG;
status = dbChannelGetField(chan, DBR_SHORT, &newSt, &options, &zero, pfl);
status = dbChannelGet(chan, DBR_SHORT, &newSt, &options, &zero, pfl);
pold->status = newSt.status;
pold->severity = newSt.severity;
strncpy(pold->units, newSt.units, MAX_UNITS_SIZE);
@@ -440,7 +443,7 @@ int dbChannel_get_count(
pold->lower_warning_limit = newSt.lower_warning_limit;
pold->lower_alarm_limit = newSt.lower_alarm_limit;
options = 0;
status = dbChannelGetField(chan, DBR_SHORT, &pold->value, &options,
status = dbChannelGet(chan, DBR_SHORT, &pold->value, &options,
nRequest, pfl);
}
break;
@@ -457,7 +460,7 @@ int dbChannel_get_count(
options = DBR_STATUS | DBR_UNITS | DBR_PRECISION | DBR_GR_DOUBLE |
DBR_AL_DOUBLE;
status = dbChannelGetField(chan, DBR_FLOAT, &newSt, &options, &zero, pfl);
status = dbChannelGet(chan, DBR_FLOAT, &newSt, &options, &zero, pfl);
pold->status = newSt.status;
pold->severity = newSt.severity;
pold->precision = (dbr_short_t) newSt.precision.dp;
@@ -470,7 +473,7 @@ int dbChannel_get_count(
pold->upper_warning_limit = epicsConvertDoubleToFloat(newSt.upper_warning_limit);
pold->lower_warning_limit = epicsConvertDoubleToFloat(newSt.lower_warning_limit);
options = 0;
status = dbChannelGetField(chan, DBR_FLOAT, &pold->value, &options,
status = dbChannelGet(chan, DBR_FLOAT, &pold->value, &options,
nRequest, pfl);
}
break;
@@ -486,7 +489,7 @@ int dbChannel_get_count(
} newSt;
options = DBR_STATUS | DBR_UNITS | DBR_GR_LONG | DBR_AL_LONG;
status = dbChannelGetField(chan, DBR_UCHAR, &newSt, &options, &zero, pfl);
status = dbChannelGet(chan, DBR_UCHAR, &newSt, &options, &zero, pfl);
pold->status = newSt.status;
pold->severity = newSt.severity;
strncpy(pold->units, newSt.units, MAX_UNITS_SIZE);
@@ -498,7 +501,7 @@ int dbChannel_get_count(
pold->lower_warning_limit = newSt.lower_warning_limit;
pold->lower_alarm_limit = newSt.lower_alarm_limit;
options = 0;
status = dbChannelGetField(chan, DBR_UCHAR, &pold->value, &options,
status = dbChannelGet(chan, DBR_UCHAR, &pold->value, &options,
nRequest, pfl);
}
break;
@@ -513,7 +516,7 @@ int dbChannel_get_count(
} newSt;
options = DBR_STATUS | DBR_UNITS | DBR_GR_LONG | DBR_AL_LONG;
status = dbChannelGetField(chan, DBR_LONG, &newSt, &options, &zero, pfl);
status = dbChannelGet(chan, DBR_LONG, &newSt, &options, &zero, pfl);
pold->status = newSt.status;
pold->severity = newSt.severity;
strncpy(pold->units, newSt.units, MAX_UNITS_SIZE);
@@ -525,7 +528,7 @@ int dbChannel_get_count(
pold->lower_warning_limit = newSt.lower_warning_limit;
pold->lower_alarm_limit = newSt.lower_alarm_limit;
options = 0;
status = dbChannelGetField(chan, DBR_LONG, &pold->value, &options,
status = dbChannelGet(chan, DBR_LONG, &pold->value, &options,
nRequest, pfl);
}
break;
@@ -542,7 +545,7 @@ int dbChannel_get_count(
options = DBR_STATUS | DBR_UNITS | DBR_PRECISION | DBR_GR_DOUBLE |
DBR_AL_DOUBLE;
status = dbChannelGetField(chan, DBR_DOUBLE, &newSt, &options, &zero, pfl);
status = dbChannelGet(chan, DBR_DOUBLE, &newSt, &options, &zero, pfl);
pold->status = newSt.status;
pold->severity = newSt.severity;
pold->precision = (dbr_short_t) newSt.precision.dp;
@@ -555,7 +558,7 @@ int dbChannel_get_count(
pold->lower_warning_limit = newSt.lower_warning_limit;
pold->lower_alarm_limit = newSt.lower_alarm_limit;
options = 0;
status = dbChannelGetField(chan, DBR_DOUBLE, &pold->value, &options,
status = dbChannelGet(chan, DBR_DOUBLE, &pold->value, &options,
nRequest, pfl);
}
break;
@@ -574,7 +577,7 @@ int dbChannel_get_count(
options = DBR_STATUS | DBR_UNITS | DBR_GR_LONG | DBR_CTRL_LONG |
DBR_AL_LONG;
status = dbChannelGetField(chan, DBR_SHORT, &newSt, &options, &zero, pfl);
status = dbChannelGet(chan, DBR_SHORT, &newSt, &options, &zero, pfl);
pold->status = newSt.status;
pold->severity = newSt.severity;
strncpy(pold->units, newSt.units, MAX_UNITS_SIZE);
@@ -588,7 +591,7 @@ int dbChannel_get_count(
pold->upper_ctrl_limit = newSt.upper_ctrl_limit;
pold->lower_ctrl_limit = newSt.lower_ctrl_limit;
options = 0;
status = dbChannelGetField(chan, DBR_SHORT, &pold->value, &options,
status = dbChannelGet(chan, DBR_SHORT, &pold->value, &options,
nRequest, pfl);
}
break;
@@ -606,7 +609,7 @@ int dbChannel_get_count(
options = DBR_STATUS | DBR_UNITS | DBR_PRECISION | DBR_GR_DOUBLE |
DBR_CTRL_DOUBLE | DBR_AL_DOUBLE;
status = dbChannelGetField(chan, DBR_FLOAT, &newSt, &options, &zero, pfl);
status = dbChannelGet(chan, DBR_FLOAT, &newSt, &options, &zero, pfl);
pold->status = newSt.status;
pold->severity = newSt.severity;
pold->precision = (dbr_short_t) newSt.precision.dp;
@@ -621,7 +624,7 @@ int dbChannel_get_count(
pold->upper_ctrl_limit = epicsConvertDoubleToFloat(newSt.upper_ctrl_limit);
pold->lower_ctrl_limit = epicsConvertDoubleToFloat(newSt.lower_ctrl_limit);
options = 0;
status = dbChannelGetField(chan, DBR_FLOAT, &pold->value, &options,
status = dbChannelGet(chan, DBR_FLOAT, &pold->value, &options,
nRequest, pfl);
}
break;
@@ -638,7 +641,7 @@ int dbChannel_get_count(
memset(pold, '\0', sizeof(struct dbr_ctrl_enum));
/* first get status and severity */
options = DBR_STATUS | DBR_ENUM_STRS;
status = dbChannelGetField(chan, DBR_ENUM, &newSt, &options, &zero, pfl);
status = dbChannelGet(chan, DBR_ENUM, &newSt, &options, &zero, pfl);
pold->status = newSt.status;
pold->severity = newSt.severity;
no_str = newSt.no_str;
@@ -648,7 +651,7 @@ int dbChannel_get_count(
strncpy(pold->strs[i], newSt.strs[i], sizeof(pold->strs[i]));
/*now get values*/
options = 0;
status = dbChannelGetField(chan, DBR_ENUM, &pold->value, &options,
status = dbChannelGet(chan, DBR_ENUM, &pold->value, &options,
nRequest, pfl);
}
break;
@@ -665,7 +668,7 @@ int dbChannel_get_count(
options = DBR_STATUS | DBR_UNITS | DBR_GR_LONG | DBR_CTRL_LONG |
DBR_AL_LONG;
status = dbChannelGetField(chan, DBR_UCHAR, &newSt, &options, &zero, pfl);
status = dbChannelGet(chan, DBR_UCHAR, &newSt, &options, &zero, pfl);
pold->status = newSt.status;
pold->severity = newSt.severity;
strncpy(pold->units, newSt.units, MAX_UNITS_SIZE);
@@ -679,7 +682,7 @@ int dbChannel_get_count(
pold->upper_ctrl_limit = newSt.upper_ctrl_limit;
pold->lower_ctrl_limit = newSt.lower_ctrl_limit;
options = 0;
status = dbChannelGetField(chan, DBR_UCHAR, &pold->value, &options,
status = dbChannelGet(chan, DBR_UCHAR, &pold->value, &options,
nRequest, pfl);
}
break;
@@ -696,7 +699,7 @@ int dbChannel_get_count(
options = DBR_STATUS | DBR_UNITS | DBR_GR_LONG | DBR_CTRL_LONG |
DBR_AL_LONG;
status = dbChannelGetField(chan, DBR_LONG, &newSt, &options, &zero, pfl);
status = dbChannelGet(chan, DBR_LONG, &newSt, &options, &zero, pfl);
pold->status = newSt.status;
pold->severity = newSt.severity;
strncpy(pold->units, newSt.units, MAX_UNITS_SIZE);
@@ -710,7 +713,7 @@ int dbChannel_get_count(
pold->upper_ctrl_limit = newSt.upper_ctrl_limit;
pold->lower_ctrl_limit = newSt.lower_ctrl_limit;
options = 0;
status = dbChannelGetField(chan, DBR_LONG, &pold->value, &options,
status = dbChannelGet(chan, DBR_LONG, &pold->value, &options,
nRequest, pfl);
}
break;
@@ -728,7 +731,7 @@ int dbChannel_get_count(
options = DBR_STATUS | DBR_UNITS | DBR_PRECISION | DBR_GR_DOUBLE |
DBR_CTRL_DOUBLE | DBR_AL_DOUBLE;
status = dbChannelGetField(chan, DBR_DOUBLE, &newSt, &options, &zero, pfl);
status = dbChannelGet(chan, DBR_DOUBLE, &newSt, &options, &zero, pfl);
pold->status = newSt.status;
pold->severity = newSt.severity;
pold->precision = (dbr_short_t) newSt.precision.dp;
@@ -743,7 +746,7 @@ int dbChannel_get_count(
pold->upper_ctrl_limit = newSt.upper_ctrl_limit;
pold->lower_ctrl_limit = newSt.lower_ctrl_limit;
options = 0;
status = dbChannelGetField(chan, DBR_DOUBLE, &pold->value, &options,
status = dbChannelGet(chan, DBR_DOUBLE, &pold->value, &options,
nRequest, pfl);
}
break;
@@ -756,13 +759,13 @@ int dbChannel_get_count(
} newSt;
options = DBR_STATUS;
status = dbChannelGetField(chan, DBR_STRING, &newSt, &options, &zero, pfl);
status = dbChannelGet(chan, DBR_STRING, &newSt, &options, &zero, pfl);
pold->status = newSt.status;
pold->severity = newSt.severity;
pold->ackt = newSt.ackt;
pold->acks = newSt.acks;
options = 0;
status = dbChannelGetField(chan, DBR_STRING, pold->value,
status = dbChannelGet(chan, DBR_STRING, pold->value,
&options, nRequest, pfl);
}
break;
@@ -791,8 +794,12 @@ int dbChannel_get_count(
}
break;
default:
return -1;
status = -1;
break;
}
dbScanUnlock(dbChannelRecord(chan));
if (status) return -1;
return 0;
}

View File

@@ -19,7 +19,8 @@
#ifndef INCLdb_field_logh
#define INCLdb_field_logh
#include "epicsTime.h"
#include <epicsTime.h>
#include <epicsTypes.h>
#ifdef __cplusplus
extern "C" {
@@ -32,18 +33,20 @@ extern "C" {
* priority task pending on the event queue wakes up). Strings would slow down
* events for more reasonable size values. DB fields of native type string
* will most likely change infrequently.
*
*
* Strings can be added to the set of types for which updates will be queued
* by defining the macro DB_EVENT_LOG_STRINGS. The code in db_add_event()
* will adjust automatically, it just compares field sizes.
*/
union native_value {
short dbf_int;
short dbf_short;
float dbf_float;
short dbf_enum;
char dbf_char;
long dbf_long;
double dbf_double;
epicsInt8 dbf_char;
epicsInt16 dbf_short;
epicsEnum16 dbf_enum;
epicsInt32 dbf_long;
epicsFloat32 dbf_float;
epicsFloat64 dbf_double;
#ifdef DB_EVENT_LOG_STRINGS
char dbf_string[MAX_STRING_SIZE];
char dbf_string[MAX_STRING_SIZE];
#endif
};

View File

@@ -99,7 +99,7 @@ MAIN(callbackParallelTest)
myPvt *pcbt[NCALLBACKS];
epicsTimeStamp start;
int noCpus = epicsThreadGetCPUs();
int i, j;
int i, j, slowups, faults;
/* Statistics: min/max/sum/sum^2/n for each priority */
double setupError[NUM_CALLBACK_PRIORITIES][5];
double timeError[NUM_CALLBACK_PRIORITIES][5];
@@ -109,7 +109,7 @@ MAIN(callbackParallelTest)
for (j = 0; j < 5; j++)
setupError[i][j] = timeError[i][j] = defaultError[j];
testPlan(NCALLBACKS * 2 + 1);
testPlan(4);
testDiag("Starting %d parallel callback threads", noCpus);
@@ -138,7 +138,7 @@ MAIN(callbackParallelTest)
pcbt[NCALLBACKS-1]->delay = TEST_DELAY(NCALLBACKS) + 1.0;
pcbt[NCALLBACKS-1]->pass = 0;
testOk1(epicsTimeGetCurrent(&start)==epicsTimeOK);
testOk(epicsTimeGetCurrent(&start)==epicsTimeOK, "Time-of-day clock Ok");
for (i = 0; i < NCALLBACKS ; i++) {
callbackRequest(&pcbt[i]->cb1);
@@ -147,28 +147,43 @@ MAIN(callbackParallelTest)
testDiag("Waiting %.02f sec", pcbt[NCALLBACKS-1]->delay);
epicsEventWait(finished);
slowups = 0;
faults = 0;
for (i = 0; i < NCALLBACKS ; i++) {
if(pcbt[i]->resultFail || pcbt[i]->pass!=2)
testFail("pass = %d for delay = %f", pcbt[i]->pass, pcbt[i]->delay);
testDiag("callback setup fault #%d: pass = %d for delay = %.02f",
++faults, pcbt[i]->pass, pcbt[i]->delay);
else {
double delta = epicsTimeDiffInSeconds(&pcbt[i]->pass1Time, &start);
testOk(fabs(delta) < 0.05, "callback %.02f setup time |%f| < 0.05",
if (fabs(delta) >= 0.05) {
slowups++;
testDiag("callback %.02f setup time |%f| >= 0.05 seconds",
pcbt[i]->delay, delta);
}
updateStats(setupError[i%NUM_CALLBACK_PRIORITIES], delta);
}
}
testOk(faults == 0, "%d faults during callback setup", faults);
testOk(slowups <= 1, "%d slowups during callback setup", slowups);
slowups = 0;
for (i = 0; i < NCALLBACKS ; i++) {
double delta, error;
if(pcbt[i]->resultFail || pcbt[i]->pass!=2)
continue;
delta = epicsTimeDiffInSeconds(&pcbt[i]->pass2Time, &pcbt[i]->pass1Time);
error = delta - pcbt[i]->delay;
testOk(fabs(error) < 0.05, "delay %.02f seconds, callback time error |%.04f| < 0.05",
if (fabs(error) >= 0.05) {
slowups++;
testDiag("delay %.02f seconds, delay error |%.04f| >= 0.05",
pcbt[i]->delay, error);
}
updateStats(timeError[i%NUM_CALLBACK_PRIORITIES], error);
}
testOk(slowups < 5, "%d slowups during callbacks", slowups);
testDiag("Setup time statistics");
printStats(setupError[0], "LOW");

View File

@@ -99,7 +99,7 @@ MAIN(callbackTest)
{
myPvt *pcbt[NCALLBACKS];
epicsTimeStamp start;
int i, j;
int i, j, slowups, faults;
/* Statistics: min/max/sum/sum^2/n for each priority */
double setupError[NUM_CALLBACK_PRIORITIES][5];
double timeError[NUM_CALLBACK_PRIORITIES][5];
@@ -109,7 +109,7 @@ MAIN(callbackTest)
for (j = 0; j < 5; j++)
setupError[i][j] = timeError[i][j] = defaultError[j];
testPlan(NCALLBACKS * 2 + 1);
testPlan(4);
callbackInit();
epicsThreadSleep(1.0);
@@ -135,7 +135,7 @@ MAIN(callbackTest)
pcbt[NCALLBACKS-1]->delay = TEST_DELAY(NCALLBACKS) + 1.0;
pcbt[NCALLBACKS-1]->pass = 0;
testOk1(epicsTimeGetCurrent(&start)==epicsTimeOK);
testOk(epicsTimeGetCurrent(&start)==epicsTimeOK, "Time-of-day clock Ok");
for (i = 0; i < NCALLBACKS ; i++) {
callbackRequest(&pcbt[i]->cb1);
@@ -144,28 +144,43 @@ MAIN(callbackTest)
testDiag("Waiting %.02f sec", pcbt[NCALLBACKS-1]->delay);
epicsEventWait(finished);
slowups = 0;
faults = 0;
for (i = 0; i < NCALLBACKS ; i++) {
if(pcbt[i]->resultFail || pcbt[i]->pass!=2)
testFail("pass = %d for delay = %f", pcbt[i]->pass, pcbt[i]->delay);
testDiag("callback setup fault #%d: pass = %d for delay = %.02f",
++faults, pcbt[i]->pass, pcbt[i]->delay);
else {
double delta = epicsTimeDiffInSeconds(&pcbt[i]->pass1Time, &start);
testOk(fabs(delta) < 0.05, "callback %.02f setup time |%f| < 0.05",
if (fabs(delta) >= 0.05) {
slowups++;
testDiag("callback %.02f setup time |%f| >= 0.05 seconds",
pcbt[i]->delay, delta);
}
updateStats(setupError[i%NUM_CALLBACK_PRIORITIES], delta);
}
}
testOk(faults == 0, "%d faults during callback setup", faults);
testOk(slowups <= 1, "%d slowups during callback setup", slowups);
slowups = 0;
for (i = 0; i < NCALLBACKS ; i++) {
double delta, error;
if(pcbt[i]->resultFail || pcbt[i]->pass!=2)
continue;
delta = epicsTimeDiffInSeconds(&pcbt[i]->pass2Time, &pcbt[i]->pass1Time);
error = delta - pcbt[i]->delay;
testOk(fabs(error) < 0.05, "delay %.02f seconds, callback time error |%.04f| < 0.05",
if (fabs(error) >= 0.05) {
slowups++;
testDiag("delay %.02f seconds, delay error |%.04f| >= 0.05",
pcbt[i]->delay, error);
}
updateStats(timeError[i%NUM_CALLBACK_PRIORITIES], error);
}
testOk(slowups < 5, "%d slowups during callbacks", slowups);
testDiag("Setup time statistics");
printStats(setupError[0], "LOW");

View File

@@ -1112,7 +1112,7 @@ unsigned cid
* casAccessRightsCB()
*
* If access right state changes then inform the client.
*
* asLock is held
*/
static void casAccessRightsCB(ASCLIENTPVT ascpvt, asClientStatus type)
{
@@ -1568,6 +1568,9 @@ static void sendAllUpdateAS ( struct client *client )
else if ( pciu->state == rsrvCS_inServiceUpdatePendAR ) {
access_rights_reply ( pciu );
}
else if ( pciu->state == rsrvCS_shutdown ) {
/* no-op */
}
else {
errlogPrintf (
"%s at %d: corrupt channel state detected durring AR update\n",
@@ -2047,10 +2050,15 @@ static int clear_channel_reply ( caHdrLargeArray *mp,
if ( pciu->state == rsrvCS_inService ||
pciu->state == rsrvCS_pendConnectResp ) {
ellDelete ( &client->chanList, &pciu->node );
pciu->state = rsrvCS_shutdown;
}
else if ( pciu->state == rsrvCS_inServiceUpdatePendAR ||
pciu->state == rsrvCS_pendConnectRespUpdatePendAR ) {
ellDelete ( &client->chanPendingUpdateARList, &pciu->node );
pciu->state = rsrvCS_shutdown;
}
else if ( pciu->state == rsrvCS_shutdown ) {
/* no-op */
}
else {
epicsMutexUnlock( client->chanListLock );

View File

@@ -1134,6 +1134,7 @@ static void destroyAllChannels (
epicsMutexMustLock ( client->chanListLock );
pciu = (struct channel_in_use *) ellGet ( pList );
if(pciu) pciu->state = rsrvCS_shutdown;
epicsMutexUnlock ( client->chanListLock );
if ( ! pciu ) {

View File

@@ -99,12 +99,25 @@ typedef struct client {
char disconnect; /* disconnect detected */
} client;
/* Channel state shows which struct client list a
* channel_in_us::node is in.
*
* client::chanList
* rsrvCS_pendConnectResp, rsrvCS_inService
* client::chanPendingUpdateARList
* rsrvCS_pendConnectRespUpdatePendAR, rsrvCS_inServiceUpdatePendAR
* Not in any list
* rsrvCS_shutdown
*
* rsrvCS_invalid is not used
*/
enum rsrvChanState {
rsrvCS_invalid,
rsrvCS_pendConnectResp,
rsrvCS_inService,
rsrvCS_pendConnectRespUpdatePendAR,
rsrvCS_inServiceUpdatePendAR
rsrvCS_inServiceUpdatePendAR,
rsrvCS_shutdown
};
/*

View File

@@ -155,7 +155,7 @@ epicsShareFunc long
break;
case ABS_VAL:
if (*ptop < 0.0) *ptop = - *ptop;
*ptop = fabs(*ptop);
break;
case EXP:

View File

@@ -70,12 +70,12 @@ epicsShareFunc int
/* These macros return 1 if successful, 0 on failure.
* This is analagous to the return value from sscanf()
*/
#define epicsScanLong(str, to, base) !epicsParseLong(str, to, base, NULL)
#define epicsScanULong(str, to, base) !epicsParseULong(str, to, base, NULL)
#define epicsScanLLong(str, to, base) !epicsParseLLong(str, to, base, NULL)
#define epicsScanULLong(str, to, base) !epicsParseULLong(str, to, base, NULL)
#define epicsScanFloat(str, to) !epicsParseFloat(str, to, NULL)
#define epicsScanDouble(str, to) !epicsParseDouble(str, to, NULL)
#define epicsScanLong(str, to, base) (!epicsParseLong(str, to, base, NULL))
#define epicsScanULong(str, to, base) (!epicsParseULong(str, to, base, NULL))
#define epicsScanLLong(str, to, base) (!epicsParseLLong(str, to, base, NULL))
#define epicsScanULLong(str, to, base) (!epicsParseULLong(str, to, base, NULL))
#define epicsScanFloat(str, to) (!epicsParseFloat(str, to, NULL))
#define epicsScanDouble(str, to) (!epicsParseDouble(str, to, NULL))
#ifdef __cplusplus
}

View File

@@ -265,12 +265,16 @@ void ipAddrToAsciiEnginePrivate::run ()
continue;
}
// fix for lp:1580623
// a destructing cac sets pCurrent to NULL, so
// make local copy to avoid race when releasing the guard
ipAddrToAsciiTransactionPrivate *pCur = this->pCurrent;
this->callbackInProgress = true;
{
epicsGuardRelease < epicsMutex > unguard ( guard );
// dont call callback with lock applied
this->pCurrent->pCB->transactionComplete ( this->nameTmp );
pCur->pCB->transactionComplete ( this->nameTmp );
}
this->callbackInProgress = false;

View File

@@ -73,6 +73,21 @@ epicsShareFunc void epicsShareAPI osiSockDiscoverBroadcastAddresses
struct ifreq *pifreq;
struct ifreq *pnextifreq;
osiSockAddrNode *pNewNode;
if ( pMatchAddr->sa.sa_family == AF_INET ) {
if ( pMatchAddr->ia.sin_addr.s_addr == htonl (INADDR_LOOPBACK) ) {
pNewNode = (osiSockAddrNode *) calloc (1, sizeof (*pNewNode) );
if ( pNewNode == NULL ) {
errlogPrintf ( "osiSockDiscoverBroadcastAddresses(): no memory available for configuration\n" );
return;
}
pNewNode->addr.ia.sin_family = AF_INET;
pNewNode->addr.ia.sin_port = htons ( 0 );
pNewNode->addr.ia.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
ellAdd ( pList, &pNewNode->node );
return;
}
}
/*
* use pool so that we avoid using too much stack space
@@ -100,7 +115,6 @@ epicsShareFunc void epicsShareAPI osiSockDiscoverBroadcastAddresses
for ( pifreq = pIfreqList; pifreq <= pIfreqListEnd; pifreq = pnextifreq ) {
uint32_t current_ifreqsize;
struct sockaddr_in if_addr;
/*
* find the next ifreq
@@ -124,7 +138,6 @@ epicsShareFunc void epicsShareAPI osiSockDiscoverBroadcastAddresses
ifDepenDebugPrintf ( ("osiSockDiscoverBroadcastAddresses(): interface \"%s\" was not AF_INET\n", pIfreqList->ifr_name) );
continue;
}
if_addr = *(struct sockaddr_in *)&pIfreqList->ifr_addr;
/*
* if it isnt a wildcarded interface then look for
@@ -135,7 +148,8 @@ epicsShareFunc void epicsShareAPI osiSockDiscoverBroadcastAddresses
continue;
}
if ( pMatchAddr->ia.sin_addr.s_addr != htonl (INADDR_ANY) ) {
if ( if_addr.sin_addr.s_addr != pMatchAddr->ia.sin_addr.s_addr ) {
struct sockaddr_in *pInetAddr = (struct sockaddr_in *) &pIfreqList->ifr_addr;
if ( pInetAddr->sin_addr.s_addr != pMatchAddr->ia.sin_addr.s_addr ) {
ifDepenDebugPrintf ( ("osiSockDiscoverBroadcastAddresses(): net intf \"%s\" didnt match\n", pIfreqList->ifr_name) );
continue;
}
@@ -156,6 +170,14 @@ epicsShareFunc void epicsShareAPI osiSockDiscoverBroadcastAddresses
continue;
}
/*
* dont use the loop back interface
*/
if ( pIfreqList->ifr_flags & IFF_LOOPBACK ) {
ifDepenDebugPrintf ( ("osiSockDiscoverBroadcastAddresses(): ignoring loopback interface: \"%s\"\n", pIfreqList->ifr_name) );
continue;
}
pNewNode = (osiSockAddrNode *) calloc (1, sizeof (*pNewNode) );
if ( pNewNode == NULL ) {
errlogPrintf ( "osiSockDiscoverBroadcastAddresses(): no memory available for configuration\n" );
@@ -174,14 +196,22 @@ epicsShareFunc void epicsShareAPI osiSockDiscoverBroadcastAddresses
* interface.
*/
if ( pIfreqList->ifr_flags & IFF_BROADCAST ) {
osiSockAddr baddr;
status = socket_ioctl (socket, SIOCGIFBRDADDR, pIfreqList);
if ( status ) {
errlogPrintf ("osiSockDiscoverBroadcastAddresses(): net intf \"%s\": bcast addr fetch fail\n", pIfreqList->ifr_name);
free ( pNewNode );
continue;
}
pNewNode->addr.sa = pIfreqList->ifr_broadaddr;
ifDepenDebugPrintf ( ( "found broadcast addr = %x\n", ntohl ( pNewNode->addr.ia.sin_addr.s_addr ) ) );
baddr.sa = pIfreqList->ifr_broadaddr;
if (baddr.ia.sin_family==AF_INET && baddr.ia.sin_addr.s_addr != INADDR_ANY) {
pNewNode->addr.sa = pIfreqList->ifr_broadaddr;
ifDepenDebugPrintf ( ( "found broadcast addr = %x\n", ntohl ( baddr.ia.sin_addr.s_addr ) ) );
} else {
ifDepenDebugPrintf ( ( "Ignoring broadcast addr = \n", ntohl ( baddr.ia.sin_addr.s_addr ) ) );
free ( pNewNode );
continue;
}
}
#if defined (IFF_POINTOPOINT)
else if ( pIfreqList->ifr_flags & IFF_POINTOPOINT ) {
@@ -193,22 +223,6 @@ epicsShareFunc void epicsShareAPI osiSockDiscoverBroadcastAddresses
}
pNewNode->addr.sa = pIfreqList->ifr_dstaddr;
}
#endif
#if defined(__linux__)
/* On Linux, even though the 'lo' interface doesn't set IFF_BROADCAST
* a broadcast route often exists. Assume that sending to 127.255.255.255
* reaches all local listeners.
*
* $ ip route show table local scope link dev lo
* broadcast 127.0.0.0 proto kernel src 127.0.0.1
* broadcast 127.255.255.255 proto kernel src 127.0.0.1
*/
else if ( pIfreqList->ifr_flags & IFF_LOOPBACK && if_addr.sin_addr.s_addr == htonl(INADDR_LOOPBACK) ) {
memset(&pNewNode->addr.ia, 0, sizeof(pNewNode->addr.ia));
pNewNode->addr.ia.sin_family = AF_INET;
pNewNode->addr.ia.sin_addr.s_addr = htonl(0x7fffffff);
ifDepenDebugPrintf ( ( "assume loopback broadcast addr = %x\n", ntohl ( pNewNode->addr.ia.sin_addr.s_addr ) ) );
}
#endif
else {
ifDepenDebugPrintf ( ( "osiSockDiscoverBroadcastAddresses(): net intf \"%s\": not point to point or bcast?\n", pIfreqList->ifr_name ) );

View File

@@ -66,6 +66,7 @@ dbRecStd_SRCS += devSoSoftCallback.c
dbRecStd_SRCS += devTimestamp.c
dbRecStd_SRCS += devStdio.c
dbRecStd_SRCS += devEnviron.c
dbRecStd_SRCS += asSubRecordFunctions.c

128
src/std/dev/devEnviron.c Normal file
View File

@@ -0,0 +1,128 @@
/*************************************************************************\
* Copyright (c) 2016 UChicago Argonne LLC, as Operator of Argonne
* National Laboratory.
* EPICS BASE is distributed subject to a Software License Agreement found
* in file LICENSE that is included with this distribution.
\*************************************************************************/
/* devEnviron.c */
#include <stdlib.h>
#include <string.h>
#include "alarm.h"
#include "dbCommon.h"
#include "devSup.h"
#include "errlog.h"
#include "recGbl.h"
#include "recSup.h"
#include "lsiRecord.h"
#include "stringinRecord.h"
#include "epicsExport.h"
/* lsi device support */
static long add_lsi(dbCommon *pcommon) {
lsiRecord *prec = (lsiRecord *) pcommon;
if (prec->inp.type != INST_IO)
return S_dev_badInpType;
return 0;
}
static long del_lsi(dbCommon *pcommon) {
return 0;
}
static struct dsxt dsxtLsiEnviron = {
add_lsi, del_lsi
};
static long init_lsi(int pass)
{
if (pass == 0)
devExtend(&dsxtLsiEnviron);
return 0;
}
static long read_lsi(lsiRecord *prec)
{
const char *val = getenv(prec->inp.value.instio.string);
if (val) {
strncpy(prec->val, val, prec->sizv);
prec->val[prec->sizv - 1] = 0;
prec->len = strlen(prec->val);
prec->udf = FALSE;
}
else {
prec->val[0] = 0;
prec->len = 1;
prec->udf = TRUE;
recGblSetSevr(prec, UDF_ALARM, prec->udfs);
}
return 0;
}
lsidset devLsiEnviron = {
5, NULL, init_lsi, NULL, NULL, read_lsi
};
epicsExportAddress(dset, devLsiEnviron);
/* stringin device support */
static long add_stringin(dbCommon *pcommon) {
stringinRecord *prec = (stringinRecord *) pcommon;
if (prec->inp.type != INST_IO)
return S_dev_badInpType;
return 0;
}
static long del_stringin(dbCommon *pcommon) {
return 0;
}
static struct dsxt dsxtSiEnviron = {
add_stringin, del_stringin
};
static long init_stringin(int pass)
{
if (pass == 0)
devExtend(&dsxtSiEnviron);
return 0;
}
static long read_stringin(stringinRecord *prec)
{
const char *val = getenv(prec->inp.value.instio.string);
if (val) {
strncpy(prec->val, val, MAX_STRING_SIZE);
prec->val[MAX_STRING_SIZE - 1] = 0;
prec->udf = FALSE;
}
else {
prec->val[0] = 0;
prec->udf = TRUE;
recGblSetSevr(prec, UDF_ALARM, prec->udfs);
}
return 0;
}
static struct {
dset common;
DEVSUPFUN read;
} devSiEnviron = {
{5, NULL, init_stringin, NULL, NULL}, read_stringin
};
epicsExportAddress(dset, devSiEnviron);

View File

@@ -58,5 +58,8 @@ device(lso,INST_IO,devLsoStdio,"stdio")
device(printf,INST_IO,devPrintfStdio,"stdio")
device(stringout,INST_IO,devSoStdio,"stdio")
device(lsi,INST_IO,devLsiEnviron,"getenv")
device(stringin,INST_IO,devSiEnviron,"getenv")
device(bi, INST_IO, devBiDbState, "Db State")
device(bo, INST_IO, devBoDbState, "Db State")

View File

@@ -158,9 +158,10 @@ static long put_array_info(DBADDR *paddr, long nNew)
{
lsiRecord *prec = (lsiRecord *) paddr->precord;
if (nNew == prec->sizv)
--nNew; /* truncated string */
prec->val[nNew] = 0; /* ensure data is terminated */
if (nNew >= prec->sizv)
nNew = prec->sizv - 1; /* truncated string */
if (paddr->field_type == DBF_CHAR)
prec->val[nNew] = 0; /* ensure data is terminated */
return 0;
}

View File

@@ -200,9 +200,10 @@ static long put_array_info(DBADDR *paddr, long nNew)
{
lsoRecord *prec = (lsoRecord *) paddr->precord;
if (nNew == prec->sizv)
--nNew; /* truncated string */
prec->val[nNew] = 0; /* ensure data is terminated */
if (nNew >= prec->sizv)
nNew = prec->sizv - 1; /* truncated string */
if (paddr->field_type == DBF_CHAR)
prec->val[nNew] = 0; /* ensure data is terminated */
return 0;
}

View File

@@ -37,6 +37,14 @@ testHarness_SRCS += analogMonitorTest_registerRecordDeviceDriver.cpp
TESTFILES += $(COMMON_DIR)/analogMonitorTest.dbd ../analogMonitorTest.db
TESTS += analogMonitorTest
TARGETS += $(COMMON_DIR)/regressTest.dbd
regressTest_DBD += base.dbd
TESTPROD_HOST += regressTest
regressTest_SRCS += regressTest.c
regressTest_SRCS += regressTest_registerRecordDeviceDriver.cpp
TESTFILES += $(COMMON_DIR)/regressTest.dbd ../regressArray1.db
TESTS += regressTest
# epicsRunRecordTests runs all the test programs in a known working order.
testHarness_SRCS += epicsRunRecordTests.c

View File

@@ -0,0 +1,9 @@
record(waveform, "wf") {
field(FTVL, "DOUBLE")
field(NELM, "1")
field(FLNK, "co")
}
record(calcout, "co") {
field(CALC, "A")
field(INPA, "wf")
}

View File

@@ -0,0 +1,73 @@
/*************************************************************************\
* Copyright (c) 2016 Michael Davidsaver
* EPICS BASE is distributed subject to a Software License Agreement found
* in file LICENSE that is included with this distribution.
\*************************************************************************/
#include <dbUnitTest.h>
#include <testMain.h>
#include <dbAccess.h>
#include <errlog.h>
#include <calcoutRecord.h>
#include <waveformRecord.h>
/*
* Test the some identified regressions
*/
void regressTest_registerRecordDeviceDriver(struct dbBase *);
/*
* https://bugs.launchpad.net/epics-base/+bug/1577108
*/
static
void testArrayLength1(void)
{
waveformRecord *precwf;
calcoutRecord *precco;
double *pbuf;
testdbPrepare();
testdbReadDatabase("regressTest.dbd", NULL, NULL);
regressTest_registerRecordDeviceDriver(pdbbase);
testdbReadDatabase("regressArray1.db", NULL, NULL);
precwf = (waveformRecord*)testdbRecordPtr("wf");
precco = (calcoutRecord*)testdbRecordPtr("co");
eltc(0);
testIocInitOk();
eltc(1);
dbScanLock((dbCommon*)precwf);
pbuf = (double*)precwf->bptr;
dbScanUnlock((dbCommon*)precwf);
testdbPutFieldOk("wf", DBF_DOUBLE, 2.0);
dbScanLock((dbCommon*)precwf);
testOk(precwf->nord==1, "wf.NORD = %u == 1", (unsigned)precwf->nord);
testOk(pbuf[0]==2.0, "wf.VAL[0] = %f == 2.0", pbuf[0]);
dbScanUnlock((dbCommon*)precwf);
dbScanLock((dbCommon*)precco);
testOk(precco->a==2.0, "co.A = %f == 2.0", precco->a);
dbScanUnlock((dbCommon*)precco);
testdbGetFieldEqual("co", DBF_DOUBLE, 2.0);
testIocShutdownOk();
testdbCleanup();
}
MAIN(regressTest)
{
testPlan(5);
testArrayLength1();
return testDone();
}

View File

@@ -1,7 +1,15 @@
# $Revision-Id$
#
# softIocExit.db
record(sub,"$(IOC):exit") {
field(DESC,"Exit subroutine")
field(SCAN,"Passive")
field(SNAM,"exit")
field(DESC,"Exit subroutine")
field(SCAN,"Passive")
field(SNAM,"exit")
}
record(stringin,"$(IOC):BaseVersion") {
field(DESC,"EPICS Base Version")
field(DTYP,"getenv")
field(INP,"@EPICS_VERSION_FULL")
field(PINI,"YES")
field(DISP,1)
}

View File

@@ -62,10 +62,11 @@ sub toDeclaration {
my @choices = map {
sprintf " %-31s /* %s */", @{$_}[0], escapeCcomment(@{$_}[1]);
} $this->choices;
my $num = scalar @choices;
return "typedef enum {\n" .
join(",\n", @choices) .
",\n ${name}_NUM_CHOICES\n" .
"} $name;\n\n";
"\n} $name;\n" .
"#define ${name}_NUM_CHOICES $num\n\n";
}
sub toDefinition {

View File

@@ -39,6 +39,35 @@ our %field_attrs = (
prop => qr/^(?:YES|NO)$/
);
# Convert old promptgroups into new-style
my %promptgroupMap = (
GUI_COMMON => '10 - Common',
GUI_ALARMS => '70 - Alarm',
GUI_BITS1 => '41 - Bits (1)',
GUI_BITS2 => '42 - Bits (2)',
GUI_CALC => '30 - Action',
GUI_CLOCK => '30 - Action',
GUI_COMPRESS => '30 - Action',
GUI_CONVERT => '60 - Convert',
GUI_DISPLAY => '80 - Display',
GUI_HIST => '30 - Action',
GUI_INPUTS => '40 - Input',
GUI_LINKS => '40 - Link',
GUI_MBB => '30 - Action',
GUI_MOTOR => '30 - Action',
GUI_OUTPUT => '50 - Output',
GUI_PID => '30 - Action',
GUI_PULSE => '30 - Action',
GUI_SELECT => '40 - Input',
GUI_SEQ1 => '51 - Output (1)',
GUI_SEQ2 => '52 - Output (2)',
GUI_SEQ3 => '53 - Output (3)',
GUI_SUB => '30 - Action',
GUI_TIMER => '30 - Action',
GUI_WAVE => '30 - Action',
GUI_SCAN => '20 - Scan',
);
sub new {
my ($class, $name, $type) = @_;
dieContext("Illegal field type '$type', valid field types are:",
@@ -76,6 +105,8 @@ sub number {
sub add_attribute {
my ($this, $attr, $value) = @_;
unquote $value;
$value = $promptgroupMap{$value}
if $attr eq 'promptgroup' && exists $promptgroupMap{$value};
my $match = $field_attrs{$attr};
if (defined $match) {
dieContext("Bad value '$value' for field attribute '$attr'")

View File

@@ -215,7 +215,7 @@ sub checkRelease {
delete $macros{RULES};
delete $macros{TOP};
delete $macros{TEMPLATE_TOP};
while (my ($app, $path) = each %macros) {
my %check = (TOP => $path);
my @order = ();
@@ -238,7 +238,35 @@ sub checkRelease {
}
}
}
print "\n" if ($status);
my @modules = @apps;
my $app = shift @modules;
my $latest = AbsPath($macros{$app});
my %paths = ($latest => $app);
foreach $app (@modules) {
my $path = AbsPath($macros{$app});
if ($path ne $latest && exists $paths{$path}) {
my $prev = $paths{$path};
print "\n" unless ($status);
print "This application's RELEASE file(s) define\n";
print "\t$app = $macros{$app}\n";
print "after but not adjacent to\n\t$prev = $macros{$prev}\n";
print "both of which resolve to $path\n"
if $path ne $macros{$app} || $path ne $macros{$prev};
$status = 2;
}
$paths{$path} = $app;
$latest = $path;
}
if ($status == 2) {
print "Module definitions that share paths must be grouped together.\n";
print "Either remove a definition, or move it to a line immediately\n";
print "above or below the other(s).\n";
print "Any non-module definitions belong in configure/CONFIG_SITE.\n";
$status = 1;
}
print "\n" if $status;
exit $status;
}

View File

@@ -25,8 +25,9 @@ ok !$menu->legal_choice('Choice 3'), 'Third choice not legal';
is_deeply $menu->choice(2), undef, 'Third choice undefined';
like $menu->toDeclaration, qr/ ^
\s* typedef \s+ enum \s+ {
\s+ ch1 \s+ \/\* [^*]* \*\/,
\s+ ch2 \s+ \/\* [^*]* \*\/,
\s+ test_NUM_CHOICES ,?
\s+ } \s+ test; \s* $ /x, 'C declaration';
\s* typedef \s+ enum \s+ \{ \s* \n
\s* ch1 \s+ \/\* [^*]* \*\/, \s* \n
\s* ch2 \s+ \/\* [^*]* \*\/ \s* \n
\s* \} \s* test \s* ; \s* \n
\s* \# \s* define \s+ test_NUM_CHOICES \s+ 2 \s* \n
\s* $ /x, 'C declaration';