Merge changes from 3.16 branch into 7.0
This commit is contained in:
@@ -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
|
||||
|
||||
|
||||
|
||||
+13
-15
@@ -67,8 +67,9 @@
|
||||
Software requirements
|
||||
|
||||
GNU make
|
||||
You must use GNU make, gnumake, for any EPICS builds. Set your path so
|
||||
that a gnumake version 3.81 or later is available.
|
||||
You must use the GNU version of make for EPICS builds, and we now
|
||||
recommend version 4.1 or later (version 3.82 may work on Linux, but
|
||||
doesn't on Windows).
|
||||
|
||||
Perl
|
||||
You must have Perl version 5.8.1 or later installed. The EPICS
|
||||
@@ -99,20 +100,17 @@
|
||||
|
||||
RTEMS
|
||||
For RTEMS targets, you need RTEMS core and toolset version 4.9.2 or
|
||||
later.
|
||||
4.10. The newer 4.11 or 5.x releases are not supported yet.
|
||||
|
||||
GNU readline or Tecla library
|
||||
GNU readline and Tecla libraries can be used by the IOC shell to provide
|
||||
command line editing and command line history recall and edit. GNU
|
||||
readline (or Tecla library) must be installed on your target system when
|
||||
COMMANDLINE_LIBRARY is set to READLINE (or TECLA) for that target. EPICS
|
||||
(EPICS shell) is the default specified in CONFIG_COMMON. A READLINE
|
||||
override is defined for linux-x86 in the EPICS distribution. Comment out
|
||||
COMMANDLINE_LIBRARY=READLINE in
|
||||
configure/os/CONFIG_SITE.Common.linux-x86 if readline is not installed
|
||||
on linux-x86. Command-line editing and history will then be those
|
||||
supplied by the os. On vxWorks the ledLib command-line input library is
|
||||
used instead.
|
||||
Command-line editing libraries
|
||||
GNU readline or other OS-specific libraries can be used by the IOC shell
|
||||
to provide command line editing and history recall. The default setting
|
||||
is different for each OS. On Linux the default is to use READLINE since
|
||||
most distributions include it. On MacOS the default is also READLINE
|
||||
since Apple provides a compatible library, although it isn't GNU. On
|
||||
RTEMS we support GNU readline and Tecla, although the default is to use
|
||||
neither since these have to be added to the RTEMS installation
|
||||
separately. On vxWorks we support the built-in ledLib library.
|
||||
|
||||
Host system storage requirements
|
||||
|
||||
|
||||
+15
-15
@@ -72,8 +72,8 @@
|
||||
<H3><A NAME="0_0_6"> Software requirements</A></H3>
|
||||
|
||||
<BLOCKQUOTE><B>GNU make</B><BR>
|
||||
You must use GNU make, gnumake, for any EPICS builds. Set your path
|
||||
so that a gnumake version 3.81 or later is available.
|
||||
You must use the GNU version of make for EPICS builds, and we now recommend
|
||||
version 4.1 or later (version 3.82 may work on Linux, but doesn't on Windows).
|
||||
|
||||
<P><B>Perl</B><BR>
|
||||
You must have Perl version 5.8.1 or later installed. The EPICS configuration
|
||||
@@ -103,20 +103,20 @@
|
||||
about configuring your vxWorks operating system for use with EPICS.</P>
|
||||
|
||||
<P><B>RTEMS</B><BR>
|
||||
For RTEMS targets, you need RTEMS core and toolset version 4.9.2 or later.</P>
|
||||
For RTEMS targets, you need RTEMS core and toolset version 4.9.2 or 4.10. The
|
||||
newer 4.11 or 5.x releases are not supported yet.</P>
|
||||
|
||||
<P><B>Command-line editing libraries</B><BR>
|
||||
|
||||
GNU readline or other OS-specific libraries can be used by the IOC shell to
|
||||
provide command line editing and history recall. The default setting is
|
||||
different for each OS. On Linux the default is to use READLINE since most
|
||||
distributions include it. On MacOS the default is also READLINE since Apple
|
||||
provides a compatible library, although it isn't GNU. On RTEMS we support GNU
|
||||
readline and Tecla, although the default is to use neither since these have to
|
||||
be added to the RTEMS installation separately. On vxWorks we support the
|
||||
built-in ledLib library.</P>
|
||||
|
||||
<P><B>GNU readline or Tecla library</B><BR>
|
||||
GNU readline and Tecla libraries can be used by the IOC shell to
|
||||
provide command line editing and command line history recall and edit.
|
||||
GNU readline (or Tecla library) must be installed on your target system
|
||||
when COMMANDLINE_LIBRARY is set to READLINE (or TECLA) for that target.
|
||||
EPICS (EPICS shell) is the default specified in CONFIG_COMMON. A
|
||||
READLINE override is defined for linux-x86 in the EPICS distribution.
|
||||
Comment out COMMANDLINE_LIBRARY=READLINE in
|
||||
configure/os/CONFIG_SITE.Common.linux-x86 if readline is not installed
|
||||
on linux-x86. Command-line editing and history will then be those
|
||||
supplied by the os. On vxWorks the ledLib command-line input library is
|
||||
used instead.</P>
|
||||
</BLOCKQUOTE>
|
||||
|
||||
<H3><A NAME="0_0_7"> Host system storage requirements</A></H3>
|
||||
|
||||
@@ -10,6 +10,8 @@
|
||||
|
||||
<h1 align="center">EPICS Release 7.0.2.x</h1>
|
||||
|
||||
<h2 align="center">Changes made between 3.16.1 and 3.16.2</h2>
|
||||
|
||||
<!-- Insert new items immediately below this template ...
|
||||
|
||||
<h3>Title...</h3>
|
||||
@@ -86,6 +88,16 @@ be happy to try and answer them!</p>
|
||||
|
||||
<!-- Insert inherited items immediately below here ... -->
|
||||
|
||||
<h3>Record types mbbiDirect and mbboDirect extended to 32 bit</h3>
|
||||
|
||||
<p>The VAL fields of mbbiDirect and mbboDirect records have
|
||||
been extended from <tt>DBF_USHORT</tt> (16 bit) to <tt>DBF_LONG</tt> (32 bit).
|
||||
New bit fields <tt>B10</tt>...<tt>B1F</tt> have been added.</p>
|
||||
|
||||
<p>Device support which accesses the bit fields can test if the macro
|
||||
<tt>mbbiDirectRecord1BF</tt> or <tt>mbboDirectRecord1BF</tt> is
|
||||
defined. Device support which only accesses RVAL needs no modification.</p>
|
||||
|
||||
<h3>Restore use of ledlib for VxWorks command editing</h3>
|
||||
|
||||
<p>The epicsReadline refactoring work described below unfortunately disabled the
|
||||
@@ -176,6 +188,27 @@ preprocessor macro):</p>
|
||||
#endif
|
||||
</pre></blockquote>
|
||||
|
||||
<p>If the code uses the old db_access.h types (probably because it's calling
|
||||
Channel Access APIs) then it will have to test against the EPICS version number
|
||||
instead, like this:</p>
|
||||
|
||||
<blockquote><pre>
|
||||
#include <epicsVersion.h>
|
||||
|
||||
#ifndef VERSION_INT
|
||||
# define VERSION_INT(V,R,M,P) ( ((V)<<24) | ((R)<<16) | ((M)<<8) | (P))
|
||||
#endif
|
||||
#ifndef EPICS_VERSION_INT
|
||||
# define EPICS_VERSION_INT VERSION_INT(EPICS_VERSION, EPICS_REVISION, EPICS_MODIFICATION, EPICS_PATCH_LEVEL)
|
||||
#endif
|
||||
|
||||
#if EPICS_VERSION_INT >= VERSION_INT(3,16,1,0)
|
||||
/* Code where Base has INT64 support */
|
||||
#else
|
||||
/* Code for older versions */
|
||||
#endif
|
||||
</pre></blockquote>
|
||||
|
||||
<p>Channel Access does not (and probably never will) directly support 64-bit
|
||||
integer types, so the new field types are presented to the CA server as
|
||||
<tt>DBF_DOUBLE</tt> values. This means that field values larger than 2^52
|
||||
@@ -343,15 +376,15 @@ to implement the link APIs, so will work properly after these conversions:</p>
|
||||
link type, i.e. change this code:
|
||||
|
||||
<pre>
|
||||
if (prec->siml.type == CONSTANT) {
|
||||
recGblInitConstantLink(&prec->siml, DBF_USHORT, &prec->simm);
|
||||
if (prec->siml.type == CONSTANT) {
|
||||
recGblInitConstantLink(&prec->siml, DBF_USHORT, &prec->simm);
|
||||
}
|
||||
</pre>
|
||||
|
||||
into this:
|
||||
|
||||
<pre>
|
||||
recGblInitConstantLink(&prec->siml, DBF_USHORT, &prec->simm);
|
||||
recGblInitConstantLink(&prec->siml, DBF_USHORT, &prec->simm);
|
||||
</pre>
|
||||
|
||||
Note that <tt>recGblInitConstantLink()</tt> still returns TRUE if the field was
|
||||
@@ -364,20 +397,20 @@ or undefined links, FALSE for links whose <tt>dbGetLink()</tt> routine may
|
||||
return different values on different calls. For example this:
|
||||
|
||||
<pre>
|
||||
if (prec->dol.type != CONSTANT)
|
||||
if (prec->dol.type != CONSTANT)
|
||||
</pre>
|
||||
|
||||
should become this:
|
||||
|
||||
<pre>
|
||||
if (!dbLinkIsConstant(&prec->dol))
|
||||
if (!dbLinkIsConstant(&prec->dol))
|
||||
</pre>
|
||||
|
||||
When the converted software is also required to build against older versions of
|
||||
Base, this macro definition may be useful:
|
||||
|
||||
<pre>
|
||||
#define dbLinkIsConstant(lnk) ((lnk)->type == CONSTANT)
|
||||
#define dbLinkIsConstant(lnk) ((lnk)->type == CONSTANT)
|
||||
</pre>
|
||||
</li>
|
||||
|
||||
@@ -385,7 +418,7 @@ Base, this macro definition may be useful:
|
||||
link has been resolved as a CA link using code such as
|
||||
|
||||
<pre>
|
||||
if (prec->inp.type == CA_LINK)
|
||||
if (prec->inp.type == CA_LINK)
|
||||
</pre>
|
||||
|
||||
will still compile and run, but will only work properly with the old CA link
|
||||
@@ -395,7 +428,7 @@ examine or modify data inside the link. After conversion the above line would
|
||||
probably become:
|
||||
|
||||
<pre>
|
||||
if (dbLinkIsVolatile(&prec->inp))
|
||||
if (dbLinkIsVolatile(&prec->inp))
|
||||
</pre>
|
||||
|
||||
A volatile link is one like a Channel Access link which may disconnect and
|
||||
@@ -405,7 +438,7 @@ same state they started in. For compatibility when building against older
|
||||
versions of Base, this macro definition may be useful:
|
||||
|
||||
<pre>
|
||||
#define dbLinkIsVolatile(lnk) ((lnk)->type == CA_LINK)
|
||||
#define dbLinkIsVolatile(lnk) ((lnk)->type == CA_LINK)
|
||||
</pre>
|
||||
</li>
|
||||
|
||||
@@ -710,6 +743,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
|
||||
@@ -728,6 +769,43 @@ tells git to ignore all configure/*.local files.</p>
|
||||
|
||||
<!-- Insert inherited items immediately below here ... -->
|
||||
|
||||
<h3>Fixes for Launchpad bugs</h3>
|
||||
|
||||
<p>The following launchpad bugs have fixes included:</p>
|
||||
|
||||
<ul>
|
||||
<li><a href="https://bugs.launchpad.net/epics-base/+bug/1747091">
|
||||
lp: #1747091</a>, epicsTimeGetEvent() / generalTime bug</li>
|
||||
<li><a href="https://bugs.launchpad.net/epics-base/+bug/1743076">
|
||||
lp: #1743076</a>, Segfault in ca_attach_context() during exits</li>
|
||||
<li><a href="https://bugs.launchpad.net/epics-base/+bug/1751380">
|
||||
lp: #1751380</a>, Deadlock in ca_clear_subscription()</li>
|
||||
<li><a href="https://bugs.launchpad.net/epics-base/+bug/1597809">
|
||||
lp: #1597809</a>, Setting NAME field in DB file may break IOC</li>
|
||||
<li><a href="https://bugs.launchpad.net/epics-base/+bug/1770292">
|
||||
lp: #1770292</a>, get_alarm_double() inconsistent across record types</li>
|
||||
<li><a href="https://bugs.launchpad.net/epics-base/+bug/1771298">
|
||||
lp: #1771298</a>, Conversion of NaN to integer relies on undefined
|
||||
behavior</li>
|
||||
</ul>
|
||||
|
||||
<h3>Updated VxWorks Timezone settings</h3>
|
||||
|
||||
<p>Removed the settings for 2017; fixed the hour of the change for MET.</p>
|
||||
|
||||
<h3>Fixed camonitor server side relative timestamps bug</h3>
|
||||
|
||||
<p>Initialize the first time-stamp from the first monitor, not the client-side
|
||||
current time in this configuration.</p>
|
||||
|
||||
<h3>Build changes for MSVC</h3>
|
||||
|
||||
<p>Windows builds using Visual Studio 2015 and later now use the <tt>-FS</tt>
|
||||
compiler option to allow parallel builds to work properly.</p>
|
||||
|
||||
<p>We now give the <tt>-FC</tt> option to tell the compiler to print absolute
|
||||
paths for source files in diagnostic messages.</p>
|
||||
|
||||
<h3>Extend maximum Posix epicsEventWaitWithTimeout() delay</h3>
|
||||
|
||||
<p>The Posix implementation of epicsEventWaitWithTimeout() was limiting the
|
||||
|
||||
@@ -1015,7 +1015,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 <PV name> [progress logging level] [channel duplication count]
|
||||
<pre>acctst <PV name> [progress logging level] [channel duplication count]
|
||||
[test repetition count] [enable preemptive callback]</pre>
|
||||
|
||||
<h4>Description</h4>
|
||||
@@ -2085,7 +2085,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
|
||||
@@ -2109,16 +2109,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 & CA_M_SUCCESS ) {
|
||||
printf ( "The requested ca_XXXX() operation didn't complete successfully");
|
||||
}
|
||||
if ( status & 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>
|
||||
@@ -2291,7 +2291,7 @@ int main ( int argc, char ** argv )
|
||||
unsigned nBytes;
|
||||
unsigned elementCount;
|
||||
char timeString[32];
|
||||
unsigned i;
|
||||
unsigned i;
|
||||
chid chan;
|
||||
double sum;
|
||||
int status;
|
||||
@@ -2334,7 +2334,7 @@ int main ( int argc, char ** argv )
|
||||
epicsTimeToStrftime ( timeString, sizeof ( timeString ),
|
||||
"%a %b %d %Y %H:%M:%S.%f", & pTD->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 );
|
||||
@@ -2365,7 +2365,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 */
|
||||
@@ -2388,7 +2388,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
|
||||
@@ -2680,6 +2680,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>
|
||||
@@ -2825,6 +2833,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>
|
||||
@@ -2839,16 +2853,16 @@ subscriptions (monitors) registered with the channel.</p>
|
||||
|
||||
<h3><code><a name="ca_put">ca_put()</a></code></h3>
|
||||
<pre>#include <cadef.h>
|
||||
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>
|
||||
@@ -3075,7 +3089,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>
|
||||
@@ -3159,7 +3173,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>
|
||||
@@ -3206,6 +3220,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>
|
||||
@@ -3370,7 +3391,7 @@ becomes full.</p>
|
||||
|
||||
<h3><code><a name="ca_signal">ca_signal()</a></code></h3>
|
||||
<pre>#include <cadef.h>
|
||||
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>
|
||||
@@ -3387,7 +3408,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
|
||||
@@ -3411,7 +3432,7 @@ this purpose.</p>
|
||||
|
||||
<h3><code><a
|
||||
name="ca_add_exception_event">ca_add_exception_event()</a></code></h3>
|
||||
<pre>#include <cadef.h>
|
||||
<pre>#include <cadef.h>
|
||||
typedef void (*pCallback) ( struct exception_handler_args HANDLERARGS );
|
||||
int ca_add_exception_event ( pCallback USERFUNC, void *USERARG );</pre>
|
||||
|
||||
@@ -3620,7 +3641,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;
|
||||
@@ -3960,8 +3981,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>
|
||||
@@ -3995,8 +4016,8 @@ outstanding within them at any given time.</p>
|
||||
</dl>
|
||||
|
||||
<h4>Examples</h4>
|
||||
<pre>CA_SYNC_GID gid;
|
||||
status = ca_sg_create ( &gid );
|
||||
<pre>CA_SYNC_GID gid;
|
||||
status = ca_sg_create ( &gid );
|
||||
SEVCHK ( status, Sync group create failed );</pre>
|
||||
|
||||
<h4>Returns</h4>
|
||||
@@ -4034,8 +4055,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>
|
||||
@@ -4146,7 +4167,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>
|
||||
@@ -4159,7 +4180,7 @@ status = ca_sg_reset(gid);</pre>
|
||||
<pre>#include <cadef.h>
|
||||
int ca_sg_put ( CA_SYNC_GID GID, chtype TYPE,
|
||||
chid CHID, void *PVALUE );
|
||||
int ca_sg_array_put ( CA_SYNC_GID GID, chtype TYPE,
|
||||
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
|
||||
@@ -4300,7 +4321,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>
|
||||
|
||||
@@ -45,6 +45,7 @@
|
||||
#include "epicsExport.h"
|
||||
#include "link.h"
|
||||
#include "recSup.h"
|
||||
#include "dbUnitTest.h" /* for testSyncCallback() */
|
||||
|
||||
|
||||
static int callbackQueueSize = 2000;
|
||||
@@ -352,3 +353,86 @@ void callbackRequestProcessCallbackDelayed(CALLBACK *pcallback,
|
||||
callbackSetProcess(pcallback, Priority, pRec);
|
||||
callbackRequestDelayed(pcallback, seconds);
|
||||
}
|
||||
|
||||
/* Sync. process of testSyncCallback()
|
||||
*
|
||||
* 1. For each priority, make a call to callbackRequest() for each worker.
|
||||
* 2. Wait until all callbacks are concurrently being executed
|
||||
* 3. Last worker to begin executing signals success and begins waking up other workers
|
||||
* 4. Last worker to wake signals testSyncCallback() to complete
|
||||
*/
|
||||
typedef struct {
|
||||
epicsEventId wait_phase2, wait_phase4;
|
||||
int nphase2, nphase3;
|
||||
epicsCallback cb;
|
||||
} sync_helper;
|
||||
|
||||
static void sync_callback(epicsCallback *cb)
|
||||
{
|
||||
sync_helper *helper;
|
||||
callbackGetUser(helper, cb);
|
||||
|
||||
testGlobalLock();
|
||||
|
||||
assert(helper->nphase2 > 0);
|
||||
if(--helper->nphase2!=0) {
|
||||
/* we are _not_ the last to start. */
|
||||
testGlobalUnlock();
|
||||
epicsEventMustWait(helper->wait_phase2);
|
||||
testGlobalLock();
|
||||
}
|
||||
|
||||
/* we are either the last to start, or have been
|
||||
* woken by the same and must pass the wakeup along
|
||||
*/
|
||||
epicsEventMustTrigger(helper->wait_phase2);
|
||||
|
||||
assert(helper->nphase2 == 0);
|
||||
assert(helper->nphase3 > 0);
|
||||
|
||||
if(--helper->nphase3==0) {
|
||||
/* we are the last to wake up. wake up testSyncCallback() */
|
||||
epicsEventMustTrigger(helper->wait_phase4);
|
||||
}
|
||||
|
||||
testGlobalUnlock();
|
||||
}
|
||||
|
||||
void testSyncCallback(void)
|
||||
{
|
||||
sync_helper helper[NUM_CALLBACK_PRIORITIES];
|
||||
unsigned i;
|
||||
|
||||
testDiag("Begin testSyncCallback()");
|
||||
|
||||
for(i=0; i<NUM_CALLBACK_PRIORITIES; i++) {
|
||||
helper[i].wait_phase2 = epicsEventMustCreate(epicsEventEmpty);
|
||||
helper[i].wait_phase4 = epicsEventMustCreate(epicsEventEmpty);
|
||||
|
||||
/* no real need to lock here, but do so anyway so that valgrind can establish
|
||||
* the locking requirements for sync_helper.
|
||||
*/
|
||||
testGlobalLock();
|
||||
helper[i].nphase2 = helper[i].nphase3 = callbackQueue[i].threadsRunning;
|
||||
testGlobalUnlock();
|
||||
|
||||
callbackSetUser(&helper[i], &helper[i].cb);
|
||||
callbackSetPriority(i, &helper[i].cb);
|
||||
callbackSetCallback(sync_callback, &helper[i].cb);
|
||||
|
||||
callbackRequest(&helper[i].cb);
|
||||
}
|
||||
|
||||
for(i=0; i<NUM_CALLBACK_PRIORITIES; i++) {
|
||||
epicsEventMustWait(helper[i].wait_phase4);
|
||||
}
|
||||
|
||||
for(i=0; i<NUM_CALLBACK_PRIORITIES; i++) {
|
||||
testGlobalLock();
|
||||
epicsEventDestroy(helper[i].wait_phase2);
|
||||
epicsEventDestroy(helper[i].wait_phase4);
|
||||
testGlobalUnlock();
|
||||
}
|
||||
|
||||
testDiag("Complete testSyncCallback()");
|
||||
}
|
||||
|
||||
@@ -42,7 +42,9 @@ typedef struct callbackPvt {
|
||||
int priority;
|
||||
void *user; /*for use by callback user*/
|
||||
void *timer; /*for use by callback itself*/
|
||||
}CALLBACK;
|
||||
}epicsCallback;
|
||||
|
||||
typedef epicsCallback CALLBACK;
|
||||
|
||||
typedef void (*CALLBACKFUNC)(struct callbackPvt*);
|
||||
|
||||
|
||||
@@ -293,10 +293,14 @@ static void get_alarm(DBADDR *paddr, char **ppbuffer,
|
||||
if (*options & DBR_AL_LONG) {
|
||||
struct dbr_alLong *pal = (struct dbr_alLong*) pbuffer;
|
||||
|
||||
pal->upper_alarm_limit = (epicsInt32) ald.upper_alarm_limit;
|
||||
pal->upper_warning_limit = (epicsInt32) ald.upper_warning_limit;
|
||||
pal->lower_warning_limit = (epicsInt32) ald.lower_warning_limit;
|
||||
pal->lower_alarm_limit = (epicsInt32) ald.lower_alarm_limit;
|
||||
pal->upper_alarm_limit = finite(ald.upper_alarm_limit) ?
|
||||
(epicsInt32) ald.upper_alarm_limit : 0;
|
||||
pal->upper_warning_limit = finite(ald.upper_warning_limit) ?
|
||||
(epicsInt32) ald.upper_warning_limit : 0;
|
||||
pal->lower_warning_limit = finite(ald.lower_warning_limit) ?
|
||||
(epicsInt32) ald.lower_warning_limit : 0;
|
||||
pal->lower_alarm_limit = finite(ald.lower_alarm_limit) ?
|
||||
(epicsInt32) ald.lower_alarm_limit : 0;
|
||||
|
||||
if (no_data)
|
||||
*options ^= DBR_AL_LONG; /*Turn off option*/
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
#include "osiUnistd.h"
|
||||
#include "registry.h"
|
||||
#include "epicsEvent.h"
|
||||
#include "epicsThread.h"
|
||||
|
||||
#define epicsExportSharedSymbols
|
||||
#include "dbAccess.h"
|
||||
@@ -415,3 +416,26 @@ unsigned testMonitorCount(testMonitor *mon, unsigned reset)
|
||||
return count;
|
||||
}
|
||||
|
||||
static
|
||||
epicsMutexId test_global;
|
||||
|
||||
static
|
||||
epicsThreadOnceId test_global_once = EPICS_THREAD_ONCE_INIT;
|
||||
|
||||
static
|
||||
void test_global_init(void* ignored)
|
||||
{
|
||||
test_global = epicsMutexMustCreate();
|
||||
}
|
||||
|
||||
void testGlobalLock(void)
|
||||
{
|
||||
epicsThreadOnce(&test_global_once, &test_global_init, NULL);
|
||||
epicsMutexMustLock(test_global);
|
||||
}
|
||||
|
||||
void testGlobalUnlock(void)
|
||||
{
|
||||
epicsMutexUnlock(test_global);
|
||||
}
|
||||
|
||||
|
||||
@@ -98,6 +98,65 @@ epicsShareFunc void testMonitorWait(testMonitor*);
|
||||
*/
|
||||
epicsShareFunc unsigned testMonitorCount(testMonitor*, unsigned reset);
|
||||
|
||||
/** Synchronize the shared callback queues.
|
||||
*
|
||||
* Block until all callback queue jobs which were queued, or running,
|
||||
* have completed.
|
||||
*/
|
||||
epicsShareFunc void testSyncCallback(void);
|
||||
|
||||
/** Global mutex for use by test code.
|
||||
*
|
||||
* This utility mutex is intended to be used to avoid races in situations
|
||||
* where some other syncronization primitive is being destroyed (epicsEvent,
|
||||
* epicsMutex, ...).
|
||||
*
|
||||
* For example. The following has a subtle race where the event may be
|
||||
* destroyed (free()'d) before the call to epicsEventMustSignal() has
|
||||
* returned. On some targets this leads to a use after free() error.
|
||||
*
|
||||
@code
|
||||
epicsEventId evt;
|
||||
void thread1() {
|
||||
evt = epicsEventMustCreate(...);
|
||||
// spawn thread2()
|
||||
epicsEventMustWait(evt);
|
||||
epicsEventDestroy(evt);
|
||||
}
|
||||
// ...
|
||||
void thread2() {
|
||||
epicsEventMustSignal(evt);
|
||||
}
|
||||
@endcode
|
||||
*
|
||||
* One way to avoid this race is to use a global mutex to ensure
|
||||
* that epicsEventMustSignal() has returned before destroying
|
||||
* the event.
|
||||
*
|
||||
@code
|
||||
epicsEventId evt;
|
||||
void thread1() {
|
||||
evt = epicsEventMustCreate(...);
|
||||
// spawn thread2()
|
||||
epicsEventMustWait(evt);
|
||||
testGlobalLock(); // <-- added
|
||||
epicsEventDestroy(evt);
|
||||
testGlobalUnlock(); // <-- added
|
||||
}
|
||||
// ...
|
||||
void thread2() {
|
||||
testGlobalLock(); // <-- added
|
||||
epicsEventMustSignal(evt);
|
||||
testGlobalUnlock(); // <-- added
|
||||
}
|
||||
@endcode
|
||||
*
|
||||
* This must be a global mutex to avoid simply shifting the race
|
||||
* from the event to a locally allocated mutex.
|
||||
*/
|
||||
epicsShareFunc void testGlobalLock(void);
|
||||
epicsShareFunc void testGlobalUnlock(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -47,7 +47,7 @@ epicsExportAddress(dset, devMbbiDirectSoft);
|
||||
|
||||
static long init_record(mbbiDirectRecord *prec)
|
||||
{
|
||||
if (recGblInitConstantLink(&prec->inp, DBF_ENUM, &prec->val))
|
||||
if (recGblInitConstantLink(&prec->inp, DBR_ULONG, &prec->val))
|
||||
prec->udf = FALSE;
|
||||
|
||||
return 0;
|
||||
@@ -56,7 +56,7 @@ static long init_record(mbbiDirectRecord *prec)
|
||||
static long readLocked(struct link *pinp, void *dummy)
|
||||
{
|
||||
mbbiDirectRecord *prec = (mbbiDirectRecord *) pinp->precord;
|
||||
long status = dbGetLink(pinp, DBR_USHORT, &prec->val, 0, 0);
|
||||
long status = dbGetLink(pinp, DBR_ULONG, &prec->val, 0, 0);
|
||||
|
||||
if (status) return status;
|
||||
|
||||
|
||||
@@ -41,7 +41,7 @@ typedef struct devPvt {
|
||||
struct {
|
||||
DBRstatus
|
||||
DBRtime
|
||||
epicsUInt16 value;
|
||||
epicsUInt32 value;
|
||||
} buffer;
|
||||
} devPvt;
|
||||
|
||||
@@ -58,7 +58,7 @@ static void getCallback(processNotify *ppn, notifyGetType type)
|
||||
}
|
||||
|
||||
assert(type == getFieldType);
|
||||
pdevPvt->status = dbChannelGetField(ppn->chan, DBR_USHORT,
|
||||
pdevPvt->status = dbChannelGetField(ppn->chan, DBR_ULONG,
|
||||
&pdevPvt->buffer, &pdevPvt->options, &no_elements, 0);
|
||||
}
|
||||
|
||||
@@ -153,7 +153,7 @@ static long init(int pass)
|
||||
|
||||
static long init_record(mbbiDirectRecord *prec)
|
||||
{
|
||||
if (recGblInitConstantLink(&prec->inp, DBR_ENUM, &prec->val))
|
||||
if (recGblInitConstantLink(&prec->inp, DBR_ULONG, &prec->val))
|
||||
prec->udf = FALSE;
|
||||
|
||||
return 0;
|
||||
|
||||
@@ -59,7 +59,7 @@ static long init_record(mbbiDirectRecord *prec)
|
||||
|
||||
static long read_mbbi(mbbiDirectRecord *prec)
|
||||
{
|
||||
if (!dbGetLink(&prec->inp, DBR_LONG, &prec->rval, 0, 0)) {
|
||||
if (!dbGetLink(&prec->inp, DBR_ULONG, &prec->rval, 0, 0)) {
|
||||
prec->rval &= prec->mask;
|
||||
if (dbLinkIsConstant(&prec->tsel) &&
|
||||
prec->tse == epicsTimeEventDeviceTime)
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
|
||||
static long write_mbbo(mbboDirectRecord *prec)
|
||||
{
|
||||
dbPutLink(&prec->out, DBR_USHORT, &prec->val, 1);
|
||||
dbPutLink(&prec->out, DBR_ULONG, &prec->val, 1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -29,11 +29,11 @@ static long write_mbbo(mbboDirectRecord *prec)
|
||||
if (prec->pact)
|
||||
return 0;
|
||||
|
||||
status = dbPutLinkAsync(plink, DBR_USHORT, &prec->val, 1);
|
||||
status = dbPutLinkAsync(plink, DBR_ULONG, &prec->val, 1);
|
||||
if (!status)
|
||||
prec->pact = TRUE;
|
||||
else if (status == S_db_noLSET)
|
||||
status = dbPutLink(plink, DBR_USHORT, &prec->val, 1);
|
||||
status = dbPutLink(plink, DBR_ULONG, &prec->val, 1);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
|
||||
#include "dbDefs.h"
|
||||
#include "epicsPrint.h"
|
||||
#include "epicsMath.h"
|
||||
#include "alarm.h"
|
||||
#include "callback.h"
|
||||
#include "dbAccess.h"
|
||||
@@ -248,15 +249,17 @@ static long get_alarm_double(DBADDR *paddr, struct dbr_alDouble *pad)
|
||||
{
|
||||
longinRecord *prec=(longinRecord *)paddr->precord;
|
||||
|
||||
if(dbGetFieldIndex(paddr) == indexof(VAL)){
|
||||
pad->upper_alarm_limit = prec->hihi;
|
||||
pad->upper_warning_limit = prec->high;
|
||||
pad->lower_warning_limit = prec->low;
|
||||
pad->lower_alarm_limit = prec->lolo;
|
||||
} else recGblGetAlarmDouble(paddr,pad);
|
||||
return(0);
|
||||
if (dbGetFieldIndex(paddr) == indexof(VAL)){
|
||||
pad->upper_alarm_limit = prec->hhsv ? prec->hihi : epicsNAN;
|
||||
pad->upper_warning_limit = prec->hsv ? prec->high : epicsNAN;
|
||||
pad->lower_warning_limit = prec->lsv ? prec->low : epicsNAN;
|
||||
pad->lower_alarm_limit = prec->llsv ? prec->lolo : epicsNAN;
|
||||
}
|
||||
else
|
||||
recGblGetAlarmDouble(paddr,pad);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static void checkAlarms(longinRecord *prec, epicsTimeStamp *timeLast)
|
||||
{
|
||||
enum {
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
|
||||
#include "dbDefs.h"
|
||||
#include "epicsPrint.h"
|
||||
#include "epicsMath.h"
|
||||
#include "alarm.h"
|
||||
#include "callback.h"
|
||||
#include "dbAccess.h"
|
||||
@@ -282,15 +283,17 @@ static long get_alarm_double(DBADDR *paddr,struct dbr_alDouble *pad)
|
||||
{
|
||||
longoutRecord *prec=(longoutRecord *)paddr->precord;
|
||||
|
||||
if(dbGetFieldIndex(paddr) == indexof(VAL)) {
|
||||
pad->upper_alarm_limit = prec->hihi;
|
||||
pad->upper_warning_limit = prec->high;
|
||||
pad->lower_warning_limit = prec->low;
|
||||
pad->lower_alarm_limit = prec->lolo;
|
||||
} else recGblGetAlarmDouble(paddr,pad);
|
||||
return(0);
|
||||
if (dbGetFieldIndex(paddr) == indexof(VAL)) {
|
||||
pad->upper_alarm_limit = prec->hhsv ? prec->hihi : epicsNAN;
|
||||
pad->upper_warning_limit = prec->hsv ? prec->high : epicsNAN;
|
||||
pad->lower_warning_limit = prec->lsv ? prec->low : epicsNAN;
|
||||
pad->lower_alarm_limit = prec->llsv ? prec->lolo : epicsNAN;
|
||||
}
|
||||
else
|
||||
recGblGetAlarmDouble(paddr,pad);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static void checkAlarms(longoutRecord *prec)
|
||||
{
|
||||
epicsInt32 val, hyst, lalm;
|
||||
|
||||
@@ -6,10 +6,10 @@
|
||||
* Copyright (c) 2002 Southeastern Universities Research Association, as
|
||||
* Operator of Thomas Jefferson National Accelerator Facility.
|
||||
* 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.
|
||||
\*************************************************************************/
|
||||
|
||||
/* mbbiDirectRecord.c - Record Support routines for mbboDirect records */
|
||||
/* mbbiDirectRecord.c - Record Support routines for mbbiDirect records */
|
||||
/*
|
||||
* Original Authors: Bob Dalesio and Matthew Needes
|
||||
* Date: 10-07-93
|
||||
@@ -93,7 +93,7 @@ struct mbbidset { /* multi bit binary input dset */
|
||||
static void monitor(mbbiDirectRecord *);
|
||||
static long readValue(mbbiDirectRecord *);
|
||||
|
||||
#define NUM_BITS 16
|
||||
#define NUM_BITS 32
|
||||
|
||||
static long init_record(struct dbCommon *pcommon, int pass)
|
||||
{
|
||||
@@ -114,7 +114,7 @@ static long init_record(struct dbCommon *pcommon, int pass)
|
||||
}
|
||||
|
||||
recGblInitSimm(pcommon, &prec->sscn, &prec->oldsimm, &prec->simm, &prec->siml);
|
||||
recGblInitConstantLink(&prec->siol, DBF_USHORT, &prec->sval);
|
||||
recGblInitConstantLink(&prec->siol, DBF_ULONG, &prec->sval);
|
||||
|
||||
/* Initialize MASK if the user set NOBT instead */
|
||||
if (prec->mask == 0 && prec->nobt <= 32)
|
||||
@@ -123,11 +123,11 @@ static long init_record(struct dbCommon *pcommon, int pass)
|
||||
if (pdset->init_record) {
|
||||
status = pdset->init_record(prec);
|
||||
if (status == 0) {
|
||||
epicsUInt16 val = prec->val;
|
||||
epicsUInt32 val = prec->val;
|
||||
epicsUInt8 *pBn = &prec->b0;
|
||||
int i;
|
||||
|
||||
/* Initialize B0 - BF from VAL */
|
||||
/* Initialize B0 - B1F from VAL */
|
||||
for (i = 0; i < NUM_BITS; i++, pBn++, val >>= 1)
|
||||
*pBn = !! (val & 1);
|
||||
}
|
||||
@@ -208,7 +208,7 @@ static void monitor(mbbiDirectRecord *prec)
|
||||
{
|
||||
epicsUInt16 events = recGblResetAlarms(prec);
|
||||
epicsUInt16 vl_events = events | DBE_VALUE | DBE_LOG;
|
||||
epicsUInt16 val = prec->val;
|
||||
epicsUInt32 val = prec->val;
|
||||
epicsUInt8 *pBn = &prec->b0;
|
||||
int i;
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
#*************************************************************************
|
||||
recordtype(mbbiDirect) {
|
||||
include "dbCommon.dbd"
|
||||
field(VAL,DBF_USHORT) {
|
||||
field(VAL,DBF_LONG) {
|
||||
prompt("Current Value")
|
||||
promptgroup("40 - Input")
|
||||
asl(ASL0)
|
||||
@@ -39,7 +39,7 @@ recordtype(mbbiDirect) {
|
||||
special(SPC_NOMOD)
|
||||
interest(1)
|
||||
}
|
||||
field(MLST,DBF_USHORT) {
|
||||
field(MLST,DBF_LONG) {
|
||||
prompt("Last Value Monitored")
|
||||
special(SPC_NOMOD)
|
||||
interest(3)
|
||||
@@ -54,7 +54,7 @@ recordtype(mbbiDirect) {
|
||||
promptgroup("90 - Simulate")
|
||||
interest(1)
|
||||
}
|
||||
field(SVAL,DBF_ULONG) {
|
||||
field(SVAL,DBF_LONG) {
|
||||
prompt("Simulation Value")
|
||||
}
|
||||
field(SIML,DBF_INLINK) {
|
||||
@@ -151,32 +151,112 @@ recordtype(mbbiDirect) {
|
||||
interest(1)
|
||||
}
|
||||
field(BA,DBF_UCHAR) {
|
||||
prompt("Bit A")
|
||||
prompt("Bit 10")
|
||||
pp(TRUE)
|
||||
interest(1)
|
||||
}
|
||||
field(BB,DBF_UCHAR) {
|
||||
prompt("Bit B")
|
||||
prompt("Bit 11")
|
||||
pp(TRUE)
|
||||
interest(1)
|
||||
}
|
||||
field(BC,DBF_UCHAR) {
|
||||
prompt("Bit C")
|
||||
prompt("Bit 12")
|
||||
pp(TRUE)
|
||||
interest(1)
|
||||
}
|
||||
field(BD,DBF_UCHAR) {
|
||||
prompt("Bit D")
|
||||
prompt("Bit 13")
|
||||
pp(TRUE)
|
||||
interest(1)
|
||||
}
|
||||
field(BE,DBF_UCHAR) {
|
||||
prompt("Bit E")
|
||||
prompt("Bit 14")
|
||||
pp(TRUE)
|
||||
interest(1)
|
||||
}
|
||||
field(BF,DBF_UCHAR) {
|
||||
prompt("Bit F")
|
||||
prompt("Bit 15")
|
||||
pp(TRUE)
|
||||
interest(1)
|
||||
}
|
||||
field(B10,DBF_UCHAR) {
|
||||
prompt("Bit 16")
|
||||
pp(TRUE)
|
||||
interest(1)
|
||||
}
|
||||
field(B11,DBF_UCHAR) {
|
||||
prompt("Bit 17")
|
||||
pp(TRUE)
|
||||
interest(1)
|
||||
}
|
||||
field(B12,DBF_UCHAR) {
|
||||
prompt("Bit 18")
|
||||
pp(TRUE)
|
||||
interest(1)
|
||||
}
|
||||
field(B13,DBF_UCHAR) {
|
||||
prompt("Bit 19")
|
||||
pp(TRUE)
|
||||
interest(1)
|
||||
}
|
||||
field(B14,DBF_UCHAR) {
|
||||
prompt("Bit 20")
|
||||
pp(TRUE)
|
||||
interest(1)
|
||||
}
|
||||
field(B15,DBF_UCHAR) {
|
||||
prompt("Bit 21")
|
||||
pp(TRUE)
|
||||
interest(1)
|
||||
}
|
||||
field(B16,DBF_UCHAR) {
|
||||
prompt("Bit 22")
|
||||
pp(TRUE)
|
||||
interest(1)
|
||||
}
|
||||
field(B17,DBF_UCHAR) {
|
||||
prompt("Bit 23")
|
||||
pp(TRUE)
|
||||
interest(1)
|
||||
}
|
||||
field(B18,DBF_UCHAR) {
|
||||
prompt("Bit 24")
|
||||
pp(TRUE)
|
||||
interest(1)
|
||||
}
|
||||
field(B19,DBF_UCHAR) {
|
||||
prompt("Bit 25")
|
||||
pp(TRUE)
|
||||
interest(1)
|
||||
}
|
||||
field(B1A,DBF_UCHAR) {
|
||||
prompt("Bit 26")
|
||||
pp(TRUE)
|
||||
interest(1)
|
||||
}
|
||||
field(B1B,DBF_UCHAR) {
|
||||
prompt("Bit 27")
|
||||
pp(TRUE)
|
||||
interest(1)
|
||||
}
|
||||
field(B1C,DBF_UCHAR) {
|
||||
prompt("Bit 28")
|
||||
pp(TRUE)
|
||||
interest(1)
|
||||
}
|
||||
field(B1D,DBF_UCHAR) {
|
||||
prompt("Bit 29")
|
||||
pp(TRUE)
|
||||
interest(1)
|
||||
}
|
||||
field(B1E,DBF_UCHAR) {
|
||||
prompt("Bit 30")
|
||||
pp(TRUE)
|
||||
interest(1)
|
||||
}
|
||||
field(B1F,DBF_UCHAR) {
|
||||
prompt("Bit 31")
|
||||
pp(TRUE)
|
||||
interest(1)
|
||||
}
|
||||
|
||||
@@ -95,7 +95,7 @@ static void convert(mbboDirectRecord *);
|
||||
static void monitor(mbboDirectRecord *);
|
||||
static long writeValue(mbboDirectRecord *);
|
||||
|
||||
#define NUM_BITS 16
|
||||
#define NUM_BITS 32
|
||||
|
||||
static long init_record(struct dbCommon *pcommon, int pass)
|
||||
{
|
||||
@@ -117,7 +117,7 @@ static long init_record(struct dbCommon *pcommon, int pass)
|
||||
|
||||
recGblInitSimm(pcommon, &prec->sscn, &prec->oldsimm, &prec->simm, &prec->siml);
|
||||
|
||||
if (recGblInitConstantLink(&prec->dol, DBF_USHORT, &prec->val))
|
||||
if (recGblInitConstantLink(&prec->dol, DBF_ULONG, &prec->val))
|
||||
prec->udf = FALSE;
|
||||
|
||||
/* Initialize MASK if the user set NOBT instead */
|
||||
@@ -142,8 +142,8 @@ static long init_record(struct dbCommon *pcommon, int pass)
|
||||
|
||||
if (!prec->udf &&
|
||||
prec->omsl == menuOmslsupervisory) {
|
||||
/* Set initial B0 - BF from VAL */
|
||||
epicsUInt16 val = prec->val;
|
||||
/* Set initial B0 - B1F from VAL */
|
||||
epicsUInt32 val = prec->val;
|
||||
epicsUInt8 *pBn = &prec->b0;
|
||||
int i;
|
||||
|
||||
@@ -175,9 +175,9 @@ static long process(struct dbCommon *pcommon)
|
||||
if (!pact) {
|
||||
if (!dbLinkIsConstant(&prec->dol) &&
|
||||
prec->omsl == menuOmslclosed_loop) {
|
||||
epicsUInt16 val;
|
||||
epicsUInt32 val;
|
||||
|
||||
if (dbGetLink(&prec->dol, DBR_USHORT, &val, 0, 0)) {
|
||||
if (dbGetLink(&prec->dol, DBR_ULONG, &val, 0, 0)) {
|
||||
recGblSetSevr(prec, LINK_ALARM, INVALID_ALARM);
|
||||
goto CONTINUE;
|
||||
}
|
||||
@@ -185,11 +185,11 @@ static long process(struct dbCommon *pcommon)
|
||||
}
|
||||
else if (prec->omsl == menuOmslsupervisory) {
|
||||
epicsUInt8 *pBn = &prec->b0;
|
||||
epicsUInt16 val = 0;
|
||||
epicsUInt16 bit = 1;
|
||||
epicsUInt32 val = 0;
|
||||
epicsUInt32 bit = 1;
|
||||
int i;
|
||||
|
||||
/* Construct VAL from B0 - BF */
|
||||
/* Construct VAL from B0 - B1F */
|
||||
for (i = 0; i < NUM_BITS; i++, bit <<= 1)
|
||||
if (*pBn++)
|
||||
val |= bit;
|
||||
@@ -264,7 +264,7 @@ static long special(DBADDR *paddr, int after)
|
||||
if (prec->omsl == menuOmslsupervisory) {
|
||||
/* Adjust VAL corresponding to the bit changed */
|
||||
epicsUInt8 *pBn = (epicsUInt8 *) paddr->pfield;
|
||||
int bit = 1 << (pBn - &prec->b0);
|
||||
epicsUInt32 bit = 1 << (pBn - &prec->b0);
|
||||
|
||||
if (*pBn)
|
||||
prec->val |= bit;
|
||||
@@ -278,9 +278,9 @@ static long special(DBADDR *paddr, int after)
|
||||
|
||||
case SPC_RESET: /* OMSL field modified */
|
||||
if (prec->omsl == menuOmslclosed_loop) {
|
||||
/* Construct VAL from B0 - BF */
|
||||
/* Construct VAL from B0 - B1F */
|
||||
epicsUInt8 *pBn = &prec->b0;
|
||||
epicsUInt16 val = 0, bit = 1;
|
||||
epicsUInt32 val = 0, bit = 1;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < NUM_BITS; i++, bit <<= 1)
|
||||
@@ -289,8 +289,8 @@ static long special(DBADDR *paddr, int after)
|
||||
prec->val = val;
|
||||
}
|
||||
else if (prec->omsl == menuOmslsupervisory) {
|
||||
/* Set B0 - BF from VAL and post monitors */
|
||||
epicsUInt16 val = prec->val;
|
||||
/* Set B0 - B1F from VAL and post monitors */
|
||||
epicsUInt32 val = prec->val;
|
||||
epicsUInt8 *pBn = &prec->b0;
|
||||
int i;
|
||||
|
||||
@@ -362,7 +362,7 @@ static long writeValue(mbboDirectRecord *prec)
|
||||
case menuYesNoYES: {
|
||||
recGblSetSevr(prec, SIMM_ALARM, prec->sims);
|
||||
if (prec->pact || (prec->sdly < 0.)) {
|
||||
status = dbPutLink(&prec->siol, DBR_USHORT, &prec->val, 1);
|
||||
status = dbPutLink(&prec->siol, DBR_ULONG, &prec->val, 1);
|
||||
prec->pact = FALSE;
|
||||
} else { /* !prec->pact && delay >= 0. */
|
||||
CALLBACK *pvt = prec->simpvt;
|
||||
|
||||
@@ -7,8 +7,8 @@
|
||||
# in file LICENSE that is included with this distribution.
|
||||
#*************************************************************************
|
||||
recordtype(mbboDirect) {
|
||||
include "dbCommon.dbd"
|
||||
field(VAL,DBF_USHORT) {
|
||||
include "dbCommon.dbd"
|
||||
field(VAL,DBF_LONG) {
|
||||
prompt("Word")
|
||||
promptgroup("50 - Output")
|
||||
asl(ASL0)
|
||||
@@ -38,6 +38,99 @@ recordtype(mbboDirect) {
|
||||
promptgroup("50 - Output")
|
||||
interest(1)
|
||||
}
|
||||
field(RVAL,DBF_ULONG) {
|
||||
prompt("Raw Value")
|
||||
special(SPC_NOMOD)
|
||||
pp(TRUE)
|
||||
}
|
||||
field(ORAW,DBF_ULONG) {
|
||||
prompt("Prev Raw Value")
|
||||
special(SPC_NOMOD)
|
||||
interest(3)
|
||||
}
|
||||
field(RBV,DBF_ULONG) {
|
||||
prompt("Readback Value")
|
||||
special(SPC_NOMOD)
|
||||
}
|
||||
field(ORBV,DBF_ULONG) {
|
||||
prompt("Prev Readback Value")
|
||||
special(SPC_NOMOD)
|
||||
interest(3)
|
||||
}
|
||||
field(MASK,DBF_ULONG) {
|
||||
prompt("Hardware Mask")
|
||||
special(SPC_NOMOD)
|
||||
interest(1)
|
||||
}
|
||||
field(MLST,DBF_LONG) {
|
||||
prompt("Last Value Monitored")
|
||||
special(SPC_NOMOD)
|
||||
interest(3)
|
||||
}
|
||||
field(SHFT,DBF_USHORT) {
|
||||
prompt("Shift")
|
||||
promptgroup("50 - Output")
|
||||
interest(1)
|
||||
}
|
||||
field(SIOL,DBF_OUTLINK) {
|
||||
prompt("Simulation Output Link")
|
||||
promptgroup("90 - Simulate")
|
||||
interest(1)
|
||||
}
|
||||
field(SIML,DBF_INLINK) {
|
||||
prompt("Simulation Mode Link")
|
||||
promptgroup("90 - Simulate")
|
||||
interest(1)
|
||||
}
|
||||
field(SIMM,DBF_MENU) {
|
||||
prompt("Simulation Mode")
|
||||
special(SPC_MOD)
|
||||
interest(1)
|
||||
menu(menuYesNo)
|
||||
}
|
||||
field(SIMS,DBF_MENU) {
|
||||
prompt("Simulation Mode Severity")
|
||||
promptgroup("90 - Simulate")
|
||||
interest(2)
|
||||
menu(menuAlarmSevr)
|
||||
}
|
||||
field(OLDSIMM,DBF_MENU) {
|
||||
prompt("Prev. Simulation Mode")
|
||||
special(SPC_NOMOD)
|
||||
interest(4)
|
||||
menu(menuSimm)
|
||||
}
|
||||
field(SSCN,DBF_MENU) {
|
||||
prompt("Sim. Mode Scan")
|
||||
promptgroup("90 - Simulate")
|
||||
interest(1)
|
||||
menu(menuScan)
|
||||
initial("65535")
|
||||
}
|
||||
field(SDLY,DBF_DOUBLE) {
|
||||
prompt("Sim. Mode Async Delay")
|
||||
promptgroup("90 - Simulate")
|
||||
interest(2)
|
||||
initial("-1.0")
|
||||
}
|
||||
%#include "callback.h"
|
||||
field(SIMPVT,DBF_NOACCESS) {
|
||||
prompt("Sim. Mode Private")
|
||||
special(SPC_NOMOD)
|
||||
interest(4)
|
||||
extra("CALLBACK *simpvt")
|
||||
}
|
||||
field(IVOA,DBF_MENU) {
|
||||
prompt("INVALID outpt action")
|
||||
promptgroup("50 - Output")
|
||||
interest(2)
|
||||
menu(menuIvoa)
|
||||
}
|
||||
field(IVOV,DBF_LONG) {
|
||||
prompt("INVALID output value")
|
||||
promptgroup("50 - Output")
|
||||
interest(2)
|
||||
}
|
||||
field(B0,DBF_UCHAR) {
|
||||
prompt("Bit 0")
|
||||
promptgroup("51 - Output 0-7")
|
||||
@@ -96,151 +189,170 @@ recordtype(mbboDirect) {
|
||||
}
|
||||
field(B8,DBF_UCHAR) {
|
||||
prompt("Bit 8")
|
||||
promptgroup("52 - Output 9-F")
|
||||
promptgroup("52 - Output 8-15")
|
||||
special(SPC_MOD)
|
||||
pp(TRUE)
|
||||
interest(1)
|
||||
}
|
||||
field(B9,DBF_UCHAR) {
|
||||
prompt("Bit 9")
|
||||
promptgroup("52 - Output 9-F")
|
||||
promptgroup("52 - Output 8-15")
|
||||
special(SPC_MOD)
|
||||
pp(TRUE)
|
||||
interest(1)
|
||||
}
|
||||
field(BA,DBF_UCHAR) {
|
||||
prompt("Bit 10")
|
||||
promptgroup("52 - Output 9-F")
|
||||
promptgroup("52 - Output 8-15")
|
||||
special(SPC_MOD)
|
||||
pp(TRUE)
|
||||
interest(1)
|
||||
}
|
||||
field(BB,DBF_UCHAR) {
|
||||
prompt("Bit 11")
|
||||
promptgroup("52 - Output 9-F")
|
||||
promptgroup("52 - Output 8-15")
|
||||
special(SPC_MOD)
|
||||
pp(TRUE)
|
||||
interest(1)
|
||||
}
|
||||
field(BC,DBF_UCHAR) {
|
||||
prompt("Bit 12")
|
||||
promptgroup("52 - Output 9-F")
|
||||
promptgroup("52 - Output 8-15")
|
||||
special(SPC_MOD)
|
||||
pp(TRUE)
|
||||
interest(1)
|
||||
}
|
||||
field(BD,DBF_UCHAR) {
|
||||
prompt("Bit 13")
|
||||
promptgroup("52 - Output 9-F")
|
||||
promptgroup("52 - Output 8-15")
|
||||
special(SPC_MOD)
|
||||
pp(TRUE)
|
||||
interest(1)
|
||||
}
|
||||
field(BE,DBF_UCHAR) {
|
||||
prompt("Bit 14")
|
||||
promptgroup("52 - Output 9-F")
|
||||
promptgroup("52 - Output 8-15")
|
||||
special(SPC_MOD)
|
||||
pp(TRUE)
|
||||
interest(1)
|
||||
}
|
||||
field(BF,DBF_UCHAR) {
|
||||
prompt("Bit 15")
|
||||
promptgroup("52 - Output 9-F")
|
||||
promptgroup("52 - Output 8-15")
|
||||
special(SPC_MOD)
|
||||
pp(TRUE)
|
||||
interest(1)
|
||||
}
|
||||
field(RVAL,DBF_ULONG) {
|
||||
prompt("Raw Value")
|
||||
special(SPC_NOMOD)
|
||||
pp(TRUE)
|
||||
}
|
||||
field(ORAW,DBF_ULONG) {
|
||||
prompt("Prev Raw Value")
|
||||
special(SPC_NOMOD)
|
||||
interest(3)
|
||||
}
|
||||
field(RBV,DBF_ULONG) {
|
||||
prompt("Readback Value")
|
||||
special(SPC_NOMOD)
|
||||
}
|
||||
field(ORBV,DBF_ULONG) {
|
||||
prompt("Prev Readback Value")
|
||||
special(SPC_NOMOD)
|
||||
interest(3)
|
||||
}
|
||||
field(MASK,DBF_ULONG) {
|
||||
prompt("Hardware Mask")
|
||||
special(SPC_NOMOD)
|
||||
interest(1)
|
||||
}
|
||||
field(MLST,DBF_ULONG) {
|
||||
prompt("Last Value Monitored")
|
||||
special(SPC_NOMOD)
|
||||
interest(3)
|
||||
}
|
||||
field(SHFT,DBF_ULONG) {
|
||||
prompt("Shift")
|
||||
promptgroup("50 - Output")
|
||||
interest(1)
|
||||
}
|
||||
field(SIOL,DBF_OUTLINK) {
|
||||
prompt("Simulation Output Link")
|
||||
promptgroup("90 - Simulate")
|
||||
interest(1)
|
||||
}
|
||||
field(SIML,DBF_INLINK) {
|
||||
prompt("Simulation Mode Link")
|
||||
promptgroup("90 - Simulate")
|
||||
interest(1)
|
||||
}
|
||||
field(SIMM,DBF_MENU) {
|
||||
prompt("Simulation Mode")
|
||||
field(B10,DBF_UCHAR) {
|
||||
prompt("Bit 16")
|
||||
promptgroup("53 - Output 16-23")
|
||||
special(SPC_MOD)
|
||||
pp(TRUE)
|
||||
interest(1)
|
||||
menu(menuYesNo)
|
||||
}
|
||||
field(SIMS,DBF_MENU) {
|
||||
prompt("Simulation Mode Severity")
|
||||
promptgroup("90 - Simulate")
|
||||
interest(2)
|
||||
menu(menuAlarmSevr)
|
||||
}
|
||||
field(OLDSIMM,DBF_MENU) {
|
||||
prompt("Prev. Simulation Mode")
|
||||
special(SPC_NOMOD)
|
||||
interest(4)
|
||||
menu(menuSimm)
|
||||
}
|
||||
field(SSCN,DBF_MENU) {
|
||||
prompt("Sim. Mode Scan")
|
||||
promptgroup("90 - Simulate")
|
||||
field(B11,DBF_UCHAR) {
|
||||
prompt("Bit 17")
|
||||
promptgroup("53 - Output 16-23")
|
||||
special(SPC_MOD)
|
||||
pp(TRUE)
|
||||
interest(1)
|
||||
menu(menuScan)
|
||||
initial("65535")
|
||||
}
|
||||
field(SDLY,DBF_DOUBLE) {
|
||||
prompt("Sim. Mode Async Delay")
|
||||
promptgroup("90 - Simulate")
|
||||
interest(2)
|
||||
initial("-1.0")
|
||||
field(B12,DBF_UCHAR) {
|
||||
prompt("Bit 18")
|
||||
promptgroup("53 - Output 16-23")
|
||||
special(SPC_MOD)
|
||||
pp(TRUE)
|
||||
interest(1)
|
||||
}
|
||||
%#include "callback.h"
|
||||
field(SIMPVT,DBF_NOACCESS) {
|
||||
prompt("Sim. Mode Private")
|
||||
special(SPC_NOMOD)
|
||||
interest(4)
|
||||
extra("CALLBACK *simpvt")
|
||||
field(B13,DBF_UCHAR) {
|
||||
prompt("Bit 19")
|
||||
promptgroup("53 - Output 16-23")
|
||||
special(SPC_MOD)
|
||||
pp(TRUE)
|
||||
interest(1)
|
||||
}
|
||||
field(IVOA,DBF_MENU) {
|
||||
prompt("INVALID outpt action")
|
||||
promptgroup("50 - Output")
|
||||
interest(2)
|
||||
menu(menuIvoa)
|
||||
field(B14,DBF_UCHAR) {
|
||||
prompt("Bit 20")
|
||||
promptgroup("53 - Output 16-23")
|
||||
special(SPC_MOD)
|
||||
pp(TRUE)
|
||||
interest(1)
|
||||
}
|
||||
field(IVOV,DBF_USHORT) {
|
||||
prompt("INVALID output value")
|
||||
promptgroup("50 - Output")
|
||||
interest(2)
|
||||
field(B15,DBF_UCHAR) {
|
||||
prompt("Bit 21")
|
||||
promptgroup("53 - Output 16-23")
|
||||
special(SPC_MOD)
|
||||
pp(TRUE)
|
||||
interest(1)
|
||||
}
|
||||
field(B16,DBF_UCHAR) {
|
||||
prompt("Bit 22")
|
||||
promptgroup("53 - Output 16-23")
|
||||
special(SPC_MOD)
|
||||
pp(TRUE)
|
||||
interest(1)
|
||||
}
|
||||
field(B17,DBF_UCHAR) {
|
||||
prompt("Bit 23")
|
||||
promptgroup("53 - Output 16-23")
|
||||
special(SPC_MOD)
|
||||
pp(TRUE)
|
||||
interest(1)
|
||||
}
|
||||
field(B18,DBF_UCHAR) {
|
||||
prompt("Bit 24")
|
||||
promptgroup("54 - Output 24-31")
|
||||
special(SPC_MOD)
|
||||
pp(TRUE)
|
||||
interest(1)
|
||||
}
|
||||
field(B19,DBF_UCHAR) {
|
||||
prompt("Bit 25")
|
||||
promptgroup("54 - Output 24-31")
|
||||
special(SPC_MOD)
|
||||
pp(TRUE)
|
||||
interest(1)
|
||||
}
|
||||
field(B1A,DBF_UCHAR) {
|
||||
prompt("Bit 26")
|
||||
promptgroup("54 - Output 24-31")
|
||||
special(SPC_MOD)
|
||||
pp(TRUE)
|
||||
interest(1)
|
||||
}
|
||||
field(B1B,DBF_UCHAR) {
|
||||
prompt("Bit 27")
|
||||
promptgroup("54 - Output 24-31")
|
||||
special(SPC_MOD)
|
||||
pp(TRUE)
|
||||
interest(1)
|
||||
}
|
||||
field(B1C,DBF_UCHAR) {
|
||||
prompt("Bit 28")
|
||||
promptgroup("54 - Output 24-31")
|
||||
special(SPC_MOD)
|
||||
pp(TRUE)
|
||||
interest(1)
|
||||
}
|
||||
field(B1D,DBF_UCHAR) {
|
||||
prompt("Bit 29")
|
||||
promptgroup("54 - Output 24-31")
|
||||
special(SPC_MOD)
|
||||
pp(TRUE)
|
||||
interest(1)
|
||||
}
|
||||
field(B1E,DBF_UCHAR) {
|
||||
prompt("Bit 30")
|
||||
promptgroup("54 - Output 24-31")
|
||||
special(SPC_MOD)
|
||||
pp(TRUE)
|
||||
interest(1)
|
||||
}
|
||||
field(B1F,DBF_UCHAR) {
|
||||
prompt("Bit 31")
|
||||
promptgroup("54 - Output 24-31")
|
||||
special(SPC_MOD)
|
||||
pp(TRUE)
|
||||
interest(1)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,12 +34,13 @@
|
||||
|
||||
|
||||
/* Flag bits */
|
||||
#define F_CHAR 1
|
||||
#define F_SHORT 2
|
||||
#define F_LONG 4
|
||||
#define F_LEFT 8
|
||||
#define F_BADFMT 0x10
|
||||
#define F_BADLNK 0x20
|
||||
#define F_CHAR 1
|
||||
#define F_SHORT 2
|
||||
#define F_LONG 4
|
||||
#define F_LONGLONG 8
|
||||
#define F_LEFT 0x10
|
||||
#define F_BADFMT 0x40
|
||||
#define F_BADLNK 0x80
|
||||
#define F_BAD (F_BADFMT | F_BADLNK)
|
||||
|
||||
#define GET_PRINT(VALTYPE, DBRTYPE) \
|
||||
@@ -129,13 +130,20 @@ static void doPrintf(printfRecord *prec)
|
||||
flags |= F_BADLNK;
|
||||
break;
|
||||
case 'h':
|
||||
if (flags & F_SHORT)
|
||||
if (flags & (F_LONGLONG | F_LONG | F_CHAR))
|
||||
flags |= F_BADFMT;
|
||||
else if (flags & F_SHORT)
|
||||
flags = (flags & ~F_SHORT) | F_CHAR;
|
||||
else
|
||||
flags |= F_SHORT;
|
||||
break;
|
||||
case 'l':
|
||||
flags |= F_LONG;
|
||||
if (flags & (F_LONGLONG | F_SHORT | F_CHAR))
|
||||
flags |= F_BADFMT;
|
||||
else if (flags & F_LONG)
|
||||
flags = (flags & ~F_LONG) | F_LONGLONG;
|
||||
else
|
||||
flags |= F_LONG;
|
||||
break;
|
||||
default:
|
||||
if (strchr("diouxXeEfFgGcs%", ch) == NULL)
|
||||
@@ -175,6 +183,9 @@ static void doPrintf(printfRecord *prec)
|
||||
else if (flags & F_SHORT) {
|
||||
GET_PRINT(epicsInt16, DBR_SHORT);
|
||||
}
|
||||
else if (flags & F_LONGLONG) {
|
||||
GET_PRINT(epicsInt64, DBR_INT64);
|
||||
}
|
||||
else { /* F_LONG has no real effect */
|
||||
GET_PRINT(epicsInt32, DBR_LONG);
|
||||
}
|
||||
@@ -187,6 +198,9 @@ static void doPrintf(printfRecord *prec)
|
||||
else if (flags & F_SHORT) {
|
||||
GET_PRINT(epicsUInt16, DBR_USHORT);
|
||||
}
|
||||
else if (flags & F_LONGLONG) {
|
||||
GET_PRINT(epicsUInt64, DBR_UINT64);
|
||||
}
|
||||
else { /* F_LONG has no real effect */
|
||||
GET_PRINT(epicsUInt32, DBR_ULONG);
|
||||
}
|
||||
|
||||
@@ -198,8 +198,8 @@ static long get_alarm_double(DBADDR *paddr,struct dbr_alDouble *pad)
|
||||
|
||||
static void checkAlarms(xxxRecord *prec)
|
||||
{
|
||||
double val;
|
||||
float hyst, lalm, hihi, high, low, lolo;
|
||||
double val, hyst, lalm;
|
||||
float hihi, high, low, lolo;
|
||||
unsigned short hhsv, llsv, hsv, lsv;
|
||||
|
||||
if(prec->udf == TRUE ){
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
TOP = ../..
|
||||
include $(TOP)/configure/CONFIG
|
||||
ARCH = $(EPICS_HOST_ARCH)
|
||||
TARGETS = envPaths
|
||||
include $(TOP)/configure/RULES.ioc
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
TOP = ../..
|
||||
include $(TOP)/configure/CONFIG
|
||||
ARCH = $(EPICS_HOST_ARCH)
|
||||
TARGETS = envPaths
|
||||
include $(TOP)/configure/RULES.ioc
|
||||
|
||||
@@ -46,7 +46,7 @@ BEGIN {
|
||||
|
||||
my $tool = 'dbdToHtml';
|
||||
|
||||
use vars qw($opt_D @opt_I $opt_o);
|
||||
our ($opt_D, @opt_I, $opt_o);
|
||||
getopts('DI@o:') or
|
||||
die "Usage: $tool [-D] [-I dir] [-o file.html] file.dbd.pod\n";
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ use EPICS::Readfile;
|
||||
|
||||
my $tool = 'dbdToMenuH.pl';
|
||||
|
||||
use vars qw($opt_D @opt_I $opt_o $opt_s);
|
||||
our ($opt_D, @opt_I, $opt_o, $opt_s);
|
||||
getopts('DI@o:') or
|
||||
die "Usage: $tool: [-D] [-I dir] [-o menu.h] menu.dbd [menu.h]\n";
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ use EPICS::Readfile;
|
||||
|
||||
my $tool = 'dbdToRecordtypeH.pl';
|
||||
|
||||
use vars qw($opt_D @opt_I $opt_o $opt_s);
|
||||
our ($opt_D, @opt_I, $opt_o, $opt_s);
|
||||
getopts('DI@o:s') or
|
||||
die "Usage: $tool [-D] [-I dir] [-o xRecord.h] xRecord.dbd [xRecord.h]\n";
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -118,6 +129,13 @@ testHarness_SRCS += simmTest_registerRecordDeviceDriver.cpp
|
||||
TESTFILES += $(COMMON_DIR)/simmTest.dbd $(COMMON_DIR)/simmTest.db
|
||||
TESTS += simmTest
|
||||
|
||||
TESTPROD_HOST += mbbioDirectTest
|
||||
mbbioDirectTest_SRCS += mbbioDirectTest.c
|
||||
mbbioDirectTest_SRCS += recTestIoc_registerRecordDeviceDriver.cpp
|
||||
testHarness_SRCS += mbbioDirectTest.c
|
||||
TESTFILES += ../mbbioDirectTest.db
|
||||
TESTS += mbbioDirectTest
|
||||
|
||||
# epicsRunRecordTests runs all the test programs in a known working order.
|
||||
testHarness_SRCS += epicsRunRecordTests.c
|
||||
|
||||
|
||||
@@ -21,6 +21,8 @@ int linkRetargetLinkTest(void);
|
||||
int linkInitTest(void);
|
||||
int asyncSoftTest(void);
|
||||
int simmTest(void);
|
||||
int mbbioDirectTest(void);
|
||||
int scanEventTest(void);
|
||||
|
||||
void epicsRunRecordTests(void)
|
||||
{
|
||||
@@ -44,5 +46,9 @@ void epicsRunRecordTests(void)
|
||||
|
||||
runTest(simmTest);
|
||||
|
||||
runTest(mbbioDirectTest);
|
||||
|
||||
runTest(scanEventTest);
|
||||
|
||||
epicsExit(0); /* Trigger test harness */
|
||||
}
|
||||
|
||||
@@ -0,0 +1,145 @@
|
||||
/*************************************************************************\
|
||||
* Copyright (c) 2017 Dirk Zimoch
|
||||
* EPICS BASE is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
\*************************************************************************/
|
||||
|
||||
/* Theory of Operation
|
||||
*
|
||||
* For each of the two soft device supports (soft/raw soft/soft),
|
||||
* there is a combination of mbboDirect -> val / sim -> mbbiDirect.
|
||||
*
|
||||
* The intermediate records are of type double (val) and long (sim) to
|
||||
* check conversion from/to double.
|
||||
*
|
||||
* For each device support, the following is done:
|
||||
* 1. The mbboDirect record is set to a specific value through the database.
|
||||
* 2. The initial value is checked on both ends.
|
||||
* 3. Two single bits (5 & 31) are toggled, values checked on both ends.
|
||||
* 4. Sim mode is activated, one bit (0) is toggled, values are checked,
|
||||
* data path (old value in val, new value in sim) is checked.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "dbAccess.h"
|
||||
#include "errlog.h"
|
||||
#include "dbStaticLib.h"
|
||||
#include "dbTest.h"
|
||||
#include "dbUnitTest.h"
|
||||
#include "testMain.h"
|
||||
#include "epicsThread.h"
|
||||
#include "epicsExport.h"
|
||||
|
||||
static
|
||||
void testmbbioFields(const char* rec, unsigned int value)
|
||||
{
|
||||
char field[40];
|
||||
unsigned int i;
|
||||
|
||||
testdbGetFieldEqual(rec, DBF_ULONG, value);
|
||||
for (i=0; i < 32; i++)
|
||||
{
|
||||
sprintf(field,"%s.B%X", rec, i);
|
||||
testdbGetFieldEqual(field, DBF_ULONG, (value>>i)&1);
|
||||
}
|
||||
}
|
||||
|
||||
static
|
||||
void testmbbioRecords(unsigned int count, unsigned int value)
|
||||
{
|
||||
char rec[40];
|
||||
unsigned int i;
|
||||
|
||||
for (i = 1; i <= count; i++)
|
||||
{
|
||||
sprintf(rec, "do%d", i);
|
||||
testDiag(" ### %s ###", rec);
|
||||
testmbbioFields(rec, value);
|
||||
sprintf(rec, "di%d", i);
|
||||
testmbbioFields(rec, value);
|
||||
}
|
||||
}
|
||||
|
||||
static
|
||||
void putN(const char* pattern, unsigned int count, unsigned int value)
|
||||
{
|
||||
char field[40];
|
||||
unsigned int i;
|
||||
|
||||
for (i = 1; i <= count; i++)
|
||||
{
|
||||
sprintf(field, pattern, i);
|
||||
testdbPutFieldOk(field, DBF_ULONG, value);
|
||||
}
|
||||
}
|
||||
|
||||
static
|
||||
void testN(const char* pattern, unsigned int count, unsigned int value)
|
||||
{
|
||||
char field[40];
|
||||
unsigned int i;
|
||||
|
||||
for (i = 1; i <= count; i++)
|
||||
{
|
||||
sprintf(field, pattern, i);
|
||||
testdbGetFieldEqual(field, DBF_ULONG, value);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void recTestIoc_registerRecordDeviceDriver(struct dbBase *);
|
||||
|
||||
MAIN(mbbioDirectTest)
|
||||
{
|
||||
unsigned int value = 0xdeadbeef;
|
||||
unsigned int simvalue = 0;
|
||||
char macros [40];
|
||||
const unsigned int N = 2;
|
||||
|
||||
testPlan(N*((32+1)*2*4+4+3));
|
||||
|
||||
testdbPrepare();
|
||||
|
||||
testdbReadDatabase("recTestIoc.dbd", NULL, NULL);
|
||||
|
||||
recTestIoc_registerRecordDeviceDriver(pdbbase);
|
||||
|
||||
sprintf(macros, "INIT=%#x", value);
|
||||
testdbReadDatabase("mbbioDirectTest.db", NULL, macros);
|
||||
|
||||
eltc(0);
|
||||
testIocInitOk();
|
||||
eltc(1);
|
||||
|
||||
testDiag("##### check initial value #####");
|
||||
testmbbioRecords(N, value);
|
||||
|
||||
testDiag("##### set bit 5 #####");
|
||||
putN("do%u.B5", N, 1);
|
||||
value |= (1<<5);
|
||||
testN("val%d", N, value);
|
||||
testmbbioRecords(N, value);
|
||||
|
||||
testDiag("##### clear bit 31 (0x1f) #####");
|
||||
putN("do%u.B1F", N, 0);
|
||||
value &= ~(1<<31);
|
||||
testN("val%d", N, value);
|
||||
testmbbioRecords(N, value);
|
||||
|
||||
testDiag("##### simulation mode #####");
|
||||
dbpf("sim", "1");
|
||||
simvalue = value & ~1;
|
||||
putN("do%u.B0", N, 0);
|
||||
/* old value in lo* */
|
||||
testN("val%d", N, value);
|
||||
/* sim value in sim* */
|
||||
testN("sim%d", N, simvalue);
|
||||
testmbbioRecords(N, simvalue);
|
||||
|
||||
testIocShutdownOk();
|
||||
|
||||
testdbCleanup();
|
||||
|
||||
return testDone();
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
record(bo, "sim") {
|
||||
field(ZNAM, "off")
|
||||
field(ONAM, "simulation")
|
||||
}
|
||||
|
||||
record(mbboDirect, "do1") {
|
||||
field(DOL, "$(INIT=0)")
|
||||
field(DTYP, "Soft Channel")
|
||||
field(OUT, "val1 PP")
|
||||
field(SIOL, "sim1 PP")
|
||||
field(SIML, "sim")
|
||||
field(PINI, "YES")
|
||||
}
|
||||
record(ao, "val1") {
|
||||
field(FLNK, "di1")
|
||||
}
|
||||
record(longout, "sim1") {
|
||||
field(FLNK, "di1")
|
||||
}
|
||||
record(mbbiDirect, "di1") {
|
||||
field(DTYP, "Soft Channel")
|
||||
field(INP, "val1")
|
||||
field(SIOL, "sim1 PP")
|
||||
field(SIML, "sim")
|
||||
}
|
||||
|
||||
|
||||
record(mbboDirect, "do2") {
|
||||
field(DOL, "$(INIT=0)")
|
||||
field(DTYP, "Raw Soft Channel")
|
||||
field(OUT, "val2 PP")
|
||||
field(SIOL, "sim2 PP")
|
||||
field(SIML, "sim")
|
||||
field(PINI, "YES")
|
||||
}
|
||||
record(ao, "val2") {
|
||||
field(FLNK, "di2")
|
||||
}
|
||||
record(longout, "sim2") {
|
||||
field(FLNK, "di2")
|
||||
}
|
||||
record(mbbiDirect, "di2") {
|
||||
field(DTYP, "Raw Soft Channel")
|
||||
field(INP, "val2")
|
||||
field(SIML, "sim")
|
||||
field(SIOL, "sim2")
|
||||
}
|
||||
@@ -0,0 +1,143 @@
|
||||
/*************************************************************************\
|
||||
* 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);
|
||||
|
||||
testdbPrepare();
|
||||
|
||||
memset(aliases, 0, sizeof(aliases));
|
||||
memset(expected_count, 0, sizeof(expected_count));
|
||||
|
||||
testdbReadDatabase("scanEventTest.dbd", NULL, NULL);
|
||||
|
||||
scanEventTest_registerRecordDeviceDriver(pdbbase);
|
||||
for (i = 0; i < NELEMENTS(events); i++) {
|
||||
char substitutions[256];
|
||||
sprintf(substitutions, "N=%d,EVENT=%s", i, events[i].name);
|
||||
testdbReadDatabase("scanEventTest.db", NULL, substitutions);
|
||||
}
|
||||
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 */
|
||||
testSyncCallback();
|
||||
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)]);
|
||||
}
|
||||
|
||||
testIocShutdownOk();
|
||||
|
||||
testdbCleanup();
|
||||
|
||||
return testDone();
|
||||
}
|
||||
@@ -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") {
|
||||
}
|
||||
@@ -24,8 +24,7 @@ use lib ("$Bin/../../lib/perl", $Bin);
|
||||
use EPICS::Path;
|
||||
use EPICS::Release;
|
||||
|
||||
use vars qw($arch $top $iocroot $root);
|
||||
|
||||
our ($arch, $top, $iocroot, $root);
|
||||
our ($opt_a, $opt_t, $opt_T);
|
||||
|
||||
getopts('a:t:T:') or HELP_MESSAGE();
|
||||
|
||||
Reference in New Issue
Block a user