Merge changes from 3.15 into 3.16

This commit is contained in:
Andrew Johnson
2018-05-12 22:39:37 -05:00
12 changed files with 403 additions and 181 deletions
+10 -6
View File
@@ -79,16 +79,20 @@ CPP = cl -nologo -C -E
# Configure OS vendor C++ compiler
#
# __STDC__=0 gives us both:
# 1) define STDC for code (pretend ANSI conformance)
# 2) set it to 0 to use MS C "extensions" (open for _open etc.)
# because MS uses: if __STDC__ ... disable many nice things
#
# -EHsc - generate code for exceptions
# -GR - generate code for run time type identification
#
CCC = cl -EHsc -GR
CODE_CPPFLAGS += -nologo -D__STDC__=0
# Other compiler flags, used for CPP, C and C++
#
# -FC - Show absolute path of source file in diagnostics
# -D__STDC__=0 gives us both:
# 1) define STDC for code (pretend ANSI conformance)
# 2) set it to 0 to use MS C "extensions" (open for _open etc.)
# because MS uses: if __STDC__ ... disable many nice things
#
CODE_CPPFLAGS += -nologo -FC -D__STDC__=0
CODE_CPPFLAGS += -D_CRT_SECURE_NO_DEPRECATE -D_CRT_NONSTDC_NO_DEPRECATE
+8
View File
@@ -662,6 +662,14 @@ of its CALLBACK objects.</p>
<!-- Insert inherited items immediately below here ... -->
<h3>Fix problem with numeric soft events</h3>
<p>Changing from numeric to named soft events introduced an incompatibility
when a numeric event 1-255 is converted from a DOUBLE, e.g. from a calc record.
The <tt>post_event()</tt> API is not marked deprecated any more.
<p>Also <code>scanpel</code> has been modified to accept a glob pattern for
event name filtering and to show events with no connected records as well.</p>
<h3>Add osiSockOptMcastLoop_t and osiSockTest</h3>
<p>Added a new OS-independent typedef for multicast socket options, and a test
+57 -36
View File
@@ -1021,7 +1021,7 @@ d:/user/epics/base-3.15/lib/win32-x86/Com.lib</code></p>
<h2><a name="CommandUtils">Command Line Utilities</a></h2>
<h3><a name="acctst">acctst</a></h3>
<pre>acctst &lt;PV name&gt; [progress logging level] [channel duplication count]
<pre>acctst &lt;PV name&gt; [progress logging level] [channel duplication count]
[test repetition count] [enable preemptive callback]</pre>
<h4>Description</h4>
@@ -2091,7 +2091,7 @@ example, be beneficial when tuning an archiver installation.</p>
<p>Significant performance gains can be realized when the CA client library
doesn't wait for a response to return from the server after each request. All
requests which require interaction with a CA server are accumulated (buffered)
and not forwarded to the IOC until one of <code>ca_flush_io()</code>,
and not forwarded to the IOC 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 allowing several operations to be
efficiently sent over the network together. Any process variable values written
@@ -2115,16 +2115,16 @@ shouldn't test the success of a CA function call by checking to see if the
returned value is zero as is the UNIX convention. Below are several methods to
test CA function returns. See <a href="#ca_signal"><code>ca_signal()</code> and
<code>SEVCHK()</code></a> for more information on this topic.</p>
<pre>status = ca_XXXX();
SEVCHK( status, "ca_XXXX() returned failure status");
<pre>status = ca_XXXX();
SEVCHK( status, "ca_XXXX() returned failure status");
if ( status &amp; CA_M_SUCCESS ) {
printf ( "The requested ca_XXXX() operation didn't complete successfully");
}
if ( status &amp; CA_M_SUCCESS ) {
printf ( "The requested ca_XXXX() operation didn't complete successfully");
}
if ( status != ECA_NORMAL ) {
if ( status != ECA_NORMAL ) {
printf("The requested ca_XXXX() operation didn't complete successfully because \"%s\"\n",
ca_message ( status ) );
ca_message ( status ) );
}</pre>
<h3><a name="Channel">Channel Access Data Types</a></h3>
@@ -2297,7 +2297,7 @@ int main ( int argc, char ** argv )
unsigned nBytes;
unsigned elementCount;
char timeString[32];
unsigned i;
unsigned i;
chid chan;
double sum;
int status;
@@ -2340,7 +2340,7 @@ int main ( int argc, char ** argv )
epicsTimeToStrftime ( timeString, sizeof ( timeString ),
"%a %b %d %Y %H:%M:%S.%f", &amp; pTD-&gt;stamp );
printf ( "The sum of elements in %s at %s was %f\n",
printf ( "The sum of elements in %s at %s was %f\n",
argv[1], timeString, sum );
ca_clear_channel ( chan );
@@ -2371,7 +2371,7 @@ executing within the user's callback function.</p>
<pre>typedef struct event_handler_args {
void *usr; /* user argument supplied with request */
chanId chid; /* channel id */
long type; /* the type of the item returned */
long type; /* the type of the item returned */
long count; /* the element count of the item returned */
const void *dbr; /* a pointer to the item returned */
int status; /* ECA_XXX status of the requested op from the server */
@@ -2394,7 +2394,7 @@ attached to the request, an exception handler is executed in the client. The
default exception handler prints a message on the console and exits if the
exception condition is severe. Certain internal exceptions within the CA client
library, and failures detected by the SEVCHK macro may also cause the exception
handler to be invoked. To modify this behavior see
handler to be invoked. To modify this behavior see
<code><a href="#ca_add_exception_event">ca_add_exception_event</a>()</code>.</p>
<h3><a name="Server">Server and Client Share the Same Address Space on The Same
@@ -2686,6 +2686,14 @@ automatically released by the system when the process exits and
vxWorks or RTEMS no cleanup occurs unless the application calls
<code>ca_context_destroy()</code>.</p>
<p>Note: This operation blocks until any user callbacks for any channel
created in the current context have run to completion. If callbacks take a
lock (mutex) then it is the user's responsibility to ensure that this lock
is not held when <code>ca_clear_context()</code> is called, otherwise a
deadlock may ensue. (See also
<code><a href="#ca_clear_channel">ca_clear_channel</a>()</code> and
<code><a href="#ca_clear_event">ca_clear_subscription</a>()</code>.)</p>
<h4>Returns</h4>
<p>ECA_NORMAL - Normal successful completion</p>
@@ -2831,6 +2839,12 @@ efficiently sent over the network in one message.</p>
clearing a channel does shutdown and reclaim any channel state change event
subscriptions (monitors) registered with the channel.</p>
<p>Note: This operation blocks until any user callbacks for this channel
have run to completion. If callbacks take a lock (mutex) then it is the
user's responsibility to ensure that this lock is not held when
<code>ca_clear_channel()</code> is called, otherwise a deadlock may ensue.
(See also <code><a href="#ca_clear_event">ca_clear_subscription</a>()</code>.)</p>
<h4>Arguments</h4>
<dl>
<dt><code>CHID</code></dt>
@@ -2845,16 +2859,16 @@ subscriptions (monitors) registered with the channel.</p>
<h3><code><a name="ca_put">ca_put()</a></code></h3>
<pre>#include &lt;cadef.h&gt;
int ca_put ( chtype TYPE,
chid CHID, void *PVALUE );
int ca_array_put ( chtype TYPE, unsigned long COUNT,
int ca_put ( chtype TYPE,
chid CHID, void *PVALUE );
int ca_array_put ( chtype TYPE, unsigned long COUNT,
chid CHID, const void *PVALUE);
typedef void ( caEventCallBackFunc ) (struct event_handler_args);
int ca_put_callback ( chtype TYPE,
chid CHID, const void *PVALUE,
caEventCallBackFunc PFUNC, void *USERARG );
int ca_array_put_callback ( chtype TYPE, unsigned long COUNT,
chid CHID, const void *PVALUE,
int ca_put_callback ( chtype TYPE,
chid CHID, const void *PVALUE,
caEventCallBackFunc PFUNC, void *USERARG );
int ca_array_put_callback ( chtype TYPE, unsigned long COUNT,
chid CHID, const void *PVALUE,
caEventCallBackFunc PFUNC, void *USERARG );</pre>
<h4>Description</h4>
@@ -3081,7 +3095,7 @@ when a CA get request is initiated.</p>
typedef void ( caEventCallBackFunc ) (struct event_handler_args);
int ca_create_subscription ( chtype TYPE, unsigned long COUNT,
chid CHID, unsigned long MASK,
caEventCallBackFunc USERFUNC, void *USERARG,
caEventCallBackFunc USERFUNC, void *USERARG,
evid *PEVID );</pre>
<h4>Description</h4>
@@ -3165,7 +3179,7 @@ indicating the current state of the channel.</p>
<dt><code>MASK</code></dt>
<dd>A mask with bits set for each of the event trigger types requested. The
event trigger mask must be a <em>bitwise or</em> of one or more of the
following constants.
following constants.
<ul>
<li>DBE_VALUE - Trigger events when the channel value exceeds the
monitor dead band</li>
@@ -3212,6 +3226,13 @@ and not forwarded to the server until one of <code>ca_flush_io()</code>, <code>c
<code>ca_pend_event()</code>, or <code>ca_sg_block()</code> are called. This allows several requests to be
efficiently sent together in one message.</p>
<p>Note: This operation blocks until any user callbacks for this channel
have run to completion. If callbacks take a lock (mutex) then it is the
user's responsibility to ensure that this lock is not held when
<code>ca_clear_subscription()</code> is called, otherwise a deadlock may
ensue. (See also <code><a
href="#ca_clear_channel">ca_clear_channel</a>()</code>.)</p>
<h4>Arguments</h4>
<dl>
<dt>EVID</dt>
@@ -3376,7 +3397,7 @@ becomes full.</p>
<h3><code><a name="ca_signal">ca_signal()</a></code></h3>
<pre>#include &lt;cadef.h&gt;
int ca_signal ( long CA_STATUS, const char * CONTEXT_STRING );
int ca_signal ( long CA_STATUS, const char * CONTEXT_STRING );
void SEVCHK( CA_STATUS, CONTEXT_STRING );</pre>
<h4>Description</h4>
@@ -3393,7 +3414,7 @@ recommended error handler for simple applications which do not wish to write
code testing the status returned from each channel access call.</p>
<h4>Examples</h4>
<pre>status = ca_context_create (...);
<pre>status = ca_context_create (...);
SEVCHK ( status, "Unable to create a CA client context" );</pre>
<p>If the application only wishes to print the message associated with an error
@@ -3417,7 +3438,7 @@ this purpose.</p>
<h3><code><a
name="ca_add_exception_event">ca_add_exception_event()</a></code></h3>
<pre>#include &lt;cadef.h&gt;
<pre>#include &lt;cadef.h&gt;
typedef void (*pCallback) ( struct exception_handler_args HANDLERARGS );
int ca_add_exception_event ( pCallback USERFUNC, void *USERARG );</pre>
@@ -3626,7 +3647,7 @@ specified channel.</p>
<dt><code>PFUNC</code></dt>
<dd>Pointer to a user supplied callback function. A null pointer uninstalls
the current handler. The following arguments are passed <em>by value</em>
to the supplied callback handler.
to the supplied callback handler.
<pre>typedef struct ca_access_rights {
unsigned read_access:1;
unsigned write_access:1;
@@ -3966,8 +3987,8 @@ type.</p>
prints diagnostics to standard out.</p>
<h4>Examples</h4>
<pre>void ca_test_event ();
status = ca_create_subscription ( type, chid, ca_test_event, NULL, NULL );
<pre>void ca_test_event ();
status = ca_create_subscription ( type, chid, ca_test_event, NULL, NULL );
SEVCHK ( status, .... );</pre>
<h4>See Also</h4>
@@ -4001,8 +4022,8 @@ outstanding within them at any given time.</p>
</dl>
<h4>Examples</h4>
<pre>CA_SYNC_GID gid;
status = ca_sg_create ( &amp;gid );
<pre>CA_SYNC_GID gid;
status = ca_sg_create ( &amp;gid );
SEVCHK ( status, Sync group create failed );</pre>
<h4>Returns</h4>
@@ -4040,8 +4061,8 @@ int ca_sg_delete ( CA_SYNC_GID GID );</pre>
</dl>
<h4>Examples</h4>
<pre>CA_SYNC_GID gid;
status = ca_sg_delete ( gid );
<pre>CA_SYNC_GID gid;
status = ca_sg_delete ( gid );
SEVCHK ( status, Sync group delete failed );</pre>
<h4>Returns</h4>
@@ -4152,7 +4173,7 @@ will not block unless additional subsequent requests are made.</p>
</dl>
<h4>Examples</h4>
<pre>CA_SYNC_GID gid;
<pre>CA_SYNC_GID gid;
status = ca_sg_reset(gid);</pre>
<h4>Returns</h4>
@@ -4165,7 +4186,7 @@ status = ca_sg_reset(gid);</pre>
<pre>#include &lt;cadef.h&gt;
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,
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
@@ -4306,7 +4327,7 @@ reissued.</p>
<h3><code><a name="ca_client_status">ca_client_status()</a></code></h3>
<pre>int ca_client_status ( unsigned level );
int ca_context_status ( struct ca_client_context *CONTEXT,
int ca_context_status ( struct ca_client_context *CONTEXT,
unsigned LEVEL );</pre>
<h4>Description</h4>
+62 -60
View File
@@ -4,7 +4,7 @@
* Copyright (c) 2002 The Regents of the University of California, as
* Operator of Los Alamos National Laboratory.
* EPICS BASE is distributed subject to a Software License Agreement found
* in file LICENSE that is included with this distribution.
* in file LICENSE that is included with this distribution.
\*************************************************************************/
/*
* Author Jeffrey O. Hill
@@ -26,33 +26,39 @@
#include "casAsyncIOI.h"
#include "casMonitor.h"
casPVI::casPVI ( casPV & intf ) :
pCAS ( NULL ), pPV ( & intf ), nMonAttached ( 0u ),
// Use casErrMessage instead of errMessage to show PV name
#define casErrMessage(S, PM) \
errPrintf(S, __FILE__, __LINE__, ", %s, %s", getName(), PM)
casPVI::casPVI ( casPV & intf ) :
pCAS ( NULL ), pPV ( & intf ), nMonAttached ( 0u ),
nIOAttached ( 0u ), deletePending ( false ) {}
casPVI::~casPVI ()
{
//
// all channels should have been destroyed
// (otherwise the server tool is yanking the
//
// all channels should have been destroyed
// (otherwise the server tool is yanking the
// PV out from under the server)
//
casVerify ( this->chanList.count() == 0u );
//
casVerify ( this->chanList.count() == 0u );
//
// all outstanding IO should have been deleted
// when we destroyed the channels
//
casVerify ( this->nIOAttached == 0u );
//
// all outstanding IO should have been deleted
// when we destroyed the channels
//
casVerify ( this->nIOAttached == 0u );
if ( this->nIOAttached ) {
errlogPrintf ( "The number of IO objected attached is %u\n", this->nIOAttached );
errlogPrintf ( "%u IO objects still attached in destructor\n",
this->nIOAttached );
}
//
// all monitors should have been deleted
// when we destroyed the channels
//
casVerify ( this->nMonAttached == 0u );
//
// all monitors should have been deleted
// when we destroyed the channels
//
casVerify ( this->nMonAttached == 0u );
{
epicsGuard < epicsMutex > guard ( this->mutex );
@@ -139,26 +145,26 @@ caStatus casPVI::attachToServer ( caServerI & cas )
caStatus casPVI::updateEnumStringTable ( casCtx & ctxIn )
{
epicsGuard < epicsMutex > guard ( this->mutex );
//
// create a gdd with the "enum string table" application type
//
// gddArray(int app, aitEnum prim, int dimen, ...);
gdd * pTmp = new gddScalar ( gddAppType_enums );
if ( pTmp == NULL ) {
errMessage ( S_cas_noMemory,
casErrMessage ( S_cas_noMemory,
"unable to create gdd for read of application type \"enums\" string"
" conversion table for enumerated PV" );
return S_cas_noMemory;
}
caStatus status = convertContainerMemberToAtomic ( *pTmp,
caStatus status = convertContainerMemberToAtomic ( *pTmp,
gddAppType_enums, MAX_ENUM_STATES );
if ( status != S_cas_success ) {
pTmp->unreference ();
errMessage ( status,
"unable to to config gdd for read of application type \"enums\" string"
" conversion table for enumerated PV");
casErrMessage ( status,
"unable to config gdd for read of application type \"enums\" string"
" conversion table for enumerated PV" );
return status;
}
@@ -169,12 +175,11 @@ caStatus casPVI::updateEnumStringTable ( casCtx & ctxIn )
if ( status == S_cas_success ) {
updateEnumStringTableAsyncCompletion ( *pTmp );
}
else if ( status != S_casApp_asyncCompletion &&
else if ( status != S_casApp_asyncCompletion &&
status != S_casApp_postponeAsyncIO ) {
errPrintf ( status, __FILE__, __LINE__,
"- unable to read application type \"enums\" "
" (string conversion table) from enumerated native type PV \"%s\"",
this->getName() );
casErrMessage ( status,
"unable to read application type \"enums\" "
" (string conversion table) from enumerated native type PV" );
}
pTmp->unreference ();
@@ -185,40 +190,39 @@ caStatus casPVI::updateEnumStringTable ( casCtx & ctxIn )
void casPVI::updateEnumStringTableAsyncCompletion ( const gdd & resp )
{
epicsGuard < epicsMutex > guard ( this->mutex );
if ( resp.isContainer() ) {
errMessage ( S_cas_badType,
"application type \"enums\" string conversion table for"
" enumerated PV was a container (expected vector of strings)" );
casErrMessage ( S_cas_badType,
"Invalid \"enums\" string conversion table for"
" enumerated PV (container instead of vector of strings)" );
return;
}
if ( resp.dimension() == 0 ) {
if ( resp.primitiveType() == aitEnumString ) {
aitString *pStr = (aitString *) resp.dataVoid ();
if ( ! this->enumStrTbl.setString ( 0, pStr->string() ) ) {
errMessage ( S_cas_noMemory,
casErrMessage ( S_cas_noMemory,
"no memory to set enumerated PV string cache" );
}
}
else if ( resp.primitiveType() == aitEnumFixedString ) {
aitFixedString *pStr = (aitFixedString *) resp.dataVoid ();
if ( ! this->enumStrTbl.setString ( 0, pStr->fixed_string ) ) {
errMessage ( S_cas_noMemory,
casErrMessage ( S_cas_noMemory,
"no memory to set enumerated PV string cache" );
}
}
else {
errPrintf ( S_cas_badType, __FILE__, __LINE__,
casErrMessage ( S_cas_badType,
"application type \"enums\" string conversion"
" table for enumerated PV \"%s\" isnt a string type?",
getName() );
" table for enumerated PV isnt a string type?" );
}
}
else if ( resp.dimension() == 1 ) {
gddStatus gdd_status;
aitIndex index, first, count;
gdd_status = resp.getBound ( 0, first, count );
assert ( gdd_status == 0 );
@@ -232,7 +236,7 @@ void casPVI::updateEnumStringTableAsyncCompletion ( const gdd & resp )
aitString *pStr = (aitString *) resp.dataVoid ();
for ( index = 0; index<count; index++ ) {
if ( ! this->enumStrTbl.setString ( index, pStr[index].string() ) ) {
errMessage ( S_cas_noMemory,
casErrMessage ( S_cas_noMemory,
"no memory to set enumerated PV string cache" );
}
}
@@ -241,19 +245,18 @@ void casPVI::updateEnumStringTableAsyncCompletion ( const gdd & resp )
aitFixedString *pStr = (aitFixedString *) resp.dataVoid ();
for ( index = 0; index < count; index++ ) {
if ( ! this->enumStrTbl.setString ( index, pStr[index].fixed_string ) ) {
errMessage ( S_cas_noMemory,
casErrMessage ( S_cas_noMemory,
"no memory to set enumerated PV string cache" );
}
}
}
else {
errMessage ( S_cas_badType,
"application type \"enums\" string conversion"
" table for enumerated PV isnt a string type?" );
casErrMessage( S_cas_badType,
"bad \"enums\" string conversion table for enumerated PV" );
}
}
else {
errMessage ( S_cas_badType,
casErrMessage ( S_cas_badType,
"application type \"enums\" string conversion table"
" for enumerated PV was multi-dimensional"
" (expected vector of strings)" );
@@ -287,7 +290,7 @@ void casPVI::postEvent ( const casEventMask & select, const gdd & event )
}
}
caStatus casPVI::installMonitor (
caStatus casPVI::installMonitor (
casMonitor & mon, tsDLList < casMonitor > & monitorList )
{
epicsGuard < epicsMutex > guard ( this->mutex );
@@ -303,14 +306,14 @@ caStatus casPVI::installMonitor (
}
}
casMonitor * casPVI::removeMonitor (
casMonitor * casPVI::removeMonitor (
tsDLList < casMonitor > & list, ca_uint32_t clientIdIn )
{
epicsGuard < epicsMutex > guard ( this->mutex );
casMonitor * pMon = 0;
//
// (it is reasonable to do a linear search here because
// sane clients will require only one or two monitors
// sane clients will require only one or two monitors
// per channel)
//
tsDLIter < casMonitor > iter = list.firstIter ();
@@ -359,9 +362,9 @@ void casPVI::installChannel ( chanIntfForPV & chan )
epicsGuard < epicsMutex > guard ( this->mutex );
this->chanList.add ( chan );
}
void casPVI::removeChannel (
chanIntfForPV & chan, tsDLList < casMonitor > & src,
void casPVI::removeChannel (
chanIntfForPV & chan, tsDLList < casMonitor > & src,
tsDLList < casMonitor > & dest )
{
epicsGuard < epicsMutex > guard ( this->mutex );
@@ -380,8 +383,8 @@ void casPVI::clearOutstandingReads ( tsDLList < casAsyncIOI > & ioList )
{
epicsGuard < epicsMutex > guard ( this->mutex );
// cancel any pending asynchronous IO
tsDLIter < casAsyncIOI > iterIO =
// cancel any pending asynchronous IO
tsDLIter < casAsyncIOI > iterIO =
ioList.firstIter ();
while ( iterIO.valid () ) {
tsDLIter < casAsyncIOI > tmp = iterIO;
@@ -407,7 +410,7 @@ void casPVI::destroyAllIO ( tsDLList < casAsyncIOI > & ioList )
}
}
void casPVI::installIO (
void casPVI::installIO (
tsDLList < casAsyncIOI > & ioList, casAsyncIOI & io )
{
epicsGuard < epicsMutex > guard ( this->mutex );
@@ -416,7 +419,7 @@ void casPVI::installIO (
this->nIOAttached++;
}
void casPVI::uninstallIO (
void casPVI::uninstallIO (
tsDLList < casAsyncIOI > & ioList, casAsyncIOI & io )
{
{
@@ -517,8 +520,8 @@ aitEnum casPVI::bestExternalType () const
}
}
// CA only does 1D arrays for now
aitIndex casPVI::nativeCount ()
// CA only does 1D arrays for now
aitIndex casPVI::nativeCount ()
{
epicsGuard < epicsMutex > guard ( this->mutex );
if ( this->pPV ) {
@@ -542,4 +545,3 @@ const char * casPVI::getName () const
return "<disconnected>";
}
}
+46 -39
View File
@@ -6,7 +6,7 @@
* Copyright (c) 2013 Helmholtz-Zentrum Berlin
* für Materialien und Energie GmbH.
* EPICS BASE is distributed subject to a Software License Agreement found
* in file LICENSE that is included with this distribution.
* in file LICENSE that is included with this distribution.
\*************************************************************************/
/* dbScan.c */
/* tasks and subroutines to scan the database */
@@ -109,8 +109,8 @@ static char *priorityName[NUM_CALLBACK_PRIORITIES] = {
typedef struct event_list {
CALLBACK callback[NUM_CALLBACK_PRIORITIES];
scan_list scan_list[NUM_CALLBACK_PRIORITIES];
struct event_list *next;
char event_name[MAX_STRING_SIZE];
struct event_list *next;
char eventname[1]; /* actually arbitrary size */
} event_list;
static event_list * volatile pevent_list[256];
static epicsMutexId event_lock;
@@ -247,11 +247,6 @@ void scanAdd(struct dbCommon *precord)
event_list *pel;
eventname = precord->evnt;
if (strlen(eventname) >= MAX_STRING_SIZE) {
recGblRecordError(S_db_badField, (void *)precord,
"scanAdd: too long EVNT value");
return;
}
prio = precord->prio;
if (prio < 0 || prio >= NUM_CALLBACK_PRIORITIES) {
recGblRecordError(-1, (void *)precord,
@@ -315,24 +310,17 @@ void scanDelete(struct dbCommon *precord)
recGblRecordError(-1, (void *)precord,
"scanDelete detected illegal SCAN value");
} else if (scan == menuScanEvent) {
char* eventname;
int prio;
event_list *pel;
scan_list *psl = 0;
eventname = precord->evnt;
prio = precord->prio;
if (prio < 0 || prio >= NUM_CALLBACK_PRIORITIES) {
recGblRecordError(-1, (void *)precord,
"scanDelete detected illegal PRIO field");
return;
}
do /* multithreading: make sure pel is consistent */
pel = pevent_list[0];
while (pel != pevent_list[0]);
for (; pel; pel=pel->next) {
if (strcmp(pel->event_name, eventname) == 0) break;
}
pel = eventNameToHandle(precord->evnt);
if (pel && (psl = &pel->scan_list[prio]))
deleteFromList(precord, psl);
} else if (scan == menuScanI_O_Intr) {
@@ -420,14 +408,12 @@ int scanpel(const char* eventname) /* print event list */
int prio;
event_list *pel;
do /* multithreading: make sure pel is consistent */
pel = pevent_list[0];
while (pel != pevent_list[0]);
for (; pel; pel = pel->next) {
if (!eventname || strcmp(pel->event_name, eventname) == 0) {
for (pel = pevent_list[0]; pel; pel = pel->next) {
if (!eventname || epicsStrGlobMatch(pel->eventname, eventname)) {
printf("Event \"%s\"\n", pel->eventname);
for (prio = 0; prio < NUM_CALLBACK_PRIORITIES; prio++) {
if (ellCount(&pel->scan_list[prio].list) == 0) continue;
sprintf(message, "Event \"%s\" Priority %s", pel->event_name, priorityName[prio]);
sprintf(message, " Priority %s", priorityName[prio]);
printList(&pel->scan_list[prio], message);
}
}
@@ -478,20 +464,52 @@ event_list *eventNameToHandle(const char *eventname)
int prio;
event_list *pel;
static epicsThreadOnceId onceId = EPICS_THREAD_ONCE_INIT;
double eventnumber = 0;
size_t namelength;
if (!eventname || eventname[0] == 0)
return NULL;
if (!eventname) return NULL;
while (isspace((int) eventname[0])) eventname++;
if (!eventname[0]) return NULL;
namelength = strlen(eventname);
while (isspace((int) eventname[namelength-1])) namelength--;
/* Backward compatibility with numeric events:
Treat any string that represents a double with an
integer part between 0 and 255 the same as the integer
because it is most probably a conversion from double
like from a calc record.
*/
if (epicsParseDouble(eventname, &eventnumber, NULL) == 0)
{
if (eventnumber >= 0 && eventnumber < 256)
{
if (eventnumber < 1)
return NULL; /* 0 is no event */
if ((pel = pevent_list[(int)eventnumber]) != NULL)
return pel;
}
else
eventnumber = 0; /* not a numeric event between 1 and 255 */
}
epicsThreadOnce(&onceId, eventOnce, NULL);
epicsMutexMustLock(event_lock);
for (pel = pevent_list[0]; pel; pel=pel->next) {
if (strcmp(pel->event_name, eventname) == 0) break;
if (strncmp(pel->eventname, eventname, namelength) == 0
&& pel->eventname[namelength] == 0)
break;
}
if (pel == NULL) {
pel = calloc(1, sizeof(event_list));
pel = calloc(1, sizeof(event_list) + namelength);
if (!pel)
goto done;
strcpy(pel->event_name, eventname);
if (eventnumber > 0) {
/* backward compatibility: make all numeric events look like integers */
sprintf(pel->eventname, "%i", (int)eventnumber);
pevent_list[(int)eventnumber] = pel;
}
else
strncpy(pel->eventname, eventname, namelength);
for (prio = 0; prio < NUM_CALLBACK_PRIORITIES; prio++) {
callbackSetUser(&pel->scan_list[prio], &pel->callback[prio]);
callbackSetPriority(prio, &pel->callback[prio]);
@@ -501,12 +519,6 @@ event_list *eventNameToHandle(const char *eventname)
}
pel->next=pevent_list[0];
pevent_list[0]=pel;
{ /* backward compatibility */
char* p;
long e = strtol(eventname, &p, 0);
if (*p == 0 && e > 0 && e <= 255)
pevent_list[e] = pel;
}
}
done:
epicsMutexUnlock(event_lock);
@@ -528,13 +540,8 @@ void postEvent(event_list *pel)
/* backward compatibility */
void post_event(int event)
{
event_list* pel;
if (event <= 0 || event > 255) return;
do { /* multithreading: make sure pel is consistent */
pel = pevent_list[event];
} while (pel != pevent_list[event]);
postEvent(pel);
postEvent(pevent_list[event]);
}
static void ioscanOnce(void *arg)
+1 -1
View File
@@ -51,7 +51,7 @@ epicsShareFunc void scanCleanup(void);
epicsShareFunc EVENTPVT eventNameToHandle(const char* event);
epicsShareFunc void postEvent(EVENTPVT epvt);
epicsShareFunc void post_event(int event) EPICS_DEPRECATED;
epicsShareFunc void post_event(int event);
epicsShareFunc void scanAdd(struct dbCommon *);
epicsShareFunc void scanDelete(struct dbCommon *);
epicsShareFunc double scanPeriod(int scan);
+45 -37
View File
@@ -1064,32 +1064,39 @@ static void dbRecordHead(char *recordType, char *name, int visible)
static void dbRecordField(char *name,char *value)
{
DBENTRY *pdbentry;
tempListNode *ptempListNode;
long status;
DBENTRY *pdbentry;
tempListNode *ptempListNode;
long status;
if(duplicate) return;
if (duplicate) return;
ptempListNode = (tempListNode *)ellFirst(&tempList);
pdbentry = ptempListNode->item;
status = dbFindField(pdbentry,name);
if(status) {
epicsPrintf("Record \"%s\" does not have a field \"%s\"\n",
dbGetRecordName(pdbentry), name);
yyerror(NULL);
return;
if (status) {
epicsPrintf("Record \"%s\" does not have a field \"%s\"\n",
dbGetRecordName(pdbentry), name);
yyerror(NULL);
return;
}
if (pdbentry->indfield == 0) {
epicsPrintf("Can't set \"NAME\" field of record \"%s\"\n",
dbGetRecordName(pdbentry));
yyerror(NULL);
return;
}
if (*value == '"') {
/* jsonSTRING values still have their quotes */
/* jsonSTRING values still have their quotes */
value++;
value[strlen(value) - 1] = 0;
value[strlen(value) - 1] = 0;
}
dbTranslateEscape(value, value); /* in-place; safe & legal */
status = dbPutString(pdbentry,value);
if(status) {
if (status) {
char msg[128];
errSymLookup(status, msg, sizeof(msg));
epicsPrintf("Can't set \"%s.%s\" to \"%s\" %s\n",
dbGetRecordName(pdbentry), name, value, msg);
dbGetRecordName(pdbentry), name, value, msg);
yyerror(NULL);
return;
}
@@ -1097,39 +1104,39 @@ static void dbRecordField(char *name,char *value)
static void dbRecordInfo(char *name, char *value)
{
DBENTRY *pdbentry;
tempListNode *ptempListNode;
long status;
DBENTRY *pdbentry;
tempListNode *ptempListNode;
long status;
if(duplicate) return;
if (duplicate) return;
ptempListNode = (tempListNode *)ellFirst(&tempList);
pdbentry = ptempListNode->item;
if (*value == '"') {
/* jsonSTRING values still have their quotes */
/* jsonSTRING values still have their quotes */
value++;
value[strlen(value) - 1] = 0;
value[strlen(value) - 1] = 0;
}
dbTranslateEscape(value, value); /* yuck: in-place, but safe */
status = dbPutInfo(pdbentry,name,value);
if(status) {
epicsPrintf("Can't set \"%s\" info \"%s\" to \"%s\"\n",
if (status) {
epicsPrintf("Can't set \"%s\" info \"%s\" to \"%s\"\n",
dbGetRecordName(pdbentry), name, value);
yyerror(NULL);
return;
yyerror(NULL);
return;
}
}
static void dbRecordAlias(char *name)
{
DBENTRY *pdbentry;
tempListNode *ptempListNode;
long status;
DBENTRY *pdbentry;
tempListNode *ptempListNode;
long status;
if(duplicate) return;
if (duplicate) return;
ptempListNode = (tempListNode *)ellFirst(&tempList);
pdbentry = ptempListNode->item;
status = dbCreateAlias(pdbentry, name);
if(status) {
if (status) {
epicsPrintf("Can't create alias \"%s\" for \"%s\"\n",
name, dbGetRecordName(pdbentry));
yyerror(NULL);
@@ -1139,15 +1146,16 @@ static void dbRecordAlias(char *name)
static void dbAlias(char *name, char *alias)
{
DBENTRY dbEntry;
DBENTRY *pdbEntry = &dbEntry;
DBENTRY dbEntry;
DBENTRY *pdbEntry = &dbEntry;
dbInitEntry(pdbbase, pdbEntry);
if (dbFindRecord(pdbEntry, name)) {
epicsPrintf("Alias \"%s\" refers to unknown record \"%s\"\n",
alias, name);
yyerror(NULL);
} else if (dbCreateAlias(pdbEntry, alias)) {
}
else if (dbCreateAlias(pdbEntry, alias)) {
epicsPrintf("Can't create alias \"%s\" referring to \"%s\"\n",
alias, name);
yyerror(NULL);
@@ -1157,14 +1165,14 @@ static void dbAlias(char *name, char *alias)
static void dbRecordBody(void)
{
DBENTRY *pdbentry;
DBENTRY *pdbentry;
if(duplicate) {
duplicate = FALSE;
return;
if (duplicate) {
duplicate = FALSE;
return;
}
pdbentry = (DBENTRY *)popFirstTemp();
if(ellCount(&tempList))
yyerrorAbort("dbRecordBody: tempList not empty");
if (ellCount(&tempList))
yyerrorAbort("dbRecordBody: tempList not empty");
dbFreeEntry(pdbentry);
}
+2 -2
View File
@@ -146,9 +146,9 @@ Note: Negative index numbers address from the end of the array, with C<-1> being
=item Square bracket notation C<[start:increment:end]> (shorthand)
The common square bracket notation with can be used in place of JSON.
The common square bracket notation which can be used in place of JSON.
Any parameter may be omitted (keeping the colons) to use the default value.
If only one colon is included, this means C<[start:end]> with a increment of 1.
If only one colon is included, this means C<[start:end]> with an increment of 1.
If only a single parameter is used C<[index]> the filter returns one element.
=item Start index C<"s">
+11
View File
@@ -96,6 +96,17 @@ testHarness_SRCS += analogMonitorTest_registerRecordDeviceDriver.cpp
TESTFILES += $(COMMON_DIR)/analogMonitorTest.dbd ../analogMonitorTest.db
TESTS += analogMonitorTest
TARGETS += $(COMMON_DIR)/scanEventTest.dbd
DBDDEPENDS_FILES += scanEventTest.dbd$(DEP)
scanEventTest_DBD += base.dbd
TESTPROD_HOST += scanEventTest
scanEventTest_SRCS += scanEventTest.c
scanEventTest_SRCS += scanEventTest_registerRecordDeviceDriver.cpp
testHarness_SRCS += scanEventTest.c
testHarness_SRCS += scanEventTest_registerRecordDeviceDriver.cpp
TESTFILES += $(COMMON_DIR)/scanEventTest.dbd ../scanEventTest.db
TESTS += scanEventTest
TARGETS += $(COMMON_DIR)/regressTest.dbd
DBDDEPENDS_FILES += regressTest.dbd$(DEP)
regressTest_DBD += base.dbd
+3
View File
@@ -20,6 +20,7 @@ int asTest(void);
int linkRetargetLinkTest(void);
int linkInitTest(void);
int asyncSoftTest(void);
int scanEventTest(void);
void epicsRunRecordTests(void)
{
@@ -41,5 +42,7 @@ void epicsRunRecordTests(void)
runTest(asyncSoftTest);
runTest(scanEventTest);
epicsExit(0); /* Trigger test harness */
}
+142
View File
@@ -0,0 +1,142 @@
/*************************************************************************\
* Copyright (c) 2018 Paul Scherrer Institut
* EPICS BASE is distributed subject to a Software License Agreement found
* in file LICENSE that is included with this distribution.
\*************************************************************************/
/*
* Author: Dirk Zimoch <dirk.zimoch@psi.ch>
*/
#include <string.h>
#include "dbStaticLib.h"
#include "dbAccessDefs.h"
#include "dbUnitTest.h"
#include "testMain.h"
#include "osiFileName.h"
#include "epicsThread.h"
#include "dbScan.h"
void scanEventTest_registerRecordDeviceDriver(struct dbBase *);
/* test name to event number:
num = 0 is no event,
num > 0 is numeric event
num < 0 is string event (use same unique number for aliases)
*/
const struct {char* name; int num;} events[] = {
/* No events */
{NULL, 0},
{"", 0},
{" ", 0},
{"0", 0},
{"0.000000", 0},
{"-0.00000", 0},
{"0.9", 0},
/* Numeric events */
{"2", 2},
{"2.000000", 2},
{"2.5", 2},
{" 2.5 ", 2},
{"+0x02", 2},
{"3", 3},
/* Named events */
{"info 1", -1},
{" info 1 ", -1},
{"-0.9", -2},
{"-2", -3},
{"-2.000000", -4},
{"-2.5", -5},
{" -2.5 ", -5},
{"nan", -6},
{"NaN", -7},
{"-NAN", -8},
{"-inf", -9},
{"inf", -10},
};
MAIN(scanEventTest)
{
int i, e;
int aliases[512] ;
int expected_count[512];
#define INDX(i) 256-events[i].num
#define MAXEV 5
testPlan(NELEMENTS(events)*2+(MAXEV+1)*5);
memset(aliases, 0, sizeof(aliases));
memset(expected_count, 0, sizeof(expected_count));
if (dbReadDatabase(&pdbbase, "scanEventTest.dbd",
"." OSI_PATH_LIST_SEPARATOR ".." OSI_PATH_LIST_SEPARATOR
"../O.Common" OSI_PATH_LIST_SEPARATOR "O.Common", NULL))
testAbort("Database description 'scanEventTest.dbd' not found");
scanEventTest_registerRecordDeviceDriver(pdbbase);
for (i = 0; i < NELEMENTS(events); i++) {
char substitutions[256];
sprintf(substitutions, "N=%d,EVENT=%s", i, events[i].name);
if (dbReadDatabase(&pdbbase, "scanEventTest.db",
"." OSI_PATH_LIST_SEPARATOR "..", substitutions))
testAbort("Error reading test database 'scanEventTest.db'");
}
testIocInitOk();
testDiag("Test if eventNameToHandle() strips spaces and handles numeric events");
for (i = 0; i < NELEMENTS(events); i++) {
EVENTPVT pev = eventNameToHandle(events[i].name);
/* test that some names are not events (num=0) */
if (events[i].num == 0)
testOk(pev == NULL, "\"%s\" -> no event", events[i].name);
else
{
expected_count[INDX(i)]++; /* +1 for postEvent */
if (events[i].num > 0)
{
testOk(pev != NULL, "\"%s\" -> numeric event %d", events[i].name, events[i].num);
expected_count[INDX(i)]++; /* +1 for post_event */
}
else
{
/* test that some strings resolve the same event (num!=0) */
if (!aliases[INDX(i)])
{
aliases[INDX(i)] = i;
testOk(pev != NULL, "\"%s\" -> new named event", events[i].name);
}
else
{
testOk(pev == eventNameToHandle(events[aliases[INDX(i)]].name),
"\"%s\" alias for \"%s\"", events[i].name, events[aliases[INDX(i)]].name);
}
}
}
post_event(events[i].num); /* triggers numeric events only */
postEvent(pev);
}
testDiag("Check calculated numeric events (backward compatibility)");
for (e = 0; e <= MAXEV; e++) {
testdbPutFieldOk("eventnum", DBR_LONG, e);
testdbGetFieldEqual("e1", DBR_LONG, e);
testdbGetFieldEqual("e2", DBR_LONG, e);
testdbPutFieldOk("e3", DBR_LONG, e);
testdbPutFieldOk("e3.PROC", DBR_LONG, 1);
for (i = 0; i < NELEMENTS(events); i++)
if (e > 0 && e < 256 && events[i].num == e) { /* numeric events */
expected_count[INDX(i)]+=3; /* +1 for eventnum->e1, +1 for e2<-eventnum, +1 for e3 */
break;
}
}
/* Allow records to finish processing */
epicsThreadSleep(0.1);
testDiag("Check if events have been processed the expected number of times");
for (i = 0; i < NELEMENTS(events); i++) {
char pvname[100];
sprintf(pvname, "c%d", i);
testDiag("Event \"%s\" expected %d times", events[i].name, expected_count[INDX(i)]);
testdbGetFieldEqual(pvname, DBR_LONG, expected_count[INDX(i)]);
}
return testDone();
}
+16
View File
@@ -0,0 +1,16 @@
record(calc, "c$(N)") {
field(SCAN, "Event")
field(EVNT, "$(EVENT)")
field(CALC, "VAL+1")
}
record(dfanout, "eventnum") {
field(OUTA, "e1 PP")
field(FLNK, "e2")
}
record(event, "e1") {
}
record(event, "e2") {
field(INP, "eventnum")
}
record(event, "e3") {
}