Merge remote-tracking branch 'linksup/master'
* linksup/master: (112 commits)
ioc/dbStatic: rename link debugging info tags
jlif future proof for 64-bit json parser
Demonstrate and use numeric literals in calc args
separate jlink and lset debug flags
std/link: runtime conditional debugging in calc/const
std/rec/test: extend linkRetargetLink w/ jlink
ioc/db: dbUnitTest add testdbPutArrFieldOk()
ioc/db: cleanup dbEvent freeLists
ioc/db: dbUnitTest testMonitor leaks dbChannel
std/link: all calc example
jlink conditional debug print
ioc/as: dbCore needs ca
Additional linkInitTest checks
Minimize work done in readLocked routine
Minimize work done in readLocked() routine
Cosmetic changes to various soft device supports
Enhancements to subArray record & soft device support
Fix bug in eventRecord::init_record
Updates to Release Notes and links.html
Make dbLinkIs{Constant|Volatile}() return only true/false
...
Conflicts:
documentation/RELEASE_NOTES.html
src/ioc/db/dbAccess.c
This commit is contained in:
@@ -52,6 +52,304 @@ Older clients will be ignored by newer servers.</p>
|
||||
<p>This allows removal of UDP echo and similar protocol features which
|
||||
are not compatible with secure protocol design practice.</p>
|
||||
|
||||
<h3>Lookup-tables using the subArrray record</h3>
|
||||
|
||||
<p>The subArray record can now be used as a lookup-table from a constant array
|
||||
specified in its INP field. For example:</p>
|
||||
|
||||
<pre>
|
||||
record(subArray, "powers-of-2") {
|
||||
field(FTVL, "LONG")
|
||||
field(MALM, 12)
|
||||
field(INP, [1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048])
|
||||
field(INDX, 0)
|
||||
field(NELM, 1)
|
||||
}
|
||||
</pre>
|
||||
|
||||
<p>The INDX field selects which power of 2 to set the VAL field to. In previous
|
||||
releases the INP field would have to have been pointed to a separate waveform
|
||||
record that was initialized with the array values somehow at initialization
|
||||
time.</p>
|
||||
|
||||
<h3>Synchronized Timestamps with TSEL=-2</h3>
|
||||
|
||||
<p>Most Soft Channel input device support routines have supported fetching the
|
||||
timestamp through the INP link along with the input data. However before now
|
||||
there was no guarantee that the timestamp provided by a CA link came from the
|
||||
same update as the data, since the two were read from the CA input buffer at
|
||||
separate times without maintaining a lock on that buffer in between. This
|
||||
shortcoming could be fixed as a result of the new link support code, which
|
||||
allows code using a link to pass a subroutine to the link type which will be run
|
||||
with the link locked. The subroutine may make multiple requests for metadata
|
||||
from the link, but must not block.</p>
|
||||
|
||||
|
||||
<h3>Extensible Link Types</h3>
|
||||
|
||||
<blockquote>
|
||||
|
||||
<p>A major new feature introduced with this release of EPICS Base is an
|
||||
Extensible Link Type mechanism, also known as Link Support or JSON Link Types.
|
||||
This addition permits new kinds of link I/O to be added to an IOC in a similar
|
||||
manner to the other extension points already supported (e.g. record, device and
|
||||
driver support).</p>
|
||||
|
||||
<p>A new link type must implement two related APIs, one for parsing the JSON
|
||||
string which provides the link address and the other which implements the link
|
||||
operations that get called at run-time to perform I/O. The link type is built
|
||||
into the IOC by providing a new <tt>link</tt> entry in a DBD file.</p>
|
||||
|
||||
|
||||
<h4>New Link Types Added</h4>
|
||||
|
||||
<p>This release contains two new JSON link types, <tt>const</tt> and
|
||||
<tt>calc</tt>:</p>
|
||||
|
||||
<ul>
|
||||
|
||||
<li>The <tt>const</tt> link type is almost equivalent to the old CONSTANT link
|
||||
type with the updates described below to accept arrays and strings, except that
|
||||
there is no need to wrap a scalar string constant inside array brackets since a
|
||||
constant string will never be confused with a PV name.</li>
|
||||
|
||||
<li>The <tt>calc</tt> link type allows CALC expressions to be used to combine
|
||||
values from other JSON links to produce its value. Until additional JSON link
|
||||
types are created though, the <tt>calc</tt> link type has little practical
|
||||
utility as it can currently only fetch inputs from other <tt>calc</tt> links or
|
||||
from <tt>const</tt> links.</li>
|
||||
|
||||
</ul>
|
||||
|
||||
<pre>
|
||||
field(INP, {calc:{expr:"A+B+1",
|
||||
args:[5, # A
|
||||
{const:6}] # B
|
||||
}})
|
||||
</pre>
|
||||
|
||||
<p>The new link types are documented in a
|
||||
<a href="links.html">separate</a><!-- href for the EPICS website -->
|
||||
<a href="../html/links.html">document</a><!-- href for install tree -->
|
||||
.</p>
|
||||
|
||||
|
||||
<h4>Device Support Addressing using <tt>JSON_LINK</tt></h3>
|
||||
|
||||
<p>The API to allow device support to use JSON addresses is currently
|
||||
incomplete; developers are advised not to try creating device support that
|
||||
specifies a <tt>JSON_LINK</tt> address type.</p>
|
||||
|
||||
|
||||
<h4>Support Routine Modifications for Extensible Link Types</h4>
|
||||
|
||||
<p>For link fields in external record types and soft device support to be able
|
||||
to use the new link types properly, various changes are required to utilize the
|
||||
new Link Support API as defined in the dbLink.h header file and outlined below.
|
||||
The existing built-in Database and Channel Access link types have been altered
|
||||
to implement the link APIs, so will work properly after these conversions:</p>
|
||||
|
||||
<ul>
|
||||
|
||||
<li>Make all calls to <tt>recGblInitConstantLink()</tt> unconditional on the
|
||||
link type, i.e. change this code:
|
||||
|
||||
<pre>
|
||||
if (prec->siml.type == CONSTANT) {
|
||||
recGblInitConstantLink(&prec->siml, DBF_USHORT, &prec->simm);
|
||||
}
|
||||
</pre>
|
||||
|
||||
into this:
|
||||
|
||||
<pre>
|
||||
recGblInitConstantLink(&prec->siml, DBF_USHORT, &prec->simm);
|
||||
</pre>
|
||||
|
||||
Note that <tt>recGblInitConstantLink()</tt> still returns TRUE if the field was
|
||||
successfully initialized from the link (implying the link is constant).<br />
|
||||
This change will work properly with all Base releases currently in use.</li>
|
||||
|
||||
<li>Code that needs to identify a constant link should be modified to use the
|
||||
new routine <tt>dbLinkIsConstant()</tt> instead, which returns TRUE for constant
|
||||
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)
|
||||
</pre>
|
||||
|
||||
should become this:
|
||||
|
||||
<pre>
|
||||
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)
|
||||
</pre>
|
||||
</li>
|
||||
|
||||
<li>Any code that calls dbCa routines directly, or that explicitly checks if a
|
||||
link has been resolved as a CA link using code such as
|
||||
|
||||
<pre>
|
||||
if (prec->inp.type == CA_LINK)
|
||||
</pre>
|
||||
|
||||
will still compile and run, but will only work properly with the old CA link
|
||||
type. To operate with the new extensible link types such code must be modified
|
||||
to use the new generic routines defined in dbLink.h and should never attempt to
|
||||
examine or modify data inside the link. After conversion the above line would
|
||||
probably become:
|
||||
|
||||
<pre>
|
||||
if (dbLinkIsVolatile(&prec->inp))
|
||||
</pre>
|
||||
|
||||
A volatile link is one like a Channel Access link which may disconnect and
|
||||
reconnect without notice at runtime. Database links and constant links are not
|
||||
volatile; unless their link address is changed they will always remain in the
|
||||
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)
|
||||
</pre>
|
||||
</li>
|
||||
|
||||
<li>The current connection state of a volatile link can be found using the
|
||||
routine <tt>dbIsLinkConnected()</tt> which will only return TRUE for a volatile
|
||||
link that is currently connected. Code using the older dbCa API returning this
|
||||
information used to look like this:
|
||||
|
||||
<pre>
|
||||
stat = dbCaIsLinkConnected(plink);
|
||||
</pre>
|
||||
|
||||
which should become:
|
||||
<pre>
|
||||
stat = dbIsLinkConnected(plink);
|
||||
</pre>
|
||||
|
||||
Similar changes should be made for calls to the other dbCa routines.</li>
|
||||
|
||||
|
||||
<li>A full example can be found by looking at the changes to the calcout record
|
||||
type, which has been modified in this release to use the new dbLink generic
|
||||
API.</li>
|
||||
|
||||
</ul>
|
||||
|
||||
</blockquote>
|
||||
|
||||
|
||||
<h3>Constant Link Values</h3>
|
||||
|
||||
<p>Previously a constant link (i.e. a link that did not point to another PV,
|
||||
either locally or over Channel Access) was only able to provide a single numeric
|
||||
value to a record initialization; any string given in a link field that was not
|
||||
recognized as a number was treated as a PV name. In this release, constant links
|
||||
can be expressed using JSON array syntax and may provide array initialization of
|
||||
values containing integers, doubles or strings. An array containing a single
|
||||
string value can also be used to initialize scalar strings, so the stringin,
|
||||
stringout, lsi (long string input), lso (long string output), printf, waveform,
|
||||
subArray and aai (analog array input) record types and/or their soft device
|
||||
supports have been modified to support this.</p>
|
||||
|
||||
<p>Some examples of constant array and string initialized records are:</p>
|
||||
|
||||
<pre>
|
||||
record(stringin, "const:string") {
|
||||
field(INP, ["Not-a-PV-name"])
|
||||
}
|
||||
record(waveform, "const:longs") {
|
||||
field(FTVL, LONG)
|
||||
field(NELM, 10)
|
||||
field(INP, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
|
||||
}
|
||||
record(aai, "const:doubles") {
|
||||
field(FTVL, DOUBLE)
|
||||
field(NELM, 10)
|
||||
field(INP, [0, 1, 1.6e-19, 2.718, 3.141593])
|
||||
}
|
||||
record(aSub, "select") {
|
||||
field(FTA, STRING)
|
||||
field(NOA, 4)
|
||||
field(INPA, ["Zero", "One", "Two", "Three"])
|
||||
field(FTB, SHORT)
|
||||
field(NOB, 1)
|
||||
field(FTVA, STRING)
|
||||
field(NOVA, 1)
|
||||
field(SNAM, "select_asub")
|
||||
}
|
||||
</pre>
|
||||
|
||||
<p>Reminder: Link initialization with constant values normally only occurs at
|
||||
record initialization time. The calcout and printf record types are the only
|
||||
exceptions in the Base record types to this rule, so it is generally not useful
|
||||
to change a const link value after iocInit.</p>
|
||||
|
||||
|
||||
<h3>Database Parsing of "Relaxed JSON" Values</h3>
|
||||
|
||||
<p>A database file can now provide a "relaxed JSON" value for a database field
|
||||
value or an info tag. Only a few field types can currently accept such values,
|
||||
but the capability is now available for use in other places in the future. When
|
||||
writing to a JSON-capable field at run-time however, only strictly compliant
|
||||
JSON may be used (the dbStaticLib parser rewrites relaxed JSON values into
|
||||
strict JSON before passing them to the datase for interpretation, where the
|
||||
strict rules must be followed).</p>
|
||||
|
||||
<p>"Relaxed JSON" was developed to maximize compatibility with the previous
|
||||
database parser rules and reduce the number of double-quotes that would be
|
||||
needed for strict JSON syntax. The parser does accept strict JSON too though,
|
||||
which should be used when machine-generating database files. The differences
|
||||
are:</p>
|
||||
|
||||
<ul>
|
||||
|
||||
<li>Strings containing only the characters <tt><b>a-z A-Z 0-9 _ - + .</b></tt>
|
||||
do not have to be enclosed in double-quote characters.</li>
|
||||
|
||||
<li>The above rule applies to map keys as well as to regular string values.</li>
|
||||
|
||||
<li>The JSON keywords <tt>null</tt>, <tt>true</tt> and <tt>false</tt> (all
|
||||
lower-case) will be recognized as keywords, so they must be quoted to use any of
|
||||
these single words as a string.</li>
|
||||
|
||||
<li>Comments may be used, introduced as usual by the <tt><b>#</b></tt>
|
||||
character and extending to the end of the line.</li>
|
||||
|
||||
</ul>
|
||||
|
||||
<p>A JSON field or info value is only enclosed in quotes when the value being
|
||||
provided is a single string, and even here the quotes can be omitted in some
|
||||
cases as described above. The following shows both correct and incorrect
|
||||
excerpts from a database file:</p>
|
||||
|
||||
<pre>
|
||||
record(ai, math:pi) {
|
||||
field(INP, {const: 3.14159265358979}) # Correct
|
||||
field(SIOL, "{const: 3.142857}") # Wrong
|
||||
|
||||
info(autosave, { # White-space and comments are allowed
|
||||
fields:[DESC, SIMM],
|
||||
pass0:[VAL]
|
||||
}) # Correct
|
||||
}
|
||||
</pre>
|
||||
|
||||
<p>Note that the record, field and info-tag names do <em>not</em> accept JSON
|
||||
values, so they follows the older bareword rules for quoting where the colon
|
||||
<tt><b>:</b></tt> and several additional characters are legal in a bareword
|
||||
string. Only the value (after the comma) is parsed as JSON. The autosave module
|
||||
has not been modified to accept JSON syntax, the above is only an example of
|
||||
how JSON might be used.</p>
|
||||
|
||||
<h3>Echoless comments in iocsh</h3>
|
||||
|
||||
<p>The way comments are parsed by the iocsh interpreter has changed. The
|
||||
|
||||
@@ -23,5 +23,4 @@ dbCore_SRCS += asIocRegister.c
|
||||
|
||||
PROD_HOST += ascheck
|
||||
ascheck_SRCS = ascheck.c
|
||||
ascheck_LIBS = dbCore
|
||||
|
||||
ascheck_LIBS = dbCore ca
|
||||
|
||||
@@ -14,14 +14,18 @@ SRC_DIRS += $(IOCDIR)/db
|
||||
INC += callback.h
|
||||
INC += dbAccess.h
|
||||
INC += dbAccessDefs.h
|
||||
INC += dbCa.h
|
||||
INC += dbAddr.h
|
||||
INC += dbBkpt.h
|
||||
INC += dbCa.h
|
||||
INC += dbChannel.h
|
||||
INC += dbConstLink.h
|
||||
INC += dbConvert.h
|
||||
INC += dbConvertFast.h
|
||||
INC += dbConvertJSON.h
|
||||
INC += dbDbLink.h
|
||||
INC += dbExtractArray.h
|
||||
INC += dbEvent.h
|
||||
INC += dbJLink.h
|
||||
INC += dbLink.h
|
||||
INC += dbLock.h
|
||||
INC += dbNotify.h
|
||||
@@ -64,9 +68,13 @@ dbCore_SRCS += dbLock.c
|
||||
dbCore_SRCS += dbAccess.c
|
||||
dbCore_SRCS += dbBkpt.c
|
||||
dbCore_SRCS += dbChannel.c
|
||||
dbCore_SRCS += dbConstLink.c
|
||||
dbCore_SRCS += dbConvert.c
|
||||
dbCore_SRCS += dbConvertJSON.c
|
||||
dbCore_SRCS += dbDbLink.c
|
||||
dbCore_SRCS += dbFastLinkConv.c
|
||||
dbCore_SRCS += dbExtractArray.c
|
||||
dbCore_SRCS += dbJLink.c
|
||||
dbCore_SRCS += dbLink.c
|
||||
dbCore_SRCS += dbNotify.c
|
||||
dbCore_SRCS += dbScan.c
|
||||
|
||||
@@ -41,7 +41,6 @@
|
||||
#include "dbAddr.h"
|
||||
#include "dbBase.h"
|
||||
#include "dbBkpt.h"
|
||||
#include "dbCa.h"
|
||||
#include "dbCommonPvt.h"
|
||||
#include "dbConvertFast.h"
|
||||
#include "dbConvert.h"
|
||||
@@ -668,7 +667,7 @@ long dbNameToAddr(const char *pname, DBADDR *paddr)
|
||||
paddr->dbr_field_type = DBR_CHAR;
|
||||
} else if (dbfType >= DBF_INLINK && dbfType <= DBF_FWDLINK) {
|
||||
/* Clients see a char array, but keep original dbfType */
|
||||
paddr->no_elements = PVNAME_STRINGSZ + 12;
|
||||
paddr->no_elements = PVLINK_STRINGSZ;
|
||||
paddr->field_size = 1;
|
||||
paddr->dbr_field_type = DBR_CHAR;
|
||||
} else {
|
||||
@@ -1033,7 +1032,7 @@ static long dbPutFieldLink(DBADDR *paddr,
|
||||
return S_db_badDbrtype;
|
||||
}
|
||||
|
||||
status = dbParseLink(pstring, pfldDes->field_type, &link_info);
|
||||
status = dbParseLink(pstring, pfldDes->field_type, &link_info, 0);
|
||||
if (status)
|
||||
return status;
|
||||
|
||||
@@ -1106,23 +1105,12 @@ static long dbPutFieldLink(DBADDR *paddr,
|
||||
}
|
||||
}
|
||||
|
||||
switch (plink->type) { /* Old link type */
|
||||
case DB_LINK:
|
||||
case CA_LINK:
|
||||
case CONSTANT:
|
||||
dbRemoveLink(&locker, plink); /* link type becomes PV_LINK */
|
||||
break;
|
||||
|
||||
case PV_LINK:
|
||||
case MACRO_LINK:
|
||||
break; /* should never get here */
|
||||
|
||||
default: /* Hardware address */
|
||||
if (!isDevLink) {
|
||||
status = S_db_badHWaddr;
|
||||
goto restoreScan;
|
||||
}
|
||||
break;
|
||||
if (dbLinkIsDefined(plink)) {
|
||||
dbRemoveLink(&locker, plink); /* Clear out old link */
|
||||
}
|
||||
else if (!isDevLink) {
|
||||
status = S_db_badHWaddr;
|
||||
goto restoreScan;
|
||||
}
|
||||
|
||||
if (special) status = dbPutSpecial(paddr, 0);
|
||||
@@ -1155,6 +1143,7 @@ static long dbPutFieldLink(DBADDR *paddr,
|
||||
switch (plink->type) { /* New link type */
|
||||
case PV_LINK:
|
||||
case CONSTANT:
|
||||
case JSON_LINK:
|
||||
dbAddLink(&locker, plink, pfldDes->field_type, pdbaddr);
|
||||
break;
|
||||
|
||||
|
||||
@@ -182,6 +182,7 @@ struct dbr_alDouble {DBRalDouble};
|
||||
#define S_db_badChoice (M_dbAccess|13) /*Illegal choice*/
|
||||
#define S_db_badField (M_dbAccess|15) /*Illegal field value*/
|
||||
#define S_db_lsetLogic (M_dbAccess|17) /*Logic error generating lock sets*/
|
||||
#define S_db_noLSET (M_dbAccess|21) /*No link support table or entry*/
|
||||
#define S_db_noRSET (M_dbAccess|31) /*missing record support entry table*/
|
||||
#define S_db_noSupport (M_dbAccess|33) /*RSET or DSXT routine not defined*/
|
||||
#define S_db_BadSub (M_dbAccess|35) /*Subroutine not found*/
|
||||
|
||||
@@ -49,6 +49,7 @@
|
||||
#include "dbLock.h"
|
||||
#include "dbScan.h"
|
||||
#include "link.h"
|
||||
#include "recGbl.h"
|
||||
#include "recSup.h"
|
||||
|
||||
/* defined in dbContext.cpp
|
||||
@@ -246,11 +247,8 @@ epicsShareFunc unsigned long dbCaGetUpdateCount(struct link *plink)
|
||||
void dbCaCallbackProcess(void *userPvt)
|
||||
{
|
||||
struct link *plink = (struct link *)userPvt;
|
||||
dbCommon *pdbCommon = plink->precord;
|
||||
|
||||
dbScanLock(pdbCommon);
|
||||
pdbCommon->rset->process(pdbCommon);
|
||||
dbScanUnlock(pdbCommon);
|
||||
dbLinkAsyncComplete(plink);
|
||||
}
|
||||
|
||||
void dbCaShutdown(void)
|
||||
@@ -354,8 +352,8 @@ void dbCaRemoveLink(struct dbLocker *locker, struct link *plink)
|
||||
addAction(pca, CA_CLEAR_CHANNEL);
|
||||
}
|
||||
|
||||
long dbCaGetLink(struct link *plink,short dbrType, void *pdest,
|
||||
epicsEnum16 *pstat, epicsEnum16 *psevr, long *nelements)
|
||||
long dbCaGetLink(struct link *plink, short dbrType, void *pdest,
|
||||
long *nelements)
|
||||
{
|
||||
caLink *pca = (caLink *)plink->value.pv_link.pvt;
|
||||
long status = 0;
|
||||
@@ -427,13 +425,23 @@ long dbCaGetLink(struct link *plink,short dbrType, void *pdest,
|
||||
aConvert(&dbAddr, pdest, ntoget, ntoget, 0);
|
||||
}
|
||||
done:
|
||||
if (pstat) *pstat = pca->stat;
|
||||
if (psevr) *psevr = pca->sevr;
|
||||
if (link_action) addAction(pca, link_action);
|
||||
if (link_action)
|
||||
addAction(pca, link_action);
|
||||
if (!status)
|
||||
recGblInheritSevr(plink->value.pv_link.pvlMask & pvlOptMsMode,
|
||||
plink->precord, pca->stat, pca->sevr);
|
||||
epicsMutexUnlock(pca->lock);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static long dbCaPutAsync(struct link *plink,short dbrType,
|
||||
const void *pbuffer,long nRequest)
|
||||
{
|
||||
return dbCaPutLinkCallback(plink, dbrType, pbuffer, nRequest,
|
||||
dbCaCallbackProcess, plink);
|
||||
}
|
||||
|
||||
long dbCaPutLinkCallback(struct link *plink,short dbrType,
|
||||
const void *pbuffer,long nRequest,dbCaCallback callback,void *userPvt)
|
||||
{
|
||||
@@ -690,6 +698,17 @@ static long getUnits(const struct link *plink,
|
||||
return gotAttributes ? 0 : -1;
|
||||
}
|
||||
|
||||
static long doLocked(struct link *plink, dbLinkUserCallback rtn, void *priv)
|
||||
{
|
||||
caLink *pca;
|
||||
long status;
|
||||
|
||||
pcaGetCheck
|
||||
status = rtn(plink, priv);
|
||||
epicsMutexUnlock(pca->lock);
|
||||
return status;
|
||||
}
|
||||
|
||||
static void scanComplete(void *raw, dbCommon *prec)
|
||||
{
|
||||
caLink *pca = raw;
|
||||
@@ -723,15 +742,17 @@ static void scanLinkOnce(dbCommon *prec, caLink *pca) {
|
||||
}
|
||||
|
||||
static lset dbCa_lset = {
|
||||
dbCaRemoveLink,
|
||||
0, 1, /* not Constant, Volatile */
|
||||
NULL, dbCaRemoveLink,
|
||||
NULL, NULL, NULL,
|
||||
isConnected,
|
||||
getDBFtype, getElements,
|
||||
dbCaGetLink,
|
||||
getControlLimits, getGraphicLimits, getAlarmLimits,
|
||||
getPrecision, getUnits,
|
||||
getAlarm, getTimeStamp,
|
||||
dbCaPutLink,
|
||||
scanForward
|
||||
dbCaPutLink, dbCaPutAsync,
|
||||
scanForward, doLocked
|
||||
};
|
||||
|
||||
static void connectionCallback(struct connection_handler_args arg)
|
||||
|
||||
@@ -33,8 +33,7 @@ epicsShareFunc long dbCaAddLink(struct dbLocker *locker, struct link *plink, sho
|
||||
epicsShareFunc void dbCaRemoveLink(struct dbLocker *locker, struct link *plink);
|
||||
|
||||
epicsShareFunc long dbCaGetLink(struct link *plink,
|
||||
short dbrType, void *pbuffer, epicsEnum16 *pstat, epicsEnum16 *psevr,
|
||||
long *nRequest);
|
||||
short dbrType, void *pbuffer, long *nRequest);
|
||||
|
||||
epicsShareFunc long dbCaGetAttributes(const struct link *plink,
|
||||
dbCaCallback callback, void *userPvt);
|
||||
|
||||
@@ -531,7 +531,7 @@ dbChannel * dbChannelCreate(const char *name)
|
||||
paddr->dbr_field_type = DBR_CHAR;
|
||||
} else if (dbfType >= DBF_INLINK && dbfType <= DBF_FWDLINK) {
|
||||
/* Clients see a char array, but keep original dbfType */
|
||||
paddr->no_elements = PVNAME_STRINGSZ + 12;
|
||||
paddr->no_elements = PVLINK_STRINGSZ;
|
||||
paddr->field_size = 1;
|
||||
paddr->dbr_field_type = DBR_CHAR;
|
||||
} else {
|
||||
|
||||
126
src/ioc/db/dbConstLink.c
Normal file
126
src/ioc/db/dbConstLink.c
Normal file
@@ -0,0 +1,126 @@
|
||||
/*************************************************************************\
|
||||
* Copyright (c) 2010 UChicago Argonne LLC, as Operator of Argonne
|
||||
* National Laboratory.
|
||||
* Copyright (c) 2002 The Regents of the University of California, as
|
||||
* Operator of Los Alamos National Laboratory.
|
||||
* EPICS BASE is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
\*************************************************************************/
|
||||
/* dbConstLink.c
|
||||
*
|
||||
* Original Authors: Bob Dalesio, Marty Kraimer
|
||||
* Current Author: Andrew Johnson
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "dbDefs.h"
|
||||
|
||||
#define epicsExportSharedSymbols
|
||||
#include "dbAccessDefs.h"
|
||||
#include "dbAddr.h"
|
||||
#include "dbCommon.h"
|
||||
#include "dbConstLink.h"
|
||||
#include "dbConvertFast.h"
|
||||
#include "dbConvertJSON.h"
|
||||
#include "dbFldTypes.h"
|
||||
#include "dbLink.h"
|
||||
#include "link.h"
|
||||
|
||||
/***************************** Constant Links *****************************/
|
||||
|
||||
/* Forward definition */
|
||||
static lset dbConst_lset;
|
||||
|
||||
void dbConstInitLink(struct link *plink)
|
||||
{
|
||||
plink->lset = &dbConst_lset;
|
||||
}
|
||||
|
||||
void dbConstAddLink(struct link *plink)
|
||||
{
|
||||
plink->lset = &dbConst_lset;
|
||||
}
|
||||
|
||||
/**************************** Member functions ****************************/
|
||||
|
||||
static long dbConstLoadScalar(struct link *plink, short dbrType, void *pbuffer)
|
||||
{
|
||||
const char *pstr = plink->value.constantStr;
|
||||
size_t len;
|
||||
|
||||
if (!pstr)
|
||||
return S_db_badField;
|
||||
len = strlen(pstr);
|
||||
|
||||
/* Choice values must be numeric */
|
||||
if (dbrType == DBF_MENU || dbrType == DBF_ENUM || dbrType == DBF_DEVICE)
|
||||
dbrType = DBF_USHORT;
|
||||
|
||||
if (*pstr == '[' && pstr[len-1] == ']') {
|
||||
/* Convert from JSON array */
|
||||
long nReq = 1;
|
||||
|
||||
return dbPutConvertJSON(pstr, dbrType, pbuffer, &nReq);
|
||||
}
|
||||
|
||||
return dbFastPutConvertRoutine[DBR_STRING][dbrType]
|
||||
(pstr, pbuffer, NULL);
|
||||
}
|
||||
|
||||
static long dbConstLoadLS(struct link *plink, char *pbuffer, epicsUInt32 size,
|
||||
epicsUInt32 *plen)
|
||||
{
|
||||
const char *pstr = plink->value.constantStr;
|
||||
|
||||
if (!pstr)
|
||||
return S_db_badField;
|
||||
|
||||
return dbLSConvertJSON(pstr, pbuffer, size, plen);
|
||||
}
|
||||
|
||||
static long dbConstLoadArray(struct link *plink, short dbrType, void *pbuffer,
|
||||
long *pnReq)
|
||||
{
|
||||
const char *pstr = plink->value.constantStr;
|
||||
|
||||
if (!pstr)
|
||||
return S_db_badField;
|
||||
|
||||
/* Choice values must be numeric */
|
||||
if (dbrType == DBF_MENU || dbrType == DBF_ENUM || dbrType == DBF_DEVICE)
|
||||
dbrType = DBF_USHORT;
|
||||
|
||||
return dbPutConvertJSON(pstr, dbrType, pbuffer, pnReq);
|
||||
}
|
||||
|
||||
static long dbConstGetNelements(const struct link *plink, long *nelements)
|
||||
{
|
||||
*nelements = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long dbConstGetValue(struct link *plink, short dbrType, void *pbuffer,
|
||||
long *pnRequest)
|
||||
{
|
||||
if (pnRequest)
|
||||
*pnRequest = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static lset dbConst_lset = {
|
||||
1, 0, /* Constant, not Volatile */
|
||||
NULL, NULL,
|
||||
dbConstLoadScalar,
|
||||
dbConstLoadLS,
|
||||
dbConstLoadArray,
|
||||
NULL,
|
||||
NULL, dbConstGetNelements,
|
||||
dbConstGetValue,
|
||||
NULL, NULL, NULL,
|
||||
NULL, NULL,
|
||||
NULL, NULL,
|
||||
NULL, NULL,
|
||||
NULL, NULL
|
||||
};
|
||||
34
src/ioc/db/dbConstLink.h
Normal file
34
src/ioc/db/dbConstLink.h
Normal file
@@ -0,0 +1,34 @@
|
||||
/*************************************************************************\
|
||||
* Copyright (c) 2016 UChicago Argonne LLC, as Operator of Argonne
|
||||
* National Laboratory.
|
||||
* Copyright (c) 2002 The Regents of the University of California, as
|
||||
* Operator of Los Alamos National Laboratory.
|
||||
* EPICS BASE is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
\*************************************************************************/
|
||||
/* dbConstLink.h
|
||||
*
|
||||
* Created on: April 3rd, 2016
|
||||
* Author: Andrew Johnson
|
||||
*/
|
||||
|
||||
#ifndef INC_dbConstLink_H
|
||||
#define INC_dbConstLink_H
|
||||
|
||||
#include "shareLib.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct link;
|
||||
|
||||
epicsShareFunc void dbConstInitLink(struct link *plink);
|
||||
epicsShareFunc void dbConstAddLink(struct link *plink);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* INC_dbConstLink_H */
|
||||
|
||||
257
src/ioc/db/dbConvertJSON.c
Normal file
257
src/ioc/db/dbConvertJSON.c
Normal file
@@ -0,0 +1,257 @@
|
||||
/*************************************************************************\
|
||||
* Copyright (c) 2016 UChicago Argonne LLC, as Operator of Argonne
|
||||
* National Laboratory.
|
||||
* EPICS BASE is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
\*************************************************************************/
|
||||
/* dbConvertJSON.c */
|
||||
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "dbDefs.h"
|
||||
#include "errlog.h"
|
||||
#include "yajl_alloc.h"
|
||||
#include "yajl_parse.h"
|
||||
|
||||
#define epicsExportSharedSymbols
|
||||
#include "dbAccessDefs.h"
|
||||
#include "dbConvertFast.h"
|
||||
#include "dbConvertJSON.h"
|
||||
|
||||
typedef long (*FASTCONVERT)();
|
||||
|
||||
typedef struct parseContext {
|
||||
int depth;
|
||||
short dbrType;
|
||||
short dbrSize;
|
||||
char *pdest;
|
||||
int elems;
|
||||
} parseContext;
|
||||
|
||||
static int dbcj_null(void *ctx) {
|
||||
return 0; /* Illegal */
|
||||
}
|
||||
|
||||
static int dbcj_boolean(void *ctx, int val) {
|
||||
return 0; /* Illegal */
|
||||
}
|
||||
|
||||
static int dbcj_integer(void *ctx, long num) {
|
||||
parseContext *parser = (parseContext *) ctx;
|
||||
epicsInt32 val32 = num;
|
||||
FASTCONVERT conv = dbFastPutConvertRoutine[DBF_LONG][parser->dbrType];
|
||||
|
||||
if (parser->elems > 0) {
|
||||
conv(&val32, parser->pdest, NULL);
|
||||
parser->pdest += parser->dbrSize;
|
||||
parser->elems--;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int dblsj_integer(void *ctx, long num) {
|
||||
return 0; /* Illegal */
|
||||
}
|
||||
|
||||
static int dbcj_double(void *ctx, double num) {
|
||||
parseContext *parser = (parseContext *) ctx;
|
||||
FASTCONVERT conv = dbFastPutConvertRoutine[DBF_DOUBLE][parser->dbrType];
|
||||
|
||||
if (parser->elems > 0) {
|
||||
conv(&num, parser->pdest, NULL);
|
||||
parser->pdest += parser->dbrSize;
|
||||
parser->elems--;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int dblsj_double(void *ctx, double num) {
|
||||
return 0; /* Illegal */
|
||||
}
|
||||
|
||||
static int dbcj_string(void *ctx, const unsigned char *val, unsigned int len) {
|
||||
parseContext *parser = (parseContext *) ctx;
|
||||
char *pdest = parser->pdest;
|
||||
|
||||
/* Not attempting to handle char-array fields here, they need more
|
||||
* metadata about the field than we have available at the moment.
|
||||
*/
|
||||
if (parser->dbrType != DBF_STRING) {
|
||||
errlogPrintf("dbConvertJSON: String provided, numeric value(s) expected\n");
|
||||
return 0; /* Illegal */
|
||||
}
|
||||
|
||||
if (parser->elems > 0) {
|
||||
if (len > parser->dbrSize - 1)
|
||||
len = parser->dbrSize - 1;
|
||||
strncpy(pdest, (const char *) val, len);
|
||||
pdest[len] = 0;
|
||||
parser->pdest += parser->dbrSize;
|
||||
parser->elems--;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int dblsj_string(void *ctx, const unsigned char *val, unsigned int len) {
|
||||
parseContext *parser = (parseContext *) ctx;
|
||||
char *pdest = parser->pdest;
|
||||
|
||||
if (parser->dbrType != DBF_STRING) {
|
||||
errlogPrintf("dbConvertJSON: dblsj_string dbrType error\n");
|
||||
return 0; /* Illegal */
|
||||
}
|
||||
|
||||
if (parser->elems > 0) {
|
||||
if (len > parser->dbrSize - 1)
|
||||
len = parser->dbrSize - 1;
|
||||
strncpy(pdest, (const char *) val, len);
|
||||
pdest[len] = 0;
|
||||
parser->pdest = pdest + len;
|
||||
parser->elems = 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int dbcj_start_map(void *ctx) {
|
||||
errlogPrintf("dbConvertJSON: Map type not supported\n");
|
||||
return 0; /* Illegal */
|
||||
}
|
||||
|
||||
static int dbcj_map_key(void *ctx, const unsigned char *key, unsigned int len) {
|
||||
return 0; /* Illegal */
|
||||
}
|
||||
|
||||
static int dbcj_end_map(void *ctx) {
|
||||
return 0; /* Illegal */
|
||||
}
|
||||
|
||||
static int dbcj_start_array(void *ctx) {
|
||||
parseContext *parser = (parseContext *) ctx;
|
||||
|
||||
if (++parser->depth > 1)
|
||||
errlogPrintf("dbConvertJSON: Embedded arrays not supported\n");
|
||||
|
||||
return (parser->depth == 1);
|
||||
}
|
||||
|
||||
static int dbcj_end_array(void *ctx) {
|
||||
parseContext *parser = (parseContext *) ctx;
|
||||
|
||||
parser->depth--;
|
||||
return (parser->depth == 0);
|
||||
}
|
||||
|
||||
|
||||
static yajl_callbacks dbcj_callbacks = {
|
||||
dbcj_null, dbcj_boolean, dbcj_integer, dbcj_double, NULL, dbcj_string,
|
||||
dbcj_start_map, dbcj_map_key, dbcj_end_map,
|
||||
dbcj_start_array, dbcj_end_array
|
||||
};
|
||||
|
||||
static const yajl_parser_config dbcj_config =
|
||||
{ 0, 0 }; /* allowComments = NO, checkUTF8 = NO */
|
||||
|
||||
long dbPutConvertJSON(const char *json, short dbrType,
|
||||
void *pdest, long *pnRequest)
|
||||
{
|
||||
parseContext context, *parser = &context;
|
||||
yajl_alloc_funcs dbcj_alloc;
|
||||
yajl_handle yh;
|
||||
yajl_status ys;
|
||||
size_t jlen = strlen(json);
|
||||
long status;
|
||||
|
||||
parser->depth = 0;
|
||||
parser->dbrType = dbrType;
|
||||
parser->dbrSize = dbValueSize(dbrType);
|
||||
parser->pdest = pdest;
|
||||
parser->elems = *pnRequest;
|
||||
|
||||
yajl_set_default_alloc_funcs(&dbcj_alloc);
|
||||
yh = yajl_alloc(&dbcj_callbacks, &dbcj_config, &dbcj_alloc, parser);
|
||||
if (!yh)
|
||||
return S_db_noMemory;
|
||||
|
||||
ys = yajl_parse(yh, (const unsigned char *) json, (unsigned int) jlen);
|
||||
if (ys == yajl_status_insufficient_data)
|
||||
ys = yajl_parse_complete(yh);
|
||||
|
||||
switch (ys) {
|
||||
case yajl_status_ok:
|
||||
*pnRequest -= parser->elems;
|
||||
status = 0;
|
||||
break;
|
||||
|
||||
case yajl_status_error: {
|
||||
unsigned char *err = yajl_get_error(yh, 1,
|
||||
(const unsigned char *) json, (unsigned int) jlen);
|
||||
fprintf(stderr, "dbConvertJSON: %s\n", err);
|
||||
yajl_free_error(yh, err);
|
||||
}
|
||||
/* fall through */
|
||||
default:
|
||||
status = S_db_badField;
|
||||
}
|
||||
|
||||
yajl_free(yh);
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
static yajl_callbacks dblsj_callbacks = {
|
||||
dbcj_null, dbcj_boolean, dblsj_integer, dblsj_double, NULL, dblsj_string,
|
||||
dbcj_start_map, dbcj_map_key, dbcj_end_map,
|
||||
dbcj_start_array, dbcj_end_array
|
||||
};
|
||||
|
||||
long dbLSConvertJSON(const char *json, char *pdest, epicsUInt32 size,
|
||||
epicsUInt32 *plen)
|
||||
{
|
||||
parseContext context, *parser = &context;
|
||||
yajl_alloc_funcs dbcj_alloc;
|
||||
yajl_handle yh;
|
||||
yajl_status ys;
|
||||
size_t jlen = strlen(json);
|
||||
long status;
|
||||
|
||||
if (!size) {
|
||||
*plen = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
parser->depth = 0;
|
||||
parser->dbrType = DBF_STRING;
|
||||
parser->dbrSize = size;
|
||||
parser->pdest = pdest;
|
||||
parser->elems = 1;
|
||||
|
||||
yajl_set_default_alloc_funcs(&dbcj_alloc);
|
||||
yh = yajl_alloc(&dblsj_callbacks, &dbcj_config, &dbcj_alloc, parser);
|
||||
if (!yh)
|
||||
return S_db_noMemory;
|
||||
|
||||
ys = yajl_parse(yh, (const unsigned char *) json, (unsigned int) jlen);
|
||||
if (ys == yajl_status_insufficient_data)
|
||||
ys = yajl_parse_complete(yh);
|
||||
|
||||
switch (ys) {
|
||||
case yajl_status_ok:
|
||||
*plen = (char *) parser->pdest - pdest + 1;
|
||||
status = 0;
|
||||
break;
|
||||
|
||||
case yajl_status_error: {
|
||||
unsigned char *err = yajl_get_error(yh, 1,
|
||||
(const unsigned char *) json, (unsigned int) jlen);
|
||||
fprintf(stderr, "dbLoadLS_JSON: %s\n", err);
|
||||
yajl_free_error(yh, err);
|
||||
}
|
||||
/* fall through */
|
||||
default:
|
||||
status = S_db_badField;
|
||||
}
|
||||
|
||||
yajl_free(yh);
|
||||
return status;
|
||||
}
|
||||
28
src/ioc/db/dbConvertJSON.h
Normal file
28
src/ioc/db/dbConvertJSON.h
Normal file
@@ -0,0 +1,28 @@
|
||||
/*************************************************************************\
|
||||
* Copyright (c) 2016 UChicago Argonne LLC, as Operator of Argonne
|
||||
* National Laboratory.
|
||||
* EPICS BASE is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
\*************************************************************************/
|
||||
/* dbConvertJSON.h */
|
||||
|
||||
#ifndef INC_dbConvertJSON_H
|
||||
#define INC_dbConvertJSON_H
|
||||
|
||||
#include <shareLib.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* This name should probably be changed to inclue "array" */
|
||||
epicsShareFunc long dbPutConvertJSON(const char *json, short dbrType,
|
||||
void *pdest, long *psize);
|
||||
epicsShareFunc long dbLSConvertJSON(const char *json, char *pdest,
|
||||
epicsUInt32 size, epicsUInt32 *plen);
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* INC_dbConvertJSON_H */
|
||||
|
||||
358
src/ioc/db/dbDbLink.c
Normal file
358
src/ioc/db/dbDbLink.c
Normal file
@@ -0,0 +1,358 @@
|
||||
/*************************************************************************\
|
||||
* Copyright (c) 2010 UChicago Argonne LLC, as Operator of Argonne
|
||||
* National Laboratory.
|
||||
* Copyright (c) 2002 The Regents of the University of California, as
|
||||
* Operator of Los Alamos National Laboratory.
|
||||
* EPICS BASE is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
\*************************************************************************/
|
||||
/* dbDbLink.c
|
||||
*
|
||||
* Original Authors: Bob Dalesio, Marty Kraimer
|
||||
* Current Author: Andrew Johnson
|
||||
*/
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "alarm.h"
|
||||
#include "cantProceed.h"
|
||||
#include "cvtFast.h"
|
||||
#include "dbDefs.h"
|
||||
#include "ellLib.h"
|
||||
#include "epicsTime.h"
|
||||
#include "errlog.h"
|
||||
|
||||
#include "caeventmask.h"
|
||||
|
||||
#define epicsExportSharedSymbols
|
||||
#include "dbAccessDefs.h"
|
||||
#include "dbAddr.h"
|
||||
#include "dbBase.h"
|
||||
#include "dbBkpt.h"
|
||||
#include "dbCommon.h"
|
||||
#include "dbConvertFast.h"
|
||||
#include "dbConvert.h"
|
||||
#include "db_field_log.h"
|
||||
#include "dbFldTypes.h"
|
||||
#include "dbLink.h"
|
||||
#include "dbLockPvt.h"
|
||||
#include "dbNotify.h"
|
||||
#include "dbScan.h"
|
||||
#include "dbStaticLib.h"
|
||||
#include "devSup.h"
|
||||
#include "link.h"
|
||||
#include "recGbl.h"
|
||||
#include "recSup.h"
|
||||
#include "special.h"
|
||||
|
||||
/***************************** Database Links *****************************/
|
||||
|
||||
/* Forward definition */
|
||||
static lset dbDb_lset;
|
||||
|
||||
long dbDbInitLink(struct link *plink, short dbfType)
|
||||
{
|
||||
DBADDR dbaddr;
|
||||
long status;
|
||||
DBADDR *pdbAddr;
|
||||
|
||||
status = dbNameToAddr(plink->value.pv_link.pvname, &dbaddr);
|
||||
if (status)
|
||||
return status;
|
||||
|
||||
plink->lset = &dbDb_lset;
|
||||
plink->type = DB_LINK;
|
||||
pdbAddr = dbCalloc(1, sizeof(struct dbAddr));
|
||||
*pdbAddr = dbaddr; /* structure copy */
|
||||
plink->value.pv_link.pvt = pdbAddr;
|
||||
ellAdd(&dbaddr.precord->bklnk, &plink->value.pv_link.backlinknode);
|
||||
/* merging into the same lockset is deferred to the caller.
|
||||
* cf. initPVLinks()
|
||||
*/
|
||||
dbLockSetMerge(NULL, plink->precord, dbaddr.precord);
|
||||
assert(plink->precord->lset->plockSet == dbaddr.precord->lset->plockSet);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void dbDbAddLink(struct dbLocker *locker, struct link *plink, short dbfType,
|
||||
DBADDR *ptarget)
|
||||
{
|
||||
plink->lset = &dbDb_lset;
|
||||
plink->type = DB_LINK;
|
||||
plink->value.pv_link.pvt = ptarget;
|
||||
ellAdd(&ptarget->precord->bklnk, &plink->value.pv_link.backlinknode);
|
||||
|
||||
/* target record is already locked in dbPutFieldLink() */
|
||||
dbLockSetMerge(locker, plink->precord, ptarget->precord);
|
||||
}
|
||||
|
||||
static void dbDbRemoveLink(struct dbLocker *locker, struct link *plink)
|
||||
{
|
||||
DBADDR *pdbAddr = (DBADDR *) plink->value.pv_link.pvt;
|
||||
|
||||
plink->type = PV_LINK;
|
||||
|
||||
/* locker is NULL when an isolated IOC is closing its links */
|
||||
if (locker) {
|
||||
plink->value.pv_link.pvt = 0;
|
||||
plink->value.pv_link.getCvt = 0;
|
||||
plink->value.pv_link.pvlMask = 0;
|
||||
plink->value.pv_link.lastGetdbrType = 0;
|
||||
ellDelete(&pdbAddr->precord->bklnk, &plink->value.pv_link.backlinknode);
|
||||
dbLockSetSplit(locker, plink->precord, pdbAddr->precord);
|
||||
}
|
||||
free(pdbAddr);
|
||||
}
|
||||
|
||||
static int dbDbIsConnected(const struct link *plink)
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static int dbDbGetDBFtype(const struct link *plink)
|
||||
{
|
||||
DBADDR *paddr = (DBADDR *) plink->value.pv_link.pvt;
|
||||
|
||||
return paddr->field_type;
|
||||
}
|
||||
|
||||
static long dbDbGetElements(const struct link *plink, long *nelements)
|
||||
{
|
||||
DBADDR *paddr = (DBADDR *) plink->value.pv_link.pvt;
|
||||
|
||||
*nelements = paddr->no_elements;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long dbDbGetValue(struct link *plink, short dbrType, void *pbuffer,
|
||||
long *pnRequest)
|
||||
{
|
||||
struct pv_link *ppv_link = &plink->value.pv_link;
|
||||
DBADDR *paddr = ppv_link->pvt;
|
||||
dbCommon *precord = plink->precord;
|
||||
long status;
|
||||
|
||||
/* scan passive records if link is process passive */
|
||||
if (ppv_link->pvlMask & pvlOptPP) {
|
||||
unsigned char pact = precord->pact;
|
||||
|
||||
precord->pact = TRUE;
|
||||
status = dbScanPassive(precord, paddr->precord);
|
||||
precord->pact = pact;
|
||||
if (status)
|
||||
return status;
|
||||
}
|
||||
|
||||
if (ppv_link->getCvt && ppv_link->lastGetdbrType == dbrType) {
|
||||
status = ppv_link->getCvt(paddr->pfield, pbuffer, paddr);
|
||||
} else {
|
||||
unsigned short dbfType = paddr->field_type;
|
||||
|
||||
if (dbrType < 0 || dbrType > DBR_ENUM || dbfType > DBF_DEVICE)
|
||||
return S_db_badDbrtype;
|
||||
|
||||
if (paddr->no_elements == 1 && (!pnRequest || *pnRequest == 1)
|
||||
&& paddr->special != SPC_DBADDR
|
||||
&& paddr->special != SPC_ATTRIBUTE) {
|
||||
ppv_link->getCvt = dbFastGetConvertRoutine[dbfType][dbrType];
|
||||
status = ppv_link->getCvt(paddr->pfield, pbuffer, paddr);
|
||||
} else {
|
||||
ppv_link->getCvt = NULL;
|
||||
status = dbGet(paddr, dbrType, pbuffer, NULL, pnRequest, NULL);
|
||||
}
|
||||
ppv_link->lastGetdbrType = dbrType;
|
||||
}
|
||||
|
||||
if (!status)
|
||||
recGblInheritSevr(plink->value.pv_link.pvlMask & pvlOptMsMode,
|
||||
plink->precord, paddr->precord->stat, paddr->precord->sevr);
|
||||
return status;
|
||||
}
|
||||
|
||||
static long dbDbGetControlLimits(const struct link *plink, double *low,
|
||||
double *high)
|
||||
{
|
||||
DBADDR *paddr = (DBADDR *) plink->value.pv_link.pvt;
|
||||
struct buffer {
|
||||
DBRctrlDouble
|
||||
double value;
|
||||
} buffer;
|
||||
long options = DBR_CTRL_DOUBLE;
|
||||
long number_elements = 0;
|
||||
long status = dbGet(paddr, DBR_DOUBLE, &buffer, &options, &number_elements,
|
||||
NULL);
|
||||
|
||||
if (status)
|
||||
return status;
|
||||
|
||||
*low = buffer.lower_ctrl_limit;
|
||||
*high = buffer.upper_ctrl_limit;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long dbDbGetGraphicLimits(const struct link *plink, double *low,
|
||||
double *high)
|
||||
{
|
||||
DBADDR *paddr = (DBADDR *) plink->value.pv_link.pvt;
|
||||
struct buffer {
|
||||
DBRgrDouble
|
||||
double value;
|
||||
} buffer;
|
||||
long options = DBR_GR_DOUBLE;
|
||||
long number_elements = 0;
|
||||
long status = dbGet(paddr, DBR_DOUBLE, &buffer, &options, &number_elements,
|
||||
NULL);
|
||||
|
||||
if (status)
|
||||
return status;
|
||||
|
||||
*low = buffer.lower_disp_limit;
|
||||
*high = buffer.upper_disp_limit;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long dbDbGetAlarmLimits(const struct link *plink, double *lolo,
|
||||
double *low, double *high, double *hihi)
|
||||
{
|
||||
DBADDR *paddr = (DBADDR *) plink->value.pv_link.pvt;
|
||||
struct buffer {
|
||||
DBRalDouble
|
||||
double value;
|
||||
} buffer;
|
||||
long options = DBR_AL_DOUBLE;
|
||||
long number_elements = 0;
|
||||
long status = dbGet(paddr, DBR_DOUBLE, &buffer, &options, &number_elements,
|
||||
0);
|
||||
|
||||
if (status)
|
||||
return status;
|
||||
|
||||
*lolo = buffer.lower_alarm_limit;
|
||||
*low = buffer.lower_warning_limit;
|
||||
*high = buffer.upper_warning_limit;
|
||||
*hihi = buffer.upper_alarm_limit;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long dbDbGetPrecision(const struct link *plink, short *precision)
|
||||
{
|
||||
DBADDR *paddr = (DBADDR *) plink->value.pv_link.pvt;
|
||||
struct buffer {
|
||||
DBRprecision
|
||||
double value;
|
||||
} buffer;
|
||||
long options = DBR_PRECISION;
|
||||
long number_elements = 0;
|
||||
long status = dbGet(paddr, DBR_DOUBLE, &buffer, &options, &number_elements,
|
||||
0);
|
||||
|
||||
if (status)
|
||||
return status;
|
||||
|
||||
*precision = (short) buffer.precision.dp;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long dbDbGetUnits(const struct link *plink, char *units, int unitsSize)
|
||||
{
|
||||
DBADDR *paddr = (DBADDR *) plink->value.pv_link.pvt;
|
||||
struct buffer {
|
||||
DBRunits
|
||||
double value;
|
||||
} buffer;
|
||||
long options = DBR_UNITS;
|
||||
long number_elements = 0;
|
||||
long status = dbGet(paddr, DBR_DOUBLE, &buffer, &options, &number_elements,
|
||||
0);
|
||||
|
||||
if (status)
|
||||
return status;
|
||||
|
||||
strncpy(units, buffer.units, unitsSize);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long dbDbGetAlarm(const struct link *plink, epicsEnum16 *status,
|
||||
epicsEnum16 *severity)
|
||||
{
|
||||
DBADDR *paddr = (DBADDR *) plink->value.pv_link.pvt;
|
||||
|
||||
if (status)
|
||||
*status = paddr->precord->stat;
|
||||
if (severity)
|
||||
*severity = paddr->precord->sevr;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long dbDbGetTimeStamp(const struct link *plink, epicsTimeStamp *pstamp)
|
||||
{
|
||||
DBADDR *paddr = (DBADDR *) plink->value.pv_link.pvt;
|
||||
|
||||
*pstamp = paddr->precord->time;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long dbDbPutValue(struct link *plink, short dbrType,
|
||||
const void *pbuffer, long nRequest)
|
||||
{
|
||||
struct pv_link *ppv_link = &plink->value.pv_link;
|
||||
struct dbCommon *psrce = plink->precord;
|
||||
DBADDR *paddr = (DBADDR *) ppv_link->pvt;
|
||||
dbCommon *pdest = paddr->precord;
|
||||
long status = dbPut(paddr, dbrType, pbuffer, nRequest);
|
||||
|
||||
recGblInheritSevr(ppv_link->pvlMask & pvlOptMsMode, pdest, psrce->nsta,
|
||||
psrce->nsev);
|
||||
if (status)
|
||||
return status;
|
||||
|
||||
if (paddr->pfield == (void *) &pdest->proc ||
|
||||
(ppv_link->pvlMask & pvlOptPP && pdest->scan == 0)) {
|
||||
/* if dbPutField caused asyn record to process */
|
||||
/* ask for reprocessing*/
|
||||
if (pdest->putf) {
|
||||
pdest->rpro = TRUE;
|
||||
} else { /* process dest record with source's PACT true */
|
||||
unsigned char pact;
|
||||
|
||||
if (psrce && psrce->ppn)
|
||||
dbNotifyAdd(psrce, pdest);
|
||||
pact = psrce->pact;
|
||||
psrce->pact = TRUE;
|
||||
status = dbProcess(pdest);
|
||||
psrce->pact = pact;
|
||||
}
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
static void dbDbScanFwdLink(struct link *plink)
|
||||
{
|
||||
dbCommon *precord = plink->precord;
|
||||
dbAddr *paddr = (dbAddr *) plink->value.pv_link.pvt;
|
||||
|
||||
dbScanPassive(precord, paddr->precord);
|
||||
}
|
||||
|
||||
static long doLocked(struct link *plink, dbLinkUserCallback rtn, void *priv)
|
||||
{
|
||||
return rtn(plink, priv);
|
||||
}
|
||||
|
||||
static lset dbDb_lset = {
|
||||
0, 0, /* not Constant, not Volatile */
|
||||
NULL, dbDbRemoveLink,
|
||||
NULL, NULL, NULL,
|
||||
dbDbIsConnected,
|
||||
dbDbGetDBFtype, dbDbGetElements,
|
||||
dbDbGetValue,
|
||||
dbDbGetControlLimits, dbDbGetGraphicLimits, dbDbGetAlarmLimits,
|
||||
dbDbGetPrecision, dbDbGetUnits,
|
||||
dbDbGetAlarm, dbDbGetTimeStamp,
|
||||
dbDbPutValue, NULL,
|
||||
dbDbScanFwdLink, doLocked
|
||||
};
|
||||
35
src/ioc/db/dbDbLink.h
Normal file
35
src/ioc/db/dbDbLink.h
Normal file
@@ -0,0 +1,35 @@
|
||||
/*************************************************************************\
|
||||
* Copyright (c) 2016 UChicago Argonne LLC, as Operator of Argonne
|
||||
* National Laboratory.
|
||||
* Copyright (c) 2002 The Regents of the University of California, as
|
||||
* Operator of Los Alamos National Laboratory.
|
||||
* EPICS BASE is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
\*************************************************************************/
|
||||
/* dbDbLink.h
|
||||
*
|
||||
* Created on: April 3rd, 2016
|
||||
* Author: Andrew Johnson
|
||||
*/
|
||||
|
||||
#ifndef INC_dbDbLink_H
|
||||
#define INC_dbDbLink_H
|
||||
|
||||
#include "shareLib.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct link;
|
||||
struct dbLocker;
|
||||
|
||||
epicsShareFunc long dbDbInitLink(struct link *plink, short dbfType);
|
||||
epicsShareFunc void dbDbAddLink(struct dbLocker *locker, struct link *plink,
|
||||
short dbfType, DBADDR *ptarget);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* INC_dbDbLink_H */
|
||||
@@ -321,6 +321,22 @@ fail:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
epicsShareFunc void db_cleanup_events(void)
|
||||
{
|
||||
freeListCleanup(dbevEventUserFreeList);
|
||||
dbevEventUserFreeList = NULL;
|
||||
|
||||
freeListCleanup(dbevEventQueueFreeList);
|
||||
dbevEventQueueFreeList = NULL;
|
||||
|
||||
freeListCleanup(dbevEventSubscriptionFreeList);
|
||||
dbevEventSubscriptionFreeList = NULL;
|
||||
|
||||
freeListCleanup(dbevFieldLogFreeList);
|
||||
dbevFieldLogFreeList = NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* DB_CLOSE_EVENTS()
|
||||
*
|
||||
|
||||
@@ -63,6 +63,10 @@ epicsShareFunc void db_flush_extra_labor_event (dbEventCtx);
|
||||
epicsShareFunc int db_post_extra_labor (dbEventCtx ctx);
|
||||
epicsShareFunc void db_event_change_priority ( dbEventCtx ctx, unsigned epicsPriority );
|
||||
|
||||
#ifdef EPICS_PRIVATE_API
|
||||
epicsShareFunc void db_cleanup_events(void);
|
||||
#endif
|
||||
|
||||
typedef void EVENTFUNC (void *user_arg, struct dbChannel *chan,
|
||||
int eventsRemaining, struct db_field_log *pfl);
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
#include "dbCaTest.h"
|
||||
#include "dbEvent.h"
|
||||
#include "dbIocRegister.h"
|
||||
#include "dbJLink.h"
|
||||
#include "dbLock.h"
|
||||
#include "dbNotify.h"
|
||||
#include "dbScan.h"
|
||||
@@ -109,6 +110,16 @@ static void dbcarCallFunc(const iocshArgBuf *args)
|
||||
dbcar(args[0].sval,args[1].ival);
|
||||
}
|
||||
|
||||
/* dbjlr */
|
||||
static const iocshArg dbjlrArg0 = { "record name",iocshArgString};
|
||||
static const iocshArg dbjlrArg1 = { "level",iocshArgInt};
|
||||
static const iocshArg * const dbjlrArgs[2] = {&dbjlrArg0,&dbjlrArg1};
|
||||
static const iocshFuncDef dbjlrFuncDef = {"dbjlr",2,dbjlrArgs};
|
||||
static void dbjlrCallFunc(const iocshArgBuf *args)
|
||||
{
|
||||
dbjlr(args[0].sval,args[1].ival);
|
||||
}
|
||||
|
||||
/* dbel */
|
||||
static const iocshArg dbelArg0 = { "record name",iocshArgString};
|
||||
static const iocshArg dbelArg1 = { "level",iocshArgInt};
|
||||
@@ -395,6 +406,7 @@ void dbIocRegister(void)
|
||||
iocshRegister(&dbsrFuncDef,dbsrCallFunc);
|
||||
iocshRegister(&dbcarFuncDef,dbcarCallFunc);
|
||||
iocshRegister(&dbelFuncDef,dbelCallFunc);
|
||||
iocshRegister(&dbjlrFuncDef,dbjlrCallFunc);
|
||||
|
||||
iocshRegister(&dbLoadDatabaseFuncDef,dbLoadDatabaseCallFunc);
|
||||
iocshRegister(&dbLoadRecordsFuncDef,dbLoadRecordsCallFunc);
|
||||
|
||||
540
src/ioc/db/dbJLink.c
Normal file
540
src/ioc/db/dbJLink.c
Normal file
@@ -0,0 +1,540 @@
|
||||
/*************************************************************************\
|
||||
* Copyright (c) 2016 UChicago Argonne LLC, as Operator of Argonne
|
||||
* National Laboratory.
|
||||
* EPICS BASE is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
\*************************************************************************/
|
||||
/* dbJLink.c */
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "epicsAssert.h"
|
||||
#include "dbmf.h"
|
||||
#include "errlog.h"
|
||||
#include "yajl_alloc.h"
|
||||
#include "yajl_parse.h"
|
||||
|
||||
#define epicsExportSharedSymbols
|
||||
#include "dbAccessDefs.h"
|
||||
#include "dbCommon.h"
|
||||
#include "dbStaticLib.h"
|
||||
#include "dbStaticPvt.h"
|
||||
#include "dbLink.h"
|
||||
#include "dbJLink.h"
|
||||
#include "dbLock.h"
|
||||
#include "dbStaticLib.h"
|
||||
#include "link.h"
|
||||
|
||||
#define IFDEBUG(n) if(parser->parse_debug)
|
||||
|
||||
typedef struct parseContext {
|
||||
jlink *pjlink;
|
||||
jlink *product;
|
||||
short dbfType;
|
||||
short jsonDepth;
|
||||
unsigned key_is_link:1;
|
||||
unsigned parse_debug:1;
|
||||
unsigned lset_debug:1;
|
||||
} parseContext;
|
||||
|
||||
#define CALL_OR_STOP(routine) !(routine) ? jlif_stop : (routine)
|
||||
|
||||
static int dbjl_return(parseContext *parser, jlif_result result) {
|
||||
jlink *pjlink = parser->pjlink;
|
||||
|
||||
IFDEBUG(10) {
|
||||
printf("dbjl_return(%s@%p, %d)\t", pjlink ? pjlink->pif->name : "", pjlink, result);
|
||||
printf(" jsonDepth=%d, parseDepth=%d, key_is_link=%d\n",
|
||||
parser->jsonDepth, pjlink ? pjlink->parseDepth : 0, parser->key_is_link);
|
||||
}
|
||||
|
||||
if (result == jlif_stop && pjlink) {
|
||||
jlink *parent;
|
||||
|
||||
while ((parent = pjlink->parent)) {
|
||||
pjlink->pif->free_jlink(pjlink);
|
||||
pjlink = parent;
|
||||
}
|
||||
pjlink->pif->free_jlink(pjlink);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static int dbjl_value(parseContext *parser, jlif_result result) {
|
||||
jlink *pjlink = parser->pjlink;
|
||||
jlink *parent;
|
||||
|
||||
IFDEBUG(10) {
|
||||
printf("dbjl_value(%s@%p, %d)\t", pjlink ? pjlink->pif->name : "", pjlink, result);
|
||||
printf(" jsonDepth=%d, parseDepth=%d, key_is_link=%d\n",
|
||||
parser->jsonDepth, pjlink ? pjlink->parseDepth : 0, parser->key_is_link);
|
||||
}
|
||||
|
||||
if (result == jlif_stop || pjlink->parseDepth > 0)
|
||||
return dbjl_return(parser, result);
|
||||
|
||||
parent = pjlink->parent;
|
||||
if (!parent) {
|
||||
parser->product = pjlink;
|
||||
} else if (parent->pif->end_child) {
|
||||
parent->pif->end_child(parent, pjlink);
|
||||
}
|
||||
pjlink->debug = 0;
|
||||
|
||||
parser->pjlink = parent;
|
||||
|
||||
IFDEBUG(8)
|
||||
printf("dbjl_value: product = %p\n", pjlink);
|
||||
|
||||
return jlif_continue;
|
||||
}
|
||||
|
||||
static int dbjl_null(void *ctx) {
|
||||
parseContext *parser = (parseContext *) ctx;
|
||||
jlink *pjlink = parser->pjlink;
|
||||
|
||||
IFDEBUG(10)
|
||||
printf("dbjl_null(%s@%p)\n", pjlink ? pjlink->pif->name : "", pjlink);
|
||||
|
||||
assert(pjlink);
|
||||
return dbjl_value(parser,
|
||||
CALL_OR_STOP(pjlink->pif->parse_null)(pjlink));
|
||||
}
|
||||
|
||||
static int dbjl_boolean(void *ctx, int val) {
|
||||
parseContext *parser = (parseContext *) ctx;
|
||||
jlink *pjlink = parser->pjlink;
|
||||
|
||||
assert(pjlink);
|
||||
return dbjl_value(parser,
|
||||
CALL_OR_STOP(pjlink->pif->parse_boolean)(pjlink, val));
|
||||
}
|
||||
|
||||
static int dbjl_integer(void *ctx, long num) {
|
||||
parseContext *parser = (parseContext *) ctx;
|
||||
jlink *pjlink = parser->pjlink;
|
||||
|
||||
IFDEBUG(10)
|
||||
printf("dbjl_integer(%s@%p, %ld)\n",
|
||||
pjlink->pif->name, pjlink, num);
|
||||
|
||||
assert(pjlink);
|
||||
return dbjl_value(parser,
|
||||
CALL_OR_STOP(pjlink->pif->parse_integer)(pjlink, num));
|
||||
}
|
||||
|
||||
static int dbjl_double(void *ctx, double num) {
|
||||
parseContext *parser = (parseContext *) ctx;
|
||||
jlink *pjlink = parser->pjlink;
|
||||
|
||||
IFDEBUG(10)
|
||||
printf("dbjl_double(%s@%p, %g)\n",
|
||||
pjlink->pif->name, pjlink, num);
|
||||
|
||||
assert(pjlink);
|
||||
return dbjl_value(parser,
|
||||
CALL_OR_STOP(pjlink->pif->parse_double)(pjlink, num));
|
||||
}
|
||||
|
||||
static int dbjl_string(void *ctx, const unsigned char *val, unsigned len) {
|
||||
parseContext *parser = (parseContext *) ctx;
|
||||
jlink *pjlink = parser->pjlink;
|
||||
|
||||
IFDEBUG(10)
|
||||
printf("dbjl_string(%s@%p, \"%.*s\")\n",
|
||||
pjlink->pif->name, pjlink, len, val);
|
||||
|
||||
assert(pjlink);
|
||||
return dbjl_value(parser,
|
||||
CALL_OR_STOP(pjlink->pif->parse_string)(pjlink, (const char *) val, len));
|
||||
}
|
||||
|
||||
static int dbjl_start_map(void *ctx) {
|
||||
parseContext *parser = (parseContext *) ctx;
|
||||
jlink *pjlink = parser->pjlink;
|
||||
int result;
|
||||
|
||||
if (!pjlink) {
|
||||
IFDEBUG(10) {
|
||||
printf("dbjl_start_map(NULL)\t");
|
||||
printf(" jsonDepth=%d, parseDepth=00, key_is_link=%d\n",
|
||||
parser->jsonDepth, parser->key_is_link);
|
||||
}
|
||||
|
||||
assert(parser->jsonDepth == 0);
|
||||
parser->jsonDepth++;
|
||||
parser->key_is_link = 1;
|
||||
return jlif_continue; /* Opening '{' */
|
||||
}
|
||||
|
||||
IFDEBUG(10) {
|
||||
printf("dbjl_start_map(%s@%p)\t", pjlink ? pjlink->pif->name : "", pjlink);
|
||||
printf(" jsonDepth=%d, parseDepth=%d, key_is_link=%d\n",
|
||||
parser->jsonDepth, pjlink ? pjlink->parseDepth : 0, parser->key_is_link);
|
||||
}
|
||||
|
||||
pjlink->parseDepth++;
|
||||
parser->jsonDepth++;
|
||||
|
||||
result = CALL_OR_STOP(pjlink->pif->parse_start_map)(pjlink);
|
||||
if (result == jlif_key_child_link) {
|
||||
parser->key_is_link = 1;
|
||||
result = jlif_continue;
|
||||
}
|
||||
|
||||
IFDEBUG(10)
|
||||
printf("dbjl_start_map -> %d\n", result);
|
||||
|
||||
return dbjl_return(parser, result);
|
||||
}
|
||||
|
||||
static int dbjl_map_key(void *ctx, const unsigned char *key, unsigned len) {
|
||||
parseContext *parser = (parseContext *) ctx;
|
||||
jlink *pjlink = parser->pjlink;
|
||||
char *link_name;
|
||||
linkSup *linkSup;
|
||||
jlif *pjlif;
|
||||
|
||||
if (!parser->key_is_link) {
|
||||
if (!pjlink) {
|
||||
errlogPrintf("dbJLinkInit: Illegal second link key '%.*s'\n",
|
||||
len, key);
|
||||
return dbjl_return(parser, jlif_stop);
|
||||
}
|
||||
|
||||
IFDEBUG(10) {
|
||||
printf("dbjl_map_key(%s@%p, \"%.*s\")\t",
|
||||
pjlink->pif->name, pjlink, len, key);
|
||||
printf(" jsonDepth=%d, parseDepth=%d, key_is_link=%d\n",
|
||||
parser->jsonDepth, pjlink ? pjlink->parseDepth : 0, parser->key_is_link);
|
||||
}
|
||||
|
||||
assert(pjlink->parseDepth > 0);
|
||||
return dbjl_return(parser,
|
||||
CALL_OR_STOP(pjlink->pif->parse_map_key)(pjlink,
|
||||
(const char *) key, len));
|
||||
}
|
||||
|
||||
IFDEBUG(10) {
|
||||
printf("dbjl_map_key(NULL, \"%.*s\")\t", len, key);
|
||||
printf(" jsonDepth=%d, parseDepth=00, key_is_link=%d\n",
|
||||
parser->jsonDepth, parser->key_is_link);
|
||||
}
|
||||
|
||||
link_name = dbmfStrndup((const char *) key, len);
|
||||
|
||||
linkSup = dbFindLinkSup(pdbbase, link_name);
|
||||
if (!linkSup) {
|
||||
errlogPrintf("dbJLinkInit: Link type '%s' not found\n",
|
||||
link_name);
|
||||
dbmfFree(link_name);
|
||||
return dbjl_return(parser, jlif_stop);
|
||||
}
|
||||
|
||||
pjlif = linkSup->pjlif;
|
||||
if (!pjlif) {
|
||||
errlogPrintf("dbJLinkInit: Support for Link type '%s' not loaded\n",
|
||||
link_name);
|
||||
dbmfFree(link_name);
|
||||
return dbjl_return(parser, jlif_stop);
|
||||
}
|
||||
|
||||
dbmfFree(link_name);
|
||||
|
||||
pjlink = pjlif->alloc_jlink(parser->dbfType);
|
||||
if (!pjlink) {
|
||||
errlogPrintf("dbJLinkInit: Out of memory\n");
|
||||
return dbjl_return(parser, jlif_stop);
|
||||
}
|
||||
pjlink->pif = pjlif;
|
||||
pjlink->parent = NULL;
|
||||
pjlink->parseDepth = 0;
|
||||
pjlink->debug = !!parser->lset_debug;
|
||||
|
||||
if (parser->pjlink) {
|
||||
/* We're starting a child link, save its parent */
|
||||
pjlink->parent = parser->pjlink;
|
||||
}
|
||||
parser->pjlink = pjlink;
|
||||
parser->key_is_link = 0;
|
||||
|
||||
IFDEBUG(8)
|
||||
printf("dbjl_map_key: New %s@%p\n", pjlink ? pjlink->pif->name : "", pjlink);
|
||||
|
||||
return jlif_continue;
|
||||
}
|
||||
|
||||
static int dbjl_end_map(void *ctx) {
|
||||
parseContext *parser = (parseContext *) ctx;
|
||||
jlink *pjlink = parser->pjlink;
|
||||
jlif_result result;
|
||||
|
||||
IFDEBUG(10) {
|
||||
printf("dbjl_end_map(%s@%p)\t",
|
||||
pjlink ? pjlink->pif->name : "NULL", pjlink);
|
||||
printf(" jsonDepth=%d, parseDepth=%d, key_is_link=%d\n",
|
||||
parser->jsonDepth, pjlink ? pjlink->parseDepth : 0,
|
||||
parser->key_is_link);
|
||||
}
|
||||
|
||||
parser->jsonDepth--;
|
||||
if (pjlink) {
|
||||
pjlink->parseDepth--;
|
||||
|
||||
result = dbjl_value(parser,
|
||||
CALL_OR_STOP(pjlink->pif->parse_end_map)(pjlink));
|
||||
}
|
||||
else {
|
||||
result = jlif_continue;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static int dbjl_start_array(void *ctx) {
|
||||
parseContext *parser = (parseContext *) ctx;
|
||||
jlink *pjlink = parser->pjlink;
|
||||
|
||||
IFDEBUG(10) {
|
||||
printf("dbjl_start_array(%s@%p)\t", pjlink ? pjlink->pif->name : "", pjlink);
|
||||
printf(" jsonDepth=%d, parseDepth=%d, key_is_link=%d\n",
|
||||
parser->jsonDepth, pjlink ? pjlink->parseDepth : 0, parser->key_is_link);
|
||||
}
|
||||
|
||||
assert(pjlink);
|
||||
pjlink->parseDepth++;
|
||||
parser->jsonDepth++;
|
||||
|
||||
return dbjl_return(parser,
|
||||
CALL_OR_STOP(pjlink->pif->parse_start_array)(pjlink));
|
||||
}
|
||||
|
||||
static int dbjl_end_array(void *ctx) {
|
||||
parseContext *parser = (parseContext *) ctx;
|
||||
jlink *pjlink = parser->pjlink;
|
||||
|
||||
IFDEBUG(10) {
|
||||
printf("dbjl_end_array(%s@%p)\t", pjlink ? pjlink->pif->name : "", pjlink);
|
||||
printf(" jsonDepth=%d, parseDepth=%d, key_is_link=%d\n",
|
||||
parser->jsonDepth, pjlink ? pjlink->parseDepth : 0, parser->key_is_link);
|
||||
}
|
||||
|
||||
assert(pjlink);
|
||||
pjlink->parseDepth--;
|
||||
parser->jsonDepth--;
|
||||
|
||||
return dbjl_value(parser,
|
||||
CALL_OR_STOP(pjlink->pif->parse_end_array)(pjlink));
|
||||
}
|
||||
|
||||
|
||||
static yajl_callbacks dbjl_callbacks = {
|
||||
dbjl_null, dbjl_boolean, dbjl_integer, dbjl_double, NULL, dbjl_string,
|
||||
dbjl_start_map, dbjl_map_key, dbjl_end_map, dbjl_start_array, dbjl_end_array
|
||||
};
|
||||
|
||||
static const yajl_parser_config dbjl_config =
|
||||
{ 0, 0 }; /* allowComments = NO, checkUTF8 = NO */
|
||||
|
||||
long dbJLinkParse(const char *json, size_t jlen, short dbfType,
|
||||
jlink **ppjlink, unsigned opts)
|
||||
{
|
||||
parseContext context, *parser = &context;
|
||||
yajl_alloc_funcs dbjl_allocs;
|
||||
yajl_handle yh;
|
||||
yajl_status ys;
|
||||
long status;
|
||||
|
||||
parser->pjlink = NULL;
|
||||
parser->product = NULL;
|
||||
parser->dbfType = dbfType;
|
||||
parser->jsonDepth = 0;
|
||||
parser->key_is_link = 0;
|
||||
parser->parse_debug = !!(opts&LINK_DEBUG_JPARSE);
|
||||
parser->lset_debug = !!(opts&LINK_DEBUG_LSET);
|
||||
|
||||
IFDEBUG(10)
|
||||
printf("dbJLinkInit(\"%.*s\", %d, %p)\n",
|
||||
(int) jlen, json, dbfType, ppjlink);
|
||||
|
||||
IFDEBUG(10)
|
||||
printf("dbJLinkInit: jsonDepth=%d, key_is_link=%d\n",
|
||||
parser->jsonDepth, parser->key_is_link);
|
||||
|
||||
yajl_set_default_alloc_funcs(&dbjl_allocs);
|
||||
yh = yajl_alloc(&dbjl_callbacks, &dbjl_config, &dbjl_allocs, parser);
|
||||
if (!yh)
|
||||
return S_db_noMemory;
|
||||
|
||||
ys = yajl_parse(yh, (const unsigned char *) json, (unsigned) jlen);
|
||||
if (ys == yajl_status_insufficient_data)
|
||||
ys = yajl_parse_complete(yh);
|
||||
|
||||
switch (ys) {
|
||||
unsigned char *err;
|
||||
|
||||
case yajl_status_ok:
|
||||
assert(parser->jsonDepth == 0);
|
||||
*ppjlink = parser->product;
|
||||
status = 0;
|
||||
break;
|
||||
|
||||
case yajl_status_error:
|
||||
err = yajl_get_error(yh, 1, (const unsigned char *) json, (unsigned) jlen);
|
||||
errlogPrintf("dbJLinkInit: %s\n", err);
|
||||
yajl_free_error(yh, err);
|
||||
dbJLinkFree(parser->pjlink);
|
||||
/* fall through */
|
||||
default:
|
||||
status = S_db_badField;
|
||||
}
|
||||
|
||||
yajl_free(yh);
|
||||
return status;
|
||||
}
|
||||
|
||||
long dbJLinkInit(struct link *plink)
|
||||
{
|
||||
jlink *pjlink;
|
||||
|
||||
assert(plink);
|
||||
pjlink = plink->value.json.jlink;
|
||||
|
||||
if (pjlink)
|
||||
plink->lset = pjlink->pif->get_lset(pjlink);
|
||||
|
||||
dbLinkOpen(plink);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void dbJLinkFree(jlink *pjlink)
|
||||
{
|
||||
if (pjlink)
|
||||
pjlink->pif->free_jlink(pjlink);
|
||||
}
|
||||
|
||||
void dbJLinkReport(jlink *pjlink, int level, int indent) {
|
||||
if (pjlink && pjlink->pif->report)
|
||||
pjlink->pif->report(pjlink, level, indent);
|
||||
}
|
||||
|
||||
long dbJLinkMapChildren(struct link *plink, jlink_map_fn rtn, void *ctx)
|
||||
{
|
||||
jlink *pjlink;
|
||||
long status;
|
||||
|
||||
if (!plink || plink->type != JSON_LINK)
|
||||
return 0;
|
||||
|
||||
pjlink = plink->value.json.jlink;
|
||||
if (!pjlink)
|
||||
return 0;
|
||||
|
||||
status = rtn(pjlink, ctx);
|
||||
if (!status && pjlink->pif->map_children)
|
||||
status = pjlink->pif->map_children(pjlink, rtn, ctx);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
long dbjlr(const char *recname, int level)
|
||||
{
|
||||
DBENTRY dbentry;
|
||||
DBENTRY * const pdbentry = &dbentry;
|
||||
long status;
|
||||
|
||||
if (!recname || recname[0] == '\0' || !strcmp(recname, "*")) {
|
||||
recname = NULL;
|
||||
printf("JSON links in all records\n\n");
|
||||
}
|
||||
else
|
||||
printf("JSON links in record '%s'\n\n", recname);
|
||||
|
||||
dbInitEntry(pdbbase, pdbentry);
|
||||
for (status = dbFirstRecordType(pdbentry);
|
||||
status == 0;
|
||||
status = dbNextRecordType(pdbentry)) {
|
||||
for (status = dbFirstRecord(pdbentry);
|
||||
status == 0;
|
||||
status = dbNextRecord(pdbentry)) {
|
||||
dbRecordType *pdbRecordType = pdbentry->precordType;
|
||||
dbCommon *precord = pdbentry->precnode->precord;
|
||||
char *prec = (char *) precord;
|
||||
int i;
|
||||
|
||||
if (recname && strcmp(recname, dbGetRecordName(pdbentry)))
|
||||
continue;
|
||||
if (dbIsAlias(pdbentry))
|
||||
continue;
|
||||
|
||||
printf(" %s record '%s':\n", pdbRecordType->name, precord->name);
|
||||
|
||||
dbScanLock(precord);
|
||||
for (i = 0; i < pdbRecordType->no_links; i++) {
|
||||
int idx = pdbRecordType->link_ind[i];
|
||||
dbFldDes *pdbFldDes = pdbRecordType->papFldDes[idx];
|
||||
DBLINK *plink = (DBLINK *) (prec + pdbFldDes->offset);
|
||||
|
||||
if (plink->type != JSON_LINK)
|
||||
continue;
|
||||
if (!dbLinkIsDefined(plink))
|
||||
continue;
|
||||
|
||||
printf(" Link field '%s':\n", pdbFldDes->name);
|
||||
dbJLinkReport(plink->value.json.jlink, level, 6);
|
||||
}
|
||||
dbScanUnlock(precord);
|
||||
if (recname)
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
done:
|
||||
return 0;
|
||||
}
|
||||
|
||||
long dbJLinkMapAll(char *recname, jlink_map_fn rtn, void *ctx)
|
||||
{
|
||||
DBENTRY dbentry;
|
||||
DBENTRY * const pdbentry = &dbentry;
|
||||
long status;
|
||||
|
||||
if (recname && (recname[0] = '\0' || !strcmp(recname, "*")))
|
||||
recname = NULL;
|
||||
|
||||
dbInitEntry(pdbbase, pdbentry);
|
||||
for (status = dbFirstRecordType(pdbentry);
|
||||
status == 0;
|
||||
status = dbNextRecordType(pdbentry)) {
|
||||
for (status = dbFirstRecord(pdbentry);
|
||||
status == 0;
|
||||
status = dbNextRecord(pdbentry)) {
|
||||
dbRecordType *pdbRecordType = pdbentry->precordType;
|
||||
dbCommon *precord = pdbentry->precnode->precord;
|
||||
char *prec = (char *) precord;
|
||||
int i;
|
||||
|
||||
if (recname && strcmp(recname, dbGetRecordName(pdbentry)))
|
||||
continue;
|
||||
if (dbIsAlias(pdbentry))
|
||||
continue;
|
||||
|
||||
dbScanLock(precord);
|
||||
for (i = 0; i < pdbRecordType->no_links; i++) {
|
||||
int idx = pdbRecordType->link_ind[i];
|
||||
dbFldDes *pdbFldDes = pdbRecordType->papFldDes[idx];
|
||||
DBLINK *plink = (DBLINK *) (prec + pdbFldDes->offset);
|
||||
|
||||
status = dbJLinkMapChildren(plink, rtn, ctx);
|
||||
if (status)
|
||||
goto unlock;
|
||||
}
|
||||
unlock:
|
||||
dbScanUnlock(precord);
|
||||
if (status || recname)
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
done:
|
||||
return status;
|
||||
}
|
||||
133
src/ioc/db/dbJLink.h
Normal file
133
src/ioc/db/dbJLink.h
Normal file
@@ -0,0 +1,133 @@
|
||||
/*************************************************************************\
|
||||
* Copyright (c) 2016 UChicago Argonne LLC, as Operator of Argonne
|
||||
* National Laboratory.
|
||||
* EPICS BASE is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
\*************************************************************************/
|
||||
/* dbJLink.h */
|
||||
|
||||
#ifndef INC_dbJLink_H
|
||||
#define INC_dbJLink_H
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <shareLib.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef enum {
|
||||
jlif_stop = 0,
|
||||
jlif_continue = 1
|
||||
} jlif_result;
|
||||
|
||||
typedef enum {
|
||||
jlif_key_stop = jlif_stop,
|
||||
jlif_key_continue = jlif_continue,
|
||||
jlif_key_child_link
|
||||
} jlif_key_result;
|
||||
|
||||
struct link;
|
||||
struct lset;
|
||||
struct jlif;
|
||||
|
||||
typedef struct jlink {
|
||||
struct jlif *pif; /* Link methods */
|
||||
struct jlink *parent; /* NULL for top-level links */
|
||||
int parseDepth; /* Used by parser, unused afterwards */
|
||||
unsigned debug:1; /* set by caller of jlif operations to request debug output to console */
|
||||
/* Link types extend or embed this structure for private storage */
|
||||
} jlink;
|
||||
|
||||
typedef long (*jlink_map_fn)(jlink *, void *ctx);
|
||||
|
||||
typedef struct jlif {
|
||||
/* Optional parser methods below given as NULL are equivalent to
|
||||
* providing a routine that always returns jlif_stop, meaning that
|
||||
* this JSON construct is not allowed at this point in the parse.
|
||||
*/
|
||||
|
||||
const char *name;
|
||||
/* Name for the link type, used in link value */
|
||||
|
||||
jlink* (*alloc_jlink)(short dbfType);
|
||||
/* Required, allocate new link structure */
|
||||
|
||||
void (*free_jlink)(jlink *);
|
||||
/* Required, release all resources allocated for link */
|
||||
|
||||
jlif_result (*parse_null)(jlink *);
|
||||
/* Optional, parser saw a null value */
|
||||
|
||||
jlif_result (*parse_boolean)(jlink *, int val);
|
||||
/* Optional, parser saw a boolean value */
|
||||
|
||||
jlif_result (*parse_integer)(jlink *, long long num);
|
||||
/* Optional, parser saw an integer value */
|
||||
|
||||
jlif_result (*parse_double)(jlink *, double num);
|
||||
/* Optional, parser saw a double value */
|
||||
|
||||
jlif_result (*parse_string)(jlink *, const char *val, size_t len);
|
||||
/* Optional, parser saw a string value */
|
||||
|
||||
jlif_key_result (*parse_start_map)(jlink *);
|
||||
/* Optional, parser saw an open-brace '{'. Return jlif_key_child_link
|
||||
* to expect a child link next (extra key/value pairs may follow).
|
||||
*/
|
||||
|
||||
jlif_result (*parse_map_key)(jlink *, const char *key, size_t len);
|
||||
/* Optional, parser saw a map key */
|
||||
|
||||
jlif_result (*parse_end_map)(jlink *);
|
||||
/* Optional, parser saw a close-brace '}' */
|
||||
|
||||
jlif_result (*parse_start_array)(jlink *);
|
||||
/* Optional, parser saw an open-bracket */
|
||||
|
||||
jlif_result (*parse_end_array)(jlink *);
|
||||
/* Optional, parser saw a close-bracket */
|
||||
|
||||
void (*end_child)(jlink *parent, jlink *child);
|
||||
/* Optional, called with pointer to the new child link after
|
||||
* parse_start_map() returned jlif_key_child_link */
|
||||
|
||||
struct lset* (*get_lset)(const jlink *);
|
||||
/* Required, return lset for this link instance */
|
||||
|
||||
void (*report)(const jlink *, int level, int indent);
|
||||
/* Optional, print status information about this link instance, then
|
||||
* if (level > 0) print a link identifier (at indent+2) and call
|
||||
* dbJLinkReport(child, level-1, indent+4)
|
||||
* for each child.
|
||||
*/
|
||||
|
||||
long (*map_children)(jlink *, jlink_map_fn rtn, void *ctx);
|
||||
/* Optional, call dbJLinkMapChildren() on all embedded links.
|
||||
* Stop immediately and return status if non-zero.
|
||||
*/
|
||||
|
||||
/* Link types must NOT extend this table with their own routines,
|
||||
* this space is reserved for extensions to the jlink interface.
|
||||
*/
|
||||
} jlif;
|
||||
|
||||
epicsShareFunc long dbJLinkParse(const char *json, size_t len, short dbfType,
|
||||
jlink **ppjlink, unsigned opts);
|
||||
epicsShareFunc long dbJLinkInit(struct link *plink);
|
||||
|
||||
epicsShareFunc void dbJLinkFree(jlink *);
|
||||
epicsShareFunc void dbJLinkReport(jlink *, int level, int indent);
|
||||
|
||||
epicsShareFunc long dbJLinkMapChildren(struct link *,
|
||||
jlink_map_fn rtn, void *ctx);
|
||||
|
||||
epicsShareFunc long dbjlr(const char *recname, int level);
|
||||
epicsShareFunc long dbJLinkMapAll(char *recname, jlink_map_fn rtn, void *ctx);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* INC_dbJLink_H */
|
||||
|
||||
@@ -19,62 +19,35 @@
|
||||
#include <string.h>
|
||||
|
||||
#include "alarm.h"
|
||||
#include "cantProceed.h"
|
||||
#include "cvtFast.h"
|
||||
#include "dbDefs.h"
|
||||
#include "ellLib.h"
|
||||
#include "epicsThread.h"
|
||||
#include "epicsTime.h"
|
||||
#include "errlog.h"
|
||||
|
||||
#include "caeventmask.h"
|
||||
|
||||
#define epicsExportSharedSymbols
|
||||
#include "callback.h"
|
||||
#include "dbAccessDefs.h"
|
||||
#include "dbAddr.h"
|
||||
#include "dbBase.h"
|
||||
#include "dbBkpt.h"
|
||||
#include "dbCa.h"
|
||||
#include "dbCommon.h"
|
||||
#include "dbConvertFast.h"
|
||||
#include "dbConvert.h"
|
||||
#include "dbEvent.h"
|
||||
#include "dbConstLink.h"
|
||||
#include "dbDbLink.h"
|
||||
#include "db_field_log.h"
|
||||
#include "dbFldTypes.h"
|
||||
#include "dbFldTypes.h"
|
||||
#include "dbJLink.h"
|
||||
#include "dbLink.h"
|
||||
#include "dbLockPvt.h"
|
||||
#include "dbNotify.h"
|
||||
#include "dbLock.h"
|
||||
#include "dbScan.h"
|
||||
#include "dbStaticLib.h"
|
||||
#include "devSup.h"
|
||||
#include "epicsEvent.h"
|
||||
#include "errMdef.h"
|
||||
#include "link.h"
|
||||
#include "recGbl.h"
|
||||
#include "recSup.h"
|
||||
#include "special.h"
|
||||
|
||||
static void inherit_severity(const struct pv_link *ppv_link, dbCommon *pdest,
|
||||
epicsEnum16 stat, epicsEnum16 sevr)
|
||||
{
|
||||
switch (ppv_link->pvlMask & pvlOptMsMode) {
|
||||
case pvlOptNMS:
|
||||
break;
|
||||
case pvlOptMSI:
|
||||
if (sevr < INVALID_ALARM)
|
||||
break;
|
||||
/* Fall through */
|
||||
case pvlOptMS:
|
||||
recGblSetSevr(pdest, LINK_ALARM, sevr);
|
||||
break;
|
||||
case pvlOptMSS:
|
||||
recGblSetSevr(pdest, stat, sevr);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* How to identify links in error messages */
|
||||
static const char * link_field_name(const struct link *plink)
|
||||
{
|
||||
@@ -94,355 +67,6 @@ static const char * link_field_name(const struct link *plink)
|
||||
}
|
||||
|
||||
|
||||
/***************************** Constant Links *****************************/
|
||||
|
||||
/* Forward definition */
|
||||
static lset dbConst_lset;
|
||||
|
||||
static void dbConstInitLink(struct link *plink)
|
||||
{
|
||||
plink->lset = &dbConst_lset;
|
||||
}
|
||||
|
||||
static void dbConstAddLink(struct link *plink)
|
||||
{
|
||||
plink->lset = &dbConst_lset;
|
||||
}
|
||||
|
||||
static long dbConstLoadLink(struct link *plink, short dbrType, void *pbuffer)
|
||||
{
|
||||
if (!plink->value.constantStr)
|
||||
return S_db_badField;
|
||||
|
||||
plink->lset = &dbConst_lset;
|
||||
|
||||
/* Constant strings are always numeric */
|
||||
if (dbrType == DBF_MENU || dbrType == DBF_ENUM || dbrType == DBF_DEVICE)
|
||||
dbrType = DBF_USHORT;
|
||||
|
||||
return dbFastPutConvertRoutine[DBR_STRING][dbrType]
|
||||
(plink->value.constantStr, pbuffer, NULL);
|
||||
}
|
||||
|
||||
static long dbConstGetNelements(const struct link *plink, long *nelements)
|
||||
{
|
||||
*nelements = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long dbConstGetValue(struct link *plink, short dbrType, void *pbuffer,
|
||||
epicsEnum16 *pstat, epicsEnum16 *psevr, long *pnRequest)
|
||||
{
|
||||
if (pnRequest)
|
||||
*pnRequest = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static lset dbConst_lset = {
|
||||
NULL,
|
||||
NULL,
|
||||
NULL, dbConstGetNelements,
|
||||
dbConstGetValue,
|
||||
NULL, NULL, NULL,
|
||||
NULL, NULL,
|
||||
NULL, NULL,
|
||||
NULL,
|
||||
NULL
|
||||
};
|
||||
|
||||
/***************************** Database Links *****************************/
|
||||
|
||||
/* Forward definition */
|
||||
static lset dbDb_lset;
|
||||
|
||||
static long dbDbInitLink(struct link *plink, short dbfType)
|
||||
{
|
||||
DBADDR dbaddr;
|
||||
long status;
|
||||
DBADDR *pdbAddr;
|
||||
|
||||
status = dbNameToAddr(plink->value.pv_link.pvname, &dbaddr);
|
||||
if (status)
|
||||
return status;
|
||||
|
||||
plink->lset = &dbDb_lset;
|
||||
plink->type = DB_LINK;
|
||||
pdbAddr = dbCalloc(1, sizeof(struct dbAddr));
|
||||
*pdbAddr = dbaddr; /* structure copy */
|
||||
plink->value.pv_link.pvt = pdbAddr;
|
||||
ellAdd(&dbaddr.precord->bklnk, &plink->value.pv_link.backlinknode);
|
||||
/* merging into the same lockset is deferred to the caller.
|
||||
* cf. initPVLinks()
|
||||
*/
|
||||
dbLockSetMerge(NULL, plink->precord, dbaddr.precord);
|
||||
assert(plink->precord->lset->plockSet == dbaddr.precord->lset->plockSet);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void dbDbAddLink(dbLocker *locker, struct link *plink, short dbfType, DBADDR *ptarget)
|
||||
{
|
||||
plink->lset = &dbDb_lset;
|
||||
plink->type = DB_LINK;
|
||||
plink->value.pv_link.pvt = ptarget;
|
||||
ellAdd(&ptarget->precord->bklnk, &plink->value.pv_link.backlinknode);
|
||||
|
||||
/* target record is already locked in dbPutFieldLink() */
|
||||
dbLockSetMerge(locker, plink->precord, ptarget->precord);
|
||||
}
|
||||
|
||||
static void dbDbRemoveLink(dbLocker *locker, struct link *plink)
|
||||
{
|
||||
DBADDR *pdbAddr = (DBADDR *) plink->value.pv_link.pvt;
|
||||
plink->value.pv_link.pvt = 0;
|
||||
plink->value.pv_link.getCvt = 0;
|
||||
plink->value.pv_link.pvlMask = 0;
|
||||
plink->value.pv_link.lastGetdbrType = 0;
|
||||
plink->type = PV_LINK;
|
||||
plink->lset = NULL;
|
||||
ellDelete(&pdbAddr->precord->bklnk, &plink->value.pv_link.backlinknode);
|
||||
dbLockSetSplit(locker, plink->precord, pdbAddr->precord);
|
||||
free(pdbAddr);
|
||||
}
|
||||
|
||||
static int dbDbIsConnected(const struct link *plink)
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static int dbDbGetDBFtype(const struct link *plink)
|
||||
{
|
||||
DBADDR *paddr = (DBADDR *) plink->value.pv_link.pvt;
|
||||
|
||||
return paddr->field_type;
|
||||
}
|
||||
|
||||
static long dbDbGetElements(const struct link *plink, long *nelements)
|
||||
{
|
||||
DBADDR *paddr = (DBADDR *) plink->value.pv_link.pvt;
|
||||
|
||||
*nelements = paddr->no_elements;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long dbDbGetValue(struct link *plink, short dbrType, void *pbuffer,
|
||||
epicsEnum16 *pstat, epicsEnum16 *psevr, long *pnRequest)
|
||||
{
|
||||
struct pv_link *ppv_link = &plink->value.pv_link;
|
||||
DBADDR *paddr = ppv_link->pvt;
|
||||
dbCommon *precord = plink->precord;
|
||||
long status;
|
||||
|
||||
/* scan passive records if link is process passive */
|
||||
if (ppv_link->pvlMask & pvlOptPP) {
|
||||
unsigned char pact = precord->pact;
|
||||
|
||||
precord->pact = TRUE;
|
||||
status = dbScanPassive(precord, paddr->precord);
|
||||
precord->pact = pact;
|
||||
if (status)
|
||||
return status;
|
||||
}
|
||||
*pstat = paddr->precord->stat;
|
||||
*psevr = paddr->precord->sevr;
|
||||
|
||||
if (ppv_link->getCvt && ppv_link->lastGetdbrType == dbrType) {
|
||||
status = ppv_link->getCvt(paddr->pfield, pbuffer, paddr);
|
||||
} else {
|
||||
unsigned short dbfType = paddr->field_type;
|
||||
|
||||
if (dbrType < 0 || dbrType > DBR_ENUM || dbfType > DBF_DEVICE)
|
||||
return S_db_badDbrtype;
|
||||
|
||||
if (paddr->no_elements == 1 && (!pnRequest || *pnRequest == 1)
|
||||
&& paddr->special != SPC_DBADDR
|
||||
&& paddr->special != SPC_ATTRIBUTE) {
|
||||
ppv_link->getCvt = dbFastGetConvertRoutine[dbfType][dbrType];
|
||||
status = ppv_link->getCvt(paddr->pfield, pbuffer, paddr);
|
||||
} else {
|
||||
ppv_link->getCvt = NULL;
|
||||
status = dbGet(paddr, dbrType, pbuffer, NULL, pnRequest, NULL);
|
||||
}
|
||||
ppv_link->lastGetdbrType = dbrType;
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
static long dbDbGetControlLimits(const struct link *plink, double *low,
|
||||
double *high)
|
||||
{
|
||||
DBADDR *paddr = (DBADDR *) plink->value.pv_link.pvt;
|
||||
struct buffer {
|
||||
DBRctrlDouble
|
||||
double value;
|
||||
} buffer;
|
||||
long options = DBR_CTRL_DOUBLE;
|
||||
long number_elements = 0;
|
||||
long status = dbGet(paddr, DBR_DOUBLE, &buffer, &options, &number_elements,
|
||||
NULL);
|
||||
|
||||
if (status)
|
||||
return status;
|
||||
|
||||
*low = buffer.lower_ctrl_limit;
|
||||
*high = buffer.upper_ctrl_limit;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long dbDbGetGraphicLimits(const struct link *plink, double *low,
|
||||
double *high)
|
||||
{
|
||||
DBADDR *paddr = (DBADDR *) plink->value.pv_link.pvt;
|
||||
struct buffer {
|
||||
DBRgrDouble
|
||||
double value;
|
||||
} buffer;
|
||||
long options = DBR_GR_DOUBLE;
|
||||
long number_elements = 0;
|
||||
long status = dbGet(paddr, DBR_DOUBLE, &buffer, &options, &number_elements,
|
||||
NULL);
|
||||
|
||||
if (status)
|
||||
return status;
|
||||
|
||||
*low = buffer.lower_disp_limit;
|
||||
*high = buffer.upper_disp_limit;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long dbDbGetAlarmLimits(const struct link *plink, double *lolo,
|
||||
double *low, double *high, double *hihi)
|
||||
{
|
||||
DBADDR *paddr = (DBADDR *) plink->value.pv_link.pvt;
|
||||
struct buffer {
|
||||
DBRalDouble
|
||||
double value;
|
||||
} buffer;
|
||||
long options = DBR_AL_DOUBLE;
|
||||
long number_elements = 0;
|
||||
long status = dbGet(paddr, DBR_DOUBLE, &buffer, &options, &number_elements,
|
||||
0);
|
||||
|
||||
if (status)
|
||||
return status;
|
||||
|
||||
*lolo = buffer.lower_alarm_limit;
|
||||
*low = buffer.lower_warning_limit;
|
||||
*high = buffer.upper_warning_limit;
|
||||
*hihi = buffer.upper_alarm_limit;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long dbDbGetPrecision(const struct link *plink, short *precision)
|
||||
{
|
||||
DBADDR *paddr = (DBADDR *) plink->value.pv_link.pvt;
|
||||
struct buffer {
|
||||
DBRprecision
|
||||
double value;
|
||||
} buffer;
|
||||
long options = DBR_PRECISION;
|
||||
long number_elements = 0;
|
||||
long status = dbGet(paddr, DBR_DOUBLE, &buffer, &options, &number_elements,
|
||||
0);
|
||||
|
||||
if (status)
|
||||
return status;
|
||||
|
||||
*precision = (short) buffer.precision.dp;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long dbDbGetUnits(const struct link *plink, char *units, int unitsSize)
|
||||
{
|
||||
DBADDR *paddr = (DBADDR *) plink->value.pv_link.pvt;
|
||||
struct buffer {
|
||||
DBRunits
|
||||
double value;
|
||||
} buffer;
|
||||
long options = DBR_UNITS;
|
||||
long number_elements = 0;
|
||||
long status = dbGet(paddr, DBR_DOUBLE, &buffer, &options, &number_elements,
|
||||
0);
|
||||
|
||||
if (status)
|
||||
return status;
|
||||
|
||||
strncpy(units, buffer.units, unitsSize);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long dbDbGetAlarm(const struct link *plink, epicsEnum16 *status,
|
||||
epicsEnum16 *severity)
|
||||
{
|
||||
DBADDR *paddr = (DBADDR *) plink->value.pv_link.pvt;
|
||||
|
||||
if (status)
|
||||
*status = paddr->precord->stat;
|
||||
if (severity)
|
||||
*severity = paddr->precord->sevr;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long dbDbGetTimeStamp(const struct link *plink, epicsTimeStamp *pstamp)
|
||||
{
|
||||
DBADDR *paddr = (DBADDR *) plink->value.pv_link.pvt;
|
||||
|
||||
*pstamp = paddr->precord->time;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long dbDbPutValue(struct link *plink, short dbrType,
|
||||
const void *pbuffer, long nRequest)
|
||||
{
|
||||
struct pv_link *ppv_link = &plink->value.pv_link;
|
||||
struct dbCommon *psrce = plink->precord;
|
||||
DBADDR *paddr = (DBADDR *) ppv_link->pvt;
|
||||
dbCommon *pdest = paddr->precord;
|
||||
long status = dbPut(paddr, dbrType, pbuffer, nRequest);
|
||||
|
||||
inherit_severity(ppv_link, pdest, psrce->nsta, psrce->nsev);
|
||||
if (status)
|
||||
return status;
|
||||
|
||||
if (paddr->pfield == (void *) &pdest->proc ||
|
||||
(ppv_link->pvlMask & pvlOptPP && pdest->scan == 0)) {
|
||||
/* if dbPutField caused asyn record to process */
|
||||
/* ask for reprocessing*/
|
||||
if (pdest->putf) {
|
||||
pdest->rpro = TRUE;
|
||||
} else { /* process dest record with source's PACT true */
|
||||
unsigned char pact;
|
||||
|
||||
if (psrce && psrce->ppn)
|
||||
dbNotifyAdd(psrce, pdest);
|
||||
pact = psrce->pact;
|
||||
psrce->pact = TRUE;
|
||||
status = dbProcess(pdest);
|
||||
psrce->pact = pact;
|
||||
}
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
static void dbDbScanFwdLink(struct link *plink)
|
||||
{
|
||||
dbCommon *precord = plink->precord;
|
||||
dbAddr *paddr = (dbAddr *) plink->value.pv_link.pvt;
|
||||
|
||||
dbScanPassive(precord, paddr->precord);
|
||||
}
|
||||
|
||||
static lset dbDb_lset = {
|
||||
dbDbRemoveLink,
|
||||
dbDbIsConnected,
|
||||
dbDbGetDBFtype, dbDbGetElements,
|
||||
dbDbGetValue,
|
||||
dbDbGetControlLimits, dbDbGetGraphicLimits, dbDbGetAlarmLimits,
|
||||
dbDbGetPrecision, dbDbGetUnits,
|
||||
dbDbGetAlarm, dbDbGetTimeStamp,
|
||||
dbDbPutValue,
|
||||
dbDbScanFwdLink
|
||||
};
|
||||
|
||||
/***************************** Generic Link API *****************************/
|
||||
|
||||
void dbInitLink(struct link *plink, short dbfType)
|
||||
@@ -454,6 +78,11 @@ void dbInitLink(struct link *plink, short dbfType)
|
||||
return;
|
||||
}
|
||||
|
||||
if (plink->type == JSON_LINK) {
|
||||
dbJLinkInit(plink);
|
||||
return;
|
||||
}
|
||||
|
||||
if (plink->type != PV_LINK)
|
||||
return;
|
||||
|
||||
@@ -487,7 +116,8 @@ void dbInitLink(struct link *plink, short dbfType)
|
||||
}
|
||||
}
|
||||
|
||||
void dbAddLink(dbLocker *locker, struct link *plink, short dbfType, DBADDR *ptarget)
|
||||
void dbAddLink(struct dbLocker *locker, struct link *plink, short dbfType,
|
||||
DBADDR *ptarget)
|
||||
{
|
||||
struct dbCommon *precord = plink->precord;
|
||||
|
||||
@@ -496,6 +126,18 @@ void dbAddLink(dbLocker *locker, struct link *plink, short dbfType, DBADDR *ptar
|
||||
return;
|
||||
}
|
||||
|
||||
if (plink->type == JSON_LINK) {
|
||||
/*
|
||||
* FIXME: Can't create DB links as dbJLink types yet,
|
||||
* dbLock.c doesn't have any way to find/track them.
|
||||
*/
|
||||
dbJLinkInit(plink);
|
||||
return;
|
||||
}
|
||||
|
||||
if (plink->type != PV_LINK)
|
||||
return;
|
||||
|
||||
if (plink == &precord->tsel)
|
||||
recGblTSELwasModified(plink);
|
||||
|
||||
@@ -518,16 +160,15 @@ void dbAddLink(dbLocker *locker, struct link *plink, short dbfType, DBADDR *ptar
|
||||
}
|
||||
}
|
||||
|
||||
long dbLoadLink(struct link *plink, short dbrType, void *pbuffer)
|
||||
void dbLinkOpen(struct link *plink)
|
||||
{
|
||||
if (plink->type == CONSTANT)
|
||||
return dbConstLoadLink(plink, dbrType, pbuffer);
|
||||
lset *plset = plink->lset;
|
||||
|
||||
/* Could pass a type hint to the other link types here */
|
||||
return S_db_notFound;
|
||||
if (plset && plset->openLink)
|
||||
plset->openLink(plink);
|
||||
}
|
||||
|
||||
void dbRemoveLink(dbLocker *locker, struct link *plink)
|
||||
void dbRemoveLink(struct dbLocker *locker, struct link *plink)
|
||||
{
|
||||
lset *plset = plink->lset;
|
||||
|
||||
@@ -536,6 +177,59 @@ void dbRemoveLink(dbLocker *locker, struct link *plink)
|
||||
plset->removeLink(locker, plink);
|
||||
plink->lset = NULL;
|
||||
}
|
||||
if (plink->type == JSON_LINK)
|
||||
plink->value.json.jlink = NULL;
|
||||
}
|
||||
|
||||
int dbLinkIsDefined(const struct link *plink)
|
||||
{
|
||||
return (plink->lset != 0);
|
||||
}
|
||||
|
||||
int dbLinkIsConstant(const struct link *plink)
|
||||
{
|
||||
lset *plset = plink->lset;
|
||||
|
||||
return !plset || plset->isConstant;
|
||||
}
|
||||
|
||||
int dbLinkIsVolatile(const struct link *plink)
|
||||
{
|
||||
lset *plset = plink->lset;
|
||||
|
||||
return plset && plset->isVolatile;
|
||||
}
|
||||
|
||||
long dbLoadLink(struct link *plink, short dbrType, void *pbuffer)
|
||||
{
|
||||
lset *plset = plink->lset;
|
||||
|
||||
if (plset && plset->loadScalar)
|
||||
return plset->loadScalar(plink, dbrType, pbuffer);
|
||||
|
||||
return S_db_noLSET;
|
||||
}
|
||||
|
||||
long dbLoadLinkLS(struct link *plink, char *pbuffer, epicsUInt32 size,
|
||||
epicsUInt32 *plen)
|
||||
{
|
||||
lset *plset = plink->lset;
|
||||
|
||||
if (plset && plset->loadLS)
|
||||
return plset->loadLS(plink, pbuffer, size, plen);
|
||||
|
||||
return S_db_noLSET;
|
||||
}
|
||||
|
||||
long dbLoadLinkArray(struct link *plink, short dbrType, void *pbuffer,
|
||||
long *pnRequest)
|
||||
{
|
||||
lset *plset = plink->lset;
|
||||
|
||||
if (plset && plset->loadArray)
|
||||
return plset->loadArray(plink, dbrType, pbuffer, pnRequest);
|
||||
|
||||
return S_db_noLSET;
|
||||
}
|
||||
|
||||
int dbIsLinkConnected(const struct link *plink)
|
||||
@@ -563,7 +257,7 @@ long dbGetNelements(const struct link *plink, long *nelements)
|
||||
lset *plset = plink->lset;
|
||||
|
||||
if (!plset || !plset->getElements)
|
||||
return S_db_badField;
|
||||
return S_db_noLSET;
|
||||
|
||||
return plset->getElements(plink, nelements);
|
||||
}
|
||||
@@ -572,24 +266,20 @@ long dbGetLink(struct link *plink, short dbrType, void *pbuffer,
|
||||
long *poptions, long *pnRequest)
|
||||
{
|
||||
struct dbCommon *precord = plink->precord;
|
||||
epicsEnum16 sevr = 0, stat = 0;
|
||||
lset *plset = plink->lset;
|
||||
long status;
|
||||
|
||||
if (poptions && *poptions) {
|
||||
printf("dbGetLinkValue: Use of poptions no longer supported\n");
|
||||
printf("dbGetLink: Use of poptions no longer supported\n");
|
||||
*poptions = 0;
|
||||
}
|
||||
|
||||
if (!plset || !plset->getValue)
|
||||
return -1;
|
||||
|
||||
status = plset->getValue(plink, dbrType, pbuffer, &stat, &sevr, pnRequest);
|
||||
if (status) {
|
||||
status = plset->getValue(plink, dbrType, pbuffer, pnRequest);
|
||||
if (status)
|
||||
recGblSetSevr(precord, LINK_ALARM, INVALID_ALARM);
|
||||
} else {
|
||||
inherit_severity(&plink->value.pv_link, precord, stat, sevr);
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
@@ -598,7 +288,7 @@ long dbGetControlLimits(const struct link *plink, double *low, double *high)
|
||||
lset *plset = plink->lset;
|
||||
|
||||
if (!plset || !plset->getControlLimits)
|
||||
return S_db_notFound;
|
||||
return S_db_noLSET;
|
||||
|
||||
return plset->getControlLimits(plink, low, high);
|
||||
}
|
||||
@@ -608,7 +298,7 @@ long dbGetGraphicLimits(const struct link *plink, double *low, double *high)
|
||||
lset *plset = plink->lset;
|
||||
|
||||
if (!plset || !plset->getGraphicLimits)
|
||||
return S_db_notFound;
|
||||
return S_db_noLSET;
|
||||
|
||||
return plset->getGraphicLimits(plink, low, high);
|
||||
}
|
||||
@@ -619,7 +309,7 @@ long dbGetAlarmLimits(const struct link *plink, double *lolo, double *low,
|
||||
lset *plset = plink->lset;
|
||||
|
||||
if (!plset || !plset->getAlarmLimits)
|
||||
return S_db_notFound;
|
||||
return S_db_noLSET;
|
||||
|
||||
return plset->getAlarmLimits(plink, lolo, low, high, hihi);
|
||||
}
|
||||
@@ -629,7 +319,7 @@ long dbGetPrecision(const struct link *plink, short *precision)
|
||||
lset *plset = plink->lset;
|
||||
|
||||
if (!plset || !plset->getPrecision)
|
||||
return S_db_notFound;
|
||||
return S_db_noLSET;
|
||||
|
||||
return plset->getPrecision(plink, precision);
|
||||
}
|
||||
@@ -639,7 +329,7 @@ long dbGetUnits(const struct link *plink, char *units, int unitsSize)
|
||||
lset *plset = plink->lset;
|
||||
|
||||
if (!plset || !plset->getUnits)
|
||||
return S_db_notFound;
|
||||
return S_db_noLSET;
|
||||
|
||||
return plset->getUnits(plink, units, unitsSize);
|
||||
}
|
||||
@@ -650,7 +340,7 @@ long dbGetAlarm(const struct link *plink, epicsEnum16 *status,
|
||||
lset *plset = plink->lset;
|
||||
|
||||
if (!plset || !plset->getAlarm)
|
||||
return S_db_notFound;
|
||||
return S_db_noLSET;
|
||||
|
||||
return plset->getAlarm(plink, status, severity);
|
||||
}
|
||||
@@ -660,7 +350,7 @@ long dbGetTimeStamp(const struct link *plink, epicsTimeStamp *pstamp)
|
||||
lset *plset = plink->lset;
|
||||
|
||||
if (!plset || !plset->getTimeStamp)
|
||||
return S_db_notFound;
|
||||
return S_db_noLSET;
|
||||
|
||||
return plset->getTimeStamp(plink, pstamp);
|
||||
}
|
||||
@@ -672,7 +362,7 @@ long dbPutLink(struct link *plink, short dbrType, const void *pbuffer,
|
||||
long status;
|
||||
|
||||
if (!plset || !plset->putValue)
|
||||
return S_db_notFound;
|
||||
return S_db_noLSET;
|
||||
|
||||
status = plset->putValue(plink, dbrType, pbuffer, nRequest);
|
||||
if (status) {
|
||||
@@ -683,6 +373,33 @@ long dbPutLink(struct link *plink, short dbrType, const void *pbuffer,
|
||||
return status;
|
||||
}
|
||||
|
||||
void dbLinkAsyncComplete(struct link *plink)
|
||||
{
|
||||
dbCommon *pdbCommon = plink->precord;
|
||||
|
||||
dbScanLock(pdbCommon);
|
||||
pdbCommon->rset->process(pdbCommon);
|
||||
dbScanUnlock(pdbCommon);
|
||||
}
|
||||
|
||||
long dbPutLinkAsync(struct link *plink, short dbrType, const void *pbuffer,
|
||||
long nRequest)
|
||||
{
|
||||
lset *plset = plink->lset;
|
||||
long status;
|
||||
|
||||
if (!plset || !plset->putAsync)
|
||||
return S_db_noLSET;
|
||||
|
||||
status = plset->putAsync(plink, dbrType, pbuffer, nRequest);
|
||||
if (status) {
|
||||
struct dbCommon *precord = plink->precord;
|
||||
|
||||
recGblSetSevr(precord, LINK_ALARM, INVALID_ALARM);
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
void dbScanFwdLink(struct link *plink)
|
||||
{
|
||||
lset *plset = plink->lset;
|
||||
@@ -691,22 +408,20 @@ void dbScanFwdLink(struct link *plink)
|
||||
plset->scanForward(plink);
|
||||
}
|
||||
|
||||
/* Helper functions for long string support */
|
||||
|
||||
long dbLoadLinkLS(struct link *plink, char *pbuffer, epicsUInt32 size,
|
||||
epicsUInt32 *plen)
|
||||
long dbLinkDoLocked(struct link *plink, dbLinkUserCallback rtn,
|
||||
void *priv)
|
||||
{
|
||||
if (plink->type == CONSTANT &&
|
||||
plink->value.constantStr) {
|
||||
strncpy(pbuffer, plink->value.constantStr, --size);
|
||||
pbuffer[size] = 0;
|
||||
*plen = (epicsUInt32) strlen(pbuffer) + 1;
|
||||
return 0;
|
||||
}
|
||||
lset *plset = plink->lset;
|
||||
|
||||
return S_db_notFound;
|
||||
if (!rtn || !plset || !plset->doLocked)
|
||||
return S_db_noLSET;
|
||||
|
||||
return plset->doLocked(plink, rtn, priv);
|
||||
}
|
||||
|
||||
|
||||
/* Helper functions for long string support */
|
||||
|
||||
long dbGetLinkLS(struct link *plink, char *pbuffer, epicsUInt32 size,
|
||||
epicsUInt32 *plen)
|
||||
{
|
||||
|
||||
@@ -27,13 +27,34 @@ extern "C" {
|
||||
|
||||
struct dbLocker;
|
||||
|
||||
typedef long (*dbLinkUserCallback)(struct link *plink, void *priv);
|
||||
|
||||
typedef struct lset {
|
||||
/* Characteristics of the link type */
|
||||
const unsigned isConstant:1;
|
||||
const unsigned isVolatile:1;
|
||||
|
||||
/* Activation */
|
||||
void (*openLink)(struct link *plink);
|
||||
|
||||
/* Destructor */
|
||||
void (*removeLink)(struct dbLocker *locker, struct link *plink);
|
||||
|
||||
/* Const init, data type hinting */
|
||||
long (*loadScalar)(struct link *plink, short dbrType, void *pbuffer);
|
||||
long (*loadLS)(struct link *plink, char *pbuffer, epicsUInt32 size,
|
||||
epicsUInt32 *plen);
|
||||
long (*loadArray)(struct link *plink, short dbrType, void *pbuffer,
|
||||
long *pnRequest);
|
||||
|
||||
/* Metadata */
|
||||
int (*isConnected)(const struct link *plink);
|
||||
int (*getDBFtype)(const struct link *plink);
|
||||
long (*getElements)(const struct link *plink, long *nelements);
|
||||
|
||||
/* Get data */
|
||||
long (*getValue)(struct link *plink, short dbrType, void *pbuffer,
|
||||
epicsEnum16 *pstat, epicsEnum16 *psevr, long *pnRequest);
|
||||
long *pnRequest);
|
||||
long (*getControlLimits)(const struct link *plink, double *lo, double *hi);
|
||||
long (*getGraphicLimits)(const struct link *plink, double *lo, double *hi);
|
||||
long (*getAlarmLimits)(const struct link *plink, double *lolo, double *lo,
|
||||
@@ -43,23 +64,41 @@ typedef struct lset {
|
||||
long (*getAlarm)(const struct link *plink, epicsEnum16 *status,
|
||||
epicsEnum16 *severity);
|
||||
long (*getTimeStamp)(const struct link *plink, epicsTimeStamp *pstamp);
|
||||
|
||||
/* Put data */
|
||||
long (*putValue)(struct link *plink, short dbrType,
|
||||
const void *pbuffer, long nRequest);
|
||||
long (*putAsync)(struct link *plink, short dbrType,
|
||||
const void *pbuffer, long nRequest);
|
||||
|
||||
/* Process */
|
||||
void (*scanForward)(struct link *plink);
|
||||
|
||||
/* Atomicity */
|
||||
long (*doLocked)(struct link *plink, dbLinkUserCallback rtn, void *priv);
|
||||
} lset;
|
||||
|
||||
#define dbGetSevr(link, sevr) \
|
||||
dbGetAlarm(link, NULL, sevr)
|
||||
|
||||
epicsShareFunc void dbInitLink(struct link *plink, short dbfType);
|
||||
epicsShareFunc void dbAddLink(struct dbLocker *locker, struct link *plink, short dbfType,
|
||||
DBADDR *ptarget);
|
||||
epicsShareFunc long dbLoadLink(struct link *plink, short dbrType,
|
||||
void *pbuffer);
|
||||
epicsShareFunc void dbAddLink(struct dbLocker *locker, struct link *plink,
|
||||
short dbfType, DBADDR *ptarget);
|
||||
|
||||
epicsShareFunc void dbLinkOpen(struct link *plink);
|
||||
epicsShareFunc void dbRemoveLink(struct dbLocker *locker, struct link *plink);
|
||||
|
||||
epicsShareFunc int dbLinkIsDefined(const struct link *plink); /* 0 or 1 */
|
||||
epicsShareFunc int dbLinkIsConstant(const struct link *plink); /* 0 or 1 */
|
||||
epicsShareFunc int dbLinkIsVolatile(const struct link *plink); /* 0 or 1 */
|
||||
|
||||
epicsShareFunc long dbLoadLink(struct link *plink, short dbrType,
|
||||
void *pbuffer);
|
||||
epicsShareFunc long dbLoadLinkArray(struct link *, short dbrType, void *pbuffer,
|
||||
long *pnRequest);
|
||||
|
||||
epicsShareFunc long dbGetNelements(const struct link *plink, long *nelements);
|
||||
epicsShareFunc int dbIsLinkConnected(const struct link *plink);
|
||||
epicsShareFunc int dbIsLinkConnected(const struct link *plink); /* 0 or 1 */
|
||||
epicsShareFunc int dbGetLinkDBFtype(const struct link *plink);
|
||||
epicsShareFunc long dbGetLink(struct link *, short dbrType, void *pbuffer,
|
||||
long *options, long *nRequest);
|
||||
@@ -78,8 +117,14 @@ epicsShareFunc long dbGetTimeStamp(const struct link *plink,
|
||||
epicsTimeStamp *pstamp);
|
||||
epicsShareFunc long dbPutLink(struct link *plink, short dbrType,
|
||||
const void *pbuffer, long nRequest);
|
||||
epicsShareFunc void dbLinkAsyncComplete(struct link *plink);
|
||||
epicsShareFunc long dbPutLinkAsync(struct link *plink, short dbrType,
|
||||
const void *pbuffer, long nRequest);
|
||||
epicsShareFunc void dbScanFwdLink(struct link *plink);
|
||||
|
||||
epicsShareFunc long dbLinkDoLocked(struct link *plink, dbLinkUserCallback rtn,
|
||||
void *priv);
|
||||
|
||||
epicsShareFunc long dbLoadLinkLS(struct link *plink, char *pbuffer,
|
||||
epicsUInt32 size, epicsUInt32 *plen);
|
||||
epicsShareFunc long dbGetLinkLS(struct link *plink, char *pbuffer,
|
||||
|
||||
@@ -190,6 +190,8 @@ void dbScanLock(dbCommon *precord)
|
||||
lockRecord * const lr = precord->lset;
|
||||
lockSet *ls;
|
||||
|
||||
assert(lr);
|
||||
|
||||
ls = dbLockGetRef(lr);
|
||||
assert(epicsAtomicGetIntT(&ls->refcount)>0);
|
||||
|
||||
|
||||
@@ -12,6 +12,8 @@
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#define EPICS_PRIVATE_API
|
||||
|
||||
#include "dbmf.h"
|
||||
#include "epicsUnitTest.h"
|
||||
#include "osiFileName.h"
|
||||
@@ -36,6 +38,7 @@ static ELLLIST testEvtList; /* holds testMonitor::node */
|
||||
struct testMonitor {
|
||||
ELLNODE node;
|
||||
dbEventSubscription sub;
|
||||
dbChannel *chan;
|
||||
epicsEventId event;
|
||||
unsigned count;
|
||||
};
|
||||
@@ -89,6 +92,7 @@ void testIocShutdownOk(void)
|
||||
void testdbCleanup(void)
|
||||
{
|
||||
dbFreeBase(pdbbase);
|
||||
db_cleanup_events();
|
||||
initHookFree();
|
||||
registryFree();
|
||||
pdbbase = NULL;
|
||||
@@ -106,8 +110,8 @@ long testdbVPutField(const char* pv, short dbrType, va_list ap)
|
||||
DBADDR addr;
|
||||
union anybuf pod;
|
||||
|
||||
if(dbNameToAddr(pv, &addr)) {
|
||||
testFail("Missing PV %s", pv);
|
||||
if (dbNameToAddr(pv, &addr)) {
|
||||
testFail("Missing PV \"%s\"", pv);
|
||||
return S_dbLib_recNotFound;
|
||||
}
|
||||
|
||||
@@ -152,7 +156,7 @@ void testdbPutFieldOk(const char* pv, short dbrType, ...)
|
||||
ret = testdbVPutField(pv, dbrType, ap);
|
||||
va_end(ap);
|
||||
|
||||
testOk(ret==0, "dbPutField(%s, %d, ...) == %ld", pv, dbrType, ret);
|
||||
testOk(ret==0, "dbPutField(\"%s\", %d, ...) -> %#lx (%s)", pv, dbrType, ret, errSymMsg(ret));
|
||||
}
|
||||
|
||||
void testdbPutFieldFail(long status, const char* pv, short dbrType, ...)
|
||||
@@ -164,10 +168,8 @@ void testdbPutFieldFail(long status, const char* pv, short dbrType, ...)
|
||||
ret = testdbVPutField(pv, dbrType, ap);
|
||||
va_end(ap);
|
||||
|
||||
if(ret==status)
|
||||
testPass("dbPutField(\"%s\", %d, ...) == %ld", pv, dbrType, status);
|
||||
else
|
||||
testFail("dbPutField(\"%s\", %d, ...) != %ld (%ld)", pv, dbrType, status, ret);
|
||||
testOk(ret==status, "dbPutField(\"%s\", %d, ...) -> %#lx (%s) == %#lx (%s)",
|
||||
pv, dbrType, status, errSymMsg(status), ret, errSymMsg(ret));
|
||||
}
|
||||
|
||||
void testdbGetFieldEqual(const char* pv, short dbrType, ...)
|
||||
@@ -187,13 +189,13 @@ void testdbVGetFieldEqual(const char* pv, short dbrType, va_list ap)
|
||||
long status;
|
||||
|
||||
if(dbNameToAddr(pv, &addr)) {
|
||||
testFail("Missing PV %s", pv);
|
||||
testFail("Missing PV \"%s\"", pv);
|
||||
return;
|
||||
}
|
||||
|
||||
status = dbGetField(&addr, dbrType, pod.bytes, NULL, &nReq, NULL);
|
||||
if(status) {
|
||||
testFail("dbGetField(\"%s\",%d,...) returns %ld", pv, dbrType, status);
|
||||
if (status) {
|
||||
testFail("dbGetField(\"%s\", %d, ...) -> %#lx (%s)", pv, dbrType, status, errSymMsg(status));
|
||||
return;
|
||||
} else if(nReq==0) {
|
||||
testFail("dbGetField(\"%s\", %d, ...) -> zero length", pv, dbrType);
|
||||
@@ -225,6 +227,21 @@ void testdbVGetFieldEqual(const char* pv, short dbrType, va_list ap)
|
||||
}
|
||||
}
|
||||
|
||||
void testdbPutArrFieldOk(const char* pv, short dbrType, unsigned long count, const void *pbuf)
|
||||
{
|
||||
DBADDR addr;
|
||||
long status;
|
||||
|
||||
if (dbNameToAddr(pv, &addr)) {
|
||||
testFail("Missing PV \"%s\"", pv);
|
||||
return;
|
||||
}
|
||||
|
||||
status = dbPutField(&addr, dbrType, pbuf, count);
|
||||
|
||||
testOk(status==0, "dbPutField(\"%s\", dbr=%d, count=%lu, ...) -> %ld", pv, dbrType, count, status);
|
||||
}
|
||||
|
||||
void testdbGetArrFieldEqual(const char* pv, short dbfType, long nRequest, unsigned long cnt, const void *pbufraw)
|
||||
{
|
||||
DBADDR addr;
|
||||
@@ -295,8 +312,8 @@ dbCommon* testdbRecordPtr(const char* pv)
|
||||
{
|
||||
DBADDR addr;
|
||||
|
||||
if(dbNameToAddr(pv, &addr))
|
||||
testAbort("Missing record %s", pv);
|
||||
if (dbNameToAddr(pv, &addr))
|
||||
testAbort("Missing record \"%s\"", pv);
|
||||
|
||||
return addr.precord;
|
||||
}
|
||||
@@ -324,7 +341,7 @@ testMonitor* testMonitorCreate(const char* pvname, unsigned mask, unsigned opt)
|
||||
|
||||
mon->event = epicsEventMustCreate(epicsEventEmpty);
|
||||
|
||||
chan = dbChannelCreate(pvname);
|
||||
chan = mon->chan = dbChannelCreate(pvname);
|
||||
if(!chan)
|
||||
testAbort("testMonitorCreate - dbChannelCreate(\"%s\") fails", pvname);
|
||||
if(!!(status=dbChannelOpen(chan)))
|
||||
@@ -355,6 +372,8 @@ void testMonitorDestroy(testMonitor *mon)
|
||||
|
||||
db_cancel_event(mon->sub);
|
||||
|
||||
dbChannelDelete(mon->chan);
|
||||
|
||||
epicsEventDestroy(mon->event);
|
||||
|
||||
free(mon);
|
||||
|
||||
@@ -55,6 +55,8 @@ epicsShareFunc long testdbVPutField(const char* pv, short dbrType, va_list ap);
|
||||
epicsShareFunc void testdbGetFieldEqual(const char* pv, short dbrType, ...);
|
||||
epicsShareFunc void testdbVGetFieldEqual(const char* pv, short dbrType, va_list ap);
|
||||
|
||||
epicsShareFunc void testdbPutArrFieldOk(const char* pv, short dbrType, unsigned long count, const void *pbuf);
|
||||
|
||||
/**
|
||||
* @param pv PV name string
|
||||
* @param dbfType One of the DBF_* macros from dbAccess.h
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
#include <string.h>
|
||||
#include <limits.h>
|
||||
|
||||
#include "alarm.h"
|
||||
#include "dbDefs.h"
|
||||
#include "epicsMath.h"
|
||||
#include "epicsPrint.h"
|
||||
@@ -30,7 +31,6 @@
|
||||
#include "dbAccessDefs.h"
|
||||
#include "dbAddr.h"
|
||||
#include "dbBase.h"
|
||||
#include "dbCa.h"
|
||||
#include "dbCommon.h"
|
||||
#include "dbEvent.h"
|
||||
#include "db_field_log.h"
|
||||
@@ -214,7 +214,27 @@ int recGblSetSevr(void *precord, epicsEnum16 new_stat, epicsEnum16 new_sevr)
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
||||
void recGblInheritSevr(int msMode, void *precord, epicsEnum16 stat,
|
||||
epicsEnum16 sevr)
|
||||
{
|
||||
switch (msMode) {
|
||||
case pvlOptNMS:
|
||||
break;
|
||||
case pvlOptMSI:
|
||||
if (sevr < INVALID_ALARM)
|
||||
break;
|
||||
/* Fall through */
|
||||
case pvlOptMS:
|
||||
recGblSetSevr(precord, LINK_ALARM, sevr);
|
||||
break;
|
||||
case pvlOptMSS:
|
||||
recGblSetSevr(precord, stat, sevr);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void recGblFwdLink(void *precord)
|
||||
{
|
||||
dbCommon *pdbc = precord;
|
||||
@@ -236,7 +256,7 @@ void recGblGetTimeStamp(void *pvoid)
|
||||
dbCommon* prec = (dbCommon*)pvoid;
|
||||
struct link *plink = &prec->tsel;
|
||||
|
||||
if (plink->type != CONSTANT) {
|
||||
if (!dbLinkIsConstant(plink)) {
|
||||
struct pv_link *ppv_link = &plink->value.pv_link;
|
||||
|
||||
if (ppv_link->pvlMask & pvlOptTSELisTime) {
|
||||
|
||||
@@ -59,6 +59,8 @@ epicsShareFunc int recGblInitConstantLink(struct link *plink,
|
||||
epicsShareFunc unsigned short recGblResetAlarms(void *precord);
|
||||
epicsShareFunc int recGblSetSevr(void *precord, epicsEnum16 new_stat,
|
||||
epicsEnum16 new_sevr);
|
||||
epicsShareFunc void recGblInheritSevr(int msMode, void *precord, epicsEnum16 stat,
|
||||
epicsEnum16 sevr);
|
||||
epicsShareFunc void recGblFwdLink(void *precord);
|
||||
epicsShareFunc void recGblGetTimeStamp(void *precord);
|
||||
epicsShareFunc void recGblTSELwasModified(struct link *plink);
|
||||
|
||||
@@ -18,7 +18,9 @@ TESTLIBRARY = dbTestIoc
|
||||
dbTestIoc_SRCS += arrRecord.c
|
||||
dbTestIoc_SRCS += xRecord.c
|
||||
dbTestIoc_SRCS += dbLinkdset.c
|
||||
dbTestIoc_SRCS += xLink.c
|
||||
dbTestIoc_SRCS += devx.c
|
||||
dbTestIoc_SRCS += jlinkz.c
|
||||
dbTestIoc_LIBS = dbCore ca Com
|
||||
|
||||
TARGETS += $(COMMON_DIR)/dbTestIoc.dbd
|
||||
@@ -26,10 +28,11 @@ DBDDEPENDS_FILES += dbTestIoc.dbd$(DEP)
|
||||
dbTestIoc_DBD += menuGlobal.dbd
|
||||
dbTestIoc_DBD += menuConvert.dbd
|
||||
dbTestIoc_DBD += menuScan.dbd
|
||||
#dbTestIoc_DBD += arrRecord.dbd
|
||||
dbTestIoc_DBD += xRecord.dbd
|
||||
dbTestIoc_DBD += arrRecord.dbd
|
||||
dbTestIoc_DBD += xLink.dbd
|
||||
dbTestIoc_DBD += devx.dbd
|
||||
dbTestIoc_DBD += jlinkz.dbd
|
||||
dbTestIoc_DBD += dbLinkdset.dbd
|
||||
TESTFILES += $(COMMON_DIR)/dbTestIoc.dbd ../xRecord.db
|
||||
|
||||
@@ -54,7 +57,7 @@ dbPutLinkTest_SRCS += dbPutLinkTest.c
|
||||
dbPutLinkTest_SRCS += dbTestIoc_registerRecordDeviceDriver.cpp
|
||||
testHarness_SRCS += dbPutLinkTest.c
|
||||
TESTS += dbPutLinkTest
|
||||
TESTFILES += ../dbPutLinkTest.db ../dbBadLink.db
|
||||
TESTFILES += ../dbPutLinkTest.db ../dbPutLinkTestJ.db ../dbBadLink.db
|
||||
|
||||
TESTPROD_HOST += dbLockTest
|
||||
dbLockTest_SRCS += dbLockTest.c
|
||||
|
||||
@@ -44,6 +44,9 @@ epicsShareExtern short epicsShareAPI ca_field_type (chid chan);
|
||||
|
||||
void dbTestIoc_registerRecordDeviceDriver(struct dbBase *);
|
||||
|
||||
static epicsEventId waitEvent;
|
||||
static unsigned waitCounter;
|
||||
|
||||
static
|
||||
void waitForUpdateN(DBLINK *plink, unsigned long n)
|
||||
{
|
||||
@@ -58,13 +61,46 @@ void putLink(DBLINK *plink, short dbr, const void*buf, long nReq)
|
||||
|
||||
ret = dbPutLink(plink, dbr, buf, nReq);
|
||||
if(ret) {
|
||||
testFail("putLink fails %ld\n", ret);
|
||||
testFail("putLink fails %ld", ret);
|
||||
} else {
|
||||
testPass("putLink ok\n");
|
||||
testPass("putLink ok");
|
||||
dbCaSync();
|
||||
}
|
||||
}
|
||||
|
||||
static long getTwice(struct link *psrclnk, void *dummy)
|
||||
{
|
||||
epicsInt32 val1, val2;
|
||||
long status = dbGetLink(psrclnk, DBR_LONG, &val1, 0, 0);
|
||||
|
||||
if (status) return status;
|
||||
|
||||
epicsThreadSleep(0.5);
|
||||
status = dbGetLink(psrclnk, DBR_LONG, &val2, 0, 0);
|
||||
if (status) return status;
|
||||
|
||||
testDiag("val1 = %d, val2 = %d", val1, val2);
|
||||
return (val1 == val2) ? 0 : -1;
|
||||
}
|
||||
|
||||
static void countUp(void *parm)
|
||||
{
|
||||
xRecord *ptarg = (xRecord *)parm;
|
||||
epicsInt32 val;
|
||||
|
||||
for (val = 1; val < 10; val++) {
|
||||
dbScanLock((dbCommon*)ptarg);
|
||||
ptarg->val = val;
|
||||
db_post_events(ptarg, &ptarg->val, DBE_VALUE|DBE_ALARM|DBE_ARCHIVE);
|
||||
dbScanUnlock((dbCommon*)ptarg);
|
||||
|
||||
epicsThreadSleep(0.1);
|
||||
}
|
||||
|
||||
if (waitEvent)
|
||||
epicsEventMustTrigger(waitEvent);
|
||||
}
|
||||
|
||||
static void testNativeLink(void)
|
||||
{
|
||||
xRecord *psrc, *ptarg;
|
||||
@@ -124,6 +160,27 @@ static void testNativeLink(void)
|
||||
testOk1(ptarg->val==1010);
|
||||
dbScanUnlock((dbCommon*)ptarg);
|
||||
|
||||
assert(!waitEvent);
|
||||
waitEvent = epicsEventMustCreate(epicsEventEmpty);
|
||||
|
||||
/* Start counter */
|
||||
epicsThreadCreate("countUp", epicsThreadPriorityHigh,
|
||||
epicsThreadGetStackSize(epicsThreadStackSmall), countUp, ptarg);
|
||||
|
||||
dbScanLock((dbCommon*)psrc);
|
||||
/* Check that unlocked gets change */
|
||||
temp = getTwice(psrclnk, NULL);
|
||||
testOk(temp == -1, "unlocked, getTwice returned %d (-1)", temp);
|
||||
|
||||
/* Check locked gets are atomic */
|
||||
temp = dbLinkDoLocked(psrclnk, getTwice, NULL);
|
||||
testOk(temp == 0, "locked, getTwice returned %d (0)", temp);
|
||||
dbScanUnlock((dbCommon*)psrc);
|
||||
|
||||
epicsEventMustWait(waitEvent);
|
||||
epicsEventDestroy(waitEvent);
|
||||
waitEvent = NULL;
|
||||
|
||||
testIocShutdownOk();
|
||||
|
||||
testdbCleanup();
|
||||
@@ -189,9 +246,6 @@ static void testStringLink(void)
|
||||
testdbCleanup();
|
||||
}
|
||||
|
||||
static epicsEventId waitEvent;
|
||||
static unsigned waitCounter;
|
||||
|
||||
static void wasproc(xRecord *prec)
|
||||
{
|
||||
waitCounter++;
|
||||
@@ -562,7 +616,7 @@ static void testCAC(void)
|
||||
|
||||
MAIN(dbCaLinkTest)
|
||||
{
|
||||
testPlan(99);
|
||||
testPlan(101);
|
||||
testNativeLink();
|
||||
testStringLink();
|
||||
testCP();
|
||||
|
||||
@@ -29,6 +29,7 @@ long link_test_noop(void *junk)
|
||||
static dset devxLTest ## LTYPE = {4, NULL, &link_test_init, &link_test_noop, &link_test_noop}; \
|
||||
epicsExportAddress(dset, devxLTest ## LTYPE);
|
||||
|
||||
DEFDSET(JSON_LINK)
|
||||
DEFDSET(VME_IO)
|
||||
DEFDSET(CAMAC_IO)
|
||||
DEFDSET(AB_IO)
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
device(x, JSON_LINK, devxLTestJSON_LINK, "Unit Test JSON_LINK")
|
||||
device(x, VME_IO, devxLTestVME_IO, "Unit Test VME_IO")
|
||||
device(x, CAMAC_IO, devxLTestCAMAC_IO, "Unit Test CAMAC_IO")
|
||||
device(x, AB_IO, devxLTestAB_IO, "Unit Test AB_IO")
|
||||
|
||||
@@ -24,8 +24,10 @@
|
||||
#include "osiFileName.h"
|
||||
#include "dbmf.h"
|
||||
#include "errlog.h"
|
||||
#include <epicsAtomic.h>
|
||||
|
||||
#include "xRecord.h"
|
||||
#include "jlinkz.h"
|
||||
|
||||
#include "testMain.h"
|
||||
|
||||
@@ -60,6 +62,7 @@ static const struct testParseDataT {
|
||||
{"#B11 C12 N13 A14 F15 @cparam", {CAMAC_IO, "cparam", 0, "BCNAF", {11, 12, 13, 14, 15}}},
|
||||
{" #B111 C112 N113 @cparam", {CAMAC_IO, "cparam", 0, "BCN", {111, 112, 113}}},
|
||||
{" @hello world ", {INST_IO, "hello world", 0, "", /*{}*/}},
|
||||
{" {\"x\":true} ", {JSON_LINK, "{\"x\":true}", 0, "", /*{}*/}},
|
||||
{NULL}
|
||||
};
|
||||
|
||||
@@ -67,7 +70,7 @@ static void testLinkParse(void)
|
||||
{
|
||||
const struct testParseDataT *td = testParseData;
|
||||
dbLinkInfo info;
|
||||
testDiag("link parsing");
|
||||
testDiag("\n# Checking link parsing\n#");
|
||||
testdbPrepare();
|
||||
|
||||
testdbReadDatabase("dbTestIoc.dbd", NULL, NULL);
|
||||
@@ -80,27 +83,32 @@ static void testLinkParse(void)
|
||||
testIocInitOk();
|
||||
eltc(1);
|
||||
|
||||
for(;td->str; td++) {
|
||||
for (;td->str; td++) {
|
||||
int i, N;
|
||||
testDiag("Parse \"%s\"", td->str);
|
||||
testOk1(dbParseLink(td->str, DBF_INLINK, &info)==0);
|
||||
testOk1(info.ltype==td->info.ltype);
|
||||
if(td->info.target)
|
||||
testDiag("Parsing \"%s\"", td->str);
|
||||
testOk(dbParseLink(td->str, DBF_INLINK, &info, 0) == 0, "Parser returned OK");
|
||||
if (!testOk(info.ltype == td->info.ltype, "Link type value"))
|
||||
testDiag("Expected %d, got %d", td->info.ltype, info.ltype);
|
||||
if (td->info.target)
|
||||
testStrcmp(0, info.target, td->info.target);
|
||||
if(info.ltype==td->info.ltype) {
|
||||
switch(info.ltype) {
|
||||
if (info.ltype == td->info.ltype) {
|
||||
switch (info.ltype) {
|
||||
case PV_LINK:
|
||||
testOk1(info.modifiers==td->info.modifiers);
|
||||
if (!testOk(info.modifiers == td->info.modifiers,
|
||||
"PV Link modifier flags"))
|
||||
testDiag("Expected %d, got %d", td->info.modifiers,
|
||||
info.modifiers);
|
||||
break;
|
||||
case VME_IO:
|
||||
case CAMAC_IO:
|
||||
testStrcmp(0, info.hwid, td->info.hwid);
|
||||
N = strlen(td->info.hwid);
|
||||
for(i=0; i<N; i++)
|
||||
for (i=0; i<N; i++)
|
||||
testOk(info.hwnums[i]==td->info.hwnums[i], "%d == %d",
|
||||
info.hwnums[i], td->info.hwnums[i]);
|
||||
}
|
||||
}
|
||||
free(info.target);
|
||||
dbFreeLinkInfo(&info);
|
||||
}
|
||||
|
||||
testIocShutdownOk();
|
||||
@@ -124,7 +132,7 @@ static void testLinkFailParse(void)
|
||||
{
|
||||
const char * const *td = testParseFailData;
|
||||
dbLinkInfo info;
|
||||
testDiag("link parsing of invalid input");
|
||||
testDiag("\n# Check parsing of invalid inputs\n#");
|
||||
testdbPrepare();
|
||||
|
||||
testdbReadDatabase("dbTestIoc.dbd", NULL, NULL);
|
||||
@@ -138,8 +146,8 @@ static void testLinkFailParse(void)
|
||||
eltc(1);
|
||||
|
||||
for(;*td; td++) {
|
||||
testDiag("Expect failure \"%s\"", *td);
|
||||
testOk1(dbParseLink(*td, DBF_INLINK, &info)==S_dbLib_badField);
|
||||
testOk(dbParseLink(*td, DBF_INLINK, &info, 0) == S_dbLib_badField,
|
||||
"dbParseLink correctly rejected \"%s\"", *td);
|
||||
}
|
||||
|
||||
testIocShutdownOk();
|
||||
@@ -179,7 +187,7 @@ static void testCADBSet(void)
|
||||
const struct testDataT *td = testSetData;
|
||||
xRecord *prec;
|
||||
DBLINK *plink;
|
||||
testDiag("DB/CA link retargeting");
|
||||
testDiag("\n# Checking DB/CA link retargeting\n#");
|
||||
testdbPrepare();
|
||||
|
||||
testdbReadDatabase("dbTestIoc.dbd", NULL, NULL);
|
||||
@@ -196,21 +204,22 @@ static void testCADBSet(void)
|
||||
plink = &prec->lnk;
|
||||
|
||||
for (;td->linkstring;td++) {
|
||||
testDiag("x1.LNK <- \"%s\"", td->linkstring);
|
||||
testDiag("Trying field value \"%s\"", td->linkstring);
|
||||
|
||||
testdbPutFieldOk("x1.LNK", DBF_STRING, td->linkstring);
|
||||
if (td->linkback)
|
||||
testdbGetFieldEqual("x1.LNK", DBF_STRING, td->linkback);
|
||||
else
|
||||
testdbGetFieldEqual("x1.LNK", DBF_STRING, td->linkstring);
|
||||
testOk1(plink->type==td->linkType);
|
||||
if (!testOk(plink->type == td->linkType, "Link type"))
|
||||
testDiag("Expected %d, got %d", td->linkType, plink->type);
|
||||
|
||||
if (plink->type==td->linkType) {
|
||||
switch(td->linkType) {
|
||||
if (plink->type == td->linkType) {
|
||||
switch (td->linkType) {
|
||||
case CONSTANT:
|
||||
if(plink->value.constantStr)
|
||||
testOk1(strcmp(plink->value.constantStr,td->linkstring)==0);
|
||||
else if(td->linkstring[0]=='\0')
|
||||
if (plink->value.constantStr)
|
||||
testOk1(strcmp(plink->value.constantStr, td->linkstring) == 0);
|
||||
else if (td->linkstring[0]=='\0')
|
||||
testPass("Empty String");
|
||||
else
|
||||
testFail("oops");
|
||||
@@ -218,7 +227,7 @@ static void testCADBSet(void)
|
||||
|
||||
case DB_LINK:
|
||||
case CA_LINK:
|
||||
testOk(plink->value.pv_link.pvlMask==td->pvlMask,
|
||||
testOk(plink->value.pv_link.pvlMask == td->pvlMask,
|
||||
"pvlMask %x == %x", plink->value.pv_link.pvlMask, td->pvlMask);
|
||||
break;
|
||||
}
|
||||
@@ -239,6 +248,7 @@ typedef struct {
|
||||
} testHWDataT;
|
||||
|
||||
static const testHWDataT testHWData[] = {
|
||||
{"rJSON_LINK", JSON_LINK, "{\"x\":true}", {0}, "{\"x\":true}"},
|
||||
{"rVME_IO", VME_IO, "#C100 S101 @parm VME_IO", {100, 101}, "parm VME_IO"},
|
||||
{"rCAMAC_IO", CAMAC_IO, "#B11 C12 N13 A14 F15 @parm CAMAC_IO", {11, 12, 13, 14, 15}, "parm CAMAC_IO"},
|
||||
{"rAB_IO", AB_IO, "#L21 A22 C23 S24 @parm AB_IO", {21, 22, 23, 24}, "parm AB_IO"},
|
||||
@@ -255,63 +265,66 @@ static const testHWDataT testHWData[] = {
|
||||
static void testLink(DBLINK *plink, const testHWDataT *td)
|
||||
{
|
||||
switch(td->ltype) {
|
||||
case JSON_LINK:
|
||||
testOk1(strcmp(plink->value.json.string, td->parm) == 0);
|
||||
break;
|
||||
case VME_IO:
|
||||
testOk1(plink->value.vmeio.card==td->vals[0]);
|
||||
testOk1(plink->value.vmeio.signal==td->vals[1]);
|
||||
testOk1(strcmp(plink->value.vmeio.parm, td->parm)==0);
|
||||
testOk1(plink->value.vmeio.card == td->vals[0]);
|
||||
testOk1(plink->value.vmeio.signal == td->vals[1]);
|
||||
testOk1(strcmp(plink->value.vmeio.parm, td->parm) == 0);
|
||||
break;
|
||||
case CAMAC_IO:
|
||||
testOk1(plink->value.camacio.b==td->vals[0]);
|
||||
testOk1(plink->value.camacio.c==td->vals[1]);
|
||||
testOk1(plink->value.camacio.n==td->vals[2]);
|
||||
testOk1(plink->value.camacio.a==td->vals[3]);
|
||||
testOk1(plink->value.camacio.f==td->vals[4]);
|
||||
testOk1(strcmp(plink->value.camacio.parm, td->parm)==0);
|
||||
testOk1(plink->value.camacio.b == td->vals[0]);
|
||||
testOk1(plink->value.camacio.c == td->vals[1]);
|
||||
testOk1(plink->value.camacio.n == td->vals[2]);
|
||||
testOk1(plink->value.camacio.a == td->vals[3]);
|
||||
testOk1(plink->value.camacio.f == td->vals[4]);
|
||||
testOk1(strcmp(plink->value.camacio.parm, td->parm) == 0);
|
||||
break;
|
||||
case AB_IO:
|
||||
testOk1(plink->value.abio.link==td->vals[0]);
|
||||
testOk1(plink->value.abio.adapter==td->vals[1]);
|
||||
testOk1(plink->value.abio.card==td->vals[2]);
|
||||
testOk1(plink->value.abio.signal==td->vals[3]);
|
||||
testOk1(strcmp(plink->value.abio.parm, td->parm)==0);
|
||||
testOk1(plink->value.abio.link == td->vals[0]);
|
||||
testOk1(plink->value.abio.adapter == td->vals[1]);
|
||||
testOk1(plink->value.abio.card == td->vals[2]);
|
||||
testOk1(plink->value.abio.signal == td->vals[3]);
|
||||
testOk1(strcmp(plink->value.abio.parm, td->parm) == 0);
|
||||
break;
|
||||
case GPIB_IO:
|
||||
testOk1(plink->value.gpibio.link==td->vals[0]);
|
||||
testOk1(plink->value.gpibio.addr==td->vals[1]);
|
||||
testOk1(strcmp(plink->value.gpibio.parm, td->parm)==0);
|
||||
testOk1(plink->value.gpibio.link == td->vals[0]);
|
||||
testOk1(plink->value.gpibio.addr == td->vals[1]);
|
||||
testOk1(strcmp(plink->value.gpibio.parm, td->parm) == 0);
|
||||
break;
|
||||
case BITBUS_IO:
|
||||
testOk1(plink->value.bitbusio.link==td->vals[0]);
|
||||
testOk1(plink->value.bitbusio.node==td->vals[1]);
|
||||
testOk1(plink->value.bitbusio.port==td->vals[2]);
|
||||
testOk1(plink->value.bitbusio.signal==td->vals[3]);
|
||||
testOk1(strcmp(plink->value.bitbusio.parm, td->parm)==0);
|
||||
testOk1(plink->value.bitbusio.link == td->vals[0]);
|
||||
testOk1(plink->value.bitbusio.node == td->vals[1]);
|
||||
testOk1(plink->value.bitbusio.port == td->vals[2]);
|
||||
testOk1(plink->value.bitbusio.signal == td->vals[3]);
|
||||
testOk1(strcmp(plink->value.bitbusio.parm, td->parm) == 0);
|
||||
break;
|
||||
case INST_IO:
|
||||
testOk1(strcmp(plink->value.instio.string, td->parm)==0);
|
||||
testOk1(strcmp(plink->value.instio.string, td->parm) == 0);
|
||||
break;
|
||||
case BBGPIB_IO:
|
||||
testOk1(plink->value.bbgpibio.link==td->vals[0]);
|
||||
testOk1(plink->value.bbgpibio.bbaddr==td->vals[1]);
|
||||
testOk1(plink->value.bbgpibio.gpibaddr==td->vals[2]);
|
||||
testOk1(strcmp(plink->value.bbgpibio.parm, td->parm)==0);
|
||||
testOk1(plink->value.bbgpibio.link == td->vals[0]);
|
||||
testOk1(plink->value.bbgpibio.bbaddr == td->vals[1]);
|
||||
testOk1(plink->value.bbgpibio.gpibaddr == td->vals[2]);
|
||||
testOk1(strcmp(plink->value.bbgpibio.parm, td->parm) == 0);
|
||||
break;
|
||||
case RF_IO:
|
||||
testOk1(plink->value.rfio.cryo==td->vals[0]);
|
||||
testOk1(plink->value.rfio.micro==td->vals[1]);
|
||||
testOk1(plink->value.rfio.dataset==td->vals[2]);
|
||||
testOk1(plink->value.rfio.element==td->vals[3]);
|
||||
testOk1(plink->value.rfio.cryo == td->vals[0]);
|
||||
testOk1(plink->value.rfio.micro == td->vals[1]);
|
||||
testOk1(plink->value.rfio.dataset == td->vals[2]);
|
||||
testOk1(plink->value.rfio.element == td->vals[3]);
|
||||
break;
|
||||
case VXI_IO:
|
||||
if(plink->value.vxiio.flag==VXIDYNAMIC) {
|
||||
testOk1(plink->value.vxiio.frame==td->vals[0]);
|
||||
testOk1(plink->value.vxiio.slot==td->vals[1]);
|
||||
testOk1(plink->value.vxiio.signal==td->vals[2]);
|
||||
if(plink->value.vxiio.flag == VXIDYNAMIC) {
|
||||
testOk1(plink->value.vxiio.frame == td->vals[0]);
|
||||
testOk1(plink->value.vxiio.slot == td->vals[1]);
|
||||
testOk1(plink->value.vxiio.signal == td->vals[2]);
|
||||
} else {
|
||||
testOk1(plink->value.vxiio.la==td->vals[0]);
|
||||
testOk1(plink->value.vxiio.signal==td->vals[1]);
|
||||
testOk1(plink->value.vxiio.la == td->vals[0]);
|
||||
testOk1(plink->value.vxiio.signal == td->vals[1]);
|
||||
}
|
||||
testOk1(strcmp(plink->value.vxiio.parm, td->parm)==0);
|
||||
testOk1(strcmp(plink->value.vxiio.parm, td->parm) == 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -319,7 +332,7 @@ static void testLink(DBLINK *plink, const testHWDataT *td)
|
||||
static void testHWInitSet(void)
|
||||
{
|
||||
const testHWDataT *td = testHWData;
|
||||
testDiag("HW link parsing during initialization");
|
||||
testDiag("\n# Checking HW link parsing during initialization\n#");
|
||||
testdbPrepare();
|
||||
|
||||
testdbReadDatabase("dbTestIoc.dbd", NULL, NULL);
|
||||
@@ -332,13 +345,14 @@ static void testHWInitSet(void)
|
||||
testIocInitOk();
|
||||
eltc(1);
|
||||
|
||||
for(;td->recname;td++) {
|
||||
for (;td->recname; td++) {
|
||||
char buf[MAX_STRING_SIZE];
|
||||
xRecord *prec;
|
||||
DBLINK *plink;
|
||||
|
||||
testDiag("%s == \"%s\"", td->recname, td->wval);
|
||||
|
||||
prec = (xRecord*)testdbRecordPtr(td->recname);
|
||||
prec = (xRecord *) testdbRecordPtr(td->recname);
|
||||
plink = &prec->inp;
|
||||
|
||||
strcpy(buf, td->recname);
|
||||
@@ -346,9 +360,11 @@ static void testHWInitSet(void)
|
||||
|
||||
testdbGetFieldEqual(buf, DBR_STRING, td->wval);
|
||||
|
||||
testOk(plink->type==td->ltype, "link type %d == %d",
|
||||
plink->type, td->ltype);
|
||||
if(plink->type==td->ltype) {
|
||||
if (!testOk(plink->type == td->ltype, "Link type")) {
|
||||
testDiag("Expected %d, got %d",
|
||||
td->ltype, plink->type);
|
||||
}
|
||||
else {
|
||||
testLink(plink, td);
|
||||
}
|
||||
|
||||
@@ -360,6 +376,7 @@ static void testHWInitSet(void)
|
||||
}
|
||||
|
||||
static const testHWDataT testHWData2[] = {
|
||||
{"rJSON_LINK", JSON_LINK, "{\"x\":true}", {0}, "{\"x\":true}"},
|
||||
{"rVME_IO", VME_IO, "#C200 S201 @another VME_IO", {200, 201}, "another VME_IO"},
|
||||
{"rCAMAC_IO", CAMAC_IO, "#B111 C112 N113 A114 F115 @CAMAC_IO", {111, 112, 113, 114, 115}, "CAMAC_IO"},
|
||||
{"rAB_IO", AB_IO, "#L121 A122 C123 S124 @another AB_IO", {121, 122, 123, 124}, "another AB_IO"},
|
||||
@@ -376,7 +393,8 @@ static const testHWDataT testHWData2[] = {
|
||||
static void testHWMod(void)
|
||||
{
|
||||
const testHWDataT *td = testHWData2;
|
||||
testDiag("HW link parsing during retarget");
|
||||
|
||||
testDiag("\n# Checking HW link parsing during retarget\n#");
|
||||
testdbPrepare();
|
||||
|
||||
testdbReadDatabase("dbTestIoc.dbd", NULL, NULL);
|
||||
@@ -405,9 +423,11 @@ static void testHWMod(void)
|
||||
|
||||
testdbGetFieldEqual(buf, DBR_STRING, td->wval);
|
||||
|
||||
testOk(plink->type==td->ltype, "link type %d == %d",
|
||||
plink->type, td->ltype);
|
||||
if(plink->type==td->ltype) {
|
||||
if (!testOk(plink->type == td->ltype, "Link type")) {
|
||||
testDiag("Expected %d, got %d",
|
||||
td->ltype, plink->type);
|
||||
}
|
||||
else {
|
||||
testLink(plink, td);
|
||||
}
|
||||
|
||||
@@ -422,42 +442,42 @@ static void testLinkInitFail(void)
|
||||
{
|
||||
xRecord *prec;
|
||||
DBLINK *plink;
|
||||
testDiag("Link parsing failures during initialization");
|
||||
testDiag("\n# Checking link parse failures at iocInit\n#");
|
||||
testdbPrepare();
|
||||
|
||||
testdbReadDatabase("dbTestIoc.dbd", NULL, NULL);
|
||||
|
||||
dbTestIoc_registerRecordDeviceDriver(pdbbase);
|
||||
|
||||
|
||||
/* this load will fail */
|
||||
eltc(0);
|
||||
testOk1(dbReadDatabase(&pdbbase, "dbBadLink.db", "." OSI_PATH_LIST_SEPARATOR ".." OSI_PATH_LIST_SEPARATOR
|
||||
"../O.Common" OSI_PATH_LIST_SEPARATOR "O.Common", NULL)!=0);
|
||||
testOk(dbReadDatabase(&pdbbase, "dbBadLink.db", "." OSI_PATH_LIST_SEPARATOR
|
||||
".." OSI_PATH_LIST_SEPARATOR "../O.Common" OSI_PATH_LIST_SEPARATOR
|
||||
"O.Common", NULL) != 0, "dbReadDatabase returned error (expected)");
|
||||
|
||||
testIocInitOk();
|
||||
eltc(1);
|
||||
|
||||
testdbGetFieldEqual("eVME_IO1.INP", DBR_STRING, "#C0 S0 @");
|
||||
|
||||
prec = (xRecord*)testdbRecordPtr("eVME_IO1");
|
||||
prec = (xRecord *) testdbRecordPtr("eVME_IO1");
|
||||
plink = &prec->inp;
|
||||
testOk1(plink->type==VME_IO);
|
||||
testOk1(plink->value.vmeio.parm!=NULL);
|
||||
testOk1(plink->type == VME_IO);
|
||||
testOk1(plink->value.vmeio.parm != NULL);
|
||||
|
||||
testdbGetFieldEqual("eVME_IO2.INP", DBR_STRING, "#C0 S0 @");
|
||||
|
||||
prec = (xRecord*)testdbRecordPtr("eVME_IO2");
|
||||
prec = (xRecord *) testdbRecordPtr("eVME_IO2");
|
||||
plink = &prec->inp;
|
||||
testOk1(plink->type==VME_IO);
|
||||
testOk1(plink->value.vmeio.parm!=NULL);
|
||||
testOk1(plink->type == VME_IO);
|
||||
testOk1(plink->value.vmeio.parm != NULL);
|
||||
|
||||
testdbGetFieldEqual("eINST_IO.INP", DBR_STRING, "@");
|
||||
|
||||
prec = (xRecord*)testdbRecordPtr("eINST_IO");
|
||||
prec = (xRecord *) testdbRecordPtr("eINST_IO");
|
||||
plink = &prec->inp;
|
||||
testOk1(plink->type==INST_IO);
|
||||
testOk1(plink->value.instio.string!=NULL);
|
||||
testOk1(plink->type == INST_IO);
|
||||
testOk1(plink->value.instio.string != NULL);
|
||||
|
||||
testIocShutdownOk();
|
||||
|
||||
@@ -466,7 +486,7 @@ static void testLinkInitFail(void)
|
||||
|
||||
static void testLinkFail(void)
|
||||
{
|
||||
testDiag("Link parsing failures");
|
||||
testDiag("\n# Checking runtime link parse failures\n#");
|
||||
testdbPrepare();
|
||||
|
||||
testdbReadDatabase("dbTestIoc.dbd", NULL, NULL);
|
||||
@@ -482,9 +502,22 @@ static void testLinkFail(void)
|
||||
/* INST_IO doesn't accept empty string */
|
||||
testdbPutFieldFail(S_dbLib_badField, "rINST_IO.INP", DBR_STRING, "");
|
||||
|
||||
/* INST_IO doesn't accept empty string */
|
||||
/* INST_IO doesn't accept string without @ */
|
||||
testdbPutFieldFail(S_dbLib_badField, "rINST_IO.INP", DBR_STRING, "abc");
|
||||
|
||||
/* JSON_LINK dies when expected */
|
||||
testdbPutFieldOk("rJSON_LINK.INP", DBR_STRING, "{\"x\":true}");
|
||||
testdbPutFieldFail(S_dbLib_badField, "rJSON_LINK.INP", DBR_STRING, "{\"x\":false}");
|
||||
testdbPutFieldFail(S_dbLib_badField, "rJSON_LINK.INP", DBR_STRING, "{\"x\":null}");
|
||||
testdbPutFieldFail(S_dbLib_badField, "rJSON_LINK.INP", DBR_STRING, "{\"x\":1}");
|
||||
testdbPutFieldFail(S_dbLib_badField, "rJSON_LINK.INP", DBR_STRING, "{\"x\":1.1}");
|
||||
testdbPutFieldFail(S_dbLib_badField, "rJSON_LINK.INP", DBR_STRING, "{\"x\":\"x\"}");
|
||||
testdbPutFieldFail(S_dbLib_badField, "rJSON_LINK.INP", DBR_STRING, "{\"x\":[]}");
|
||||
testdbPutFieldFail(S_dbLib_badField, "rJSON_LINK.INP", DBR_STRING, "{\"x\":{}}");
|
||||
|
||||
/* JSON_LINK syntax errors */
|
||||
testdbPutFieldFail(S_dbLib_badField, "rJSON_LINK.INP", DBR_STRING, "{\"x\":bbbb}");
|
||||
|
||||
/* syntax errors */
|
||||
testdbPutFieldFail(S_dbLib_badField, "rVME_IO.INP", DBR_STRING, "#S201 C200 @another VME_IO");
|
||||
testdbPutFieldFail(S_dbLib_badField, "rVME_IO.INP", DBR_STRING, "C200 #S201");
|
||||
@@ -502,9 +535,73 @@ static void testLinkFail(void)
|
||||
testdbCleanup();
|
||||
}
|
||||
|
||||
static
|
||||
void testNumZ(int expect)
|
||||
{
|
||||
int numz = epicsAtomicGetIntT(&numzalloc);
|
||||
testOk(numz==expect, "numzalloc==%d (%d)", expect, numz);
|
||||
}
|
||||
|
||||
static
|
||||
void testJLink(void)
|
||||
{
|
||||
testDiag("Test json link setup/retarget");
|
||||
|
||||
testNumZ(0);
|
||||
|
||||
testDiag("Link parsing failures");
|
||||
testdbPrepare();
|
||||
|
||||
testdbReadDatabase("dbTestIoc.dbd", NULL, NULL);
|
||||
|
||||
dbTestIoc_registerRecordDeviceDriver(pdbbase);
|
||||
|
||||
testdbReadDatabase("dbPutLinkTest.db", NULL, NULL);
|
||||
testdbReadDatabase("dbPutLinkTestJ.db", NULL, NULL);
|
||||
|
||||
testNumZ(0);
|
||||
|
||||
eltc(0);
|
||||
testIocInitOk();
|
||||
eltc(1);
|
||||
|
||||
testNumZ(3);
|
||||
|
||||
testdbPutFieldOk("j1.PROC", DBF_LONG, 1);
|
||||
testdbPutFieldOk("j2.PROC", DBF_LONG, 1);
|
||||
testdbPutFieldOk("j3.PROC", DBF_LONG, 1);
|
||||
|
||||
testdbGetFieldEqual("j1.INP", DBF_STRING, "{\"z\":{\"good\":1}}");
|
||||
testdbGetFieldEqual("j1.VAL", DBF_LONG, 1);
|
||||
testdbGetFieldEqual("j2.VAL", DBF_LONG, 2);
|
||||
testdbGetFieldEqual("j3.VAL", DBF_LONG, 3);
|
||||
|
||||
testNumZ(3);
|
||||
|
||||
testdbPutFieldOk("j1.INP", DBF_STRING, "{\"z\":{\"good\":4}}");
|
||||
testdbPutFieldOk("j1.PROC", DBF_LONG, 1);
|
||||
testdbGetFieldEqual("j1.VAL", DBF_LONG, 4);
|
||||
|
||||
testNumZ(3);
|
||||
|
||||
testdbPutFieldFail(S_dbLib_badField, "j1.INP", DBF_STRING, "{\"z\":{\"fail\":5}}");
|
||||
testdbPutFieldOk("j1.PROC", DBF_LONG, 1);
|
||||
testdbGetFieldEqual("j1.VAL", DBF_LONG, 4);
|
||||
/* put failure in parsing stage doesn't modify link */
|
||||
testdbGetFieldEqual("j1.INP", DBF_STRING, "{\"z\":{\"good\":4}}");
|
||||
|
||||
testNumZ(3);
|
||||
|
||||
testIocShutdownOk();
|
||||
|
||||
testNumZ(0);
|
||||
|
||||
testdbCleanup();
|
||||
}
|
||||
|
||||
MAIN(dbPutLinkTest)
|
||||
{
|
||||
testPlan(251);
|
||||
testPlan(301);
|
||||
testLinkParse();
|
||||
testLinkFailParse();
|
||||
testCADBSet();
|
||||
@@ -512,5 +609,6 @@ MAIN(dbPutLinkTest)
|
||||
testHWMod();
|
||||
testLinkInitFail();
|
||||
testLinkFail();
|
||||
testJLink();
|
||||
return testDone();
|
||||
}
|
||||
|
||||
@@ -3,6 +3,10 @@ record(x, "x2") {}
|
||||
record(x, "x3") {}
|
||||
record(x, "x4") {}
|
||||
|
||||
record(x, "rJSON_LINK") {
|
||||
field(DTYP, "Unit Test JSON_LINK")
|
||||
field(INP, {x:true})
|
||||
}
|
||||
record(x, "rVME_IO") {
|
||||
field(DTYP, "Unit Test VME_IO")
|
||||
field(INP, "#C100 S101 @parm VME_IO")
|
||||
|
||||
12
src/ioc/db/test/dbPutLinkTestJ.db
Normal file
12
src/ioc/db/test/dbPutLinkTestJ.db
Normal file
@@ -0,0 +1,12 @@
|
||||
|
||||
record(x, "j1") {
|
||||
field(INP, {z:{good:1}})
|
||||
}
|
||||
|
||||
record(x, "j2") {
|
||||
field(INP, {z:{good:2}})
|
||||
}
|
||||
|
||||
record(x, "j3") {
|
||||
field(INP, {z:{good:3}})
|
||||
}
|
||||
@@ -141,17 +141,13 @@ epicsExportAddress(dset, devxScanIO);
|
||||
/* basic DTYP="Soft Channel" */
|
||||
static long xsoft_init_record(xRecord *prec)
|
||||
{
|
||||
if(prec->inp.type==CONSTANT)
|
||||
recGblInitConstantLink(&prec->inp, DBF_LONG, &prec->val);
|
||||
recGblInitConstantLink(&prec->inp, DBF_LONG, &prec->val);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long xsoft_read(xRecord *prec)
|
||||
{
|
||||
if(prec->inp.type==CONSTANT)
|
||||
return 0;
|
||||
dbGetLink(&prec->inp, DBR_DOUBLE, &prec->val, NULL, NULL);
|
||||
return 0;
|
||||
return dbGetLink(&prec->inp, DBR_LONG, &prec->val, NULL, NULL);
|
||||
}
|
||||
|
||||
static struct xdset devxSoft = {
|
||||
|
||||
251
src/ioc/db/test/jlinkz.c
Normal file
251
src/ioc/db/test/jlinkz.c
Normal file
@@ -0,0 +1,251 @@
|
||||
/*************************************************************************\
|
||||
* Copyright (c) 2016 Michael Davidsaver
|
||||
* EPICS BASE is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
\*************************************************************************/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <epicsExport.h>
|
||||
#include <dbAccess.h>
|
||||
#include <link.h>
|
||||
#include <alarm.h>
|
||||
#include <dbJLink.h>
|
||||
#include <dbDefs.h>
|
||||
#include <dbConvertFast.h>
|
||||
#include <epicsMutex.h>
|
||||
#include <epicsAtomic.h>
|
||||
#include <epicsUnitTest.h>
|
||||
|
||||
#define epicsExportSharedSymbols
|
||||
|
||||
#include "jlinkz.h"
|
||||
|
||||
int numzalloc;
|
||||
|
||||
|
||||
static
|
||||
void z_open(struct link *plink)
|
||||
{
|
||||
zpriv *priv = CONTAINER(plink->value.json.jlink, zpriv, base);
|
||||
|
||||
if(priv->isopen)
|
||||
testDiag("lsetZ re-open");
|
||||
priv->isopen = 1;
|
||||
testDiag("Open jlinkz %p", priv);
|
||||
}
|
||||
|
||||
static
|
||||
void z_remove(struct dbLocker *locker, struct link *plink)
|
||||
{
|
||||
zpriv *priv = CONTAINER(plink->value.json.jlink, zpriv, base);
|
||||
|
||||
epicsMutexLock(priv->lock);
|
||||
|
||||
if(!priv->isopen)
|
||||
testDiag("lsetZ remove without open");
|
||||
|
||||
epicsMutexUnlock(priv->lock);
|
||||
|
||||
testDiag("Remove/free jlinkz %p", priv);
|
||||
|
||||
epicsAtomicDecrIntT(&numzalloc);
|
||||
|
||||
epicsMutexDestroy(priv->lock);
|
||||
free(priv);
|
||||
plink->value.json.jlink = NULL; /* paranoia */
|
||||
}
|
||||
|
||||
static
|
||||
int z_connected(const struct link *plink)
|
||||
{
|
||||
return 1; /* TODO: not provided should be connected */
|
||||
}
|
||||
|
||||
static
|
||||
int z_dbftype(const struct link *plink)
|
||||
{
|
||||
return DBF_LONG;
|
||||
}
|
||||
|
||||
static
|
||||
long z_elements(const struct link *plink, long *nelements)
|
||||
{
|
||||
*nelements = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static
|
||||
long z_getval(struct link *plink, short dbrType, void *pbuffer,
|
||||
long *pnRequest)
|
||||
{
|
||||
long ret;
|
||||
long (*pconv)(const epicsInt32 *, void *, const dbAddr *) = dbFastGetConvertRoutine[DBF_LONG][dbrType];
|
||||
zpriv *priv = CONTAINER(plink->value.json.jlink, zpriv, base);
|
||||
|
||||
if(pnRequest && *pnRequest==0) return 0;
|
||||
|
||||
epicsMutexLock(priv->lock);
|
||||
ret = (*pconv)(&priv->value, pbuffer, NULL);
|
||||
epicsMutexUnlock(priv->lock);
|
||||
if(ret==0 && pnRequest) *pnRequest = 1;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* TODO: atomicly get value and alarm */
|
||||
static
|
||||
long z_getalarm(const struct link *plink, epicsEnum16 *status,
|
||||
epicsEnum16 *severity)
|
||||
{
|
||||
zpriv *priv = CONTAINER(plink->value.json.jlink, zpriv, base);
|
||||
epicsEnum16 sevr, stat;
|
||||
|
||||
epicsMutexLock(priv->lock);
|
||||
sevr = priv->isset ? 0 : INVALID_ALARM;
|
||||
stat = priv->isset ? 0 : LINK_ALARM;
|
||||
epicsMutexUnlock(priv->lock);
|
||||
|
||||
if(status) *status = stat;
|
||||
if(severity) *severity = sevr;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static
|
||||
long z_putval(struct link *plink, short dbrType,
|
||||
const void *pbuffer, long nRequest)
|
||||
{
|
||||
long ret;
|
||||
long (*pconv)(epicsInt32 *, const void *, const dbAddr *) = dbFastPutConvertRoutine[DBF_LONG][dbrType];
|
||||
zpriv *priv = CONTAINER(plink->value.json.jlink, zpriv, base);
|
||||
|
||||
if(nRequest==0) return 0;
|
||||
|
||||
epicsMutexLock(priv->lock);
|
||||
ret = (*pconv)(&priv->value, pbuffer, NULL);
|
||||
epicsMutexUnlock(priv->lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static lset lsetZ = {
|
||||
0, 0, /* non-const, non-volatile */
|
||||
&z_open,
|
||||
&z_remove,
|
||||
NULL, NULL, NULL, /* load */
|
||||
&z_connected,
|
||||
&z_dbftype,
|
||||
&z_elements,
|
||||
&z_getval,
|
||||
NULL, /* control limits */
|
||||
NULL, /* display limits */
|
||||
NULL, /* alarm limits */
|
||||
NULL, /* prec */
|
||||
NULL, /* units */
|
||||
&z_getalarm,
|
||||
NULL, /* time */
|
||||
&z_putval,
|
||||
NULL, /* putasync */
|
||||
NULL, /* forward */
|
||||
NULL, /* doLocked */
|
||||
};
|
||||
|
||||
static
|
||||
jlink* z_alloc(short dbfType)
|
||||
{
|
||||
zpriv *priv;
|
||||
priv = calloc(1, sizeof(*priv));
|
||||
if(!priv) goto fail;
|
||||
|
||||
priv->lock = epicsMutexCreate();
|
||||
if(!priv->lock) goto fail;
|
||||
|
||||
epicsAtomicIncrIntT(&numzalloc);
|
||||
|
||||
testDiag("Alloc jlinkz %p", priv);
|
||||
|
||||
return &priv->base;
|
||||
fail:
|
||||
if(priv && priv->lock) epicsMutexDestroy(priv->lock);
|
||||
free(priv);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static
|
||||
void z_free(jlink *pj)
|
||||
{
|
||||
zpriv *priv = CONTAINER(pj, zpriv, base);
|
||||
|
||||
if(priv->isopen)
|
||||
testDiag("lsetZ jlink free after open()");
|
||||
|
||||
testDiag("Free jlinkz %p", priv);
|
||||
|
||||
epicsAtomicDecrIntT(&numzalloc);
|
||||
|
||||
epicsMutexDestroy(priv->lock);
|
||||
free(priv);
|
||||
}
|
||||
|
||||
static
|
||||
jlif_result z_int(jlink *pj, long long num)
|
||||
{
|
||||
zpriv *priv = CONTAINER(pj, zpriv, base);
|
||||
|
||||
priv->value = num;
|
||||
priv->isset = 1;
|
||||
|
||||
return jlif_continue;
|
||||
}
|
||||
|
||||
static
|
||||
jlif_key_result z_start(jlink *pj)
|
||||
{
|
||||
return jlif_key_continue;
|
||||
}
|
||||
|
||||
static
|
||||
jlif_result z_key(jlink *pj, const char *key, size_t len)
|
||||
{
|
||||
zpriv *priv = CONTAINER(pj, zpriv, base);
|
||||
|
||||
if(len==4 && strncmp(key,"fail", len)==0) {
|
||||
testDiag("Found fail key jlinkz %p", priv);
|
||||
return jlif_stop;
|
||||
} else {
|
||||
return jlif_continue;
|
||||
}
|
||||
}
|
||||
|
||||
static
|
||||
jlif_result z_end(jlink *pj)
|
||||
{
|
||||
return jlif_continue;
|
||||
}
|
||||
|
||||
static
|
||||
struct lset* z_lset(const jlink *pj)
|
||||
{
|
||||
return &lsetZ;
|
||||
}
|
||||
|
||||
static jlif jlifZ = {
|
||||
"z",
|
||||
&z_alloc,
|
||||
&z_free,
|
||||
NULL, /* null */
|
||||
NULL, /* bool */
|
||||
&z_int,
|
||||
NULL, /* double */
|
||||
NULL, /* string */
|
||||
&z_start,
|
||||
&z_key,
|
||||
&z_end,
|
||||
NULL, /* start array */
|
||||
NULL, /* end array */
|
||||
NULL, /* end child */
|
||||
&z_lset,
|
||||
NULL, /* report */
|
||||
NULL /* map child */
|
||||
};
|
||||
|
||||
epicsExportAddress(jlif, jlifZ);
|
||||
1
src/ioc/db/test/jlinkz.dbd
Normal file
1
src/ioc/db/test/jlinkz.dbd
Normal file
@@ -0,0 +1 @@
|
||||
link("z", "jlifZ")
|
||||
27
src/ioc/db/test/jlinkz.h
Normal file
27
src/ioc/db/test/jlinkz.h
Normal file
@@ -0,0 +1,27 @@
|
||||
/*************************************************************************\
|
||||
* Copyright (c) 2016 Michael Davidsaver
|
||||
* EPICS BASE is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
\*************************************************************************/
|
||||
|
||||
#ifndef JLINKZ_H
|
||||
#define JLINKZ_H
|
||||
|
||||
#include <dbJLink.h>
|
||||
#include <epicsMutex.h>
|
||||
#include <epicsTypes.h>
|
||||
|
||||
#include <shareLib.h>
|
||||
|
||||
epicsShareExtern
|
||||
int numzalloc;
|
||||
|
||||
typedef struct {
|
||||
jlink base;
|
||||
epicsMutexId lock;
|
||||
unsigned isset:1;
|
||||
unsigned isopen:1;
|
||||
epicsInt32 value;
|
||||
} zpriv;
|
||||
|
||||
#endif /* JLINKZ_H */
|
||||
88
src/ioc/db/test/xLink.c
Normal file
88
src/ioc/db/test/xLink.c
Normal file
@@ -0,0 +1,88 @@
|
||||
/*************************************************************************\
|
||||
* Copyright (c) 2016 UChicago Argonne LLC, as Operator of Argonne
|
||||
* National Laboratory.
|
||||
* EPICS BASE is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
\*************************************************************************/
|
||||
/* xLink.c */
|
||||
|
||||
#include "dbDefs.h"
|
||||
#include "dbLink.h"
|
||||
#include "dbJLink.h"
|
||||
#include "epicsExport.h"
|
||||
|
||||
|
||||
typedef struct xlink {
|
||||
jlink jlink; /* embedded object */
|
||||
/* ... */
|
||||
} xlink;
|
||||
|
||||
static lset xlink_lset;
|
||||
|
||||
static jlink* xlink_alloc(short dbfType)
|
||||
{
|
||||
xlink *xlink = calloc(1, sizeof(struct xlink));
|
||||
|
||||
return &xlink->jlink;
|
||||
}
|
||||
|
||||
static void xlink_free(jlink *pjlink)
|
||||
{
|
||||
xlink *xlink = CONTAINER(pjlink, struct xlink, jlink);
|
||||
|
||||
free(xlink);
|
||||
}
|
||||
|
||||
static jlif_result xlink_boolean(jlink *pjlink, int val)
|
||||
{
|
||||
return val; /* False triggers a parse failure */
|
||||
}
|
||||
|
||||
static struct lset* xlink_get_lset(const jlink *pjlink)
|
||||
{
|
||||
return &xlink_lset;
|
||||
}
|
||||
|
||||
|
||||
static void xlink_remove(struct dbLocker *locker, struct link *plink)
|
||||
{
|
||||
xlink_free(plink->value.json.jlink);
|
||||
}
|
||||
|
||||
static long xlink_getNelements(const struct link *plink, long *nelements)
|
||||
{
|
||||
*nelements = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long xlink_getValue(struct link *plink, short dbrType, void *pbuffer,
|
||||
long *pnRequest)
|
||||
{
|
||||
if (pnRequest)
|
||||
*pnRequest = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static lset xlink_lset = {
|
||||
1, 0, /* Constant, not Volatile */
|
||||
NULL, xlink_remove,
|
||||
NULL, NULL, NULL, NULL,
|
||||
NULL, xlink_getNelements, xlink_getValue,
|
||||
NULL, NULL, NULL,
|
||||
NULL, NULL,
|
||||
NULL, NULL,
|
||||
NULL, NULL,
|
||||
NULL, NULL
|
||||
};
|
||||
|
||||
static jlif xlinkIf = {
|
||||
"x", xlink_alloc, xlink_free,
|
||||
NULL, xlink_boolean, NULL, NULL, NULL,
|
||||
NULL, NULL, NULL,
|
||||
NULL, NULL,
|
||||
NULL, xlink_get_lset,
|
||||
NULL, NULL
|
||||
};
|
||||
epicsExportAddress(jlif, xlinkIf);
|
||||
|
||||
1
src/ioc/db/test/xLink.dbd
Normal file
1
src/ioc/db/test/xLink.dbd
Normal file
@@ -0,0 +1 @@
|
||||
link(x, xlinkIf)
|
||||
@@ -44,6 +44,13 @@ typedef struct devSup {
|
||||
struct dsxt *pdsxt; /* Extended device support */
|
||||
}devSup;
|
||||
|
||||
typedef struct linkSup {
|
||||
ELLNODE node;
|
||||
char *name;
|
||||
char *jlif_name;
|
||||
struct jlif *pjlif;
|
||||
} linkSup;
|
||||
|
||||
typedef struct dbDeviceMenu {
|
||||
int nChoice;
|
||||
char **papChoice;
|
||||
@@ -163,6 +170,7 @@ typedef struct dbBase {
|
||||
ELLLIST menuList;
|
||||
ELLLIST recordTypeList;
|
||||
ELLLIST drvList;
|
||||
ELLLIST linkList;
|
||||
ELLLIST registrarList;
|
||||
ELLLIST functionList;
|
||||
ELLLIST variableList;
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
/*************************************************************************\
|
||||
* Copyright (c) 2009 UChicago Argonne LLC, as Operator of Argonne
|
||||
* Copyright (c) 2016 UChicago Argonne LLC, as Operator of Argonne
|
||||
* National Laboratory.
|
||||
* Copyright (c) 2002 The Regents of the University of California, as
|
||||
* Operator of Los Alamos National Laboratory.
|
||||
* EPICS BASE is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
\*************************************************************************/
|
||||
|
||||
newline "\n"
|
||||
backslash "\\"
|
||||
doublequote "\""
|
||||
@@ -15,6 +16,19 @@ escape {backslash}.
|
||||
stringchar [^"\n\\]
|
||||
bareword [a-zA-Z0-9_\-+:.\[\]<>;]
|
||||
|
||||
punctuation [:,\[\]{}]
|
||||
normalchar [^"\\\0-\x1f]
|
||||
barechar [a-zA-Z0-9_\-+.]
|
||||
escapedchar ({backslash}["\\/bfnrt])
|
||||
hexdigit [0-9a-fA-F]
|
||||
unicodechar ({backslash}"u"{hexdigit}{4})
|
||||
jsonchar ({normalchar}|{escapedchar}|{unicodechar})
|
||||
jsondqstr ({doublequote}{jsonchar}*{doublequote})
|
||||
int ("-"?([0-9]|[1-9][0-9]+))
|
||||
frac ("."[0-9]+)
|
||||
exp ([eE][+-]?[0-9]+)
|
||||
number ({int}{frac}?{exp}?)
|
||||
|
||||
%{
|
||||
#undef YY_INPUT
|
||||
#define YY_INPUT(b,r,ms) (r=(*db_yyinput)((char *)b,ms))
|
||||
@@ -27,6 +41,8 @@ static int yyreset(void)
|
||||
|
||||
%}
|
||||
|
||||
%x JSON
|
||||
|
||||
%%
|
||||
|
||||
"include" return(tokenINCLUDE);
|
||||
@@ -38,6 +54,7 @@ static int yyreset(void)
|
||||
"field" return(tokenFIELD);
|
||||
"device" return(tokenDEVICE);
|
||||
"driver" return(tokenDRIVER);
|
||||
"link" return(tokenLINK);
|
||||
"breaktable" return(tokenBREAKTABLE);
|
||||
"record" return(tokenRECORD);
|
||||
"grecord" return(tokenGRECORD);
|
||||
@@ -48,18 +65,18 @@ static int yyreset(void)
|
||||
"variable" return(tokenVARIABLE);
|
||||
|
||||
{bareword}+ { /* unquoted string or number */
|
||||
yylval.Str = dbmfStrdup(yytext);
|
||||
yylval.Str = dbmfStrdup((char *) yytext);
|
||||
return(tokenSTRING);
|
||||
}
|
||||
|
||||
{doublequote}({stringchar}|{escape})*{doublequote} { /* quoted string */
|
||||
yylval.Str = dbmfStrdup(yytext+1);
|
||||
yylval.Str = dbmfStrdup((char *) yytext+1);
|
||||
yylval.Str[strlen(yylval.Str)-1] = '\0';
|
||||
return(tokenSTRING);
|
||||
}
|
||||
|
||||
%.* { /*C definition in recordtype*/
|
||||
yylval.Str = dbmfStrdup(yytext+1);
|
||||
yylval.Str = dbmfStrdup((char *) yytext+1);
|
||||
return(tokenCDEFS);
|
||||
}
|
||||
|
||||
@@ -69,14 +86,36 @@ static int yyreset(void)
|
||||
")" return(yytext[0]);
|
||||
"," return(yytext[0]);
|
||||
|
||||
{comment}.* ;
|
||||
{whitespace} ;
|
||||
|
||||
{doublequote}({stringchar}|{escape})*{newline} { /* bad string */
|
||||
yyerrorAbort("Newline in string, closing quote missing");
|
||||
}
|
||||
|
||||
. {
|
||||
<JSON>"null" return jsonNULL;
|
||||
<JSON>"true" return jsonTRUE;
|
||||
<JSON>"false" return jsonFALSE;
|
||||
|
||||
<JSON>{punctuation} return yytext[0];
|
||||
|
||||
<JSON>{jsondqstr} {
|
||||
yylval.Str = dbmfStrdup((char *) yytext);
|
||||
return jsonSTRING;
|
||||
}
|
||||
|
||||
<JSON>{number} {
|
||||
yylval.Str = dbmfStrdup((char *) yytext);
|
||||
return jsonNUMBER;
|
||||
}
|
||||
|
||||
<JSON>{barechar}+ {
|
||||
yylval.Str = dbmfStrdup((char *) yytext);
|
||||
return jsonBARE;
|
||||
}
|
||||
|
||||
<INITIAL,JSON>{comment}.* ;
|
||||
|
||||
<INITIAL,JSON>{whitespace} ;
|
||||
|
||||
<INITIAL,JSON>. {
|
||||
char message[40];
|
||||
YY_BUFFER_STATE *dummy=0;
|
||||
|
||||
|
||||
@@ -77,6 +77,7 @@ static short findOrAddGuiGroup(const char *name);
|
||||
static void dbDevice(char *recordtype,char *linktype,
|
||||
char *dsetname,char *choicestring);
|
||||
static void dbDriver(char *name);
|
||||
static void dbLinkType(char *name, char *jlif_name);
|
||||
static void dbRegistrar(char *name);
|
||||
static void dbFunction(char *name);
|
||||
static void dbVariable(char *name, char *type);
|
||||
@@ -815,6 +816,26 @@ static void dbDriver(char *name)
|
||||
ellAdd(&pdbbase->drvList,&pdrvSup->node);
|
||||
}
|
||||
|
||||
static void dbLinkType(char *name, char *jlif_name)
|
||||
{
|
||||
linkSup *pLinkSup;
|
||||
GPHENTRY *pgphentry;
|
||||
|
||||
pgphentry = gphFind(pdbbase->pgpHash, name, &pdbbase->linkList);
|
||||
if (pgphentry) {
|
||||
return;
|
||||
}
|
||||
pLinkSup = dbCalloc(1,sizeof(linkSup));
|
||||
pLinkSup->name = epicsStrDup(name);
|
||||
pLinkSup->jlif_name = epicsStrDup(jlif_name);
|
||||
pgphentry = gphAdd(pdbbase->pgpHash, pLinkSup->name, &pdbbase->linkList);
|
||||
if (!pgphentry) {
|
||||
yyerrorAbort("gphAdd failed");
|
||||
}
|
||||
pgphentry->userPvt = pLinkSup;
|
||||
ellAdd(&pdbbase->linkList, &pLinkSup->node);
|
||||
}
|
||||
|
||||
static void dbRegistrar(char *name)
|
||||
{
|
||||
dbText *ptext;
|
||||
@@ -1057,7 +1078,12 @@ static void dbRecordField(char *name,char *value)
|
||||
yyerror(NULL);
|
||||
return;
|
||||
}
|
||||
dbTranslateEscape(value, value); /* yuck: in-place, but safe */
|
||||
if (*value == '"') {
|
||||
/* jsonSTRING values still have their quotes */
|
||||
value++;
|
||||
value[strlen(value) - 1] = 0;
|
||||
}
|
||||
dbTranslateEscape(value, value); /* in-place; safe & legal */
|
||||
status = dbPutString(pdbentry,value);
|
||||
if(status) {
|
||||
epicsPrintf("Can't set \"%s.%s\" to \"%s\"\n",
|
||||
@@ -1076,6 +1102,12 @@ static void dbRecordInfo(char *name, char *value)
|
||||
if(duplicate) return;
|
||||
ptempListNode = (tempListNode *)ellFirst(&tempList);
|
||||
pdbentry = ptempListNode->item;
|
||||
if (*value == '"') {
|
||||
/* jsonSTRING values still have their quotes */
|
||||
value++;
|
||||
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",
|
||||
|
||||
@@ -86,6 +86,14 @@ static void dbDumpDriverCallFunc(const iocshArgBuf *args)
|
||||
dbDumpDriver(*iocshPpdbbase);
|
||||
}
|
||||
|
||||
/* dbDumpLink */
|
||||
static const iocshArg * const dbDumpLinkArgs[] = { &argPdbbase};
|
||||
static const iocshFuncDef dbDumpLinkFuncDef = {"dbDumpLink",1,dbDumpLinkArgs};
|
||||
static void dbDumpLinkCallFunc(const iocshArgBuf *args)
|
||||
{
|
||||
dbDumpLink(*iocshPpdbbase);
|
||||
}
|
||||
|
||||
/* dbDumpRegistrar */
|
||||
static const iocshArg * const dbDumpRegistrarArgs[] = { &argPdbbase};
|
||||
static const iocshFuncDef dbDumpRegistrarFuncDef = {"dbDumpRegistrar",1,dbDumpRegistrarArgs};
|
||||
@@ -160,6 +168,7 @@ void dbStaticIocRegister(void)
|
||||
iocshRegister(&dbDumpFieldFuncDef, dbDumpFieldCallFunc);
|
||||
iocshRegister(&dbDumpDeviceFuncDef, dbDumpDeviceCallFunc);
|
||||
iocshRegister(&dbDumpDriverFuncDef, dbDumpDriverCallFunc);
|
||||
iocshRegister(&dbDumpLinkFuncDef, dbDumpLinkCallFunc);
|
||||
iocshRegister(&dbDumpRegistrarFuncDef,dbDumpRegistrarCallFunc);
|
||||
iocshRegister(&dbDumpFunctionFuncDef, dbDumpFunctionCallFunc);
|
||||
iocshRegister(&dbDumpVariableFuncDef, dbDumpVariableCallFunc);
|
||||
|
||||
@@ -44,6 +44,7 @@
|
||||
#include "special.h"
|
||||
|
||||
#include "dbCommon.h"
|
||||
#include "dbJLink.h"
|
||||
|
||||
int dbStaticDebug = 0;
|
||||
static char *pNullString = "";
|
||||
@@ -65,6 +66,7 @@ epicsShareDef maplinkType pamaplinkType[LINK_NTYPES] = {
|
||||
{"GPIB_IO",GPIB_IO},
|
||||
{"BITBUS_IO",BITBUS_IO},
|
||||
{"MACRO_LINK",MACRO_LINK},
|
||||
{"JSON_LINK",JSON_LINK},
|
||||
{"PN_LINK",PN_LINK},
|
||||
{"DB_LINK",DB_LINK},
|
||||
{"CA_LINK",CA_LINK},
|
||||
@@ -119,6 +121,10 @@ void dbFreeLinkContents(struct link *plink)
|
||||
case CONSTANT: free((void *)plink->value.constantStr); break;
|
||||
case MACRO_LINK: free((void *)plink->value.macro_link.macroStr); break;
|
||||
case PV_LINK: free((void *)plink->value.pv_link.pvname); break;
|
||||
case JSON_LINK:
|
||||
dbJLinkFree(plink->value.json.jlink);
|
||||
parm = plink->value.json.string;
|
||||
break;
|
||||
case VME_IO: parm = plink->value.vmeio.parm; break;
|
||||
case CAMAC_IO: parm = plink->value.camacio.parm; break;
|
||||
case AB_IO: parm = plink->value.abio.parm; break;
|
||||
@@ -453,6 +459,7 @@ void dbFreeBase(dbBase *pdbbase)
|
||||
dbVariableDef *pvarNext;
|
||||
drvSup *pdrvSup;
|
||||
drvSup *pdrvSupNext;
|
||||
linkSup *plinkSup;
|
||||
brkTable *pbrkTable;
|
||||
brkTable *pbrkTableNext;
|
||||
chFilterPlugin *pfilt;
|
||||
@@ -557,6 +564,11 @@ void dbFreeBase(dbBase *pdbbase)
|
||||
free((void *)pdrvSup);
|
||||
pdrvSup = pdrvSupNext;
|
||||
}
|
||||
while ((plinkSup = (linkSup *) ellGet(&pdbbase->linkList))) {
|
||||
free(plinkSup->jlif_name);
|
||||
free(plinkSup->name);
|
||||
free(plinkSup);
|
||||
}
|
||||
ptext = (dbText *)ellFirst(&pdbbase->registrarList);
|
||||
while(ptext) {
|
||||
ptextNext = (dbText *)ellNext(&ptext->node);
|
||||
@@ -1098,6 +1110,21 @@ long dbWriteDriverFP(DBBASE *pdbbase,FILE *fp)
|
||||
return(0);
|
||||
}
|
||||
|
||||
long dbWriteLinkFP(DBBASE *pdbbase, FILE *fp)
|
||||
{
|
||||
linkSup *plinkSup;
|
||||
|
||||
if (!pdbbase) {
|
||||
fprintf(stderr, "pdbbase not specified\n");
|
||||
return -1;
|
||||
}
|
||||
for (plinkSup = (linkSup *) ellFirst(&pdbbase->linkList);
|
||||
plinkSup; plinkSup = (linkSup *) ellNext(&plinkSup->node)) {
|
||||
fprintf(fp, "link(%s,%s)\n", plinkSup->name, plinkSup->jlif_name);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
long dbWriteRegistrarFP(DBBASE *pdbbase,FILE *fp)
|
||||
{
|
||||
dbText *ptext;
|
||||
@@ -1889,6 +1916,9 @@ char * dbGetString(DBENTRY *pdbentry)
|
||||
dbMsgCpy(pdbentry, "");
|
||||
}
|
||||
break;
|
||||
case JSON_LINK:
|
||||
dbMsgCpy(pdbentry, plink->value.json.string);
|
||||
break;
|
||||
case PN_LINK:
|
||||
dbMsgPrint(pdbentry, "%s%s",
|
||||
plink->value.pv_link.pvname ? plink->value.pv_link.pvname : "",
|
||||
@@ -1984,6 +2014,9 @@ char * dbGetString(DBENTRY *pdbentry)
|
||||
dbMsgCpy(pdbentry, "");
|
||||
}
|
||||
break;
|
||||
case JSON_LINK:
|
||||
dbMsgCpy(pdbentry, plink->value.json.string);
|
||||
break;
|
||||
case PV_LINK:
|
||||
case CA_LINK:
|
||||
case DB_LINK: {
|
||||
@@ -2140,6 +2173,7 @@ long dbInitRecordLinks(dbRecordType *rtyp, struct dbCommon *prec)
|
||||
*/
|
||||
case CONSTANT: plink->value.constantStr = NULL; break;
|
||||
case PV_LINK: plink->value.pv_link.pvname = callocMustSucceed(1, 1, "init PV_LINK"); break;
|
||||
case JSON_LINK: plink->value.json.string = pNullString; break;
|
||||
case VME_IO: plink->value.vmeio.parm = pNullString; break;
|
||||
case CAMAC_IO: plink->value.camacio.parm = pNullString; break;
|
||||
case AB_IO: plink->value.abio.parm = pNullString; break;
|
||||
@@ -2153,7 +2187,7 @@ long dbInitRecordLinks(dbRecordType *rtyp, struct dbCommon *prec)
|
||||
if(!plink->text)
|
||||
continue;
|
||||
|
||||
if(dbParseLink(plink->text, pflddes->field_type, &link_info)!=0) {
|
||||
if(dbParseLink(plink->text, pflddes->field_type, &link_info, 0)!=0) {
|
||||
/* This was already parsed once when ->text was set.
|
||||
* Any syntax error messages were printed at that time.
|
||||
*/
|
||||
@@ -2172,7 +2206,17 @@ long dbInitRecordLinks(dbRecordType *rtyp, struct dbCommon *prec)
|
||||
return 0;
|
||||
}
|
||||
|
||||
long dbParseLink(const char *str, short ftype, dbLinkInfo *pinfo)
|
||||
void dbFreeLinkInfo(dbLinkInfo *pinfo)
|
||||
{
|
||||
if (pinfo->ltype == JSON_LINK) {
|
||||
dbJLinkFree(pinfo->jlink);
|
||||
pinfo->jlink = NULL;
|
||||
}
|
||||
free(pinfo->target);
|
||||
pinfo->target = NULL;
|
||||
}
|
||||
|
||||
long dbParseLink(const char *str, short ftype, dbLinkInfo *pinfo, unsigned opts)
|
||||
{
|
||||
char *pstr;
|
||||
size_t len;
|
||||
@@ -2206,6 +2250,15 @@ long dbParseLink(const char *str, short ftype, dbLinkInfo *pinfo)
|
||||
memcpy(pstr, str, len);
|
||||
pstr[len] = '\0';
|
||||
|
||||
/* Check for braces => JSON */
|
||||
if (*str == '{' && str[len-1] == '}') {
|
||||
if (dbJLinkParse(str, len, ftype, &pinfo->jlink, opts))
|
||||
goto fail;
|
||||
|
||||
pinfo->ltype = JSON_LINK;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Check for other HW link types */
|
||||
if (*pstr == '#') {
|
||||
int ret;
|
||||
@@ -2253,12 +2306,9 @@ long dbParseLink(const char *str, short ftype, dbLinkInfo *pinfo)
|
||||
/* RF_IO, the string isn't needed at all */
|
||||
free(pinfo->target);
|
||||
pinfo->target = NULL;
|
||||
} else {
|
||||
/* missing parm when required, or found parm when not expected */
|
||||
free(pinfo->target);
|
||||
pinfo->target = NULL;
|
||||
return S_dbLib_badField;
|
||||
}
|
||||
else goto fail;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -2268,6 +2318,13 @@ long dbParseLink(const char *str, short ftype, dbLinkInfo *pinfo)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Link may be an array constant */
|
||||
if (pstr[0] == '[' && pstr[len-1] == ']' &&
|
||||
(strchr(pstr, ',') || strchr(pstr, '"'))) {
|
||||
pinfo->ltype = CONSTANT;
|
||||
return 0;
|
||||
}
|
||||
|
||||
pinfo->ltype = PV_LINK;
|
||||
pstr = strchr(pstr, ' '); /* find start of link modifiers (can't be seperated by tabs) */
|
||||
if (pstr) {
|
||||
@@ -2300,27 +2357,28 @@ long dbParseLink(const char *str, short ftype, dbLinkInfo *pinfo)
|
||||
|
||||
return 0;
|
||||
fail:
|
||||
free(pinfo->target);
|
||||
dbFreeLinkInfo(pinfo);
|
||||
return S_dbLib_badField;
|
||||
}
|
||||
|
||||
long dbCanSetLink(DBLINK *plink, dbLinkInfo *pinfo, devSup *devsup)
|
||||
{
|
||||
/* consume allocated string pinfo->target on failure */
|
||||
/* Release pinfo resources on failure */
|
||||
int expected_type = devsup ? devsup->link_type : CONSTANT;
|
||||
|
||||
int link_type = CONSTANT;
|
||||
if(devsup)
|
||||
link_type = devsup->link_type;
|
||||
if(link_type==pinfo->ltype)
|
||||
if (pinfo->ltype == expected_type)
|
||||
return 0;
|
||||
switch(pinfo->ltype) {
|
||||
|
||||
switch (pinfo->ltype) {
|
||||
case CONSTANT:
|
||||
case JSON_LINK:
|
||||
case PV_LINK:
|
||||
if(link_type==CONSTANT || link_type==PV_LINK)
|
||||
if (expected_type == CONSTANT ||
|
||||
expected_type == JSON_LINK ||
|
||||
expected_type == PV_LINK)
|
||||
return 0;
|
||||
default:
|
||||
free(pinfo->target);
|
||||
pinfo->target = NULL;
|
||||
dbFreeLinkInfo(pinfo);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
@@ -2344,11 +2402,24 @@ void dbSetLinkPV(DBLINK *plink, dbLinkInfo *pinfo)
|
||||
pinfo->target = NULL;
|
||||
}
|
||||
|
||||
static
|
||||
void dbSetLinkJSON(DBLINK *plink, dbLinkInfo *pinfo)
|
||||
{
|
||||
plink->type = JSON_LINK;
|
||||
plink->value.json.string = pinfo->target;
|
||||
plink->value.json.jlink = pinfo->jlink;
|
||||
|
||||
pinfo->target = NULL;
|
||||
pinfo->jlink = NULL;
|
||||
}
|
||||
|
||||
static
|
||||
void dbSetLinkHW(DBLINK *plink, dbLinkInfo *pinfo)
|
||||
{
|
||||
|
||||
switch(pinfo->ltype) {
|
||||
case JSON_LINK:
|
||||
plink->value.json.string = pinfo->target;
|
||||
break;
|
||||
case INST_IO:
|
||||
plink->value.instio.string = pinfo->target;
|
||||
break;
|
||||
@@ -2424,36 +2495,39 @@ void dbSetLinkHW(DBLINK *plink, dbLinkInfo *pinfo)
|
||||
|
||||
long dbSetLink(DBLINK *plink, dbLinkInfo *pinfo, devSup *devsup)
|
||||
{
|
||||
int ret = 0;
|
||||
int link_type = CONSTANT;
|
||||
int expected_type = devsup ? devsup->link_type : CONSTANT;
|
||||
|
||||
if(devsup)
|
||||
link_type = devsup->link_type;
|
||||
|
||||
if(link_type==CONSTANT || link_type==PV_LINK) {
|
||||
switch(pinfo->ltype) {
|
||||
if (expected_type == CONSTANT ||
|
||||
expected_type == JSON_LINK ||
|
||||
expected_type == PV_LINK) {
|
||||
switch (pinfo->ltype) {
|
||||
case CONSTANT:
|
||||
dbFreeLinkContents(plink);
|
||||
dbSetLinkConst(plink, pinfo); break;
|
||||
dbSetLinkConst(plink, pinfo);
|
||||
break;
|
||||
case PV_LINK:
|
||||
dbFreeLinkContents(plink);
|
||||
dbSetLinkPV(plink, pinfo); break;
|
||||
dbSetLinkPV(plink, pinfo);
|
||||
break;
|
||||
case JSON_LINK:
|
||||
dbFreeLinkContents(plink);
|
||||
dbSetLinkJSON(plink, pinfo);
|
||||
break;
|
||||
default:
|
||||
errlogMessage("Warning: dbSetLink: forgot to test with dbCanSetLink() or logic error");
|
||||
goto fail; /* can't assign HW link */
|
||||
}
|
||||
|
||||
} else if(link_type==pinfo->ltype) {
|
||||
}
|
||||
else if (expected_type == pinfo->ltype) {
|
||||
dbFreeLinkContents(plink);
|
||||
dbSetLinkHW(plink, pinfo);
|
||||
|
||||
} else
|
||||
}
|
||||
else
|
||||
goto fail;
|
||||
|
||||
return ret;
|
||||
return 0;
|
||||
fail:
|
||||
free(pinfo->target);
|
||||
pinfo->target = NULL;
|
||||
dbFreeLinkInfo(pinfo);
|
||||
return S_dbLib_badField;
|
||||
}
|
||||
|
||||
@@ -2511,15 +2585,28 @@ long dbPutString(DBENTRY *pdbentry,const char *pstring)
|
||||
case DBF_FWDLINK: {
|
||||
dbLinkInfo link_info;
|
||||
DBLINK *plink = (DBLINK *)pfield;
|
||||
DBENTRY infoentry;
|
||||
unsigned opts = 0;
|
||||
|
||||
status = dbParseLink(pstring, pflddes->field_type, &link_info);
|
||||
if(pdbentry->precnode && ellCount(&pdbentry->precnode->infoList)) {
|
||||
dbCopyEntryContents(pdbentry, &infoentry);
|
||||
|
||||
if(dbFindInfo(&infoentry, "base:lsetDebug")==0 && epicsStrCaseCmp(dbGetInfoString(&infoentry), "YES")==0)
|
||||
opts |= LINK_DEBUG_LSET;
|
||||
if(dbFindInfo(&infoentry, "base:jlinkDebug")==0 && epicsStrCaseCmp(dbGetInfoString(&infoentry), "YES")==0)
|
||||
opts |= LINK_DEBUG_JPARSE;
|
||||
|
||||
dbFinishEntry(&infoentry);
|
||||
}
|
||||
|
||||
status = dbParseLink(pstring, pflddes->field_type, &link_info, opts);
|
||||
if (status) break;
|
||||
|
||||
if (plink->type==CONSTANT && plink->value.constantStr==NULL) {
|
||||
/* links not yet initialized by dbInitRecordLinks() */
|
||||
free(plink->text);
|
||||
plink->text = epicsStrDup(pstring);
|
||||
free(link_info.target);
|
||||
dbFreeLinkInfo(&link_info);
|
||||
} else {
|
||||
/* assignment after init (eg. autosave restore) */
|
||||
struct dbCommon *prec = pdbentry->precnode->precord;
|
||||
@@ -3047,6 +3134,12 @@ char * dbGetRelatedField(DBENTRY *psave)
|
||||
return(rtnval);
|
||||
}
|
||||
|
||||
linkSup* dbFindLinkSup(dbBase *pdbbase, const char *name) {
|
||||
GPHENTRY *pgph = gphFind(pdbbase->pgpHash,name,&pdbbase->linkList);
|
||||
if (!pgph) return NULL;
|
||||
return (linkSup *) pgph->userPvt;
|
||||
}
|
||||
|
||||
int dbGetNLinks(DBENTRY *pdbentry)
|
||||
{
|
||||
dbRecordType *precordType = pdbentry->precordType;
|
||||
@@ -3099,67 +3192,6 @@ int dbGetLinkType(DBENTRY *pdbentry)
|
||||
return(-1);
|
||||
}
|
||||
|
||||
long dbCvtLinkToConstant(DBENTRY *pdbentry)
|
||||
{
|
||||
dbFldDes *pflddes;
|
||||
DBLINK *plink;
|
||||
|
||||
dbGetFieldAddress(pdbentry);
|
||||
pflddes = pdbentry->pflddes;
|
||||
if(!pflddes) return(-1);
|
||||
plink = (DBLINK *)pdbentry->pfield;
|
||||
if(!plink) return(-1);
|
||||
switch (pflddes->field_type) {
|
||||
case DBF_INLINK:
|
||||
case DBF_OUTLINK:
|
||||
case DBF_FWDLINK:
|
||||
if(plink->type == CONSTANT) return(0);
|
||||
if(plink->type != PV_LINK) return(S_dbLib_badLink);
|
||||
free((void *)plink->value.pv_link.pvname);
|
||||
plink->value.pv_link.pvname = NULL;
|
||||
plink->type = CONSTANT;
|
||||
if(pflddes->initial) {
|
||||
plink->value.constantStr =
|
||||
dbCalloc(strlen(pflddes->initial)+1,sizeof(char));
|
||||
strcpy(plink->value.constantStr,pflddes->initial);
|
||||
} else {
|
||||
plink->value.constantStr = NULL;
|
||||
}
|
||||
return(0);
|
||||
default:
|
||||
epicsPrintf("dbCvtLinkToConstant called for non link field\n");
|
||||
}
|
||||
return(S_dbLib_badLink);
|
||||
}
|
||||
|
||||
long dbCvtLinkToPvlink(DBENTRY *pdbentry)
|
||||
{
|
||||
dbFldDes *pflddes;
|
||||
DBLINK *plink;
|
||||
|
||||
dbGetFieldAddress(pdbentry);
|
||||
pflddes = pdbentry->pflddes;
|
||||
if(!pflddes) return(-1);
|
||||
if(!pdbentry->precnode || !pdbentry->precnode->precord) return(-1);
|
||||
plink = (DBLINK *)pdbentry->pfield;
|
||||
if(!plink) return(-1);
|
||||
switch (pflddes->field_type) {
|
||||
case DBF_INLINK:
|
||||
case DBF_OUTLINK:
|
||||
case DBF_FWDLINK:
|
||||
if(plink->type == PV_LINK) return(0);
|
||||
if(plink->type != CONSTANT) return(S_dbLib_badLink);
|
||||
free(plink->value.constantStr);
|
||||
plink->type = PV_LINK;
|
||||
plink->value.pv_link.pvlMask = 0;
|
||||
plink->value.pv_link.pvname = 0;
|
||||
return(0);
|
||||
default:
|
||||
epicsPrintf("dbCvtLinkToPvlink called for non link field\n");
|
||||
}
|
||||
return(S_dbLib_badLink);
|
||||
}
|
||||
|
||||
void dbDumpPath(DBBASE *pdbbase)
|
||||
{
|
||||
ELLLIST *ppathList;
|
||||
@@ -3395,6 +3427,15 @@ void dbDumpDriver(DBBASE *pdbbase)
|
||||
dbWriteDriverFP(pdbbase,stdout);
|
||||
}
|
||||
|
||||
void dbDumpLink(DBBASE *pdbbase)
|
||||
{
|
||||
if(!pdbbase) {
|
||||
fprintf(stderr,"pdbbase not specified\n");
|
||||
return;
|
||||
}
|
||||
dbWriteLinkFP(pdbbase,stdout);
|
||||
}
|
||||
|
||||
void dbDumpRegistrar(DBBASE *pdbbase)
|
||||
{
|
||||
if(!pdbbase) {
|
||||
|
||||
@@ -116,6 +116,7 @@ epicsShareFunc long dbWriteDeviceFP(DBBASE *pdbbase, FILE *fp);
|
||||
epicsShareFunc long dbWriteDriver(DBBASE *pdbbase,
|
||||
const char *filename);
|
||||
epicsShareFunc long dbWriteDriverFP(DBBASE *pdbbase, FILE *fp);
|
||||
epicsShareFunc long dbWriteLinkFP(DBBASE *pdbbase, FILE *fp);
|
||||
epicsShareFunc long dbWriteRegistrarFP(DBBASE *pdbbase, FILE *fp);
|
||||
epicsShareFunc long dbWriteFunctionFP(DBBASE *pdbbase, FILE *fp);
|
||||
epicsShareFunc long dbWriteVariableFP(DBBASE *pdbbase, FILE *fp);
|
||||
@@ -225,11 +226,12 @@ epicsShareFunc drvSup * dbFindDriver(dbBase *pdbbase,
|
||||
const char *name);
|
||||
epicsShareFunc char * dbGetRelatedField(DBENTRY *pdbentry);
|
||||
|
||||
epicsShareFunc linkSup * dbFindLinkSup(dbBase *pdbbase,
|
||||
const char *name);
|
||||
|
||||
epicsShareFunc int dbGetNLinks(DBENTRY *pdbentry);
|
||||
epicsShareFunc long dbGetLinkField(DBENTRY *pdbentry, int index);
|
||||
epicsShareFunc int dbGetLinkType(DBENTRY *pdbentry);
|
||||
epicsShareFunc long dbCvtLinkToConstant(DBENTRY *pdbentry);
|
||||
epicsShareFunc long dbCvtLinkToPvlink(DBENTRY *pdbentry);
|
||||
|
||||
/* Dump routines */
|
||||
epicsShareFunc void dbDumpPath(DBBASE *pdbbase);
|
||||
@@ -244,6 +246,7 @@ epicsShareFunc void dbDumpField(DBBASE *pdbbase,
|
||||
epicsShareFunc void dbDumpDevice(DBBASE *pdbbase,
|
||||
const char *recordTypeName);
|
||||
epicsShareFunc void dbDumpDriver(DBBASE *pdbbase);
|
||||
epicsShareFunc void dbDumpLink(DBBASE *pdbbase);
|
||||
epicsShareFunc void dbDumpRegistrar(DBBASE *pdbbase);
|
||||
epicsShareFunc void dbDumpFunction(DBBASE *pdbbase);
|
||||
epicsShareFunc void dbDumpVariable(DBBASE *pdbbase);
|
||||
|
||||
@@ -36,27 +36,36 @@ char *dbRecordName(DBENTRY *pdbentry);
|
||||
char *dbGetStringNum(DBENTRY *pdbentry);
|
||||
long dbPutStringNum(DBENTRY *pdbentry,const char *pstring);
|
||||
|
||||
struct jlink;
|
||||
|
||||
typedef struct dbLinkInfo {
|
||||
short ltype;
|
||||
|
||||
/* full link string for CONSTANT and PV_LINK,
|
||||
* parm string for HW links*/
|
||||
* parm string for HW links, JSON for JSON_LINK
|
||||
*/
|
||||
char *target;
|
||||
|
||||
/* for PV_LINK */
|
||||
short modifiers;
|
||||
|
||||
/* HW links */
|
||||
/* for HW links */
|
||||
char hwid[6]; /* one extra element for a nil */
|
||||
int hwnums[5];
|
||||
|
||||
/* for JSON_LINK */
|
||||
struct jlink *jlink;
|
||||
} dbLinkInfo;
|
||||
|
||||
long dbInitRecordLinks(dbRecordType *rtyp, struct dbCommon *prec);
|
||||
|
||||
#define LINK_DEBUG_LSET 1
|
||||
#define LINK_DEBUG_JPARSE 2
|
||||
|
||||
/* Parse link string. no record locks needed.
|
||||
* on success caller must free pinfo->target
|
||||
*/
|
||||
epicsShareFunc long dbParseLink(const char *str, short ftype, dbLinkInfo *pinfo);
|
||||
epicsShareFunc long dbParseLink(const char *str, short ftype, dbLinkInfo *pinfo, unsigned opts);
|
||||
/* Check if link type allow the parsed link value pinfo
|
||||
* to be assigned to the given link.
|
||||
* Record containing plink must be locked.
|
||||
@@ -68,6 +77,8 @@ long dbCanSetLink(DBLINK *plink, dbLinkInfo *pinfo, devSup *devsup);
|
||||
* Unconditionally takes ownership of pinfo->target
|
||||
*/
|
||||
long dbSetLink(DBLINK *plink, dbLinkInfo *pinfo, devSup *dset);
|
||||
/* Free dbLinkInfo storage */
|
||||
epicsShareFunc void dbFreeLinkInfo(dbLinkInfo *pinfo);
|
||||
|
||||
/* The following is for path */
|
||||
typedef struct dbPathNode {
|
||||
|
||||
@@ -17,18 +17,23 @@ static int yyAbort = 0;
|
||||
|
||||
%start database
|
||||
|
||||
%token tokenINCLUDE tokenPATH tokenADDPATH
|
||||
%token tokenALIAS tokenMENU tokenCHOICE tokenRECORDTYPE
|
||||
%token tokenFIELD tokenINFO tokenREGISTRAR
|
||||
%token tokenDEVICE tokenDRIVER tokenBREAKTABLE
|
||||
%token tokenRECORD tokenGRECORD tokenVARIABLE tokenFUNCTION
|
||||
%token <Str> tokenSTRING tokenCDEFS
|
||||
|
||||
%union
|
||||
{
|
||||
char *Str;
|
||||
}
|
||||
|
||||
%token tokenINCLUDE tokenPATH tokenADDPATH
|
||||
%token tokenALIAS tokenMENU tokenCHOICE tokenRECORDTYPE
|
||||
%token tokenFIELD tokenINFO tokenREGISTRAR
|
||||
%token tokenDEVICE tokenDRIVER tokenLINK tokenBREAKTABLE
|
||||
%token tokenRECORD tokenGRECORD tokenVARIABLE tokenFUNCTION
|
||||
%token <Str> tokenSTRING tokenCDEFS
|
||||
|
||||
%token jsonNULL jsonTRUE jsonFALSE
|
||||
%token <Str> jsonNUMBER jsonSTRING jsonBARE
|
||||
%type <Str> json_value json_object json_array
|
||||
%type <Str> json_members json_pair json_elements json_string
|
||||
|
||||
%%
|
||||
|
||||
database: /* empty */
|
||||
@@ -46,6 +51,7 @@ database_item: include
|
||||
| tokenRECORDTYPE recordtype_head recordtype_body
|
||||
| device
|
||||
| driver
|
||||
| link
|
||||
| registrar
|
||||
| function
|
||||
| variable
|
||||
@@ -162,6 +168,13 @@ driver: tokenDRIVER '(' tokenSTRING ')'
|
||||
dbDriver($3); dbmfFree($3);
|
||||
};
|
||||
|
||||
link: tokenLINK '(' tokenSTRING ',' tokenSTRING ')'
|
||||
{
|
||||
if(dbStaticDebug>2) printf("link %s %s\n",$3,$5);
|
||||
dbLinkType($3,$5);
|
||||
dbmfFree($3); dbmfFree($5);
|
||||
};
|
||||
|
||||
registrar: tokenREGISTRAR '(' tokenSTRING ')'
|
||||
{
|
||||
if(dbStaticDebug>2) printf("registrar %s\n",$3);
|
||||
@@ -239,15 +252,17 @@ record_body: /* empty */
|
||||
record_field_list: record_field_list record_field
|
||||
| record_field;
|
||||
|
||||
record_field: tokenFIELD '(' tokenSTRING ',' tokenSTRING ')'
|
||||
record_field: tokenFIELD '(' tokenSTRING ','
|
||||
{ BEGIN JSON; } json_value { BEGIN INITIAL; } ')'
|
||||
{
|
||||
if(dbStaticDebug>2) printf("record_field %s %s\n",$3,$5);
|
||||
dbRecordField($3,$5); dbmfFree($3); dbmfFree($5);
|
||||
if(dbStaticDebug>2) printf("record_field %s %s\n",$3,$6);
|
||||
dbRecordField($3,$6); dbmfFree($3); dbmfFree($6);
|
||||
}
|
||||
| tokenINFO '(' tokenSTRING ',' tokenSTRING ')'
|
||||
| tokenINFO '(' tokenSTRING ','
|
||||
{ BEGIN JSON; } json_value { BEGIN INITIAL; } ')'
|
||||
{
|
||||
if(dbStaticDebug>2) printf("record_info %s %s\n",$3,$5);
|
||||
dbRecordInfo($3,$5); dbmfFree($3); dbmfFree($5);
|
||||
if(dbStaticDebug>2) printf("record_info %s %s\n",$3,$6);
|
||||
dbRecordInfo($3,$6); dbmfFree($3); dbmfFree($6);
|
||||
}
|
||||
| tokenALIAS '(' tokenSTRING ')'
|
||||
{
|
||||
@@ -262,6 +277,69 @@ alias: tokenALIAS '(' tokenSTRING ',' tokenSTRING ')'
|
||||
dbAlias($3,$5); dbmfFree($3); dbmfFree($5);
|
||||
};
|
||||
|
||||
json_object: '{' '}'
|
||||
{
|
||||
$$ = dbmfStrdup("{}");
|
||||
if (dbStaticDebug>2) printf("json %s\n", $$);
|
||||
}
|
||||
| '{' json_members '}'
|
||||
{
|
||||
$$ = dbmfStrcat3("{", $2, "}");
|
||||
dbmfFree($2);
|
||||
if (dbStaticDebug>2) printf("json %s\n", $$);
|
||||
};
|
||||
|
||||
json_members: json_pair
|
||||
| json_pair ',' json_members
|
||||
{
|
||||
$$ = dbmfStrcat3($1, ",", $3);
|
||||
dbmfFree($1); dbmfFree($3);
|
||||
if (dbStaticDebug>2) printf("json %s\n", $$);
|
||||
};
|
||||
|
||||
json_pair: json_string ':' json_value
|
||||
{
|
||||
$$ = dbmfStrcat3($1, ":", $3);
|
||||
dbmfFree($1); dbmfFree($3);
|
||||
if (dbStaticDebug>2) printf("json %s\n", $$);
|
||||
};
|
||||
|
||||
json_string: jsonSTRING
|
||||
| jsonBARE
|
||||
{
|
||||
$$ = dbmfStrcat3("\"", $1, "\"");
|
||||
dbmfFree($1);
|
||||
if (dbStaticDebug>2) printf("json %s\n", $$);
|
||||
};
|
||||
|
||||
json_array: '[' ']'
|
||||
{
|
||||
$$ = dbmfStrdup("[]");
|
||||
if (dbStaticDebug>2) printf("json %s\n", $$);
|
||||
}
|
||||
| '[' json_elements ']'
|
||||
{
|
||||
$$ = dbmfStrcat3("[", $2, "]");
|
||||
dbmfFree($2);
|
||||
if (dbStaticDebug>2) printf("json %s\n", $$);
|
||||
};
|
||||
|
||||
json_elements: json_value
|
||||
| json_value ',' json_elements
|
||||
{
|
||||
$$ = dbmfStrcat3($1, ",", $3);
|
||||
dbmfFree($1); dbmfFree($3);
|
||||
if (dbStaticDebug>2) printf("json %s\n", $$);
|
||||
};
|
||||
|
||||
json_value: jsonNULL { $$ = dbmfStrdup("null"); }
|
||||
| jsonTRUE { $$ = dbmfStrdup("true"); }
|
||||
| jsonFALSE { $$ = dbmfStrdup("false"); }
|
||||
| jsonNUMBER
|
||||
| json_string
|
||||
| json_array
|
||||
| json_object ;
|
||||
|
||||
|
||||
%%
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@ extern "C" {
|
||||
#define GPIB_IO 5
|
||||
#define BITBUS_IO 6
|
||||
#define MACRO_LINK 7
|
||||
|
||||
#define JSON_LINK 8
|
||||
#define PN_LINK 9
|
||||
#define DB_LINK 10
|
||||
#define CA_LINK 11
|
||||
@@ -40,7 +40,7 @@ extern "C" {
|
||||
#define BBGPIB_IO 13 /* bitbus -> gpib */
|
||||
#define RF_IO 14
|
||||
#define VXI_IO 15
|
||||
#define LINK_NTYPES 15
|
||||
#define LINK_NTYPES 16
|
||||
typedef struct maplinkType {
|
||||
char *strvalue;
|
||||
int value;
|
||||
@@ -86,6 +86,12 @@ struct pv_link {
|
||||
short lastGetdbrType; /* last dbrType for DB or CA get */
|
||||
};
|
||||
|
||||
struct jlink;
|
||||
struct json_link {
|
||||
char *string;
|
||||
struct jlink *jlink;
|
||||
};
|
||||
|
||||
/* structure of a VME io channel */
|
||||
struct vmeio {
|
||||
short card;
|
||||
@@ -166,6 +172,7 @@ struct vxiio {
|
||||
union value {
|
||||
char *constantStr; /*constant string*/
|
||||
struct macro_link macro_link; /* link containing macro substitution*/
|
||||
struct json_link json; /* JSON-encoded link */
|
||||
struct pv_link pv_link; /* link to process variable*/
|
||||
struct vmeio vmeio; /* vme io point */
|
||||
struct camacio camacio; /* camac io point */
|
||||
|
||||
@@ -24,13 +24,13 @@ bareword [a-zA-Z0-9_\-+:./\\\[\]<>;]
|
||||
|
||||
{doublequote}({dstringchar}|{escape})*{doublequote} |
|
||||
{singlequote}({sstringchar}|{escape})*{singlequote} {
|
||||
yylval.Str = dbmfStrdup(yytext+1);
|
||||
yylval.Str = dbmfStrdup((char *) yytext+1);
|
||||
yylval.Str[strlen(yylval.Str)-1] = '\0';
|
||||
return(QUOTE);
|
||||
}
|
||||
|
||||
{bareword}+ {
|
||||
yylval.Str = dbmfStrdup(yytext);
|
||||
yylval.Str = dbmfStrdup((char *) yytext);
|
||||
return(WORD);
|
||||
}
|
||||
|
||||
|
||||
@@ -67,6 +67,7 @@
|
||||
#include "recSup.h"
|
||||
#include "registryDeviceSupport.h"
|
||||
#include "registryDriverSupport.h"
|
||||
#include "registryJLinks.h"
|
||||
#include "registryRecordType.h"
|
||||
#include "rsrv.h"
|
||||
|
||||
@@ -654,18 +655,14 @@ static void doCloseLinks(dbRecordType *pdbRecordType, dbCommon *precord,
|
||||
pdbRecordType->papFldDes[pdbRecordType->link_ind[j]];
|
||||
DBLINK *plink = (DBLINK *)((char *)precord + pdbFldDes->offset);
|
||||
|
||||
if (plink->type == CA_LINK) {
|
||||
if (plink->type == CA_LINK ||
|
||||
plink->type == JSON_LINK ||
|
||||
(plink->type == DB_LINK && iocBuildMode == buildIsolated)) {
|
||||
if (!locked) {
|
||||
dbScanLock(precord);
|
||||
locked = 1;
|
||||
}
|
||||
dbCaRemoveLink(NULL, plink);
|
||||
|
||||
} else if (iocBuildMode==buildIsolated && plink->type == DB_LINK) {
|
||||
/* free link, but don't split lockset like dbDbRemoveLink() */
|
||||
free(plink->value.pv_link.pvt);
|
||||
plink->type = PV_LINK;
|
||||
plink->lset = NULL;
|
||||
dbRemoveLink(NULL, plink);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@ SRC_DIRS += $(IOCDIR)/registry
|
||||
INC += registryRecordType.h
|
||||
INC += registryDeviceSupport.h
|
||||
INC += registryDriverSupport.h
|
||||
INC += registryJLinks.h
|
||||
INC += registryFunction.h
|
||||
INC += registryCommon.h
|
||||
INC += registryIocRegister.h
|
||||
@@ -21,6 +22,7 @@ INC += registryIocRegister.h
|
||||
dbCore_SRCS += registryRecordType.c
|
||||
dbCore_SRCS += registryDeviceSupport.c
|
||||
dbCore_SRCS += registryDriverSupport.c
|
||||
dbCore_SRCS += registryJLinks.c
|
||||
dbCore_SRCS += registryFunction.c
|
||||
dbCore_SRCS += registryCommon.c
|
||||
dbCore_SRCS += registryIocRegister.c
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#include "registryCommon.h"
|
||||
#include "registryDeviceSupport.h"
|
||||
#include "registryDriverSupport.h"
|
||||
#include "registryJLinks.h"
|
||||
|
||||
|
||||
void registerRecordTypes(DBBASE *pbase, int nRecordTypes,
|
||||
@@ -76,3 +77,15 @@ void registerDrivers(DBBASE *pbase, int nDrivers,
|
||||
}
|
||||
}
|
||||
|
||||
void registerJLinks(DBBASE *pbase, int nLinks, jlif * const *jlifsl)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < nLinks; i++) {
|
||||
if (!registryJLinkAdd(pbase, jlifsl[i])) {
|
||||
errlogPrintf("registryJLinkAdd failed %s\n",
|
||||
jlifsl[i]->name);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
|
||||
#include "dbStaticLib.h"
|
||||
#include "devSup.h"
|
||||
#include "dbJLink.h"
|
||||
#include "registryRecordType.h"
|
||||
#include "shareLib.h"
|
||||
|
||||
@@ -28,6 +29,8 @@ epicsShareFunc void registerDevices(
|
||||
epicsShareFunc void registerDrivers(
|
||||
DBBASE *pbase, int nDrivers,
|
||||
const char * const *driverSupportNames, struct drvet * const *drvsl);
|
||||
epicsShareFunc void registerJLinks(
|
||||
DBBASE *pbase, int nDrivers, jlif * const *jlifsl);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
23
src/ioc/registry/registryJLinks.c
Normal file
23
src/ioc/registry/registryJLinks.c
Normal file
@@ -0,0 +1,23 @@
|
||||
/*************************************************************************\
|
||||
* Copyright (c) 2016 UChicago Argonne LLC, as Operator of Argonne
|
||||
* National Laboratory.
|
||||
* EPICS BASE is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
\*************************************************************************/
|
||||
/* registryJLinks.c */
|
||||
|
||||
#include "registry.h"
|
||||
#define epicsExportSharedSymbols
|
||||
#include "dbBase.h"
|
||||
#include "dbStaticLib.h"
|
||||
#include "registryJLinks.h"
|
||||
#include "dbJLink.h"
|
||||
|
||||
epicsShareFunc int registryJLinkAdd(DBBASE *pbase, struct jlif *pjlif)
|
||||
{
|
||||
linkSup *plinkSup = dbFindLinkSup(pbase, pjlif->name);
|
||||
|
||||
if (plinkSup)
|
||||
plinkSup->pjlif = pjlif;
|
||||
return !!plinkSup;
|
||||
}
|
||||
28
src/ioc/registry/registryJLinks.h
Normal file
28
src/ioc/registry/registryJLinks.h
Normal file
@@ -0,0 +1,28 @@
|
||||
/*************************************************************************\
|
||||
* Copyright (c) 2007 UChicago Argonne LLC, as Operator of Argonne
|
||||
* National Laboratory.
|
||||
* Copyright (c) 2002 The Regents of the University of California, as
|
||||
* Operator of Los Alamos National Laboratory.
|
||||
* EPICS BASE is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
\*************************************************************************/
|
||||
|
||||
#ifndef INC_registryJLinks_H
|
||||
#define INC_registryJLinks_H
|
||||
|
||||
#include "dbBase.h"
|
||||
#include "dbJLink.h"
|
||||
#include "shareLib.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
epicsShareFunc int registryJLinkAdd(DBBASE *pbase, jlif *pjlif);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#endif /* INC_registryDriverSupport_H */
|
||||
@@ -69,7 +69,7 @@ dbmfPrivate dbmfPvt;
|
||||
static dbmfPrivate *pdbmfPvt = NULL;
|
||||
int dbmfDebug=0;
|
||||
|
||||
int epicsShareAPI dbmfInit(size_t size, int chunkItems)
|
||||
int dbmfInit(size_t size, int chunkItems)
|
||||
{
|
||||
if(pdbmfPvt) {
|
||||
printf("dbmfInit: Already initialized\n");
|
||||
@@ -95,7 +95,7 @@ int epicsShareAPI dbmfInit(size_t size, int chunkItems)
|
||||
}
|
||||
|
||||
|
||||
void* epicsShareAPI dbmfMalloc(size_t size)
|
||||
void* dbmfMalloc(size_t size)
|
||||
{
|
||||
void **pnextFree;
|
||||
void **pfreeList;
|
||||
@@ -157,15 +157,23 @@ void* epicsShareAPI dbmfMalloc(size_t size)
|
||||
return((void *)pmem);
|
||||
}
|
||||
|
||||
char * epicsShareAPI dbmfStrdup(unsigned char *str)
|
||||
char * dbmfStrdup(const char *str)
|
||||
{
|
||||
size_t len = strlen((char *) str);
|
||||
char *buf = dbmfMalloc(len + 1);
|
||||
strcpy(buf, (char *) str);
|
||||
return buf;
|
||||
size_t len = strlen(str);
|
||||
char *buf = dbmfMalloc(len + 1); /* FIXME Can return NULL */
|
||||
|
||||
return strcpy(buf, str);
|
||||
}
|
||||
|
||||
void epicsShareAPI dbmfFree(void* mem)
|
||||
char * dbmfStrndup(const char *str, size_t len)
|
||||
{
|
||||
char *buf = dbmfMalloc(len + 1); /* FIXME Can return NULL */
|
||||
|
||||
buf[len] = '\0';
|
||||
return strncpy(buf, str, len);
|
||||
}
|
||||
|
||||
void dbmfFree(void* mem)
|
||||
{
|
||||
char *pmem = (char *)mem;
|
||||
chunkNode *pchunkNode;
|
||||
@@ -195,7 +203,7 @@ void epicsShareAPI dbmfFree(void* mem)
|
||||
epicsMutexUnlock(pdbmfPvt->lock);
|
||||
}
|
||||
|
||||
int epicsShareAPI dbmfShow(int level)
|
||||
int dbmfShow(int level)
|
||||
{
|
||||
if(pdbmfPvt==NULL) {
|
||||
printf("Never initialized\n");
|
||||
@@ -231,7 +239,7 @@ int epicsShareAPI dbmfShow(int level)
|
||||
return(0);
|
||||
}
|
||||
|
||||
void epicsShareAPI dbmfFreeChunks(void)
|
||||
void dbmfFreeChunks(void)
|
||||
{
|
||||
chunkNode *pchunkNode;
|
||||
chunkNode *pnext;;
|
||||
@@ -260,21 +268,32 @@ void epicsShareAPI dbmfFreeChunks(void)
|
||||
|
||||
#else /* DBMF_FREELIST_DEBUG */
|
||||
|
||||
int epicsShareAPI dbmfInit(size_t size, int chunkItems)
|
||||
int dbmfInit(size_t size, int chunkItems)
|
||||
{ return 0; }
|
||||
|
||||
void* epicsShareAPI dbmfMalloc(size_t size)
|
||||
void* dbmfMalloc(size_t size)
|
||||
{ return malloc(size); }
|
||||
|
||||
char * epicsShareAPI dbmfStrdup(unsigned char *str)
|
||||
char * dbmfStrdup(const char *str)
|
||||
{ return strdup((char*)str); }
|
||||
|
||||
void epicsShareAPI dbmfFree(void* mem)
|
||||
void dbmfFree(void* mem)
|
||||
{ free(mem); }
|
||||
|
||||
int epicsShareAPI dbmfShow(int level)
|
||||
int dbmfShow(int level)
|
||||
{ return 0; }
|
||||
|
||||
void epicsShareAPI dbmfFreeChunks(void) {}
|
||||
void dbmfFreeChunks(void) {}
|
||||
|
||||
#endif /* DBMF_FREELIST_DEBUG */
|
||||
|
||||
char * dbmfStrcat3(const char *lhs, const char *mid, const char *rhs)
|
||||
{
|
||||
size_t len = strlen(lhs) + strlen(mid) + strlen(rhs) + 1;
|
||||
char *buf = dbmfMalloc(len);
|
||||
strcpy(buf, lhs);
|
||||
strcat(buf, mid);
|
||||
strcat(buf, rhs);
|
||||
return buf;
|
||||
}
|
||||
|
||||
|
||||
@@ -23,12 +23,15 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
epicsShareFunc int epicsShareAPI dbmfInit(size_t size, int chunkItems);
|
||||
epicsShareFunc void * epicsShareAPI dbmfMalloc(size_t bytes);
|
||||
epicsShareFunc char * epicsShareAPI dbmfStrdup(unsigned char *str);
|
||||
epicsShareFunc void epicsShareAPI dbmfFree(void* bytes);
|
||||
epicsShareFunc void epicsShareAPI dbmfFreeChunks(void);
|
||||
epicsShareFunc int epicsShareAPI dbmfShow(int level);
|
||||
epicsShareFunc int dbmfInit(size_t size, int chunkItems);
|
||||
epicsShareFunc void * dbmfMalloc(size_t bytes);
|
||||
epicsShareFunc char * dbmfStrdup(const char *str);
|
||||
epicsShareFunc char * dbmfStrndup(const char *str, size_t len);
|
||||
epicsShareFunc char * dbmfStrcat3(const char *lhs, const char *mid,
|
||||
const char *rhs);
|
||||
epicsShareFunc void dbmfFree(void *bytes);
|
||||
epicsShareFunc void dbmfFreeChunks(void);
|
||||
epicsShareFunc int dbmfShow(int level);
|
||||
|
||||
/* Rules:
|
||||
* 1) Size is always made a multiple of 8.
|
||||
|
||||
@@ -52,6 +52,7 @@ extern "C" {
|
||||
#define M_time (529 <<16) /*epicsTime*/
|
||||
|
||||
epicsShareFunc void epicsShareAPI errSymLookup(long status, char *pBuf, unsigned bufLength);
|
||||
epicsShareFunc const char* errSymMsg(long status);
|
||||
epicsShareFunc void epicsShareAPI errSymTest(unsigned short modnum, unsigned short begErrNum, unsigned short endErrNum);
|
||||
epicsShareFunc void epicsShareAPI errSymTestPrint(long errNum);
|
||||
epicsShareFunc int epicsShareAPI errSymBld(void);
|
||||
|
||||
@@ -192,11 +192,9 @@ static void errRawCopy ( long statusToDecode, char *pBuf, unsigned bufLength )
|
||||
assert ( nChar < bufLength );
|
||||
}
|
||||
}
|
||||
|
||||
/****************************************************************
|
||||
* errSymLookup
|
||||
***************************************************************/
|
||||
void epicsShareAPI errSymLookup (long status, char * pBuf, unsigned bufLength)
|
||||
|
||||
static
|
||||
const char* errSymLookupInternal(long status)
|
||||
{
|
||||
unsigned modNum;
|
||||
unsigned hashInd;
|
||||
@@ -211,9 +209,7 @@ void epicsShareAPI errSymLookup (long status, char * pBuf, unsigned bufLength)
|
||||
if ( modNum <= 500 ) {
|
||||
const char * pStr = strerror ((int) status);
|
||||
if ( pStr ) {
|
||||
strncpy(pBuf, pStr,bufLength);
|
||||
pBuf[bufLength-1] = '\0';
|
||||
return;
|
||||
return pStr;
|
||||
}
|
||||
}
|
||||
else {
|
||||
@@ -222,17 +218,35 @@ void epicsShareAPI errSymLookup (long status, char * pBuf, unsigned bufLength)
|
||||
pNextNode = *phashnode;
|
||||
while(pNextNode) {
|
||||
if(pNextNode->errNum==status){
|
||||
strncpy(pBuf, pNextNode->message, bufLength);
|
||||
pBuf[bufLength-1] = '\0';
|
||||
return;
|
||||
return pNextNode->message;
|
||||
}
|
||||
phashnode = &pNextNode->hashnode;
|
||||
pNextNode = *phashnode;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const char* errSymMsg(long status)
|
||||
{
|
||||
const char* msg = errSymLookupInternal(status);
|
||||
return msg ? msg : "<Unknown code>";
|
||||
}
|
||||
|
||||
/****************************************************************
|
||||
* errSymLookup
|
||||
***************************************************************/
|
||||
void epicsShareAPI errSymLookup (long status, char * pBuf, unsigned bufLength)
|
||||
{
|
||||
const char* msg = errSymLookupInternal(status);
|
||||
if(msg) {
|
||||
strncpy(pBuf, msg, bufLength);
|
||||
pBuf[bufLength-1] = '\0';
|
||||
return;
|
||||
}
|
||||
errRawCopy(status, pBuf, bufLength);
|
||||
}
|
||||
|
||||
|
||||
/****************************************************************
|
||||
* errSymDump
|
||||
***************************************************************/
|
||||
|
||||
@@ -86,7 +86,7 @@ foreach $line ( @err_sym_line )
|
||||
{
|
||||
print OUT "$line\n";
|
||||
# define S_symbol /* comment */
|
||||
if ($line =~ m'[ \t#]define[ \t]*(S_[A-Za-z0-9_]+).*\/\*(.+)\*\/')
|
||||
if ($line =~ m'[ \t#]define[ \t]*(S_[A-Za-z0-9_]+).*\/\* ?(.+?) ?\*\/')
|
||||
{
|
||||
$symbol[$count] = $1;
|
||||
$comment[$count]= $2;
|
||||
|
||||
@@ -59,6 +59,10 @@
|
||||
#define PVNAME_STRINGSZ 61
|
||||
#define PVNAME_SZ (PVNAME_STRINGSZ - 1)
|
||||
|
||||
/* Buffer size for the string representation of a DBF_*LINK field */
|
||||
#define PVLINK_STRINGSZ 1024
|
||||
|
||||
/* dbAccess enums/menus can have up to this many choices */
|
||||
#define DB_MAX_CHOICES 30
|
||||
|
||||
#endif /* INC_dbDefs_H */
|
||||
|
||||
@@ -222,6 +222,15 @@ int epicsStrnCaseCmp(const char *s1, const char *s2, size_t len)
|
||||
return 0;
|
||||
}
|
||||
|
||||
char * epicsStrnDup(const char *s, size_t len)
|
||||
{
|
||||
char *buf = mallocMustSucceed(len + 1, "epicsStrnDup");
|
||||
|
||||
strncpy(buf, s, len);
|
||||
buf[len] = '\0';
|
||||
return buf;
|
||||
}
|
||||
|
||||
char * epicsStrDup(const char *s)
|
||||
{
|
||||
return strcpy(mallocMustSucceed(strlen(s)+1, "epicsStrDup"), s);
|
||||
|
||||
@@ -31,6 +31,7 @@ epicsShareFunc size_t epicsStrnEscapedFromRawSize(const char *buf, size_t len);
|
||||
epicsShareFunc int epicsStrCaseCmp(const char *s1, const char *s2);
|
||||
epicsShareFunc int epicsStrnCaseCmp(const char *s1, const char *s2, size_t len);
|
||||
epicsShareFunc char * epicsStrDup(const char *s);
|
||||
epicsShareFunc char * epicsStrnDup(const char *s, size_t len);
|
||||
epicsShareFunc int epicsStrPrintEscaped(FILE *fp, const char *s, size_t n);
|
||||
#define epicsStrSnPrintEscaped epicsStrnEscapedFromRaw
|
||||
epicsShareFunc size_t epicsStrnLen(const char *s, size_t maxlen);
|
||||
|
||||
@@ -115,7 +115,11 @@ getargs(int argc, char *argv[])
|
||||
int i;
|
||||
char *s;
|
||||
|
||||
if (argc > 0) myname = argv[0];
|
||||
if (argc > 0) {
|
||||
myname = strrchr(argv[0], '/');
|
||||
if (myname) myname++;
|
||||
else myname = argv[0];
|
||||
}
|
||||
for (i = 1; i < argc; ++i)
|
||||
{
|
||||
s = argv[i];
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
void
|
||||
fatal(char *msg)
|
||||
{
|
||||
fprintf(stderr, "%s: f - %s\n", myname, msg);
|
||||
fprintf(stderr, "%s: fatal - %s\n", myname, msg);
|
||||
done(2);
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ fatal(char *msg)
|
||||
void
|
||||
no_space(void)
|
||||
{
|
||||
fprintf(stderr, "%s: f - out of space\n", myname);
|
||||
fprintf(stderr, "%s: fatal - out of space\n", myname);
|
||||
done(2);
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@ no_space(void)
|
||||
void
|
||||
open_error(char *filename)
|
||||
{
|
||||
fprintf(stderr, "%s: f - cannot open \"%s\"\n", myname, filename);
|
||||
fprintf(stderr, "%s: fatal - cannot open \"%s\"\n", myname, filename);
|
||||
done(2);
|
||||
}
|
||||
|
||||
@@ -38,7 +38,7 @@ open_error(char *filename)
|
||||
void
|
||||
unexpected_EOF(void)
|
||||
{
|
||||
fprintf(stderr, "%s: e - line %d of \"%s\", unexpected end-of-file\n",
|
||||
fprintf(stderr, "%s: error - line %d of \"%s\", unexpected end-of-file\n",
|
||||
myname, lineno, input_file_name);
|
||||
done(1);
|
||||
}
|
||||
@@ -74,7 +74,7 @@ print_pos(char *st_line, char *st_cptr)
|
||||
void
|
||||
syntax_error(int st_lineno, char *st_line, char *st_cptr)
|
||||
{
|
||||
fprintf(stderr, "%s: e - line %d of \"%s\", syntax error\n",
|
||||
fprintf(stderr, "%s: error - line %d of \"%s\", syntax error\n",
|
||||
myname, st_lineno, input_file_name);
|
||||
print_pos(st_line, st_cptr);
|
||||
done(1);
|
||||
@@ -84,7 +84,7 @@ syntax_error(int st_lineno, char *st_line, char *st_cptr)
|
||||
void
|
||||
unterminated_comment(int c_lineno, char *c_line, char *c_cptr)
|
||||
{
|
||||
fprintf(stderr, "%s: e - line %d of \"%s\", unmatched /*\n",
|
||||
fprintf(stderr, "%s: error - line %d of \"%s\", unmatched /*\n",
|
||||
myname, c_lineno, input_file_name);
|
||||
print_pos(c_line, c_cptr);
|
||||
done(1);
|
||||
@@ -94,7 +94,7 @@ unterminated_comment(int c_lineno, char *c_line, char *c_cptr)
|
||||
void
|
||||
unterminated_string(int s_lineno, char *s_line, char *s_cptr)
|
||||
{
|
||||
fprintf(stderr, "%s: e - line %d of \"%s\", unterminated string\n",
|
||||
fprintf(stderr, "%s: error - line %d of \"%s\", unterminated string\n",
|
||||
myname, s_lineno, input_file_name);
|
||||
print_pos(s_line, s_cptr);
|
||||
done(1);
|
||||
@@ -104,7 +104,7 @@ unterminated_string(int s_lineno, char *s_line, char *s_cptr)
|
||||
void
|
||||
unterminated_text(int t_lineno, char *t_line, char *t_cptr)
|
||||
{
|
||||
fprintf(stderr, "%s: e - line %d of \"%s\", unmatched %%{\n",
|
||||
fprintf(stderr, "%s: error - line %d of \"%s\", unmatched %%{\n",
|
||||
myname, t_lineno, input_file_name);
|
||||
print_pos(t_line, t_cptr);
|
||||
done(1);
|
||||
@@ -114,7 +114,7 @@ unterminated_text(int t_lineno, char *t_line, char *t_cptr)
|
||||
void
|
||||
unterminated_union(int u_lineno, char *u_line, char *u_cptr)
|
||||
{
|
||||
fprintf(stderr, "%s: e - line %d of \"%s\", unterminated %%union \
|
||||
fprintf(stderr, "%s: error - line %d of \"%s\", unterminated %%union \
|
||||
declaration\n", myname, u_lineno, input_file_name);
|
||||
print_pos(u_line, u_cptr);
|
||||
done(1);
|
||||
@@ -124,7 +124,7 @@ declaration\n", myname, u_lineno, input_file_name);
|
||||
void
|
||||
over_unionized(char *u_cptr)
|
||||
{
|
||||
fprintf(stderr, "%s: e - line %d of \"%s\", too many %%union \
|
||||
fprintf(stderr, "%s: error - line %d of \"%s\", too many %%union \
|
||||
declarations\n", myname, lineno, input_file_name);
|
||||
print_pos(line, u_cptr);
|
||||
done(1);
|
||||
@@ -134,7 +134,7 @@ declarations\n", myname, lineno, input_file_name);
|
||||
void
|
||||
illegal_tag(int t_lineno, char *t_line, char *t_cptr)
|
||||
{
|
||||
fprintf(stderr, "%s: e - line %d of \"%s\", illegal tag\n",
|
||||
fprintf(stderr, "%s: error - line %d of \"%s\", illegal tag\n",
|
||||
myname, t_lineno, input_file_name);
|
||||
print_pos(t_line, t_cptr);
|
||||
done(1);
|
||||
@@ -144,7 +144,7 @@ illegal_tag(int t_lineno, char *t_line, char *t_cptr)
|
||||
void
|
||||
illegal_character(char *c_cptr)
|
||||
{
|
||||
fprintf(stderr, "%s: e - line %d of \"%s\", illegal character\n",
|
||||
fprintf(stderr, "%s: error - line %d of \"%s\", illegal character\n",
|
||||
myname, lineno, input_file_name);
|
||||
print_pos(line, c_cptr);
|
||||
done(1);
|
||||
@@ -154,7 +154,7 @@ illegal_character(char *c_cptr)
|
||||
void
|
||||
used_reserved(char *s)
|
||||
{
|
||||
fprintf(stderr, "%s: e - line %d of \"%s\", illegal use of reserved symbol \
|
||||
fprintf(stderr, "%s: error - line %d of \"%s\", illegal use of reserved symbol \
|
||||
%s\n", myname, lineno, input_file_name, s);
|
||||
done(1);
|
||||
}
|
||||
@@ -163,7 +163,7 @@ used_reserved(char *s)
|
||||
void
|
||||
tokenized_start(char *s)
|
||||
{
|
||||
fprintf(stderr, "%s: e - line %d of \"%s\", the start symbol %s cannot be \
|
||||
fprintf(stderr, "%s: error - line %d of \"%s\", the start symbol %s cannot be \
|
||||
declared to be a token\n", myname, lineno, input_file_name, s);
|
||||
done(1);
|
||||
}
|
||||
@@ -172,7 +172,7 @@ declared to be a token\n", myname, lineno, input_file_name, s);
|
||||
void
|
||||
retyped_warning(char *s)
|
||||
{
|
||||
fprintf(stderr, "%s: w - line %d of \"%s\", the type of %s has been \
|
||||
fprintf(stderr, "%s: warning - line %d of \"%s\", the type of %s has been \
|
||||
redeclared\n", myname, lineno, input_file_name, s);
|
||||
}
|
||||
|
||||
@@ -180,7 +180,7 @@ redeclared\n", myname, lineno, input_file_name, s);
|
||||
void
|
||||
reprec_warning(char *s)
|
||||
{
|
||||
fprintf(stderr, "%s: w - line %d of \"%s\", the precedence of %s has been \
|
||||
fprintf(stderr, "%s: warning - line %d of \"%s\", the precedence of %s has been \
|
||||
redeclared\n", myname, lineno, input_file_name, s);
|
||||
}
|
||||
|
||||
@@ -188,7 +188,7 @@ redeclared\n", myname, lineno, input_file_name, s);
|
||||
void
|
||||
revalued_warning(char *s)
|
||||
{
|
||||
fprintf(stderr, "%s: w - line %d of \"%s\", the value of %s has been \
|
||||
fprintf(stderr, "%s: warning - line %d of \"%s\", the value of %s has been \
|
||||
redeclared\n", myname, lineno, input_file_name, s);
|
||||
}
|
||||
|
||||
@@ -196,7 +196,7 @@ redeclared\n", myname, lineno, input_file_name, s);
|
||||
void
|
||||
terminal_start(char *s)
|
||||
{
|
||||
fprintf(stderr, "%s: e - line %d of \"%s\", the start symbol %s is a \
|
||||
fprintf(stderr, "%s: error - line %d of \"%s\", the start symbol %s is a \
|
||||
token\n", myname, lineno, input_file_name, s);
|
||||
done(1);
|
||||
}
|
||||
@@ -205,7 +205,7 @@ token\n", myname, lineno, input_file_name, s);
|
||||
void
|
||||
restarted_warning(void)
|
||||
{
|
||||
fprintf(stderr, "%s: w - line %d of \"%s\", the start symbol has been \
|
||||
fprintf(stderr, "%s: warning - line %d of \"%s\", the start symbol has been \
|
||||
redeclared\n", myname, lineno, input_file_name);
|
||||
}
|
||||
|
||||
@@ -213,7 +213,7 @@ redeclared\n", myname, lineno, input_file_name);
|
||||
void
|
||||
no_grammar(void)
|
||||
{
|
||||
fprintf(stderr, "%s: e - line %d of \"%s\", no grammar has been \
|
||||
fprintf(stderr, "%s: error - line %d of \"%s\", no grammar has been \
|
||||
specified\n", myname, lineno, input_file_name);
|
||||
done(1);
|
||||
}
|
||||
@@ -222,7 +222,7 @@ specified\n", myname, lineno, input_file_name);
|
||||
void
|
||||
terminal_lhs(int s_lineno)
|
||||
{
|
||||
fprintf(stderr, "%s: e - line %d of \"%s\", a token appears on the lhs \
|
||||
fprintf(stderr, "%s: error - line %d of \"%s\", a token appears on the lhs \
|
||||
of a production\n", myname, s_lineno, input_file_name);
|
||||
done(1);
|
||||
}
|
||||
@@ -231,7 +231,7 @@ of a production\n", myname, s_lineno, input_file_name);
|
||||
void
|
||||
prec_redeclared(void)
|
||||
{
|
||||
fprintf(stderr, "%s: w - line %d of \"%s\", conflicting %%prec \
|
||||
fprintf(stderr, "%s: warning - line %d of \"%s\", conflicting %%prec \
|
||||
specifiers\n", myname, lineno, input_file_name);
|
||||
}
|
||||
|
||||
@@ -239,7 +239,7 @@ specifiers\n", myname, lineno, input_file_name);
|
||||
void
|
||||
unterminated_action(int a_lineno, char *a_line, char *a_cptr)
|
||||
{
|
||||
fprintf(stderr, "%s: e - line %d of \"%s\", unterminated action\n",
|
||||
fprintf(stderr, "%s: error - line %d of \"%s\", unterminated action\n",
|
||||
myname, a_lineno, input_file_name);
|
||||
print_pos(a_line, a_cptr);
|
||||
done(1);
|
||||
@@ -249,7 +249,7 @@ unterminated_action(int a_lineno, char *a_line, char *a_cptr)
|
||||
void
|
||||
dollar_warning(int a_lineno, int i)
|
||||
{
|
||||
fprintf(stderr, "%s: w - line %d of \"%s\", $%d references beyond the \
|
||||
fprintf(stderr, "%s: warning - line %d of \"%s\", $%d references beyond the \
|
||||
end of the current rule\n", myname, a_lineno, input_file_name, i);
|
||||
}
|
||||
|
||||
@@ -257,7 +257,7 @@ end of the current rule\n", myname, a_lineno, input_file_name, i);
|
||||
void
|
||||
dollar_error(int a_lineno, char *a_line, char *a_cptr)
|
||||
{
|
||||
fprintf(stderr, "%s: e - line %d of \"%s\", illegal $-name\n",
|
||||
fprintf(stderr, "%s: error - line %d of \"%s\", illegal $-name\n",
|
||||
myname, a_lineno, input_file_name);
|
||||
print_pos(a_line, a_cptr);
|
||||
done(1);
|
||||
@@ -267,7 +267,7 @@ dollar_error(int a_lineno, char *a_line, char *a_cptr)
|
||||
void
|
||||
untyped_lhs(void)
|
||||
{
|
||||
fprintf(stderr, "%s: e - line %d of \"%s\", $$ is untyped\n",
|
||||
fprintf(stderr, "%s: error - line %d of \"%s\", $$ is untyped\n",
|
||||
myname, lineno, input_file_name);
|
||||
done(1);
|
||||
}
|
||||
@@ -276,7 +276,7 @@ untyped_lhs(void)
|
||||
void
|
||||
untyped_rhs(int i, char *s)
|
||||
{
|
||||
fprintf(stderr, "%s: e - line %d of \"%s\", $%d (%s) is untyped\n",
|
||||
fprintf(stderr, "%s: error - line %d of \"%s\", $%d (%s) is untyped\n",
|
||||
myname, lineno, input_file_name, i, s);
|
||||
done(1);
|
||||
}
|
||||
@@ -285,7 +285,7 @@ untyped_rhs(int i, char *s)
|
||||
void
|
||||
unknown_rhs(int i)
|
||||
{
|
||||
fprintf(stderr, "%s: e - line %d of \"%s\", $%d is untyped\n",
|
||||
fprintf(stderr, "%s: error - line %d of \"%s\", $%d is untyped\n",
|
||||
myname, lineno, input_file_name, i);
|
||||
done(1);
|
||||
}
|
||||
@@ -294,7 +294,7 @@ unknown_rhs(int i)
|
||||
void
|
||||
default_action_warning(void)
|
||||
{
|
||||
fprintf(stderr, "%s: w - line %d of \"%s\", the default action assigns an \
|
||||
fprintf(stderr, "%s: warning - line %d of \"%s\", the default action assigns an \
|
||||
undefined value to $$\n", myname, lineno, input_file_name);
|
||||
}
|
||||
|
||||
@@ -302,7 +302,7 @@ undefined value to $$\n", myname, lineno, input_file_name);
|
||||
void
|
||||
undefined_goal(char *s)
|
||||
{
|
||||
fprintf(stderr, "%s: e - the start symbol %s is undefined\n", myname, s);
|
||||
fprintf(stderr, "%s: error - the start symbol %s is undefined\n", myname, s);
|
||||
done(1);
|
||||
}
|
||||
|
||||
@@ -310,5 +310,5 @@ undefined_goal(char *s)
|
||||
void
|
||||
undefined_symbol_warning(char *s)
|
||||
{
|
||||
fprintf(stderr, "%s: w - the symbol %s is undefined\n", myname, s);
|
||||
fprintf(stderr, "%s: warning - the symbol %s is undefined\n", myname, s);
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ SRC_DIRS += $(LIBCOM)/yacc
|
||||
|
||||
# Yet Another JSON Library
|
||||
SRC_DIRS += $(LIBCOM)/yajl
|
||||
INC += yajl_alloc.h
|
||||
INC += yajl_common.h
|
||||
INC += yajl_gen.h
|
||||
INC += yajl_parse.h
|
||||
|
||||
@@ -45,6 +45,6 @@
|
||||
#define YA_FREE(afs, ptr) (afs)->free((afs)->ctx, (ptr))
|
||||
#define YA_REALLOC(afs, ptr, sz) (afs)->realloc((afs)->ctx, (ptr), (sz))
|
||||
|
||||
void yajl_set_default_alloc_funcs(yajl_alloc_funcs * yaf);
|
||||
YAJL_API void yajl_set_default_alloc_funcs(yajl_alloc_funcs * yaf);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -20,6 +20,7 @@ dbRecStd_RCS += dbRecStd.rc
|
||||
include $(STDDIR)/rec/Makefile
|
||||
include $(STDDIR)/dev/Makefile
|
||||
include $(STDDIR)/filters/Makefile
|
||||
include $(STDDIR)/link/Makefile
|
||||
include $(STDDIR)/softIoc/Makefile
|
||||
|
||||
include $(TOP)/configure/RULES
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#include "alarm.h"
|
||||
#include "dbDefs.h"
|
||||
#include "dbAccess.h"
|
||||
#include "dbConstLink.h"
|
||||
#include "recGbl.h"
|
||||
#include "devSup.h"
|
||||
#include "cantProceed.h"
|
||||
@@ -52,36 +53,52 @@ epicsExportAddress(dset,devAaiSoft);
|
||||
|
||||
static long init_record(aaiRecord *prec)
|
||||
{
|
||||
/* INP must be a CONSTANT or a PV_LINK or a DB_LINK or a CA_LINK*/
|
||||
switch (prec->inp.type) {
|
||||
case CONSTANT:
|
||||
prec->nord = 0;
|
||||
break;
|
||||
case PV_LINK:
|
||||
case DB_LINK:
|
||||
case CA_LINK:
|
||||
break;
|
||||
default :
|
||||
recGblRecordError(S_db_badField, (void *)prec,
|
||||
"devAaiSoft (init_record) Illegal INP field");
|
||||
return(S_db_badField);
|
||||
if (prec->inp.type == CONSTANT) {
|
||||
long nRequest = prec->nelm;
|
||||
long status;
|
||||
|
||||
/* Allocate a buffer, record support hasn't done that yet */
|
||||
if (!prec->bptr) {
|
||||
prec->bptr = callocMustSucceed(nRequest, dbValueSize(prec->ftvl),
|
||||
"devAaiSoft: buffer calloc failed");
|
||||
}
|
||||
|
||||
/* This is pass 0 so link hasn't been initialized either */
|
||||
dbConstInitLink(&prec->inp);
|
||||
|
||||
status = dbLoadLinkArray(&prec->inp, prec->ftvl, prec->bptr, &nRequest);
|
||||
if (!status && nRequest > 0) {
|
||||
prec->nord = nRequest;
|
||||
prec->udf = FALSE;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long readLocked(struct link *pinp, void *dummy)
|
||||
{
|
||||
aaiRecord *prec = (aaiRecord *) pinp->precord;
|
||||
long nRequest = prec->nelm;
|
||||
long status = dbGetLink(pinp, prec->ftvl, prec->bptr, 0, &nRequest);
|
||||
|
||||
if (!status && nRequest > 0) {
|
||||
prec->nord = nRequest;
|
||||
prec->udf = FALSE;
|
||||
|
||||
if (dbLinkIsConstant(&prec->tsel) &&
|
||||
prec->tse == epicsTimeEventDeviceTime)
|
||||
dbGetTimeStamp(pinp, &prec->time);
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
static long read_aai(aaiRecord *prec)
|
||||
{
|
||||
long nRequest = prec->nelm;
|
||||
struct link *pinp = prec->simm == menuYesNoYES ? &prec->siol : &prec->inp;
|
||||
long status = dbLinkDoLocked(pinp, readLocked, NULL);
|
||||
|
||||
dbGetLink(prec->simm == menuYesNoYES ? &prec->siol : &prec->inp,
|
||||
prec->ftvl, prec->bptr, 0, &nRequest);
|
||||
if (nRequest > 0) {
|
||||
prec->nord = nRequest;
|
||||
prec->udf=FALSE;
|
||||
if (prec->tsel.type == CONSTANT &&
|
||||
prec->tse == epicsTimeEventDeviceTime)
|
||||
dbGetTimeStamp(&prec->inp, &prec->time);
|
||||
}
|
||||
if (status == S_db_noLSET)
|
||||
status = readLocked(pinp, NULL);
|
||||
|
||||
return 0;
|
||||
return status;
|
||||
}
|
||||
|
||||
@@ -52,19 +52,8 @@ epicsExportAddress(dset,devAaoSoft);
|
||||
|
||||
static long init_record(aaoRecord *prec)
|
||||
{
|
||||
/* OUT must be a CONSTANT or a PV_LINK or a DB_LINK or a CA_LINK*/
|
||||
switch (prec->out.type) {
|
||||
case CONSTANT:
|
||||
if (dbLinkIsConstant(&prec->out)) {
|
||||
prec->nord = 0;
|
||||
break;
|
||||
case PV_LINK:
|
||||
case DB_LINK:
|
||||
case CA_LINK:
|
||||
break;
|
||||
default :
|
||||
recGblRecordError(S_db_badField, prec,
|
||||
"devAaoSoft (init_record) Illegal OUT field");
|
||||
return(S_db_badField);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -50,47 +50,55 @@ epicsExportAddress(dset, devAiSoft);
|
||||
|
||||
static long init_record(aiRecord *prec)
|
||||
{
|
||||
/* INP must be CONSTANT, PV_LINK, DB_LINK or CA_LINK*/
|
||||
switch (prec->inp.type) {
|
||||
case CONSTANT:
|
||||
if (recGblInitConstantLink(&prec->inp, DBF_DOUBLE, &prec->val))
|
||||
prec->udf = FALSE;
|
||||
break;
|
||||
case PV_LINK:
|
||||
case DB_LINK:
|
||||
case CA_LINK:
|
||||
break;
|
||||
default:
|
||||
recGblRecordError(S_db_badField, (void *)prec,
|
||||
"devAiSoft (init_record) Illegal INP field");
|
||||
return S_db_badField;
|
||||
}
|
||||
if (recGblInitConstantLink(&prec->inp, DBF_DOUBLE, &prec->val))
|
||||
prec->udf = FALSE;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct aivt {
|
||||
double val;
|
||||
epicsTimeStamp *ptime;
|
||||
};
|
||||
|
||||
static long readLocked(struct link *pinp, void *vvt)
|
||||
{
|
||||
struct aivt *pvt = (struct aivt *) vvt;
|
||||
long status = dbGetLink(pinp, DBR_DOUBLE, &pvt->val, 0, 0);
|
||||
|
||||
if (!status && pvt->ptime)
|
||||
dbGetTimeStamp(pinp, pvt->ptime);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static long read_ai(aiRecord *prec)
|
||||
{
|
||||
double val;
|
||||
long status;
|
||||
struct aivt vt;
|
||||
|
||||
if (prec->inp.type == CONSTANT)
|
||||
if (dbLinkIsConstant(&prec->inp))
|
||||
return 2;
|
||||
|
||||
if (!dbGetLink(&prec->inp, DBR_DOUBLE, &val, 0, 0)) {
|
||||
vt.ptime = (dbLinkIsConstant(&prec->tsel) &&
|
||||
prec->tse == epicsTimeEventDeviceTime) ? &prec->time : NULL;
|
||||
|
||||
status = dbLinkDoLocked(&prec->inp, readLocked, &vt);
|
||||
if (status == S_db_noLSET)
|
||||
status = readLocked(&prec->inp, &vt);
|
||||
|
||||
if (!status) {
|
||||
/* Apply smoothing algorithm */
|
||||
if (prec->smoo != 0.0 && prec->dpvt && finite(prec->val))
|
||||
prec->val = val * (1.00 - prec->smoo) + (prec->val * prec->smoo);
|
||||
prec->val = vt.val * (1.0 - prec->smoo) + (prec->val * prec->smoo);
|
||||
else
|
||||
prec->val = val;
|
||||
prec->val = vt.val;
|
||||
|
||||
prec->udf = FALSE;
|
||||
prec->dpvt = &devAiSoft; /* Any non-zero value */
|
||||
|
||||
if (prec->tsel.type == CONSTANT &&
|
||||
prec->tse == epicsTimeEventDeviceTime)
|
||||
dbGetTimeStamp(&prec->inp, &prec->time);
|
||||
} else {
|
||||
prec->dpvt = NULL;
|
||||
}
|
||||
else
|
||||
prec->dpvt = NULL;
|
||||
|
||||
return 2;
|
||||
}
|
||||
|
||||
@@ -49,29 +49,31 @@ epicsExportAddress(dset, devAiSoftRaw);
|
||||
|
||||
static long init_record(aiRecord *prec)
|
||||
{
|
||||
/* INP must be CONSTANT, PV_LINK, DB_LINK or CA_LINK*/
|
||||
switch (prec->inp.type) {
|
||||
case CONSTANT:
|
||||
recGblInitConstantLink(&prec->inp, DBF_LONG, &prec->rval);
|
||||
break;
|
||||
case PV_LINK:
|
||||
case DB_LINK:
|
||||
case CA_LINK:
|
||||
break;
|
||||
default:
|
||||
recGblRecordError(S_db_badField, (void *)prec,
|
||||
"devAiSoftRaw (init_record) Illegal INP field");
|
||||
return S_db_badField;
|
||||
}
|
||||
recGblInitConstantLink(&prec->inp, DBF_LONG, &prec->rval);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long readLocked(struct link *pinp, void *dummy)
|
||||
{
|
||||
aiRecord *prec = (aiRecord *) pinp->precord;
|
||||
long status = dbGetLink(pinp, DBR_LONG, &prec->rval, 0, 0);
|
||||
|
||||
if (status) return status;
|
||||
|
||||
if (dbLinkIsConstant(&prec->tsel) &&
|
||||
prec->tse == epicsTimeEventDeviceTime)
|
||||
dbGetTimeStamp(pinp, &prec->time);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static long read_ai(aiRecord *prec)
|
||||
{
|
||||
if (!dbGetLink(&prec->inp, DBR_LONG, &prec->rval, 0, 0) &&
|
||||
prec->tsel.type == CONSTANT &&
|
||||
prec->tse == epicsTimeEventDeviceTime)
|
||||
dbGetTimeStamp(&prec->inp, &prec->time);
|
||||
long status = dbLinkDoLocked(&prec->inp, readLocked, NULL);
|
||||
|
||||
return 0;
|
||||
if (status == S_db_noLSET)
|
||||
status = readLocked(&prec->inp, NULL);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
@@ -54,17 +54,15 @@ static long write_ao(aoRecord *prec)
|
||||
struct link *plink = &prec->out;
|
||||
long status;
|
||||
|
||||
if(prec->pact) return(0);
|
||||
if(plink->type!=CA_LINK) {
|
||||
status = dbPutLink(plink,DBR_DOUBLE,&prec->oval,1);
|
||||
return(status);
|
||||
}
|
||||
status = dbCaPutLinkCallback(plink,DBR_DOUBLE,&prec->oval,1,
|
||||
dbCaCallbackProcess,plink);
|
||||
if(status) {
|
||||
recGblSetSevr(prec,LINK_ALARM,INVALID_ALARM);
|
||||
return(status);
|
||||
}
|
||||
prec->pact = TRUE;
|
||||
return(0);
|
||||
if (prec->pact)
|
||||
return 0;
|
||||
|
||||
status = dbPutLinkAsync(plink, DBR_DOUBLE, &prec->oval, 1);
|
||||
if (!status)
|
||||
prec->pact = TRUE;
|
||||
else if (status == S_db_noLSET)
|
||||
status = dbPutLink(plink, DBR_DOUBLE, &prec->oval, 1);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
@@ -66,10 +66,6 @@ static long read_bi(biRecord *prec)
|
||||
prec->udf = FALSE;
|
||||
}
|
||||
|
||||
if (prec->tsel.type == CONSTANT &&
|
||||
prec->tse == epicsTimeEventDeviceTime)
|
||||
dbGetTimeStamp(&prec->inp, &prec->time);
|
||||
|
||||
return 2;
|
||||
}
|
||||
|
||||
|
||||
@@ -47,32 +47,32 @@ epicsExportAddress(dset, devBiSoft);
|
||||
|
||||
static long init_record(biRecord *prec)
|
||||
{
|
||||
/* INP must be CONSTANT, PV_LINK, DB_LINK or CA_LINK*/
|
||||
switch (prec->inp.type) {
|
||||
case CONSTANT:
|
||||
if (recGblInitConstantLink(&prec->inp, DBF_ENUM, &prec->val))
|
||||
prec->udf = FALSE;
|
||||
break;
|
||||
case PV_LINK:
|
||||
case DB_LINK:
|
||||
case CA_LINK:
|
||||
break;
|
||||
default:
|
||||
recGblRecordError(S_db_badField, (void *)prec,
|
||||
"devBiSoft (init_record) Illegal INP field");
|
||||
return S_db_badField;
|
||||
}
|
||||
if (recGblInitConstantLink(&prec->inp, DBF_ENUM, &prec->val))
|
||||
prec->udf = FALSE;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long readLocked(struct link *pinp, void *dummy)
|
||||
{
|
||||
biRecord *prec = (biRecord *) pinp->precord;
|
||||
long status = dbGetLink(pinp, DBR_USHORT, &prec->val, 0, 0);
|
||||
|
||||
if (status) return status;
|
||||
|
||||
prec->udf = FALSE;
|
||||
if (dbLinkIsConstant(&prec->tsel) &&
|
||||
prec->tse == epicsTimeEventDeviceTime)
|
||||
dbGetTimeStamp(pinp, &prec->time);
|
||||
|
||||
return 2;
|
||||
}
|
||||
|
||||
static long read_bi(biRecord *prec)
|
||||
{
|
||||
if (!dbGetLink(&prec->inp, DBR_USHORT, &prec->val, 0, 0)) {
|
||||
if (prec->inp.type != CONSTANT)
|
||||
prec->udf = FALSE;
|
||||
if (prec->tsel.type == CONSTANT &&
|
||||
prec->tse == epicsTimeEventDeviceTime)
|
||||
dbGetTimeStamp(&prec->inp, &prec->time);
|
||||
}
|
||||
return 2;
|
||||
long status = dbLinkDoLocked(&prec->inp, readLocked, NULL);
|
||||
|
||||
if (status == S_db_noLSET)
|
||||
status = readLocked(&prec->inp, NULL);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
@@ -47,29 +47,31 @@ epicsExportAddress(dset, devBiSoftRaw);
|
||||
|
||||
static long init_record(biRecord *prec)
|
||||
{
|
||||
/* INP must be CONSTANT, PV_LINK, DB_LINK or CA_LINK*/
|
||||
switch (prec->inp.type) {
|
||||
case CONSTANT:
|
||||
recGblInitConstantLink(&prec->inp, DBF_ULONG, &prec->rval);
|
||||
break;
|
||||
case PV_LINK:
|
||||
case DB_LINK:
|
||||
case CA_LINK:
|
||||
break;
|
||||
default:
|
||||
recGblRecordError(S_db_badField, (void *)prec,
|
||||
"devBiSoftRaw (init_record) Illegal INP field");
|
||||
return S_db_badField;
|
||||
}
|
||||
recGblInitConstantLink(&prec->inp, DBF_ULONG, &prec->rval);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long readLocked(struct link *pinp, void *dummy)
|
||||
{
|
||||
biRecord *prec = (biRecord *) pinp->precord;
|
||||
long status = dbGetLink(pinp, DBR_ULONG, &prec->rval, 0, 0);
|
||||
|
||||
if (status) return status;
|
||||
|
||||
if (dbLinkIsConstant(&prec->tsel) &&
|
||||
prec->tse == epicsTimeEventDeviceTime)
|
||||
dbGetTimeStamp(pinp, &prec->time);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static long read_bi(biRecord *prec)
|
||||
{
|
||||
if (!dbGetLink(&prec->inp, DBR_ULONG, &prec->rval, 0, 0) &&
|
||||
prec->tsel.type == CONSTANT &&
|
||||
prec->tse == epicsTimeEventDeviceTime)
|
||||
dbGetTimeStamp(&prec->inp, &prec->time);
|
||||
long status = dbLinkDoLocked(&prec->inp, readLocked, NULL);
|
||||
|
||||
return 0;
|
||||
if (status == S_db_noLSET)
|
||||
status = readLocked(&prec->inp, NULL);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
@@ -3,9 +3,8 @@
|
||||
* National Laboratory.
|
||||
* Copyright (c) 2002 The Regents of the University of California, as
|
||||
* Operator of Los Alamos National Laboratory.
|
||||
* EPICS BASE Versions 3.13.7
|
||||
* and higher are distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
* EPICS BASE is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
\*************************************************************************/
|
||||
|
||||
/* devBoCallbackSoft.c */
|
||||
@@ -54,17 +53,15 @@ static long write_bo(boRecord *prec)
|
||||
struct link *plink = &prec->out;
|
||||
long status;
|
||||
|
||||
if(prec->pact) return(0);
|
||||
if(plink->type!=CA_LINK) {
|
||||
status = dbPutLink(plink,DBR_USHORT,&prec->val,1);
|
||||
return(status);
|
||||
}
|
||||
status = dbCaPutLinkCallback(plink,DBR_USHORT,&prec->val,1,
|
||||
dbCaCallbackProcess,plink);
|
||||
if(status) {
|
||||
recGblSetSevr(prec,LINK_ALARM,INVALID_ALARM);
|
||||
return(status);
|
||||
}
|
||||
prec->pact = TRUE;
|
||||
return(0);
|
||||
if (prec->pact)
|
||||
return 0;
|
||||
|
||||
status = dbPutLinkAsync(plink, DBR_USHORT, &prec->val, 1);
|
||||
if (!status)
|
||||
prec->pact = TRUE;
|
||||
else if (status == S_db_noLSET)
|
||||
status = dbPutLink(plink, DBR_USHORT, &prec->val, 1);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
@@ -48,17 +48,15 @@ static long write_calcout(calcoutRecord *prec)
|
||||
struct link *plink = &prec->out;
|
||||
long status;
|
||||
|
||||
if (prec->pact) return 0;
|
||||
if (plink->type != CA_LINK) {
|
||||
if (prec->pact)
|
||||
return 0;
|
||||
|
||||
status = dbPutLinkAsync(plink, DBR_DOUBLE, &prec->oval, 1);
|
||||
if (!status)
|
||||
prec->pact = TRUE;
|
||||
else if (status == S_db_noLSET)
|
||||
status = dbPutLink(plink, DBR_DOUBLE, &prec->oval, 1);
|
||||
return status;
|
||||
}
|
||||
status = dbCaPutLinkCallback(plink, DBR_DOUBLE, &prec->oval, 1,
|
||||
dbCaCallbackProcess, plink);
|
||||
if (status) {
|
||||
recGblSetSevr(prec, LINK_ALARM, INVALID_ALARM);
|
||||
return status;
|
||||
}
|
||||
prec->pact = TRUE;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -47,40 +47,50 @@ epicsExportAddress(dset, devEventSoft);
|
||||
|
||||
static long init_record(eventRecord *prec)
|
||||
{
|
||||
/* INP must be CONSTANT, PV_LINK, DB_LINK or CA_LINK*/
|
||||
switch (prec->inp.type) {
|
||||
case CONSTANT:
|
||||
if (recGblInitConstantLink(&prec->inp, DBF_STRING, &prec->val))
|
||||
prec->udf = FALSE;
|
||||
break;
|
||||
case PV_LINK:
|
||||
case DB_LINK:
|
||||
case CA_LINK:
|
||||
break;
|
||||
default:
|
||||
recGblRecordError(S_db_badField, (void *)prec,
|
||||
"devEventSoft (init_record) Illegal INP field");
|
||||
return S_db_badField;
|
||||
}
|
||||
if (recGblInitConstantLink(&prec->inp, DBF_STRING, prec->val))
|
||||
prec->udf = FALSE;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct eventvt {
|
||||
char newEvent[MAX_STRING_SIZE];
|
||||
epicsTimeStamp *ptime;
|
||||
};
|
||||
|
||||
static long readLocked(struct link *pinp, void *vvt)
|
||||
{
|
||||
struct eventvt *pvt = (struct eventvt *) vvt;
|
||||
long status = dbGetLink(pinp, DBR_STRING, pvt->newEvent, 0, 0);
|
||||
|
||||
if (!status && pvt->ptime)
|
||||
dbGetTimeStamp(pinp, pvt->ptime);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static long read_event(eventRecord *prec)
|
||||
{
|
||||
long status;
|
||||
char newEvent[MAX_STRING_SIZE];
|
||||
struct eventvt vt;
|
||||
|
||||
if (prec->inp.type != CONSTANT) {
|
||||
status = dbGetLink(&prec->inp, DBR_STRING, newEvent, 0, 0);
|
||||
if (status) return status;
|
||||
if (strcmp(newEvent, prec->val) != 0) {
|
||||
strcpy(prec->val, newEvent);
|
||||
if (dbLinkIsConstant(&prec->inp))
|
||||
return 0;
|
||||
|
||||
vt.ptime = (dbLinkIsConstant(&prec->tsel) &&
|
||||
prec->tse == epicsTimeEventDeviceTime) ? &prec->time : NULL;
|
||||
|
||||
status = dbLinkDoLocked(&prec->inp, readLocked, &vt);
|
||||
if (status == S_db_noLSET)
|
||||
status = readLocked(&prec->inp, &vt);
|
||||
|
||||
if (!status) {
|
||||
if (strcmp(vt.newEvent, prec->val) != 0) {
|
||||
strcpy(prec->val, vt.newEvent);
|
||||
prec->epvt = eventNameToHandle(prec->val);
|
||||
}
|
||||
prec->udf = FALSE;
|
||||
}
|
||||
prec->udf = FALSE;
|
||||
if (prec->tsel.type == CONSTANT &&
|
||||
prec->tse == epicsTimeEventDeviceTime)
|
||||
dbGetTimeStamp(&prec->inp, &prec->time);
|
||||
return 0;
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
@@ -51,21 +51,9 @@ epicsExportAddress(dset,devHistogramSoft);
|
||||
|
||||
static long init_record(histogramRecord *prec)
|
||||
{
|
||||
/* histogram.svl must be a CONSTANT or a PV_LINK or a DB_LINK or a CA_LINK*/
|
||||
switch (prec->svl.type) {
|
||||
case (CONSTANT) :
|
||||
if(recGblInitConstantLink(&prec->svl,DBF_DOUBLE,&prec->sgnl))
|
||||
prec->udf = FALSE;
|
||||
break;
|
||||
case (PV_LINK) :
|
||||
case (DB_LINK) :
|
||||
case (CA_LINK) :
|
||||
break;
|
||||
default :
|
||||
recGblRecordError(S_db_badField,(void *)prec,
|
||||
"devHistogramSoft (init_record) Illegal SVL field");
|
||||
return(S_db_badField);
|
||||
}
|
||||
if (recGblInitConstantLink(&prec->svl,DBF_DOUBLE,&prec->sgnl))
|
||||
prec->udf = FALSE;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -47,32 +47,32 @@ epicsExportAddress(dset, devLiSoft);
|
||||
|
||||
static long init_record(longinRecord *prec)
|
||||
{
|
||||
/* INP must be CONSTANT, PV_LINK, DB_LINK or CA_LINK*/
|
||||
switch (prec->inp.type) {
|
||||
case CONSTANT:
|
||||
if (recGblInitConstantLink(&prec->inp, DBF_LONG, &prec->val))
|
||||
prec->udf = FALSE;
|
||||
break;
|
||||
case PV_LINK:
|
||||
case DB_LINK:
|
||||
case CA_LINK:
|
||||
break;
|
||||
default:
|
||||
recGblRecordError(S_db_badField, (void *)prec,
|
||||
"devLiSoft (init_record) Illegal INP field");
|
||||
return S_db_badField;
|
||||
}
|
||||
if (recGblInitConstantLink(&prec->inp, DBF_LONG, &prec->val))
|
||||
prec->udf = FALSE;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long readLocked(struct link *pinp, void *dummy)
|
||||
{
|
||||
longinRecord *prec = (longinRecord *) pinp->precord;
|
||||
long status = dbGetLink(pinp, DBR_LONG, &prec->val, 0, 0);
|
||||
|
||||
if (status) return status;
|
||||
|
||||
if (dbLinkIsConstant(&prec->tsel) &&
|
||||
prec->tse == epicsTimeEventDeviceTime)
|
||||
dbGetTimeStamp(pinp, &prec->time);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static long read_longin(longinRecord *prec)
|
||||
{
|
||||
long status;
|
||||
long status = dbLinkDoLocked(&prec->inp, readLocked, NULL);
|
||||
|
||||
if (status == S_db_noLSET)
|
||||
status = readLocked(&prec->inp, NULL);
|
||||
|
||||
status = dbGetLink(&prec->inp, DBR_LONG, &prec->val, 0, 0);
|
||||
if (!status &&
|
||||
prec->tsel.type == CONSTANT &&
|
||||
prec->tse == epicsTimeEventDeviceTime)
|
||||
dbGetTimeStamp(&prec->inp, &prec->time);
|
||||
return status;
|
||||
}
|
||||
|
||||
@@ -3,10 +3,10 @@
|
||||
* National Laboratory.
|
||||
* Copyright (c) 2002 The Regents of the University of California, as
|
||||
* Operator of Los Alamos National Laboratory.
|
||||
* EPICS BASE Versions 3.13.7
|
||||
* and higher are distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
* EPICS BASE is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
\*************************************************************************/
|
||||
|
||||
/* devLoSoftCallback.c */
|
||||
/*
|
||||
* Author: Marty Kraimer
|
||||
@@ -51,17 +51,15 @@ static long write_longout(longoutRecord *prec)
|
||||
struct link *plink = &prec->out;
|
||||
long status;
|
||||
|
||||
if(prec->pact) return(0);
|
||||
if(plink->type!=CA_LINK) {
|
||||
status = dbPutLink(plink,DBR_LONG,&prec->val,1);
|
||||
return(status);
|
||||
}
|
||||
status = dbCaPutLinkCallback(plink,DBR_LONG,&prec->val,1,
|
||||
dbCaCallbackProcess,plink);
|
||||
if(status) {
|
||||
recGblSetSevr(prec,LINK_ALARM,INVALID_ALARM);
|
||||
return(status);
|
||||
}
|
||||
prec->pact = TRUE;
|
||||
return(0);
|
||||
if (prec->pact)
|
||||
return 0;
|
||||
|
||||
status = dbPutLinkAsync(plink, DBR_LONG, &prec->val, 1);
|
||||
if (!status)
|
||||
prec->pact = TRUE;
|
||||
else if (status == S_db_noLSET)
|
||||
status = dbPutLink(plink, DBR_LONG, &prec->val, 1);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
@@ -24,14 +24,26 @@ static long init_record(lsiRecord *prec)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long readLocked(struct link *pinp, void *dummy)
|
||||
{
|
||||
lsiRecord *prec = (lsiRecord *) pinp->precord;
|
||||
long status = dbGetLinkLS(pinp, prec->val, prec->sizv, &prec->len);
|
||||
|
||||
if (status) return status;
|
||||
|
||||
if (dbLinkIsConstant(&prec->tsel) &&
|
||||
prec->tse == epicsTimeEventDeviceTime)
|
||||
dbGetTimeStamp(pinp, &prec->time);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static long read_string(lsiRecord *prec)
|
||||
{
|
||||
long status = dbGetLinkLS(&prec->inp, prec->val, prec->sizv, &prec->len);
|
||||
long status = dbLinkDoLocked(&prec->inp, readLocked, NULL);
|
||||
|
||||
if (!status &&
|
||||
prec->tsel.type == CONSTANT &&
|
||||
prec->tse == epicsTimeEventDeviceTime)
|
||||
dbGetTimeStamp(&prec->inp, &prec->time);
|
||||
if (status == S_db_noLSET)
|
||||
status = readLocked(&prec->inp, NULL);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
@@ -30,21 +30,17 @@ static long write_string(lsoRecord *prec)
|
||||
len = 1;
|
||||
}
|
||||
|
||||
if (plink->type != CA_LINK)
|
||||
return dbPutLink(plink, dtyp, prec->val, len);
|
||||
status = dbPutLinkAsync(plink, dtyp, prec->val, len);
|
||||
if (!status)
|
||||
prec->pact = TRUE;
|
||||
else if (status == S_db_noLSET)
|
||||
status = dbPutLink(plink, dtyp, prec->val, len);
|
||||
|
||||
status = dbCaPutLinkCallback(plink, dtyp, prec->val, len,
|
||||
dbCaCallbackProcess, plink);
|
||||
if (status) {
|
||||
recGblSetSevr(prec, LINK_ALARM, INVALID_ALARM);
|
||||
return status;
|
||||
}
|
||||
|
||||
prec->pact = TRUE;
|
||||
return 0;
|
||||
return status;
|
||||
}
|
||||
|
||||
lsodset devLsoSoftCallback = {
|
||||
5, NULL, NULL, NULL, NULL, write_string
|
||||
};
|
||||
epicsExportAddress(dset, devLsoSoftCallback);
|
||||
|
||||
|
||||
@@ -47,32 +47,33 @@ epicsExportAddress(dset, devMbbiDirectSoft);
|
||||
|
||||
static long init_record(mbbiDirectRecord *prec)
|
||||
{
|
||||
/* INP must be CONSTANT, PV_LINK, DB_LINK or CA_LINK*/
|
||||
switch (prec->inp.type) {
|
||||
case CONSTANT:
|
||||
if (recGblInitConstantLink(&prec->inp, DBF_ENUM, &prec->val))
|
||||
prec->udf = FALSE;
|
||||
break;
|
||||
case PV_LINK:
|
||||
case DB_LINK:
|
||||
case CA_LINK:
|
||||
break;
|
||||
default:
|
||||
recGblRecordError(S_db_badField, (void *)prec,
|
||||
"devMbbiDirectSoft (init_record) Illegal INP field");
|
||||
return S_db_badField;
|
||||
}
|
||||
if (recGblInitConstantLink(&prec->inp, DBF_ENUM, &prec->val))
|
||||
prec->udf = FALSE;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long readLocked(struct link *pinp, void *dummy)
|
||||
{
|
||||
mbbiDirectRecord *prec = (mbbiDirectRecord *) pinp->precord;
|
||||
long status = dbGetLink(pinp, DBR_USHORT, &prec->val, 0, 0);
|
||||
|
||||
if (status) return status;
|
||||
|
||||
prec->udf = FALSE;
|
||||
if (dbLinkIsConstant(&prec->tsel) &&
|
||||
prec->tse == epicsTimeEventDeviceTime)
|
||||
dbGetTimeStamp(pinp, &prec->time);
|
||||
|
||||
return 2;
|
||||
}
|
||||
|
||||
static long read_mbbi(mbbiDirectRecord *prec)
|
||||
{
|
||||
if (!dbGetLink(&prec->inp, DBR_USHORT, &prec->val, 0, 0)) {
|
||||
if (prec->inp.type != CONSTANT)
|
||||
prec->udf = FALSE;
|
||||
if (prec->tsel.type == CONSTANT &&
|
||||
prec->tse == epicsTimeEventDeviceTime)
|
||||
dbGetTimeStamp(&prec->inp, &prec->time);
|
||||
}
|
||||
return 2;
|
||||
long status = dbLinkDoLocked(&prec->inp, readLocked, NULL);
|
||||
|
||||
if (status == S_db_noLSET)
|
||||
status = readLocked(&prec->inp, NULL);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
@@ -47,22 +47,12 @@ epicsExportAddress(dset, devMbbiDirectSoftRaw);
|
||||
|
||||
static long init_record(mbbiDirectRecord *prec)
|
||||
{
|
||||
/* INP must be CONSTANT, PV_LINK, DB_LINK or CA_LINK*/
|
||||
switch (prec->inp.type) {
|
||||
case CONSTANT:
|
||||
recGblInitConstantLink(&prec->inp, DBF_ULONG, &prec->rval);
|
||||
break;
|
||||
case PV_LINK:
|
||||
case DB_LINK:
|
||||
case CA_LINK:
|
||||
break;
|
||||
default:
|
||||
recGblRecordError(S_db_badField, (void *)prec,
|
||||
"devMbbiDirectSoftRaw (init_record) Illegal INP field");
|
||||
return S_db_badField;
|
||||
}
|
||||
/*to preserve old functionality*/
|
||||
if (prec->nobt == 0) prec->mask = 0xffffffff;
|
||||
recGblInitConstantLink(&prec->inp, DBF_ULONG, &prec->rval);
|
||||
|
||||
/* Preserve old functionality */
|
||||
if (prec->nobt == 0)
|
||||
prec->mask = 0xffffffff;
|
||||
|
||||
prec->mask <<= prec->shft;
|
||||
return 0;
|
||||
}
|
||||
@@ -71,7 +61,7 @@ static long read_mbbi(mbbiDirectRecord *prec)
|
||||
{
|
||||
if (!dbGetLink(&prec->inp, DBR_LONG, &prec->rval, 0, 0)) {
|
||||
prec->rval &= prec->mask;
|
||||
if (prec->tsel.type == CONSTANT &&
|
||||
if (dbLinkIsConstant(&prec->tsel) &&
|
||||
prec->tse == epicsTimeEventDeviceTime)
|
||||
dbGetTimeStamp(&prec->inp, &prec->time);
|
||||
}
|
||||
|
||||
@@ -47,32 +47,33 @@ epicsExportAddress(dset, devMbbiSoft);
|
||||
|
||||
static long init_record(mbbiRecord *prec)
|
||||
{
|
||||
/* INP must be CONSTANT, PV_LINK, DB_LINK or CA_LINK*/
|
||||
switch (prec->inp.type) {
|
||||
case CONSTANT:
|
||||
if (recGblInitConstantLink(&prec->inp, DBF_ENUM, &prec->val))
|
||||
prec->udf = FALSE;
|
||||
break;
|
||||
case PV_LINK:
|
||||
case DB_LINK:
|
||||
case CA_LINK:
|
||||
break;
|
||||
default:
|
||||
recGblRecordError(S_db_badField, (void *)prec,
|
||||
"devMbbiSoft (init_record) Illegal INP field");
|
||||
return S_db_badField;
|
||||
}
|
||||
if (recGblInitConstantLink(&prec->inp, DBF_ENUM, &prec->val))
|
||||
prec->udf = FALSE;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long readLocked(struct link *pinp, void *dummy)
|
||||
{
|
||||
mbbiRecord *prec = (mbbiRecord *) pinp->precord;
|
||||
long status = dbGetLink(pinp, DBR_USHORT, &prec->val, 0, 0);
|
||||
|
||||
if (status) return status;
|
||||
|
||||
prec->udf = FALSE;
|
||||
if (dbLinkIsConstant(&prec->tsel) &&
|
||||
prec->tse == epicsTimeEventDeviceTime)
|
||||
dbGetTimeStamp(pinp, &prec->time);
|
||||
|
||||
return 2;
|
||||
}
|
||||
|
||||
static long read_mbbi(mbbiRecord *prec)
|
||||
{
|
||||
if (!dbGetLink(&prec->inp, DBR_USHORT, &prec->val, 0, 0)) {
|
||||
if (prec->inp.type != CONSTANT)
|
||||
prec->udf = FALSE;
|
||||
if (prec->tsel.type == CONSTANT &&
|
||||
prec->tse == epicsTimeEventDeviceTime)
|
||||
dbGetTimeStamp(&prec->inp, &prec->time);
|
||||
}
|
||||
return 2;
|
||||
long status = dbLinkDoLocked(&prec->inp, readLocked, NULL);
|
||||
|
||||
if (status == S_db_noLSET)
|
||||
status = readLocked(&prec->inp, NULL);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
@@ -47,33 +47,39 @@ epicsExportAddress(dset, devMbbiSoftRaw);
|
||||
|
||||
static long init_record(mbbiRecord *prec)
|
||||
{
|
||||
/* INP must be CONSTANT, PV_LINK, DB_LINK or CA_LINK*/
|
||||
switch (prec->inp.type) {
|
||||
case CONSTANT:
|
||||
recGblInitConstantLink(&prec->inp, DBF_ULONG, &prec->rval);
|
||||
break;
|
||||
case PV_LINK:
|
||||
case DB_LINK:
|
||||
case CA_LINK:
|
||||
break;
|
||||
default:
|
||||
recGblRecordError(S_db_badField, (void *)prec,
|
||||
"devMbbiSoftRaw (init_record) Illegal INP field");
|
||||
return S_db_badField;
|
||||
}
|
||||
/*to preserve old functionality*/
|
||||
if (prec->nobt == 0) prec->mask = 0xffffffff;
|
||||
recGblInitConstantLink(&prec->inp, DBF_ULONG, &prec->rval);
|
||||
|
||||
/* Preserve old functionality*/
|
||||
if (prec->nobt == 0)
|
||||
prec->mask = 0xffffffff;
|
||||
|
||||
prec->mask <<= prec->shft;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long readLocked(struct link *pinp, void *dummy)
|
||||
{
|
||||
mbbiRecord *prec = (mbbiRecord *) pinp->precord;
|
||||
long status = dbGetLink(pinp, DBR_LONG, &prec->rval, 0, 0);
|
||||
|
||||
if (status) return status;
|
||||
|
||||
if (dbLinkIsConstant(&prec->tsel) &&
|
||||
prec->tse == epicsTimeEventDeviceTime)
|
||||
dbGetTimeStamp(pinp, &prec->time);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static long read_mbbi(mbbiRecord *prec)
|
||||
{
|
||||
if (!dbGetLink(&prec->inp, DBR_LONG, &prec->rval, 0, 0)) {
|
||||
long status = dbLinkDoLocked(&prec->inp, readLocked, NULL);
|
||||
|
||||
if (status == S_db_noLSET)
|
||||
status = readLocked(&prec->inp, NULL);
|
||||
|
||||
if (!status)
|
||||
prec->rval &= prec->mask;
|
||||
if (prec->tsel.type == CONSTANT &&
|
||||
prec->tse == epicsTimeEventDeviceTime)
|
||||
dbGetTimeStamp(&prec->inp, &prec->time);
|
||||
}
|
||||
return 0;
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
@@ -29,20 +29,13 @@ static long write_mbbo(mbboDirectRecord *prec)
|
||||
if (prec->pact)
|
||||
return 0;
|
||||
|
||||
if (plink->type != CA_LINK) {
|
||||
status = dbPutLinkAsync(plink, DBR_USHORT, &prec->val, 1);
|
||||
if (!status)
|
||||
prec->pact = TRUE;
|
||||
else if (status == S_db_noLSET)
|
||||
status = dbPutLink(plink, DBR_USHORT, &prec->val, 1);
|
||||
return status;
|
||||
}
|
||||
|
||||
status = dbCaPutLinkCallback(plink, DBR_USHORT, &prec->val, 1,
|
||||
dbCaCallbackProcess, plink);
|
||||
if (status) {
|
||||
recGblSetSevr(prec, LINK_ALARM, INVALID_ALARM);
|
||||
return status;
|
||||
}
|
||||
|
||||
prec->pact = TRUE;
|
||||
return 0;
|
||||
return status;
|
||||
}
|
||||
|
||||
/* Create the dset for devMbboSoft */
|
||||
|
||||
@@ -3,10 +3,10 @@
|
||||
* National Laboratory.
|
||||
* Copyright (c) 2002 The Regents of the University of California, as
|
||||
* Operator of Los Alamos National Laboratory.
|
||||
* EPICS BASE Versions 3.13.7
|
||||
* and higher are distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
* EPICS BASE is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
\*************************************************************************/
|
||||
|
||||
/* devMbboSoftCallback.c */
|
||||
/*
|
||||
* Author: Marty Kraimer
|
||||
@@ -50,17 +50,15 @@ static long write_mbbo(mbboRecord *prec)
|
||||
struct link *plink = &prec->out;
|
||||
long status;
|
||||
|
||||
if(prec->pact) return(0);
|
||||
if(plink->type!=CA_LINK) {
|
||||
status = dbPutLink(plink,DBR_USHORT,&prec->val,1);
|
||||
return(status);
|
||||
}
|
||||
status = dbCaPutLinkCallback(plink,DBR_USHORT,&prec->val,1,
|
||||
dbCaCallbackProcess,plink);
|
||||
if(status) {
|
||||
recGblSetSevr(prec,LINK_ALARM,INVALID_ALARM);
|
||||
return(status);
|
||||
}
|
||||
prec->pact = TRUE;
|
||||
return(0);
|
||||
if (prec->pact)
|
||||
return 0;
|
||||
|
||||
status = dbPutLinkAsync(plink, DBR_USHORT, &prec->val, 1);
|
||||
if (!status)
|
||||
prec->pact = TRUE;
|
||||
else if (status == S_db_noLSET)
|
||||
status = dbPutLink(plink, DBR_USHORT, &prec->val, 1);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
@@ -30,18 +30,13 @@ static long write_string(printfRecord *prec)
|
||||
len = 1;
|
||||
}
|
||||
|
||||
if (plink->type != CA_LINK)
|
||||
return dbPutLink(plink, dtyp, prec->val, len);
|
||||
status = dbPutLinkAsync(plink, dtyp, prec->val, len);
|
||||
if (!status)
|
||||
prec->pact = TRUE;
|
||||
else if (status == S_db_noLSET)
|
||||
status = dbPutLink(plink, dtyp, prec->val, len);
|
||||
|
||||
status = dbCaPutLinkCallback(plink, dtyp, prec->val, len,
|
||||
dbCaCallbackProcess, plink);
|
||||
if (status) {
|
||||
recGblSetSevr(prec, LINK_ALARM, INVALID_ALARM);
|
||||
return status;
|
||||
}
|
||||
|
||||
prec->pact = TRUE;
|
||||
return 0;
|
||||
return status;
|
||||
}
|
||||
|
||||
printfdset devPrintfSoftCallback = {
|
||||
|
||||
@@ -45,55 +45,86 @@ struct {
|
||||
};
|
||||
epicsExportAddress(dset, devSASoft);
|
||||
|
||||
static long init_record(subArrayRecord *prec)
|
||||
static void subset(subArrayRecord *prec, long nRequest)
|
||||
{
|
||||
/* INP must be CONSTANT, PV_LINK, DB_LINK or CA_LINK*/
|
||||
switch (prec->inp.type) {
|
||||
case CONSTANT:
|
||||
prec->nord = 0;
|
||||
break;
|
||||
case PV_LINK:
|
||||
case DB_LINK:
|
||||
case CA_LINK:
|
||||
break;
|
||||
default:
|
||||
recGblRecordError(S_db_badField, (void *)prec,
|
||||
"devSASoft (init_record) Illegal INP field");
|
||||
return S_db_badField;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
long ecount = nRequest - prec->indx;
|
||||
|
||||
static long read_sa(subArrayRecord *prec)
|
||||
{
|
||||
long nRequest = prec->indx + prec->nelm;
|
||||
long ecount;
|
||||
|
||||
if (nRequest > prec->malm)
|
||||
nRequest = prec->malm;
|
||||
|
||||
if (prec->inp.type == CONSTANT)
|
||||
nRequest = prec->nord;
|
||||
else
|
||||
dbGetLink(&prec->inp, prec->ftvl, prec->bptr, 0, &nRequest);
|
||||
|
||||
ecount = nRequest - prec->indx;
|
||||
if (ecount > 0) {
|
||||
int esize = dbValueSize(prec->ftvl);
|
||||
|
||||
if (ecount > prec->nelm)
|
||||
ecount = prec->nelm;
|
||||
|
||||
memmove(prec->bptr, (char *)prec->bptr + prec->indx * esize,
|
||||
ecount * esize);
|
||||
} else
|
||||
ecount = 0;
|
||||
|
||||
prec->nord = ecount;
|
||||
|
||||
if (nRequest > 0 &&
|
||||
prec->tsel.type == CONSTANT &&
|
||||
prec->tse == epicsTimeEventDeviceTime)
|
||||
dbGetTimeStamp(&prec->inp, &prec->time);
|
||||
|
||||
return 0;
|
||||
prec->udf = FALSE;
|
||||
}
|
||||
|
||||
static long init_record(subArrayRecord *prec)
|
||||
{
|
||||
long nRequest = prec->indx + prec->nelm;
|
||||
long status;
|
||||
|
||||
if (nRequest > prec->malm)
|
||||
nRequest = prec->malm;
|
||||
|
||||
status = dbLoadLinkArray(&prec->inp, prec->ftvl, prec->bptr, &nRequest);
|
||||
|
||||
if (!status && nRequest > 0)
|
||||
subset(prec, nRequest);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
struct sart {
|
||||
long nRequest;
|
||||
epicsTimeStamp *ptime;
|
||||
};
|
||||
|
||||
static long readLocked(struct link *pinp, void *vrt)
|
||||
{
|
||||
subArrayRecord *prec = (subArrayRecord *) pinp->precord;
|
||||
struct sart *prt = (struct sart *) vrt;
|
||||
long status = dbGetLink(pinp, prec->ftvl, prec->bptr, 0, &prt->nRequest);
|
||||
|
||||
if (!status && prt->ptime)
|
||||
dbGetTimeStamp(pinp, prt->ptime);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static long read_sa(subArrayRecord *prec)
|
||||
{
|
||||
long status;
|
||||
struct sart rt;
|
||||
|
||||
rt.nRequest = prec->indx + prec->nelm;
|
||||
if (rt.nRequest > prec->malm)
|
||||
rt.nRequest = prec->malm;
|
||||
|
||||
rt.ptime = (dbLinkIsConstant(&prec->tsel) &&
|
||||
prec->tse == epicsTimeEventDeviceTime) ? &prec->time : NULL;
|
||||
|
||||
if (dbLinkIsConstant(&prec->inp)) {
|
||||
status = dbLoadLinkArray(&prec->inp, prec->ftvl, prec->bptr, &rt.nRequest);
|
||||
if (status == S_db_badField) { /* INP was empty */
|
||||
rt.nRequest = prec->nord;
|
||||
status = 0;
|
||||
}
|
||||
}
|
||||
else {
|
||||
status = dbLinkDoLocked(&prec->inp, readLocked, &rt);
|
||||
|
||||
if (status == S_db_noLSET)
|
||||
status = readLocked(&prec->inp, &rt);
|
||||
}
|
||||
|
||||
if (!status && rt.nRequest > 0)
|
||||
subset(prec, rt.nRequest);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
@@ -49,35 +49,35 @@ epicsExportAddress(dset, devSiSoft);
|
||||
|
||||
static long init_record(stringinRecord *prec)
|
||||
{
|
||||
/* INP must be CONSTANT, PV_LINK, DB_LINK or CA_LINK*/
|
||||
switch (prec->inp.type) {
|
||||
case CONSTANT:
|
||||
if (recGblInitConstantLink(&prec->inp, DBF_STRING, prec->val))
|
||||
prec->udf = FALSE;
|
||||
break;
|
||||
case PV_LINK:
|
||||
case DB_LINK:
|
||||
case CA_LINK:
|
||||
break;
|
||||
default:
|
||||
recGblRecordError(S_db_badField, (void *)prec,
|
||||
"devSiSoft (init_record) Illegal INP field");
|
||||
return S_db_badField;
|
||||
}
|
||||
if (recGblInitConstantLink(&prec->inp, DBF_STRING, prec->val))
|
||||
prec->udf = FALSE;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long readLocked(struct link *pinp, void *dummy)
|
||||
{
|
||||
stringinRecord *prec = (stringinRecord *) pinp->precord;
|
||||
long status = dbGetLink(pinp, DBR_STRING, prec->val, 0, 0);
|
||||
|
||||
if (status) return status;
|
||||
|
||||
if (dbLinkIsConstant(&prec->tsel) &&
|
||||
prec->tse == epicsTimeEventDeviceTime)
|
||||
dbGetTimeStamp(pinp, &prec->time);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static long read_stringin(stringinRecord *prec)
|
||||
{
|
||||
long status;
|
||||
long status = dbLinkDoLocked(&prec->inp, readLocked, NULL);
|
||||
|
||||
if (status == S_db_noLSET)
|
||||
status = readLocked(&prec->inp, NULL);
|
||||
|
||||
if (!status && !dbLinkIsConstant(&prec->inp))
|
||||
prec->udf = FALSE;
|
||||
|
||||
status = dbGetLink(&prec->inp, DBR_STRING, prec->val, 0, 0);
|
||||
if (!status) {
|
||||
if (prec->inp.type != CONSTANT)
|
||||
prec->udf = FALSE;
|
||||
if (prec->tsel.type == CONSTANT &&
|
||||
prec->tse == epicsTimeEventDeviceTime)
|
||||
dbGetTimeStamp(&prec->inp, &prec->time);
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
@@ -3,10 +3,10 @@
|
||||
* National Laboratory.
|
||||
* Copyright (c) 2002 The Regents of the University of California, as
|
||||
* Operator of Los Alamos National Laboratory.
|
||||
* EPICS BASE Versions 3.13.7
|
||||
* and higher are distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
* EPICS BASE is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
\*************************************************************************/
|
||||
|
||||
/*
|
||||
* Author: Marty Kraimer
|
||||
* Date: 04NOV2003
|
||||
@@ -49,19 +49,15 @@ static long write_stringout(stringoutRecord *prec)
|
||||
struct link *plink = &prec->out;
|
||||
long status;
|
||||
|
||||
if (prec->pact) return 0;
|
||||
if (prec->pact)
|
||||
return 0;
|
||||
|
||||
if (plink->type != CA_LINK) {
|
||||
return dbPutLink(plink, DBR_STRING, &prec->val, 1);
|
||||
}
|
||||
status = dbPutLinkAsync(plink, DBR_STRING, &prec->val, 1);
|
||||
if (!status)
|
||||
prec->pact = TRUE;
|
||||
else if (status == S_db_noLSET)
|
||||
status = dbPutLink(plink, DBR_STRING, &prec->val, 1);
|
||||
|
||||
status = dbCaPutLinkCallback(plink, DBR_STRING, &prec->val, 1,
|
||||
dbCaCallbackProcess, plink);
|
||||
if (status) {
|
||||
recGblSetSevr(prec, LINK_ALARM, INVALID_ALARM);
|
||||
return status;
|
||||
}
|
||||
|
||||
prec->pact = TRUE;
|
||||
return 0;
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
@@ -48,33 +48,52 @@ epicsExportAddress(dset, devWfSoft);
|
||||
|
||||
static long init_record(waveformRecord *prec)
|
||||
{
|
||||
/* INP must be CONSTANT, PV_LINK, DB_LINK or CA_LINK*/
|
||||
switch (prec->inp.type) {
|
||||
case CONSTANT:
|
||||
prec->nord = 0;
|
||||
break;
|
||||
case PV_LINK:
|
||||
case DB_LINK:
|
||||
case CA_LINK:
|
||||
break;
|
||||
default:
|
||||
recGblRecordError(S_db_badField, (void *)prec,
|
||||
"devWfSoft (init_record) Illegal INP field");
|
||||
return(S_db_badField);
|
||||
long nelm = prec->nelm;
|
||||
long status = dbLoadLinkArray(&prec->inp, prec->ftvl, prec->bptr, &nelm);
|
||||
|
||||
if (!status && nelm > 0) {
|
||||
prec->nord = nelm;
|
||||
prec->udf = FALSE;
|
||||
}
|
||||
return 0;
|
||||
else
|
||||
prec->nord = 0;
|
||||
return status;
|
||||
}
|
||||
|
||||
struct wfrt {
|
||||
long nRequest;
|
||||
epicsTimeStamp *ptime;
|
||||
};
|
||||
|
||||
static long readLocked(struct link *pinp, void *vrt)
|
||||
{
|
||||
waveformRecord *prec = (waveformRecord *) pinp->precord;
|
||||
struct wfrt *prt = (struct wfrt *) vrt;
|
||||
long status = dbGetLink(pinp, prec->ftvl, prec->bptr, 0, &prt->nRequest);
|
||||
|
||||
if (!status && prt->ptime)
|
||||
dbGetTimeStamp(pinp, prt->ptime);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static long read_wf(waveformRecord *prec)
|
||||
{
|
||||
long nRequest = prec->nelm;
|
||||
long status;
|
||||
struct wfrt rt;
|
||||
|
||||
dbGetLink(&prec->inp, prec->ftvl, prec->bptr, 0, &nRequest);
|
||||
if (nRequest > 0) {
|
||||
prec->nord = nRequest;
|
||||
if (prec->tsel.type == CONSTANT &&
|
||||
prec->tse == epicsTimeEventDeviceTime)
|
||||
dbGetTimeStamp(&prec->inp, &prec->time);
|
||||
rt.nRequest = prec->nelm;
|
||||
rt.ptime = (dbLinkIsConstant(&prec->tsel) &&
|
||||
prec->tse == epicsTimeEventDeviceTime) ? &prec->time : NULL;
|
||||
|
||||
status = dbLinkDoLocked(&prec->inp, readLocked, &rt);
|
||||
if (status == S_db_noLSET)
|
||||
status = readLocked(&prec->inp, &rt);
|
||||
|
||||
if (!status && rt.nRequest > 0) {
|
||||
prec->nord = rt.nRequest;
|
||||
prec->udf = FALSE;
|
||||
}
|
||||
return 0;
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
18
src/std/link/Makefile
Normal file
18
src/std/link/Makefile
Normal file
@@ -0,0 +1,18 @@
|
||||
#*************************************************************************
|
||||
# Copyright (c) 2016 UChicago Argonne LLC, as Operator of Argonne
|
||||
# National Laboratory.
|
||||
# EPICS BASE is distributed subject to a Software License Agreement found
|
||||
# in file LICENSE that is included with this distribution.
|
||||
#*************************************************************************
|
||||
|
||||
# This is a Makefile fragment, see src/std/Makefile.
|
||||
|
||||
SRC_DIRS += $(STDDIR)/link
|
||||
|
||||
DBD += links.dbd
|
||||
|
||||
dbRecStd_SRCS += lnkConst.c
|
||||
dbRecStd_SRCS += lnkCalc.c
|
||||
|
||||
HTMLS += links.html
|
||||
|
||||
123
src/std/link/links.dbd.pod
Normal file
123
src/std/link/links.dbd.pod
Normal file
@@ -0,0 +1,123 @@
|
||||
=head1 Extensible Links
|
||||
|
||||
The extensible link mechanism allows new kinds of record links to be created,
|
||||
using JSON for the link address syntax.
|
||||
The IOC continues to support the older link types that do not use JSON to
|
||||
specify their link addresses.
|
||||
|
||||
The following additional link types are available in this release:
|
||||
|
||||
=over
|
||||
|
||||
=item * L<Constant|/"Constant Link const">
|
||||
|
||||
=item * L<Calc|/"Calculation Link calc">
|
||||
|
||||
=back
|
||||
|
||||
=head2 Using JSON Links
|
||||
|
||||
When setting a record link field to a JSON link address, the link specification
|
||||
must appear inside a pair of braces C< {} > expressed as a JSON (L<JavaScript
|
||||
Object Notation|http://www.json.org/>) object, which allows link parameters to
|
||||
be defined as needed by the particular link type. When link fields are set from
|
||||
an IOC database file at initialization time, the field definitions may take
|
||||
advantage of a "relaxed JSON" syntax that reduces the number of double-quote
|
||||
characters required and maintains backwards compatibility with the older
|
||||
database file syntax.
|
||||
|
||||
|
||||
=head2 Link Type Reference
|
||||
|
||||
=cut
|
||||
|
||||
link(const, lnkConstIf)
|
||||
|
||||
=head3 Constant Link C<"const">
|
||||
|
||||
Constant links provide one or more values at link initalization time, but do not
|
||||
return any data when their C<getValue()> routine is called. Most record types
|
||||
support the use of constant links by calling C<recGblInitConstantLink()> at
|
||||
record initialization, which results in the constant value being loaded into the
|
||||
target field at that time.
|
||||
|
||||
Note that for most record types (the C<printf> and C<calcout> records are the
|
||||
main exceptions) it is pointless to set an input link to a constant link at
|
||||
runtime since the link initialization that loads the field value usually only
|
||||
happens when a record is initialized. A constant link that is embedded inside
|
||||
another input link type such as a calculation link should be OK though since the
|
||||
link initialization will take place when the record's field gets set.
|
||||
|
||||
=head4 Parameters
|
||||
|
||||
A const link takes a parameter which may be an integer, double or string, or an
|
||||
array of those types. If an array contains both integers and double values the
|
||||
integers will be promoted to doubles. Mixing strings and numbers in an array
|
||||
results in an error.
|
||||
|
||||
=head4 Examples
|
||||
|
||||
{const: 3.14159265358979}
|
||||
{const: "Pi"}
|
||||
{const: [1, 2.718281828459, 3.14159265358979]}
|
||||
{const: ["One", "e", "Pi"]}
|
||||
|
||||
The JSON syntax does not support Infinity or NaN values when parsing numbers,
|
||||
but (for scalars) it is possible to provide these in a string which will be
|
||||
converted to the desired double value at initialization, for example:
|
||||
|
||||
field(INP, {const:"Inf"})
|
||||
|
||||
=cut
|
||||
|
||||
link(calc, lnkCalcIf)
|
||||
|
||||
=head3 Calculation Link C<"calc">
|
||||
|
||||
Calculation links can perform simple mathematical expressions on scalar
|
||||
(double-precision floating-point) values obtained from other link types and
|
||||
return a single double-precision floating-point result. The expressions are
|
||||
evaluated by the EPICS Calc engine, and up to 12 inputs can be provided.
|
||||
|
||||
=head4 Parameters
|
||||
|
||||
The link address is a JSON map with the following keys:
|
||||
|
||||
=over
|
||||
|
||||
=item expr
|
||||
|
||||
The primary expression to be evaluated, given as a string.
|
||||
|
||||
=item major
|
||||
|
||||
An optional expression that returns non-zero to raise a major alarm.
|
||||
|
||||
=item minor
|
||||
|
||||
An optional expression that returns non-zero to raise a minor alarm.
|
||||
|
||||
=item args
|
||||
|
||||
A JSON list of up to 12 input arguments for the expression, which are assigned
|
||||
to the inputs C<A>, C<B>, C<C>, ... C<L>. Each input argument may be either a
|
||||
numeric literal or an embedded JSON link inside C<{}> braces. The same input
|
||||
values are provided to the two alarm expressions as to the primary expression.
|
||||
|
||||
=item units
|
||||
|
||||
An optional string specifying the engineering units for the result of the
|
||||
expression. Equivalent to the C<EGU> field of a record.
|
||||
|
||||
=item prec
|
||||
|
||||
An optional integer specifying the numeric precision with which the calculation
|
||||
result should be displayed. Equivalent to the C<PREC> field of a record.
|
||||
|
||||
=back
|
||||
|
||||
=head4 Example
|
||||
|
||||
{calc: {expr:"A*B", args:[{db:"record.VAL"}, 1.5], prec:3}}
|
||||
|
||||
=cut
|
||||
642
src/std/link/lnkCalc.c
Normal file
642
src/std/link/lnkCalc.c
Normal file
@@ -0,0 +1,642 @@
|
||||
/*************************************************************************\
|
||||
* Copyright (c) 2016 UChicago Argonne LLC, as Operator of Argonne
|
||||
* National Laboratory.
|
||||
* EPICS BASE is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
\*************************************************************************/
|
||||
/* lnkCalc.c */
|
||||
|
||||
/* Current usage
|
||||
* {calc:{expr:"A", args:[{...}, ...]}}
|
||||
* First link in 'args' is 'A', second is 'B', and so forth.
|
||||
*
|
||||
* TODO:
|
||||
* Support setting individual input links instead of the args list.
|
||||
* {calc:{expr:"K", K:{...}}}
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "alarm.h"
|
||||
#include "dbDefs.h"
|
||||
#include "errlog.h"
|
||||
#include "epicsAssert.h"
|
||||
#include "epicsString.h"
|
||||
#include "epicsTypes.h"
|
||||
#include "dbAccessDefs.h"
|
||||
#include "dbConvertFast.h"
|
||||
#include "dbLink.h"
|
||||
#include "dbJLink.h"
|
||||
#include "dbStaticLib.h"
|
||||
#include "dbStaticPvt.h"
|
||||
#include "postfix.h"
|
||||
#include "recGbl.h"
|
||||
#include "epicsExport.h"
|
||||
|
||||
|
||||
typedef long (*FASTCONVERT)();
|
||||
|
||||
#define IFDEBUG(n) if(clink->jlink.debug)
|
||||
|
||||
typedef struct calc_link {
|
||||
jlink jlink; /* embedded object */
|
||||
int nArgs;
|
||||
enum {
|
||||
ps_init,
|
||||
ps_expr, ps_major, ps_minor,
|
||||
ps_args,
|
||||
ps_prec,
|
||||
ps_units,
|
||||
ps_error
|
||||
} pstate;
|
||||
epicsEnum16 stat;
|
||||
epicsEnum16 sevr;
|
||||
short prec;
|
||||
char *expr;
|
||||
char *major;
|
||||
char *minor;
|
||||
char *post_expr;
|
||||
char *post_major;
|
||||
char *post_minor;
|
||||
char *units;
|
||||
struct link inp[CALCPERFORM_NARGS];
|
||||
double arg[CALCPERFORM_NARGS];
|
||||
double val;
|
||||
} calc_link;
|
||||
|
||||
static lset lnkCalc_lset;
|
||||
|
||||
|
||||
/*************************** jlif Routines **************************/
|
||||
|
||||
static jlink* lnkCalc_alloc(short dbfType)
|
||||
{
|
||||
calc_link *clink = calloc(1, sizeof(struct calc_link));
|
||||
|
||||
IFDEBUG(10)
|
||||
printf("lnkCalc_alloc()\n");
|
||||
|
||||
clink->nArgs = 0;
|
||||
clink->pstate = ps_init;
|
||||
clink->prec = 15; /* standard value for a double */
|
||||
|
||||
IFDEBUG(10)
|
||||
printf("lnkCalc_alloc -> calc@%p\n", clink);
|
||||
|
||||
return &clink->jlink;
|
||||
}
|
||||
|
||||
static void lnkCalc_free(jlink *pjlink)
|
||||
{
|
||||
calc_link *clink = CONTAINER(pjlink, struct calc_link, jlink);
|
||||
int i;
|
||||
|
||||
IFDEBUG(10)
|
||||
printf("lnkCalc_free(calc@%p)\n", clink);
|
||||
|
||||
for (i = 0; i < clink->nArgs; i++)
|
||||
dbJLinkFree(clink->inp[i].value.json.jlink);
|
||||
|
||||
free(clink->expr);
|
||||
free(clink->major);
|
||||
free(clink->minor);
|
||||
free(clink->post_expr);
|
||||
free(clink->post_major);
|
||||
free(clink->post_minor);
|
||||
free(clink->units);
|
||||
free(clink);
|
||||
}
|
||||
|
||||
static jlif_result lnkCalc_integer(jlink *pjlink, long long num)
|
||||
{
|
||||
calc_link *clink = CONTAINER(pjlink, struct calc_link, jlink);
|
||||
|
||||
IFDEBUG(10)
|
||||
printf("lnkCalc_integer(calc@%p, %lld)\n", clink, num);
|
||||
|
||||
if (clink->pstate == ps_prec) {
|
||||
clink->prec = num;
|
||||
return jlif_continue;
|
||||
}
|
||||
|
||||
if (clink->pstate != ps_args) {
|
||||
return jlif_stop;
|
||||
errlogPrintf("lnkCalc: Unexpected integer %lld\n", num);
|
||||
}
|
||||
|
||||
if (clink->nArgs == CALCPERFORM_NARGS) {
|
||||
errlogPrintf("lnkCalc: Too many input args, limit is %d\n",
|
||||
CALCPERFORM_NARGS);
|
||||
return jlif_stop;
|
||||
}
|
||||
|
||||
clink->arg[clink->nArgs++] = num;
|
||||
|
||||
return jlif_continue;
|
||||
}
|
||||
|
||||
static jlif_result lnkCalc_double(jlink *pjlink, double num)
|
||||
{
|
||||
calc_link *clink = CONTAINER(pjlink, struct calc_link, jlink);
|
||||
|
||||
IFDEBUG(10)
|
||||
printf("lnkCalc_double(calc@%p, %g)\n", clink, num);
|
||||
|
||||
if (clink->pstate != ps_args) {
|
||||
return jlif_stop;
|
||||
errlogPrintf("lnkCalc: Unexpected double %g\n", num);
|
||||
}
|
||||
|
||||
if (clink->nArgs == CALCPERFORM_NARGS) {
|
||||
errlogPrintf("lnkCalc: Too many input args, limit is %d\n",
|
||||
CALCPERFORM_NARGS);
|
||||
return jlif_stop;
|
||||
}
|
||||
|
||||
clink->arg[clink->nArgs++] = num;
|
||||
|
||||
return jlif_continue;
|
||||
}
|
||||
|
||||
static jlif_result lnkCalc_string(jlink *pjlink, const char *val, size_t len)
|
||||
{
|
||||
calc_link *clink = CONTAINER(pjlink, struct calc_link, jlink);
|
||||
char *inbuf, *postbuf;
|
||||
short err;
|
||||
|
||||
IFDEBUG(10)
|
||||
printf("lnkCalc_string(calc@%p, \"%.*s\")\n", clink, (int) len, val);
|
||||
|
||||
if (clink->pstate == ps_units) {
|
||||
clink->units = epicsStrnDup(val, len);
|
||||
return jlif_continue;
|
||||
}
|
||||
|
||||
if (clink->pstate < ps_expr || clink->pstate > ps_minor) {
|
||||
errlogPrintf("lnkCalc: Unexpected string \"%.*s\"\n", (int) len, val);
|
||||
return jlif_stop;
|
||||
}
|
||||
|
||||
postbuf = malloc(INFIX_TO_POSTFIX_SIZE(len+1));
|
||||
if (!postbuf) {
|
||||
errlogPrintf("lnkCalc: Out of memory\n");
|
||||
return jlif_stop;
|
||||
}
|
||||
|
||||
inbuf = malloc(len+1);
|
||||
if(!inbuf) {
|
||||
errlogPrintf("lnkCalc: Out of memory\n");
|
||||
return jlif_stop;
|
||||
}
|
||||
memcpy(inbuf, val, len);
|
||||
inbuf[len] = '\0';
|
||||
|
||||
if (clink->pstate == ps_major) {
|
||||
clink->major = inbuf;
|
||||
clink->post_major = postbuf;
|
||||
}
|
||||
else if (clink->pstate == ps_minor) {
|
||||
clink->minor = inbuf;
|
||||
clink->post_minor = postbuf;
|
||||
}
|
||||
else {
|
||||
clink->expr = inbuf;
|
||||
clink->post_expr = postbuf;
|
||||
}
|
||||
|
||||
if (postfix(inbuf, postbuf, &err) < 0) {
|
||||
errlogPrintf("lnkCalc: Error in calc expression, %s\n",
|
||||
calcErrorStr(err));
|
||||
return jlif_stop;
|
||||
}
|
||||
|
||||
return jlif_continue;
|
||||
}
|
||||
|
||||
static jlif_key_result lnkCalc_start_map(jlink *pjlink)
|
||||
{
|
||||
calc_link *clink = CONTAINER(pjlink, struct calc_link, jlink);
|
||||
|
||||
IFDEBUG(10)
|
||||
printf("lnkCalc_start_map(calc@%p)\n", clink);
|
||||
|
||||
if (clink->pstate == ps_args)
|
||||
return jlif_key_child_link;
|
||||
|
||||
if (clink->pstate != ps_init) {
|
||||
errlogPrintf("lnkCalc: Unexpected map\n");
|
||||
return jlif_key_stop;
|
||||
}
|
||||
|
||||
return jlif_key_continue;
|
||||
}
|
||||
|
||||
static jlif_result lnkCalc_map_key(jlink *pjlink, const char *key, size_t len)
|
||||
{
|
||||
calc_link *clink = CONTAINER(pjlink, struct calc_link, jlink);
|
||||
|
||||
IFDEBUG(10)
|
||||
printf("lnkCalc_map_key(calc@%p, \"%.*s\")\n", pjlink, (int) len, key);
|
||||
|
||||
if (len == 4) {
|
||||
if (!strncmp(key, "expr", len) && !clink->post_expr)
|
||||
clink->pstate = ps_expr;
|
||||
else if (!strncmp(key, "args", len) && !clink->nArgs)
|
||||
clink->pstate = ps_args;
|
||||
else if (!strncmp(key, "prec", len))
|
||||
clink->pstate = ps_prec;
|
||||
else {
|
||||
errlogPrintf("lnkCalc: Unknown key \"%.4s\"\n", key);
|
||||
return jlif_stop;
|
||||
}
|
||||
}
|
||||
else if (len == 5) {
|
||||
if (!strncmp(key, "major", len) && !clink->post_major)
|
||||
clink->pstate = ps_major;
|
||||
else if (!strncmp(key, "minor", len) && !clink->post_minor)
|
||||
clink->pstate = ps_minor;
|
||||
else if (!strncmp(key, "units", len) && !clink->units)
|
||||
clink->pstate = ps_units;
|
||||
else {
|
||||
errlogPrintf("lnkCalc: Unknown key \"%.5s\"\n", key);
|
||||
return jlif_stop;
|
||||
}
|
||||
}
|
||||
else {
|
||||
errlogPrintf("lnkCalc: Unknown key \"%.*s\"\n", (int) len, key);
|
||||
return jlif_stop;
|
||||
}
|
||||
|
||||
return jlif_continue;
|
||||
}
|
||||
|
||||
static jlif_result lnkCalc_end_map(jlink *pjlink)
|
||||
{
|
||||
calc_link *clink = CONTAINER(pjlink, struct calc_link, jlink);
|
||||
|
||||
IFDEBUG(10)
|
||||
printf("lnkCalc_end_map(calc@%p)\n", clink);
|
||||
|
||||
if (clink->pstate == ps_error)
|
||||
return jlif_stop;
|
||||
else if (!clink->post_expr) {
|
||||
errlogPrintf("lnkCalc: no expression ('expr' key)\n");
|
||||
return jlif_stop;
|
||||
}
|
||||
|
||||
return jlif_continue;
|
||||
}
|
||||
|
||||
static jlif_result lnkCalc_start_array(jlink *pjlink)
|
||||
{
|
||||
calc_link *clink = CONTAINER(pjlink, struct calc_link, jlink);
|
||||
|
||||
IFDEBUG(10)
|
||||
printf("lnkCalc_start_array(calc@%p)\n", clink);
|
||||
|
||||
if (clink->pstate != ps_args) {
|
||||
errlogPrintf("lnkCalc: Unexpected array\n");
|
||||
return jlif_stop;
|
||||
}
|
||||
|
||||
return jlif_continue;
|
||||
}
|
||||
|
||||
static jlif_result lnkCalc_end_array(jlink *pjlink)
|
||||
{
|
||||
calc_link *clink = CONTAINER(pjlink, struct calc_link, jlink);
|
||||
|
||||
IFDEBUG(10)
|
||||
printf("lnkCalc_end_array(calc@%p)\n", clink);
|
||||
|
||||
if (clink->pstate == ps_error)
|
||||
return jlif_stop;
|
||||
|
||||
return jlif_continue;
|
||||
}
|
||||
|
||||
static void lnkCalc_end_child(jlink *parent, jlink *child)
|
||||
{
|
||||
calc_link *clink = CONTAINER(parent, struct calc_link, jlink);
|
||||
struct link *plink;
|
||||
|
||||
if (clink->nArgs == CALCPERFORM_NARGS) {
|
||||
dbJLinkFree(child);
|
||||
errlogPrintf("lnkCalc: Too many input args, limit is %d\n",
|
||||
CALCPERFORM_NARGS);
|
||||
clink->pstate = ps_error;
|
||||
return;
|
||||
}
|
||||
|
||||
plink = &clink->inp[clink->nArgs++];
|
||||
plink->type = JSON_LINK;
|
||||
plink->value.json.string = NULL;
|
||||
plink->value.json.jlink = child;
|
||||
}
|
||||
|
||||
static struct lset* lnkCalc_get_lset(const jlink *pjlink)
|
||||
{
|
||||
calc_link *clink = CONTAINER(pjlink, struct calc_link, jlink);
|
||||
|
||||
IFDEBUG(10)
|
||||
printf("lnkCalc_get_lset(calc@%p)\n", pjlink);
|
||||
|
||||
return &lnkCalc_lset;
|
||||
}
|
||||
|
||||
static void lnkCalc_report(const jlink *pjlink, int level, int indent)
|
||||
{
|
||||
calc_link *clink = CONTAINER(pjlink, struct calc_link, jlink);
|
||||
int i;
|
||||
|
||||
IFDEBUG(10)
|
||||
printf("lnkCalc_report(calc@%p)\n", clink);
|
||||
|
||||
printf("%*s'calc': \"%s\" = %.*g %s\n", indent, "",
|
||||
clink->expr, clink->prec, clink->val,
|
||||
clink->units ? clink->units : "");
|
||||
|
||||
if (level > 0) {
|
||||
if (clink->sevr)
|
||||
printf("%*s Alarm: %s, %s\n", indent, "",
|
||||
epicsAlarmSeverityStrings[clink->sevr],
|
||||
epicsAlarmConditionStrings[clink->stat]);
|
||||
|
||||
if (clink->post_major)
|
||||
printf("%*s Major expression: \"%s\"\n", indent, "",
|
||||
clink->major);
|
||||
if (clink->post_minor)
|
||||
printf("%*s Minor expression: \"%s\"\n", indent, "",
|
||||
clink->minor);
|
||||
|
||||
for (i = 0; i < clink->nArgs; i++) {
|
||||
struct link *plink = &clink->inp[i];
|
||||
jlink *child = plink->type == JSON_LINK ?
|
||||
plink->value.json.jlink : NULL;
|
||||
|
||||
printf("%*s Input %c: %g\n", indent, "",
|
||||
i + 'A', clink->arg[i]);
|
||||
|
||||
if (child)
|
||||
dbJLinkReport(child, level - 1, indent + 4);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
long lnkCalc_map_children(jlink *pjlink, jlink_map_fn rtn, void *ctx)
|
||||
{
|
||||
calc_link *clink = CONTAINER(pjlink, struct calc_link, jlink);
|
||||
int i;
|
||||
|
||||
IFDEBUG(10)
|
||||
printf("lnkCalc_map_children(calc@%p)\n", clink);
|
||||
|
||||
for (i = 0; i < clink->nArgs; i++) {
|
||||
struct link *child = &clink->inp[i];
|
||||
long status = dbJLinkMapChildren(child, rtn, ctx);
|
||||
|
||||
if (status)
|
||||
return status;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*************************** lset Routines **************************/
|
||||
|
||||
static void lnkCalc_open(struct link *plink)
|
||||
{
|
||||
calc_link *clink = CONTAINER(plink->value.json.jlink,
|
||||
struct calc_link, jlink);
|
||||
int i;
|
||||
|
||||
IFDEBUG(10)
|
||||
printf("lnkCalc_open(calc@%p)\n", clink);
|
||||
|
||||
for (i = 0; i < clink->nArgs; i++) {
|
||||
struct link *child = &clink->inp[i];
|
||||
|
||||
child->precord = plink->precord;
|
||||
dbJLinkInit(child);
|
||||
dbLoadLink(child, DBR_DOUBLE, &clink->arg[i]);
|
||||
}
|
||||
}
|
||||
|
||||
static void lnkCalc_remove(struct dbLocker *locker, struct link *plink)
|
||||
{
|
||||
calc_link *clink = CONTAINER(plink->value.json.jlink,
|
||||
struct calc_link, jlink);
|
||||
int i;
|
||||
|
||||
IFDEBUG(10)
|
||||
printf("lnkCalc_remove(calc@%p)\n", clink);
|
||||
|
||||
for (i = 0; i < clink->nArgs; i++) {
|
||||
struct link *child = &clink->inp[i];
|
||||
|
||||
dbRemoveLink(locker, child);
|
||||
}
|
||||
|
||||
free(clink->expr);
|
||||
free(clink->major);
|
||||
free(clink->minor);
|
||||
free(clink->post_expr);
|
||||
free(clink->post_major);
|
||||
free(clink->post_minor);
|
||||
free(clink->units);
|
||||
free(clink);
|
||||
plink->value.json.jlink = NULL;
|
||||
}
|
||||
|
||||
static int lnkCalc_isConn(const struct link *plink)
|
||||
{
|
||||
calc_link *clink = CONTAINER(plink->value.json.jlink,
|
||||
struct calc_link, jlink);
|
||||
int connected = 1;
|
||||
int i;
|
||||
|
||||
IFDEBUG(10)
|
||||
printf("lnkCalc_isConn(calc@%p)\n", clink);
|
||||
|
||||
for (i = 0; i < clink->nArgs; i++) {
|
||||
struct link *child = &clink->inp[i];
|
||||
|
||||
if (dbLinkIsVolatile(child) &&
|
||||
!dbIsLinkConnected(child))
|
||||
connected = 0;
|
||||
}
|
||||
|
||||
return connected;
|
||||
}
|
||||
|
||||
static int lnkCalc_getDBFtype(const struct link *plink)
|
||||
{
|
||||
calc_link *clink = CONTAINER(plink->value.json.jlink,
|
||||
struct calc_link, jlink);
|
||||
|
||||
IFDEBUG(10) {
|
||||
calc_link *clink = CONTAINER(plink->value.json.jlink,
|
||||
struct calc_link, jlink);
|
||||
|
||||
printf("lnkCalc_getDBFtype(calc@%p)\n", clink);
|
||||
}
|
||||
|
||||
return DBF_DOUBLE;
|
||||
}
|
||||
|
||||
static long lnkCalc_getElements(const struct link *plink, long *nelements)
|
||||
{
|
||||
calc_link *clink = CONTAINER(plink->value.json.jlink,
|
||||
struct calc_link, jlink);
|
||||
|
||||
IFDEBUG(10) {
|
||||
calc_link *clink = CONTAINER(plink->value.json.jlink,
|
||||
struct calc_link, jlink);
|
||||
|
||||
printf("lnkCalc_getElements(calc@%p, (%ld))\n",
|
||||
clink, *nelements);
|
||||
}
|
||||
|
||||
*nelements = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long lnkCalc_getValue(struct link *plink, short dbrType, void *pbuffer,
|
||||
long *pnRequest)
|
||||
{
|
||||
calc_link *clink = CONTAINER(plink->value.json.jlink,
|
||||
struct calc_link, jlink);
|
||||
int i;
|
||||
long status;
|
||||
FASTCONVERT conv = dbFastPutConvertRoutine[DBR_DOUBLE][dbrType];
|
||||
|
||||
IFDEBUG(10)
|
||||
printf("lnkCalc_getValue(calc@%p, %d, ...)\n",
|
||||
clink, dbrType);
|
||||
|
||||
for (i = 0; i < clink->nArgs; i++) {
|
||||
struct link *child = &clink->inp[i];
|
||||
long nReq = 1;
|
||||
|
||||
dbGetLink(child, DBR_DOUBLE, &clink->arg[i], NULL, &nReq);
|
||||
/* Any errors have already triggered a LINK/INVALID alarm */
|
||||
}
|
||||
clink->stat = 0;
|
||||
clink->sevr = 0;
|
||||
|
||||
if (clink->post_expr) {
|
||||
status = calcPerform(clink->arg, &clink->val, clink->post_expr);
|
||||
if (!status)
|
||||
status = conv(&clink->val, pbuffer, NULL);
|
||||
if (!status && pnRequest)
|
||||
*pnRequest = 1;
|
||||
}
|
||||
else {
|
||||
status = 0;
|
||||
if (pnRequest)
|
||||
*pnRequest = 0;
|
||||
}
|
||||
|
||||
if (!status && clink->post_major) {
|
||||
double alval = clink->val;
|
||||
|
||||
status = calcPerform(clink->arg, &alval, clink->post_major);
|
||||
if (!status && alval) {
|
||||
clink->stat = LINK_ALARM;
|
||||
clink->sevr = MAJOR_ALARM;
|
||||
recGblSetSevr(plink->precord, clink->stat, clink->sevr);
|
||||
}
|
||||
}
|
||||
|
||||
if (!status && clink->post_minor) {
|
||||
double alval = clink->val;
|
||||
|
||||
status = calcPerform(clink->arg, &alval, clink->post_minor);
|
||||
if (!status && alval) {
|
||||
clink->stat = LINK_ALARM;
|
||||
clink->sevr = MINOR_ALARM;
|
||||
recGblSetSevr(plink->precord, clink->stat, clink->sevr);
|
||||
}
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static long lnkCalc_getPrecision(const struct link *plink, short *precision)
|
||||
{
|
||||
calc_link *clink = CONTAINER(plink->value.json.jlink,
|
||||
struct calc_link, jlink);
|
||||
|
||||
IFDEBUG(10)
|
||||
printf("lnkCalc_getPrecision(calc@%p)\n", clink);
|
||||
|
||||
*precision = clink->prec;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long lnkCalc_getUnits(const struct link *plink, char *units, int len)
|
||||
{
|
||||
calc_link *clink = CONTAINER(plink->value.json.jlink,
|
||||
struct calc_link, jlink);
|
||||
|
||||
IFDEBUG(10)
|
||||
printf("lnkCalc_getUnits(calc@%p)\n", clink);
|
||||
|
||||
if (clink->units) {
|
||||
strncpy(units, clink->units, --len);
|
||||
units[len] = '\0';
|
||||
}
|
||||
else
|
||||
units[0] = '\0';
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long lnkCalc_getAlarm(const struct link *plink, epicsEnum16 *status,
|
||||
epicsEnum16 *severity)
|
||||
{
|
||||
calc_link *clink = CONTAINER(plink->value.json.jlink,
|
||||
struct calc_link, jlink);
|
||||
|
||||
IFDEBUG(10)
|
||||
printf("lnkCalc_getAlarm(calc@%p)\n", clink);
|
||||
|
||||
if (status)
|
||||
*status = clink->stat;
|
||||
if (severity)
|
||||
*severity = clink->sevr;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long doLocked(struct link *plink, dbLinkUserCallback rtn, void *priv)
|
||||
{
|
||||
return rtn(plink, priv);
|
||||
}
|
||||
|
||||
|
||||
/************************* Interface Tables *************************/
|
||||
|
||||
static lset lnkCalc_lset = {
|
||||
0, 1, /* not Constant, Volatile */
|
||||
lnkCalc_open, lnkCalc_remove,
|
||||
NULL, NULL, NULL,
|
||||
lnkCalc_isConn, lnkCalc_getDBFtype, lnkCalc_getElements,
|
||||
lnkCalc_getValue,
|
||||
NULL, NULL, NULL,
|
||||
lnkCalc_getPrecision, lnkCalc_getUnits,
|
||||
lnkCalc_getAlarm, NULL,
|
||||
NULL, NULL,
|
||||
NULL, doLocked
|
||||
};
|
||||
|
||||
static jlif lnkCalcIf = {
|
||||
"calc", lnkCalc_alloc, lnkCalc_free,
|
||||
NULL, NULL, lnkCalc_integer, lnkCalc_double, lnkCalc_string,
|
||||
lnkCalc_start_map, lnkCalc_map_key, lnkCalc_end_map,
|
||||
lnkCalc_start_array, lnkCalc_end_array,
|
||||
lnkCalc_end_child, lnkCalc_get_lset,
|
||||
lnkCalc_report, lnkCalc_map_children
|
||||
};
|
||||
epicsExportAddress(jlif, lnkCalcIf);
|
||||
|
||||
585
src/std/link/lnkConst.c
Normal file
585
src/std/link/lnkConst.c
Normal file
@@ -0,0 +1,585 @@
|
||||
/*************************************************************************\
|
||||
* Copyright (c) 2016 UChicago Argonne LLC, as Operator of Argonne
|
||||
* National Laboratory.
|
||||
* EPICS BASE is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
\*************************************************************************/
|
||||
/* lnkConst.c */
|
||||
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "dbDefs.h"
|
||||
#include "errlog.h"
|
||||
#include "epicsAssert.h"
|
||||
#include "epicsString.h"
|
||||
#include "epicsTypes.h"
|
||||
#include "dbAccessDefs.h"
|
||||
#include "dbConvertFast.h"
|
||||
#include "dbLink.h"
|
||||
#include "dbJLink.h"
|
||||
#include "epicsExport.h"
|
||||
|
||||
|
||||
#define IFDEBUG(n) if(clink->jlink.debug)
|
||||
|
||||
typedef long (*FASTCONVERT)();
|
||||
|
||||
typedef struct const_link {
|
||||
jlink jlink; /* embedded object */
|
||||
int nElems;
|
||||
enum {s0, si32, sf64, sc40, a0, ai32, af64, ac40} type;
|
||||
union {
|
||||
epicsInt32 scalar_integer; /* si32 */
|
||||
epicsFloat64 scalar_double; /* sf64 */
|
||||
char *scalar_string; /* sc40 */
|
||||
void *pmem;
|
||||
epicsInt32 *pintegers; /* ai32 */
|
||||
epicsFloat64 *pdoubles; /* af64 */
|
||||
char **pstrings; /* ac40 */
|
||||
} value;
|
||||
} const_link;
|
||||
|
||||
static lset lnkConst_lset;
|
||||
|
||||
|
||||
/*************************** jlif Routines **************************/
|
||||
|
||||
static jlink* lnkConst_alloc(short dbfType)
|
||||
{
|
||||
const_link *clink = calloc(1, sizeof(*clink));
|
||||
|
||||
IFDEBUG(10)
|
||||
printf("lnkConst_alloc()\n");
|
||||
|
||||
clink->type = s0;
|
||||
clink->nElems = 0;
|
||||
clink->value.pmem = NULL;
|
||||
|
||||
IFDEBUG(10)
|
||||
printf("lnkConst_alloc -> const@%p\n", clink);
|
||||
|
||||
return &clink->jlink;
|
||||
}
|
||||
|
||||
static void lnkConst_free(jlink *pjlink)
|
||||
{
|
||||
const_link *clink = CONTAINER(pjlink, const_link, jlink);
|
||||
|
||||
IFDEBUG(10)
|
||||
printf("lnkConst_free(const@%p) type=%d\n", pjlink, clink->type);
|
||||
|
||||
switch (clink->type) {
|
||||
int i;
|
||||
case ac40:
|
||||
for (i=0; i<clink->nElems; i++)
|
||||
free(clink->value.pstrings[i]);
|
||||
/* fall through */
|
||||
case sc40:
|
||||
case ai32:
|
||||
case af64:
|
||||
free(clink->value.pmem);
|
||||
break;
|
||||
case s0:
|
||||
case a0:
|
||||
case si32:
|
||||
case sf64:
|
||||
break;
|
||||
}
|
||||
free(clink);
|
||||
}
|
||||
|
||||
static jlif_result lnkConst_integer(jlink *pjlink, long long num)
|
||||
{
|
||||
const_link *clink = CONTAINER(pjlink, const_link, jlink);
|
||||
int newElems = clink->nElems + 1;
|
||||
|
||||
IFDEBUG(10)
|
||||
printf("lnkConst_integer(const@%p, %lld)\n", pjlink, num);
|
||||
|
||||
switch (clink->type) {
|
||||
void *buf;
|
||||
|
||||
case s0:
|
||||
clink->type = si32;
|
||||
clink->value.scalar_integer = num;
|
||||
break;
|
||||
|
||||
case a0:
|
||||
clink->type = ai32;
|
||||
/* fall through */
|
||||
case ai32:
|
||||
buf = realloc(clink->value.pmem, newElems * sizeof(epicsInt32));
|
||||
if (!buf)
|
||||
return jlif_stop;
|
||||
|
||||
clink->value.pmem = buf;
|
||||
clink->value.pintegers[clink->nElems] = num;
|
||||
break;
|
||||
|
||||
case af64:
|
||||
buf = realloc(clink->value.pmem, newElems * sizeof(epicsFloat64));
|
||||
if (!buf)
|
||||
return jlif_stop;
|
||||
|
||||
clink->value.pmem = buf;
|
||||
clink->value.pdoubles[clink->nElems] = num;
|
||||
break;
|
||||
|
||||
case ac40:
|
||||
errlogPrintf("lnkConst: Mixed data types in array\n");
|
||||
/* fall through */
|
||||
default:
|
||||
return jlif_stop;
|
||||
}
|
||||
|
||||
clink->nElems = newElems;
|
||||
return jlif_continue;
|
||||
}
|
||||
|
||||
static jlif_result lnkConst_boolean(jlink *pjlink, int val)
|
||||
{
|
||||
const_link *clink = CONTAINER(pjlink, const_link, jlink);
|
||||
IFDEBUG(10)
|
||||
printf("lnkConst_boolean(const@%p, %d)\n", pjlink, val);
|
||||
|
||||
return lnkConst_integer(pjlink, val);
|
||||
}
|
||||
|
||||
static jlif_result lnkConst_double(jlink *pjlink, double num)
|
||||
{
|
||||
const_link *clink = CONTAINER(pjlink, const_link, jlink);
|
||||
int newElems = clink->nElems + 1;
|
||||
|
||||
IFDEBUG(10)
|
||||
printf("lnkConst_double(const@%p, %g)\n", pjlink, num);
|
||||
|
||||
switch (clink->type) {
|
||||
epicsFloat64 *f64buf;
|
||||
int i;
|
||||
|
||||
case s0:
|
||||
clink->type = sf64;
|
||||
clink->value.scalar_double = num;
|
||||
break;
|
||||
|
||||
case a0:
|
||||
clink->type = af64;
|
||||
/* fall through */
|
||||
case af64:
|
||||
f64buf = realloc(clink->value.pmem, newElems * sizeof(epicsFloat64));
|
||||
if (!f64buf)
|
||||
return jlif_stop;
|
||||
|
||||
f64buf[clink->nElems] = num;
|
||||
clink->value.pdoubles = f64buf;
|
||||
break;
|
||||
|
||||
case ai32: /* promote earlier ai32 values to af64 */
|
||||
f64buf = calloc(newElems, sizeof(epicsFloat64));
|
||||
if (!f64buf)
|
||||
return jlif_stop;
|
||||
|
||||
for (i = 0; i < clink->nElems; i++) {
|
||||
f64buf[i] = clink->value.pintegers[i];
|
||||
}
|
||||
free(clink->value.pmem);
|
||||
f64buf[clink->nElems] = num;
|
||||
clink->type = af64;
|
||||
clink->value.pdoubles = f64buf;
|
||||
break;
|
||||
|
||||
case ac40:
|
||||
errlogPrintf("lnkConst: Mixed data types in array\n");
|
||||
/* fall through */
|
||||
default:
|
||||
return jlif_stop;
|
||||
}
|
||||
|
||||
clink->nElems = newElems;
|
||||
return jlif_continue;
|
||||
}
|
||||
|
||||
static jlif_result lnkConst_string(jlink *pjlink, const char *val, size_t len)
|
||||
{
|
||||
const_link *clink = CONTAINER(pjlink, const_link, jlink);
|
||||
int newElems = clink->nElems + 1;
|
||||
|
||||
IFDEBUG(10)
|
||||
printf("lnkConst_string(const@%p, \"%.*s\")\n", clink, (int) len, val);
|
||||
|
||||
switch (clink->type) {
|
||||
char **vec, *str;
|
||||
|
||||
case s0:
|
||||
str = malloc(len+1);
|
||||
if (!str)
|
||||
return jlif_stop;
|
||||
|
||||
strncpy(str, val, len);
|
||||
str[len] = '\0';
|
||||
clink->type = sc40;
|
||||
clink->value.scalar_string = str;
|
||||
break;
|
||||
|
||||
case a0:
|
||||
clink->type = ac40;
|
||||
/* fall thorough */
|
||||
case ac40:
|
||||
vec = realloc(clink->value.pmem, newElems * sizeof(char *));
|
||||
if (!vec)
|
||||
return jlif_stop;
|
||||
str = malloc(len+1);
|
||||
if (!str)
|
||||
return jlif_stop;
|
||||
|
||||
strncpy(str, val, len);
|
||||
str[len] = '\0';
|
||||
vec[clink->nElems] = str;
|
||||
clink->value.pstrings = vec;
|
||||
break;
|
||||
|
||||
case af64:
|
||||
case ai32:
|
||||
errlogPrintf("lnkConst: Mixed data types in array\n");
|
||||
/* fall thorough */
|
||||
default:
|
||||
return jlif_stop;
|
||||
}
|
||||
|
||||
clink->nElems = newElems;
|
||||
return jlif_continue;
|
||||
}
|
||||
|
||||
static jlif_result lnkConst_start_array(jlink *pjlink)
|
||||
{
|
||||
const_link *clink = CONTAINER(pjlink, const_link, jlink);
|
||||
|
||||
IFDEBUG(10)
|
||||
printf("lnkConst_start_array(const@%p)\n", pjlink);
|
||||
|
||||
if (clink->type != s0) {
|
||||
errlogPrintf("lnkConst: Embedded array value\n");
|
||||
return jlif_stop;
|
||||
}
|
||||
|
||||
clink->type = a0;
|
||||
return jlif_continue;
|
||||
}
|
||||
|
||||
static jlif_result lnkConst_end_array(jlink *pjlink)
|
||||
{
|
||||
const_link *clink = CONTAINER(pjlink, const_link, jlink);
|
||||
|
||||
IFDEBUG(10)
|
||||
printf("lnkConst_end_array(const@%p)\n", pjlink);
|
||||
|
||||
return jlif_continue;
|
||||
}
|
||||
|
||||
static struct lset* lnkConst_get_lset(const jlink *pjlink)
|
||||
{
|
||||
const_link *clink = CONTAINER(pjlink, const_link, jlink);
|
||||
|
||||
IFDEBUG(10)
|
||||
printf("lnkConst_get_lset(const@%p)\n", pjlink);
|
||||
|
||||
return &lnkConst_lset;
|
||||
}
|
||||
|
||||
|
||||
/* Report outputs:
|
||||
* 'const': integer 21
|
||||
* 'const': double 5.23
|
||||
* 'const': string "something"
|
||||
* 'const': array of 999 integers
|
||||
* [1, 2, 3]
|
||||
* 'const': array of 1 double
|
||||
* [1.2345]
|
||||
* 'const': array of 2 strings
|
||||
* ["hello", "world"]
|
||||
*
|
||||
* Array values are only printed at level 2
|
||||
* because there might be quite a few of them.
|
||||
*/
|
||||
|
||||
static void lnkConst_report(const jlink *pjlink, int level, int indent)
|
||||
{
|
||||
const_link *clink = CONTAINER(pjlink, const_link, jlink);
|
||||
const char * const type_names[4] = {
|
||||
"bug", "integer", "double", "string"
|
||||
};
|
||||
const char * const dtype = type_names[clink->type & 3];
|
||||
|
||||
IFDEBUG(10)
|
||||
printf("lnkConst_report(const@%p)\n", clink);
|
||||
|
||||
if (clink->type > a0) {
|
||||
const char * const plural = clink->nElems > 1 ? "s" : "";
|
||||
|
||||
printf("%*s'const': array of %d %s%s", indent, "",
|
||||
clink->nElems, dtype, plural);
|
||||
|
||||
if (level < 2) {
|
||||
putchar('\n');
|
||||
}
|
||||
else {
|
||||
int i;
|
||||
|
||||
switch (clink->type) {
|
||||
case ai32:
|
||||
printf("\n%*s[%d", indent+2, "", clink->value.pintegers[0]);
|
||||
for (i = 1; i < clink->nElems; i++) {
|
||||
printf(", %d", clink->value.pintegers[i]);
|
||||
}
|
||||
break;
|
||||
case af64:
|
||||
printf("\n%*s[%g", indent+2, "", clink->value.pdoubles[0]);
|
||||
for (i = 1; i < clink->nElems; i++) {
|
||||
printf(", %g", clink->value.pdoubles[i]);
|
||||
}
|
||||
break;
|
||||
case ac40:
|
||||
printf("\n%*s[\"%s\"", indent+2, "", clink->value.pstrings[0]);
|
||||
for (i = 1; i < clink->nElems; i++) {
|
||||
printf(", \"%s\"", clink->value.pstrings[i]);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
printf("]\n");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
printf("%*s'const': %s", indent, "", dtype);
|
||||
|
||||
switch (clink->type) {
|
||||
case si32:
|
||||
printf(" %d\n", clink->value.scalar_integer);
|
||||
return;
|
||||
case sf64:
|
||||
printf(" %g\n", clink->value.scalar_double);
|
||||
return;
|
||||
case sc40:
|
||||
printf(" \"%s\"\n", clink->value.scalar_string);
|
||||
return;
|
||||
default:
|
||||
printf(" -- type=%d\n", clink->type);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/*************************** lset Routines **************************/
|
||||
|
||||
static void lnkConst_remove(struct dbLocker *locker, struct link *plink)
|
||||
{
|
||||
const_link *clink = CONTAINER(plink->value.json.jlink, const_link, jlink);
|
||||
|
||||
IFDEBUG(10)
|
||||
printf("lnkConst_remove(const@%p)\n", clink);
|
||||
|
||||
lnkConst_free(plink->value.json.jlink);
|
||||
}
|
||||
|
||||
static long lnkConst_loadScalar(struct link *plink, short dbrType, void *pbuffer)
|
||||
{
|
||||
const_link *clink = CONTAINER(plink->value.json.jlink, const_link, jlink);
|
||||
long status;
|
||||
|
||||
IFDEBUG(10)
|
||||
printf("lnkConst_loadScalar(const@%p, %d, %p)\n",
|
||||
clink, dbrType, pbuffer);
|
||||
|
||||
switch (clink->type) {
|
||||
case si32:
|
||||
status = dbFastPutConvertRoutine[DBF_LONG][dbrType]
|
||||
(&clink->value.scalar_integer, pbuffer, NULL);
|
||||
break;
|
||||
|
||||
case sf64:
|
||||
status = dbFastPutConvertRoutine[DBF_DOUBLE][dbrType]
|
||||
(&clink->value.scalar_double, pbuffer, NULL);
|
||||
break;
|
||||
|
||||
case sc40:
|
||||
status = dbFastPutConvertRoutine[DBF_STRING][dbrType]
|
||||
(clink->value.scalar_string, pbuffer, NULL);
|
||||
break;
|
||||
|
||||
case ai32:
|
||||
status = dbFastPutConvertRoutine[DBF_LONG][dbrType]
|
||||
(clink->value.pintegers, pbuffer, NULL);
|
||||
break;
|
||||
|
||||
case af64:
|
||||
status = dbFastPutConvertRoutine[DBF_DOUBLE][dbrType]
|
||||
(clink->value.pdoubles, pbuffer, NULL);
|
||||
break;
|
||||
|
||||
case ac40:
|
||||
status = dbFastPutConvertRoutine[DBF_STRING][dbrType]
|
||||
(clink->value.pstrings[0], pbuffer, NULL);
|
||||
break;
|
||||
|
||||
default:
|
||||
status = S_db_badField;
|
||||
break;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static long lnkConst_loadLS(struct link *plink, char *pbuffer, epicsUInt32 size,
|
||||
epicsUInt32 *plen)
|
||||
{
|
||||
const_link *clink = CONTAINER(plink->value.json.jlink, const_link, jlink);
|
||||
const char *pstr;
|
||||
|
||||
IFDEBUG(10)
|
||||
printf("lnkConst_loadLS(const@%p, %p, %d, %d)\n",
|
||||
clink, pbuffer, size, *plen);
|
||||
|
||||
if(!size) return 0;
|
||||
|
||||
switch (clink->type) {
|
||||
case sc40:
|
||||
pstr = clink->value.scalar_string;
|
||||
break;
|
||||
|
||||
case ac40:
|
||||
pstr = clink->value.pstrings[0];
|
||||
break;
|
||||
|
||||
default:
|
||||
return S_db_badField;
|
||||
}
|
||||
|
||||
strncpy(pbuffer, pstr, --size);
|
||||
pbuffer[size] = 0;
|
||||
*plen = (epicsUInt32) strlen(pbuffer) + 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long lnkConst_loadArray(struct link *plink, short dbrType, void *pbuffer,
|
||||
long *pnReq)
|
||||
{
|
||||
const_link *clink = CONTAINER(plink->value.json.jlink, const_link, jlink);
|
||||
short dbrSize = dbValueSize(dbrType);
|
||||
char *pdest = pbuffer;
|
||||
int nElems = clink->nElems;
|
||||
FASTCONVERT conv;
|
||||
long status;
|
||||
|
||||
IFDEBUG(10)
|
||||
printf("lnkConst_loadArray(const@%p, %d, %p, (%ld))\n",
|
||||
clink, dbrType, pbuffer, *pnReq);
|
||||
|
||||
if (nElems > *pnReq)
|
||||
nElems = *pnReq;
|
||||
|
||||
switch (clink->type) {
|
||||
int i;
|
||||
|
||||
case si32:
|
||||
status = dbFastPutConvertRoutine[DBF_LONG][dbrType]
|
||||
(&clink->value.scalar_integer, pdest, NULL);
|
||||
break;
|
||||
|
||||
case sf64:
|
||||
status = dbFastPutConvertRoutine[DBF_DOUBLE][dbrType]
|
||||
(&clink->value.scalar_double, pdest, NULL);
|
||||
break;
|
||||
|
||||
case sc40:
|
||||
status = dbFastPutConvertRoutine[DBF_STRING][dbrType]
|
||||
(clink->value.scalar_string, pbuffer, NULL);
|
||||
break;
|
||||
|
||||
case ai32:
|
||||
conv = dbFastPutConvertRoutine[DBF_LONG][dbrType];
|
||||
for (i = 0; i < nElems; i++) {
|
||||
conv(&clink->value.pintegers[i], pdest, NULL);
|
||||
pdest += dbrSize;
|
||||
}
|
||||
status = 0;
|
||||
break;
|
||||
|
||||
case af64:
|
||||
conv = dbFastPutConvertRoutine[DBF_DOUBLE][dbrType];
|
||||
for (i = 0; i < nElems; i++) {
|
||||
conv(&clink->value.pdoubles[i], pdest, NULL);
|
||||
pdest += dbrSize;
|
||||
}
|
||||
status = 0;
|
||||
break;
|
||||
|
||||
case ac40:
|
||||
conv = dbFastPutConvertRoutine[DBF_STRING][dbrType];
|
||||
for (i = 0; i < nElems; i++) {
|
||||
conv(clink->value.pstrings[i], pdest, NULL);
|
||||
pdest += dbrSize;
|
||||
}
|
||||
status = 0;
|
||||
break;
|
||||
|
||||
default:
|
||||
status = S_db_badField;
|
||||
}
|
||||
*pnReq = nElems;
|
||||
return status;
|
||||
}
|
||||
|
||||
static long lnkConst_getNelements(const struct link *plink, long *nelements)
|
||||
{
|
||||
const_link *clink = CONTAINER(plink->value.json.jlink, const_link, jlink);
|
||||
|
||||
IFDEBUG(10)
|
||||
printf("lnkConst_getNelements(const@%p, (%ld))\n",
|
||||
plink->value.json.jlink, *nelements);
|
||||
|
||||
*nelements = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long lnkConst_getValue(struct link *plink, short dbrType, void *pbuffer,
|
||||
long *pnRequest)
|
||||
{
|
||||
const_link *clink = CONTAINER(plink->value.json.jlink, const_link, jlink);
|
||||
|
||||
IFDEBUG(10)
|
||||
printf("lnkConst_getValue(const@%p, %d, %p, ... (%ld))\n",
|
||||
plink->value.json.jlink, dbrType, pbuffer, *pnRequest);
|
||||
|
||||
if (pnRequest)
|
||||
*pnRequest = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/************************* Interface Tables *************************/
|
||||
|
||||
static lset lnkConst_lset = {
|
||||
1, 0, /* Constant, not Volatile */
|
||||
NULL, lnkConst_remove,
|
||||
lnkConst_loadScalar, lnkConst_loadLS, lnkConst_loadArray, NULL,
|
||||
NULL, lnkConst_getNelements, lnkConst_getValue,
|
||||
NULL, NULL, NULL,
|
||||
NULL, NULL,
|
||||
NULL, NULL,
|
||||
NULL, NULL,
|
||||
NULL, NULL
|
||||
};
|
||||
|
||||
static jlif lnkConstIf = {
|
||||
"const", lnkConst_alloc, lnkConst_free,
|
||||
NULL, lnkConst_boolean, lnkConst_integer, lnkConst_double, lnkConst_string,
|
||||
NULL, NULL, NULL,
|
||||
lnkConst_start_array, lnkConst_end_array,
|
||||
NULL, lnkConst_get_lset,
|
||||
lnkConst_report, NULL
|
||||
};
|
||||
epicsExportAddress(jlif, lnkConstIf);
|
||||
|
||||
@@ -109,10 +109,8 @@ static long init_record(struct dbCommon *pcommon, int pass)
|
||||
struct aSubRecord *prec = (struct aSubRecord *)pcommon;
|
||||
STATIC_ASSERT(sizeof(prec->onam)==sizeof(prec->snam));
|
||||
GENFUNCPTR pfunc;
|
||||
long status;
|
||||
int i;
|
||||
|
||||
status = 0;
|
||||
if (pass == 0) {
|
||||
/* Allocate memory for arrays */
|
||||
initFields(&prec->fta, &prec->noa, &prec->nea, NULL,
|
||||
@@ -123,65 +121,19 @@ static long init_record(struct dbCommon *pcommon, int pass)
|
||||
}
|
||||
|
||||
/* Initialize the Subroutine Name Link */
|
||||
switch (prec->subl.type) {
|
||||
case CONSTANT:
|
||||
recGblInitConstantLink(&prec->subl, DBF_STRING, prec->snam);
|
||||
break;
|
||||
|
||||
case PV_LINK:
|
||||
case DB_LINK:
|
||||
case CA_LINK:
|
||||
break;
|
||||
|
||||
default:
|
||||
recGblRecordError(S_db_badField, (void *)prec,
|
||||
"aSubRecord(init_record) Bad SUBL link type");
|
||||
return S_db_badField;
|
||||
}
|
||||
recGblInitConstantLink(&prec->subl, DBF_STRING, prec->snam);
|
||||
|
||||
/* Initialize Input Links */
|
||||
for (i = 0; i < NUM_ARGS; i++) {
|
||||
struct link *plink = &(&prec->inpa)[i];
|
||||
switch (plink->type) {
|
||||
case CONSTANT:
|
||||
if ((&prec->noa)[i] < 2)
|
||||
recGblInitConstantLink(plink, (&prec->fta)[i], (&prec->a)[i]);
|
||||
break;
|
||||
short dbr = (&prec->fta)[i];
|
||||
long n = (&prec->noa)[i];
|
||||
|
||||
case PV_LINK:
|
||||
case CA_LINK:
|
||||
case DB_LINK:
|
||||
break;
|
||||
|
||||
default:
|
||||
recGblRecordError(S_db_badField, (void *)prec,
|
||||
"aSubRecord(init_record) Illegal INPUT LINK");
|
||||
status = S_db_badField;
|
||||
break;
|
||||
}
|
||||
dbLoadLinkArray(plink, dbr, (&prec->a)[i], &n);
|
||||
if (n > 0)
|
||||
(&prec->nea)[i] = n;
|
||||
}
|
||||
|
||||
if (status)
|
||||
return status;
|
||||
|
||||
/* Initialize Output Links */
|
||||
for (i = 0; i < NUM_ARGS; i++) {
|
||||
switch ((&prec->outa)[i].type) {
|
||||
case CONSTANT:
|
||||
case PV_LINK:
|
||||
case CA_LINK:
|
||||
case DB_LINK:
|
||||
break;
|
||||
|
||||
default:
|
||||
recGblRecordError(S_db_badField, (void *)prec,
|
||||
"aSubRecord(init_record) Illegal OUTPUT LINK");
|
||||
status = S_db_badField;
|
||||
}
|
||||
}
|
||||
if (status)
|
||||
return status;
|
||||
|
||||
/* Call the user initialization routine if there is one */
|
||||
if (prec->inam[0] != 0) {
|
||||
pfunc = (GENFUNCPTR)registryFunctionFind(prec->inam);
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user