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:
Michael Davidsaver
2017-05-04 20:14:30 -04:00
142 changed files with 5923 additions and 1624 deletions

View File

@@ -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(&amp;prec->siml, DBF_USHORT, &amp;prec->simm);
}
</pre>
into this:
<pre>
recGblInitConstantLink(&amp;prec->siml, DBF_USHORT, &amp;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(&amp;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(&amp;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

View File

@@ -23,5 +23,4 @@ dbCore_SRCS += asIocRegister.c
PROD_HOST += ascheck
ascheck_SRCS = ascheck.c
ascheck_LIBS = dbCore
ascheck_LIBS = dbCore ca

View File

@@ -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

View File

@@ -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;

View File

@@ -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*/

View File

@@ -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)

View File

@@ -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);

View File

@@ -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
View 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
View 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
View 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;
}

View 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
View 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
View 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 */

View File

@@ -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()
*

View File

@@ -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);

View File

@@ -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
View 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
View 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 */

View File

@@ -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)
{

View File

@@ -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,

View File

@@ -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);

View File

@@ -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);

View File

@@ -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

View File

@@ -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) {

View File

@@ -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);

View File

@@ -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

View File

@@ -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();

View File

@@ -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)

View File

@@ -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")

View File

@@ -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();
}

View File

@@ -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")

View 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}})
}

View File

@@ -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
View 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);

View File

@@ -0,0 +1 @@
link("z", "jlifZ")

27
src/ioc/db/test/jlinkz.h Normal file
View 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
View 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);

View File

@@ -0,0 +1 @@
link(x, xlinkIf)

View File

@@ -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;

View File

@@ -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;

View File

@@ -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",

View File

@@ -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);

View File

@@ -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) {

View File

@@ -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);

View File

@@ -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 {

View File

@@ -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 ;
%%

View File

@@ -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 */

View File

@@ -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);
}

View File

@@ -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);
}
}

View File

@@ -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

View File

@@ -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;
}
}
}

View File

@@ -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
}

View 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;
}

View 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 */

View File

@@ -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;
}

View File

@@ -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.

View File

@@ -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);

View File

@@ -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
***************************************************************/

View File

@@ -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;

View File

@@ -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 */

View File

@@ -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);

View File

@@ -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);

View File

@@ -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];

View File

@@ -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);
}

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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);

View File

@@ -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;
}

View File

@@ -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);
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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 */

View File

@@ -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;
}

View File

@@ -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 = {

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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
View 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
View 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
View 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
View 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);

View File

@@ -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