diff --git a/documentation/RELEASE_NOTES.html b/documentation/RELEASE_NOTES.html
index 0c69d60ad..e43fde5bf 100644
--- a/documentation/RELEASE_NOTES.html
+++ b/documentation/RELEASE_NOTES.html
@@ -52,6 +52,304 @@ Older clients will be ignored by newer servers.
This allows removal of UDP echo and similar protocol features which
are not compatible with secure protocol design practice.
+Lookup-tables using the subArrray record
+
+The subArray record can now be used as a lookup-table from a constant array
+specified in its INP field. For example:
+
+
+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)
+}
+
+
+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.
+
+Synchronized Timestamps with TSEL=-2
+
+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.
+
+
+Extensible Link Types
+
+
+
+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).
+
+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 link entry in a DBD file.
+
+
+New Link Types Added
+
+This release contains two new JSON link types, const and
+calc:
+
+
+
+- The const 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.
+
+- The calc 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 calc link type has little practical
+utility as it can currently only fetch inputs from other calc links or
+from const links.
+
+
+
+
+ field(INP, {calc:{expr:"A+B+1",
+ args:[5, # A
+ {const:6}] # B
+ }})
+
+
+The new link types are documented in a
+separate
+document
+.
+
+
+Device Support Addressing using JSON_LINK
+
+
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 JSON_LINK address type.
+
+
+Support Routine Modifications for Extensible Link Types
+
+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:
+
+
+
+- Make all calls to recGblInitConstantLink() unconditional on the
+link type, i.e. change this code:
+
+
+ if (prec->siml.type == CONSTANT) {
+ recGblInitConstantLink(&prec->siml, DBF_USHORT, &prec->simm);
+ }
+
+
+into this:
+
+
+ recGblInitConstantLink(&prec->siml, DBF_USHORT, &prec->simm);
+
+
+Note that recGblInitConstantLink() still returns TRUE if the field was
+successfully initialized from the link (implying the link is constant).
+This change will work properly with all Base releases currently in use.
+
+- Code that needs to identify a constant link should be modified to use the
+new routine dbLinkIsConstant() instead, which returns TRUE for constant
+or undefined links, FALSE for links whose dbGetLink() routine may
+return different values on different calls. For example this:
+
+
+ if (prec->dol.type != CONSTANT)
+
+
+should become this:
+
+
+ if (!dbLinkIsConstant(&prec->dol))
+
+
+When the converted software is also required to build against older versions of
+Base, this macro definition may be useful:
+
+
+#define dbLinkIsConstant(lnk) ((lnk)->type == CONSTANT)
+
+
+
+- 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
+
+
+ if (prec->inp.type == CA_LINK)
+
+
+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:
+
+
+ if (dbLinkIsVolatile(&prec->inp))
+
+
+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:
+
+
+#define dbLinkIsVolatile(lnk) ((lnk)->type == CA_LINK)
+
+
+
+- The current connection state of a volatile link can be found using the
+routine dbIsLinkConnected() 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:
+
+
+ stat = dbCaIsLinkConnected(plink);
+
+
+which should become:
+
+ stat = dbIsLinkConnected(plink);
+
+
+Similar changes should be made for calls to the other dbCa routines.
+
+
+- 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.
+
+
+
+
+
+
+Constant Link Values
+
+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.
+
+Some examples of constant array and string initialized records are:
+
+
+ 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")
+ }
+
+
+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.
+
+
+Database Parsing of "Relaxed JSON" Values
+
+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).
+
+"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:
+
+
+
+- Strings containing only the characters a-z A-Z 0-9 _ - + .
+do not have to be enclosed in double-quote characters.
+
+- The above rule applies to map keys as well as to regular string values.
+
+- The JSON keywords null, true and false (all
+lower-case) will be recognized as keywords, so they must be quoted to use any of
+these single words as a string.
+
+- Comments may be used, introduced as usual by the #
+character and extending to the end of the line.
+
+
+
+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:
+
+
+ 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
+ }
+
+
+Note that the record, field and info-tag names do not accept JSON
+values, so they follows the older bareword rules for quoting where the colon
+: 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.
+
Echoless comments in iocsh
The way comments are parsed by the iocsh interpreter has changed. The
diff --git a/src/ioc/as/Makefile b/src/ioc/as/Makefile
index 971e6a594..25237c26c 100644
--- a/src/ioc/as/Makefile
+++ b/src/ioc/as/Makefile
@@ -23,5 +23,4 @@ dbCore_SRCS += asIocRegister.c
PROD_HOST += ascheck
ascheck_SRCS = ascheck.c
-ascheck_LIBS = dbCore
-
+ascheck_LIBS = dbCore ca
diff --git a/src/ioc/db/Makefile b/src/ioc/db/Makefile
index 15680bf60..d28119ff7 100644
--- a/src/ioc/db/Makefile
+++ b/src/ioc/db/Makefile
@@ -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
diff --git a/src/ioc/db/dbAccess.c b/src/ioc/db/dbAccess.c
index 7180259f5..4c6e1c7f5 100644
--- a/src/ioc/db/dbAccess.c
+++ b/src/ioc/db/dbAccess.c
@@ -41,7 +41,6 @@
#include "dbAddr.h"
#include "dbBase.h"
#include "dbBkpt.h"
-#include "dbCa.h"
#include "dbCommonPvt.h"
#include "dbConvertFast.h"
#include "dbConvert.h"
@@ -668,7 +667,7 @@ long dbNameToAddr(const char *pname, DBADDR *paddr)
paddr->dbr_field_type = DBR_CHAR;
} else if (dbfType >= DBF_INLINK && dbfType <= DBF_FWDLINK) {
/* Clients see a char array, but keep original dbfType */
- paddr->no_elements = PVNAME_STRINGSZ + 12;
+ paddr->no_elements = PVLINK_STRINGSZ;
paddr->field_size = 1;
paddr->dbr_field_type = DBR_CHAR;
} else {
@@ -1033,7 +1032,7 @@ static long dbPutFieldLink(DBADDR *paddr,
return S_db_badDbrtype;
}
- status = dbParseLink(pstring, pfldDes->field_type, &link_info);
+ status = dbParseLink(pstring, pfldDes->field_type, &link_info, 0);
if (status)
return status;
@@ -1106,23 +1105,12 @@ static long dbPutFieldLink(DBADDR *paddr,
}
}
- switch (plink->type) { /* Old link type */
- case DB_LINK:
- case CA_LINK:
- case CONSTANT:
- dbRemoveLink(&locker, plink); /* link type becomes PV_LINK */
- break;
-
- case PV_LINK:
- case MACRO_LINK:
- break; /* should never get here */
-
- default: /* Hardware address */
- if (!isDevLink) {
- status = S_db_badHWaddr;
- goto restoreScan;
- }
- break;
+ if (dbLinkIsDefined(plink)) {
+ dbRemoveLink(&locker, plink); /* Clear out old link */
+ }
+ else if (!isDevLink) {
+ status = S_db_badHWaddr;
+ goto restoreScan;
}
if (special) status = dbPutSpecial(paddr, 0);
@@ -1155,6 +1143,7 @@ static long dbPutFieldLink(DBADDR *paddr,
switch (plink->type) { /* New link type */
case PV_LINK:
case CONSTANT:
+ case JSON_LINK:
dbAddLink(&locker, plink, pfldDes->field_type, pdbaddr);
break;
diff --git a/src/ioc/db/dbAccessDefs.h b/src/ioc/db/dbAccessDefs.h
index 8427fe0fc..cc45b17fe 100644
--- a/src/ioc/db/dbAccessDefs.h
+++ b/src/ioc/db/dbAccessDefs.h
@@ -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*/
diff --git a/src/ioc/db/dbCa.c b/src/ioc/db/dbCa.c
index a7b4511f1..72ad6cd24 100644
--- a/src/ioc/db/dbCa.c
+++ b/src/ioc/db/dbCa.c
@@ -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)
diff --git a/src/ioc/db/dbCa.h b/src/ioc/db/dbCa.h
index a33c32023..de25ef59f 100644
--- a/src/ioc/db/dbCa.h
+++ b/src/ioc/db/dbCa.h
@@ -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);
diff --git a/src/ioc/db/dbChannel.c b/src/ioc/db/dbChannel.c
index d506e96b9..a790fc22f 100644
--- a/src/ioc/db/dbChannel.c
+++ b/src/ioc/db/dbChannel.c
@@ -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 {
diff --git a/src/ioc/db/dbConstLink.c b/src/ioc/db/dbConstLink.c
new file mode 100644
index 000000000..0bdc70f92
--- /dev/null
+++ b/src/ioc/db/dbConstLink.c
@@ -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
+#include
+
+#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
+};
diff --git a/src/ioc/db/dbConstLink.h b/src/ioc/db/dbConstLink.h
new file mode 100644
index 000000000..c187f9fc5
--- /dev/null
+++ b/src/ioc/db/dbConstLink.h
@@ -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 */
+
diff --git a/src/ioc/db/dbConvertJSON.c b/src/ioc/db/dbConvertJSON.c
new file mode 100644
index 000000000..e2a453549
--- /dev/null
+++ b/src/ioc/db/dbConvertJSON.c
@@ -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
+#include
+
+#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;
+}
diff --git a/src/ioc/db/dbConvertJSON.h b/src/ioc/db/dbConvertJSON.h
new file mode 100644
index 000000000..7dd8e4aed
--- /dev/null
+++ b/src/ioc/db/dbConvertJSON.h
@@ -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
+
+#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 */
+
diff --git a/src/ioc/db/dbDbLink.c b/src/ioc/db/dbDbLink.c
new file mode 100644
index 000000000..8f0e3ed03
--- /dev/null
+++ b/src/ioc/db/dbDbLink.c
@@ -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
+#include
+#include
+#include
+#include
+
+#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
+};
diff --git a/src/ioc/db/dbDbLink.h b/src/ioc/db/dbDbLink.h
new file mode 100644
index 000000000..c36772004
--- /dev/null
+++ b/src/ioc/db/dbDbLink.h
@@ -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 */
diff --git a/src/ioc/db/dbEvent.c b/src/ioc/db/dbEvent.c
index 5e386dd5c..a5830881f 100644
--- a/src/ioc/db/dbEvent.c
+++ b/src/ioc/db/dbEvent.c
@@ -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()
*
diff --git a/src/ioc/db/dbEvent.h b/src/ioc/db/dbEvent.h
index fe0e52f90..8ee109373 100644
--- a/src/ioc/db/dbEvent.h
+++ b/src/ioc/db/dbEvent.h
@@ -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);
diff --git a/src/ioc/db/dbIocRegister.c b/src/ioc/db/dbIocRegister.c
index 1a1aafb9a..07c9836c2 100644
--- a/src/ioc/db/dbIocRegister.c
+++ b/src/ioc/db/dbIocRegister.c
@@ -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);
diff --git a/src/ioc/db/dbJLink.c b/src/ioc/db/dbJLink.c
new file mode 100644
index 000000000..b68432f6e
--- /dev/null
+++ b/src/ioc/db/dbJLink.c
@@ -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
+#include
+
+#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;
+}
diff --git a/src/ioc/db/dbJLink.h b/src/ioc/db/dbJLink.h
new file mode 100644
index 000000000..61b59670b
--- /dev/null
+++ b/src/ioc/db/dbJLink.h
@@ -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
+#include
+
+#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 */
+
diff --git a/src/ioc/db/dbLink.c b/src/ioc/db/dbLink.c
index b532a3270..6fb936ae4 100644
--- a/src/ioc/db/dbLink.c
+++ b/src/ioc/db/dbLink.c
@@ -19,62 +19,35 @@
#include
#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)
{
diff --git a/src/ioc/db/dbLink.h b/src/ioc/db/dbLink.h
index a2e9175df..ad4ac2f45 100644
--- a/src/ioc/db/dbLink.h
+++ b/src/ioc/db/dbLink.h
@@ -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,
diff --git a/src/ioc/db/dbLock.c b/src/ioc/db/dbLock.c
index d0f266795..7b04daa4e 100644
--- a/src/ioc/db/dbLock.c
+++ b/src/ioc/db/dbLock.c
@@ -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);
diff --git a/src/ioc/db/dbUnitTest.c b/src/ioc/db/dbUnitTest.c
index 33d721656..644741e5e 100644
--- a/src/ioc/db/dbUnitTest.c
+++ b/src/ioc/db/dbUnitTest.c
@@ -12,6 +12,8 @@
#include
+#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);
diff --git a/src/ioc/db/dbUnitTest.h b/src/ioc/db/dbUnitTest.h
index 82954c277..31d068350 100644
--- a/src/ioc/db/dbUnitTest.h
+++ b/src/ioc/db/dbUnitTest.h
@@ -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
diff --git a/src/ioc/db/recGbl.c b/src/ioc/db/recGbl.c
index f1e3e7e62..2bf44322f 100644
--- a/src/ioc/db/recGbl.c
+++ b/src/ioc/db/recGbl.c
@@ -17,6 +17,7 @@
#include
#include
+#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) {
diff --git a/src/ioc/db/recGbl.h b/src/ioc/db/recGbl.h
index e81749a54..8eb589450 100644
--- a/src/ioc/db/recGbl.h
+++ b/src/ioc/db/recGbl.h
@@ -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);
diff --git a/src/ioc/db/test/Makefile b/src/ioc/db/test/Makefile
index e3d4164ff..37ec3da74 100644
--- a/src/ioc/db/test/Makefile
+++ b/src/ioc/db/test/Makefile
@@ -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
diff --git a/src/ioc/db/test/dbCaLinkTest.c b/src/ioc/db/test/dbCaLinkTest.c
index 5c823c4e0..838fde6ce 100644
--- a/src/ioc/db/test/dbCaLinkTest.c
+++ b/src/ioc/db/test/dbCaLinkTest.c
@@ -44,6 +44,9 @@ epicsShareExtern short epicsShareAPI ca_field_type (chid chan);
void dbTestIoc_registerRecordDeviceDriver(struct dbBase *);
+static epicsEventId waitEvent;
+static unsigned waitCounter;
+
static
void waitForUpdateN(DBLINK *plink, unsigned long n)
{
@@ -58,13 +61,46 @@ void putLink(DBLINK *plink, short dbr, const void*buf, long nReq)
ret = dbPutLink(plink, dbr, buf, nReq);
if(ret) {
- testFail("putLink fails %ld\n", ret);
+ testFail("putLink fails %ld", ret);
} else {
- testPass("putLink ok\n");
+ testPass("putLink ok");
dbCaSync();
}
}
+static long getTwice(struct link *psrclnk, void *dummy)
+{
+ epicsInt32 val1, val2;
+ long status = dbGetLink(psrclnk, DBR_LONG, &val1, 0, 0);
+
+ if (status) return status;
+
+ epicsThreadSleep(0.5);
+ status = dbGetLink(psrclnk, DBR_LONG, &val2, 0, 0);
+ if (status) return status;
+
+ testDiag("val1 = %d, val2 = %d", val1, val2);
+ return (val1 == val2) ? 0 : -1;
+}
+
+static void countUp(void *parm)
+{
+ xRecord *ptarg = (xRecord *)parm;
+ epicsInt32 val;
+
+ for (val = 1; val < 10; val++) {
+ dbScanLock((dbCommon*)ptarg);
+ ptarg->val = val;
+ db_post_events(ptarg, &ptarg->val, DBE_VALUE|DBE_ALARM|DBE_ARCHIVE);
+ dbScanUnlock((dbCommon*)ptarg);
+
+ epicsThreadSleep(0.1);
+ }
+
+ if (waitEvent)
+ epicsEventMustTrigger(waitEvent);
+}
+
static void testNativeLink(void)
{
xRecord *psrc, *ptarg;
@@ -124,6 +160,27 @@ static void testNativeLink(void)
testOk1(ptarg->val==1010);
dbScanUnlock((dbCommon*)ptarg);
+ assert(!waitEvent);
+ waitEvent = epicsEventMustCreate(epicsEventEmpty);
+
+ /* Start counter */
+ epicsThreadCreate("countUp", epicsThreadPriorityHigh,
+ epicsThreadGetStackSize(epicsThreadStackSmall), countUp, ptarg);
+
+ dbScanLock((dbCommon*)psrc);
+ /* Check that unlocked gets change */
+ temp = getTwice(psrclnk, NULL);
+ testOk(temp == -1, "unlocked, getTwice returned %d (-1)", temp);
+
+ /* Check locked gets are atomic */
+ temp = dbLinkDoLocked(psrclnk, getTwice, NULL);
+ testOk(temp == 0, "locked, getTwice returned %d (0)", temp);
+ dbScanUnlock((dbCommon*)psrc);
+
+ epicsEventMustWait(waitEvent);
+ epicsEventDestroy(waitEvent);
+ waitEvent = NULL;
+
testIocShutdownOk();
testdbCleanup();
@@ -189,9 +246,6 @@ static void testStringLink(void)
testdbCleanup();
}
-static epicsEventId waitEvent;
-static unsigned waitCounter;
-
static void wasproc(xRecord *prec)
{
waitCounter++;
@@ -562,7 +616,7 @@ static void testCAC(void)
MAIN(dbCaLinkTest)
{
- testPlan(99);
+ testPlan(101);
testNativeLink();
testStringLink();
testCP();
diff --git a/src/ioc/db/test/dbLinkdset.c b/src/ioc/db/test/dbLinkdset.c
index 6e775df7b..d7b8230f0 100644
--- a/src/ioc/db/test/dbLinkdset.c
+++ b/src/ioc/db/test/dbLinkdset.c
@@ -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)
diff --git a/src/ioc/db/test/dbLinkdset.dbd b/src/ioc/db/test/dbLinkdset.dbd
index 84d5eefe9..da2d4d946 100644
--- a/src/ioc/db/test/dbLinkdset.dbd
+++ b/src/ioc/db/test/dbLinkdset.dbd
@@ -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")
diff --git a/src/ioc/db/test/dbPutLinkTest.c b/src/ioc/db/test/dbPutLinkTest.c
index 5fcab16b8..b39b98db2 100644
--- a/src/ioc/db/test/dbPutLinkTest.c
+++ b/src/ioc/db/test/dbPutLinkTest.c
@@ -24,8 +24,10 @@
#include "osiFileName.h"
#include "dbmf.h"
#include "errlog.h"
+#include
#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; iinfo.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();
}
diff --git a/src/ioc/db/test/dbPutLinkTest.db b/src/ioc/db/test/dbPutLinkTest.db
index 7753b5b08..6742fcb39 100644
--- a/src/ioc/db/test/dbPutLinkTest.db
+++ b/src/ioc/db/test/dbPutLinkTest.db
@@ -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")
diff --git a/src/ioc/db/test/dbPutLinkTestJ.db b/src/ioc/db/test/dbPutLinkTestJ.db
new file mode 100644
index 000000000..25cf4c822
--- /dev/null
+++ b/src/ioc/db/test/dbPutLinkTestJ.db
@@ -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}})
+}
diff --git a/src/ioc/db/test/devx.c b/src/ioc/db/test/devx.c
index 104c64001..c5527f14e 100644
--- a/src/ioc/db/test/devx.c
+++ b/src/ioc/db/test/devx.c
@@ -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 = {
diff --git a/src/ioc/db/test/jlinkz.c b/src/ioc/db/test/jlinkz.c
new file mode 100644
index 000000000..6683c6779
--- /dev/null
+++ b/src/ioc/db/test/jlinkz.c
@@ -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
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#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);
diff --git a/src/ioc/db/test/jlinkz.dbd b/src/ioc/db/test/jlinkz.dbd
new file mode 100644
index 000000000..5408a88b6
--- /dev/null
+++ b/src/ioc/db/test/jlinkz.dbd
@@ -0,0 +1 @@
+link("z", "jlifZ")
diff --git a/src/ioc/db/test/jlinkz.h b/src/ioc/db/test/jlinkz.h
new file mode 100644
index 000000000..5c34d2eec
--- /dev/null
+++ b/src/ioc/db/test/jlinkz.h
@@ -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
+#include
+#include
+
+#include
+
+epicsShareExtern
+int numzalloc;
+
+typedef struct {
+ jlink base;
+ epicsMutexId lock;
+ unsigned isset:1;
+ unsigned isopen:1;
+ epicsInt32 value;
+} zpriv;
+
+#endif /* JLINKZ_H */
diff --git a/src/ioc/db/test/xLink.c b/src/ioc/db/test/xLink.c
new file mode 100644
index 000000000..12175f44e
--- /dev/null
+++ b/src/ioc/db/test/xLink.c
@@ -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);
+
diff --git a/src/ioc/db/test/xLink.dbd b/src/ioc/db/test/xLink.dbd
new file mode 100644
index 000000000..290b0ba02
--- /dev/null
+++ b/src/ioc/db/test/xLink.dbd
@@ -0,0 +1 @@
+link(x, xlinkIf)
diff --git a/src/ioc/dbStatic/dbBase.h b/src/ioc/dbStatic/dbBase.h
index ec05d50f2..df3be4352 100644
--- a/src/ioc/dbStatic/dbBase.h
+++ b/src/ioc/dbStatic/dbBase.h
@@ -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;
diff --git a/src/ioc/dbStatic/dbLex.l b/src/ioc/dbStatic/dbLex.l
index 681cb1f24..cfbb5bda4 100644
--- a/src/ioc/dbStatic/dbLex.l
+++ b/src/ioc/dbStatic/dbLex.l
@@ -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");
}
-. {
+"null" return jsonNULL;
+"true" return jsonTRUE;
+"false" return jsonFALSE;
+
+{punctuation} return yytext[0];
+
+{jsondqstr} {
+ yylval.Str = dbmfStrdup((char *) yytext);
+ return jsonSTRING;
+}
+
+{number} {
+ yylval.Str = dbmfStrdup((char *) yytext);
+ return jsonNUMBER;
+}
+
+{barechar}+ {
+ yylval.Str = dbmfStrdup((char *) yytext);
+ return jsonBARE;
+}
+
+{comment}.* ;
+
+{whitespace} ;
+
+. {
char message[40];
YY_BUFFER_STATE *dummy=0;
diff --git a/src/ioc/dbStatic/dbLexRoutines.c b/src/ioc/dbStatic/dbLexRoutines.c
index 12038a6ec..dc447e5ce 100644
--- a/src/ioc/dbStatic/dbLexRoutines.c
+++ b/src/ioc/dbStatic/dbLexRoutines.c
@@ -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",
diff --git a/src/ioc/dbStatic/dbStaticIocRegister.c b/src/ioc/dbStatic/dbStaticIocRegister.c
index 18d346c70..65acc198d 100644
--- a/src/ioc/dbStatic/dbStaticIocRegister.c
+++ b/src/ioc/dbStatic/dbStaticIocRegister.c
@@ -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);
diff --git a/src/ioc/dbStatic/dbStaticLib.c b/src/ioc/dbStatic/dbStaticLib.c
index 48bc5041e..0f2a1d97e 100644
--- a/src/ioc/dbStatic/dbStaticLib.c
+++ b/src/ioc/dbStatic/dbStaticLib.c
@@ -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) {
diff --git a/src/ioc/dbStatic/dbStaticLib.h b/src/ioc/dbStatic/dbStaticLib.h
index bc40ae6a2..84ef94aef 100644
--- a/src/ioc/dbStatic/dbStaticLib.h
+++ b/src/ioc/dbStatic/dbStaticLib.h
@@ -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);
diff --git a/src/ioc/dbStatic/dbStaticPvt.h b/src/ioc/dbStatic/dbStaticPvt.h
index ca6e51806..58d32c28c 100644
--- a/src/ioc/dbStatic/dbStaticPvt.h
+++ b/src/ioc/dbStatic/dbStaticPvt.h
@@ -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 {
diff --git a/src/ioc/dbStatic/dbYacc.y b/src/ioc/dbStatic/dbYacc.y
index 272f509c4..e61ce58b0 100644
--- a/src/ioc/dbStatic/dbYacc.y
+++ b/src/ioc/dbStatic/dbYacc.y
@@ -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 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 tokenSTRING tokenCDEFS
+
+%token jsonNULL jsonTRUE jsonFALSE
+%token jsonNUMBER jsonSTRING jsonBARE
+%type json_value json_object json_array
+%type 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 ;
+
%%
diff --git a/src/ioc/dbStatic/link.h b/src/ioc/dbStatic/link.h
index efa065a58..9055c7588 100644
--- a/src/ioc/dbStatic/link.h
+++ b/src/ioc/dbStatic/link.h
@@ -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 */
diff --git a/src/ioc/dbtemplate/dbLoadTemplate_lex.l b/src/ioc/dbtemplate/dbLoadTemplate_lex.l
index afb729517..c6a99a8a1 100644
--- a/src/ioc/dbtemplate/dbLoadTemplate_lex.l
+++ b/src/ioc/dbtemplate/dbLoadTemplate_lex.l
@@ -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);
}
diff --git a/src/ioc/misc/iocInit.c b/src/ioc/misc/iocInit.c
index aefeb6154..98f7dae11 100644
--- a/src/ioc/misc/iocInit.c
+++ b/src/ioc/misc/iocInit.c
@@ -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);
}
}
diff --git a/src/ioc/registry/Makefile b/src/ioc/registry/Makefile
index ce285a0c6..a85320a9d 100644
--- a/src/ioc/registry/Makefile
+++ b/src/ioc/registry/Makefile
@@ -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
diff --git a/src/ioc/registry/registryCommon.c b/src/ioc/registry/registryCommon.c
index e5d5768af..56664c5bb 100644
--- a/src/ioc/registry/registryCommon.c
+++ b/src/ioc/registry/registryCommon.c
@@ -19,6 +19,7 @@
#include "registryCommon.h"
#include "registryDeviceSupport.h"
#include "registryDriverSupport.h"
+#include "registryJLinks.h"
void registerRecordTypes(DBBASE *pbase, int nRecordTypes,
@@ -76,3 +77,15 @@ void registerDrivers(DBBASE *pbase, int nDrivers,
}
}
+void registerJLinks(DBBASE *pbase, int nLinks, jlif * const *jlifsl)
+{
+ int i;
+ for (i = 0; i < nLinks; i++) {
+ if (!registryJLinkAdd(pbase, jlifsl[i])) {
+ errlogPrintf("registryJLinkAdd failed %s\n",
+ jlifsl[i]->name);
+ continue;
+ }
+ }
+}
+
diff --git a/src/ioc/registry/registryCommon.h b/src/ioc/registry/registryCommon.h
index c4b601665..51b32dee3 100644
--- a/src/ioc/registry/registryCommon.h
+++ b/src/ioc/registry/registryCommon.h
@@ -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
}
diff --git a/src/ioc/registry/registryJLinks.c b/src/ioc/registry/registryJLinks.c
new file mode 100644
index 000000000..921a2cbcc
--- /dev/null
+++ b/src/ioc/registry/registryJLinks.c
@@ -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;
+}
diff --git a/src/ioc/registry/registryJLinks.h b/src/ioc/registry/registryJLinks.h
new file mode 100644
index 000000000..7e6a8933e
--- /dev/null
+++ b/src/ioc/registry/registryJLinks.h
@@ -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 */
diff --git a/src/libCom/dbmf/dbmf.c b/src/libCom/dbmf/dbmf.c
index 3f0841c05..e04a7bedf 100644
--- a/src/libCom/dbmf/dbmf.c
+++ b/src/libCom/dbmf/dbmf.c
@@ -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;
+}
+
diff --git a/src/libCom/dbmf/dbmf.h b/src/libCom/dbmf/dbmf.h
index 3740e532e..2c8a28f2c 100644
--- a/src/libCom/dbmf/dbmf.h
+++ b/src/libCom/dbmf/dbmf.h
@@ -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.
diff --git a/src/libCom/error/errMdef.h b/src/libCom/error/errMdef.h
index 1df63ad74..4dbbecaf1 100644
--- a/src/libCom/error/errMdef.h
+++ b/src/libCom/error/errMdef.h
@@ -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);
diff --git a/src/libCom/error/errSymLib.c b/src/libCom/error/errSymLib.c
index 84c093c9b..aa1f3961c 100644
--- a/src/libCom/error/errSymLib.c
+++ b/src/libCom/error/errSymLib.c
@@ -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 : "";
+}
+
+/****************************************************************
+ * 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
***************************************************************/
diff --git a/src/libCom/error/makeStatTbl.pl b/src/libCom/error/makeStatTbl.pl
index 8bc63e23c..4f53901f3 100644
--- a/src/libCom/error/makeStatTbl.pl
+++ b/src/libCom/error/makeStatTbl.pl
@@ -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;
diff --git a/src/libCom/misc/dbDefs.h b/src/libCom/misc/dbDefs.h
index 94e05e8a4..e1de3f200 100644
--- a/src/libCom/misc/dbDefs.h
+++ b/src/libCom/misc/dbDefs.h
@@ -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 */
diff --git a/src/libCom/misc/epicsString.c b/src/libCom/misc/epicsString.c
index a8098336c..e41e21b72 100644
--- a/src/libCom/misc/epicsString.c
+++ b/src/libCom/misc/epicsString.c
@@ -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);
diff --git a/src/libCom/misc/epicsString.h b/src/libCom/misc/epicsString.h
index 939da2683..093c73df4 100644
--- a/src/libCom/misc/epicsString.h
+++ b/src/libCom/misc/epicsString.h
@@ -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);
diff --git a/src/libCom/yacc/antelope.c b/src/libCom/yacc/antelope.c
index 65d360a58..7143b38ff 100644
--- a/src/libCom/yacc/antelope.c
+++ b/src/libCom/yacc/antelope.c
@@ -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];
diff --git a/src/libCom/yacc/error.c b/src/libCom/yacc/error.c
index 2c72241a5..d95c92a3a 100644
--- a/src/libCom/yacc/error.c
+++ b/src/libCom/yacc/error.c
@@ -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);
}
diff --git a/src/libCom/yajl/Makefile b/src/libCom/yajl/Makefile
index 579848555..39146ca79 100644
--- a/src/libCom/yajl/Makefile
+++ b/src/libCom/yajl/Makefile
@@ -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
diff --git a/src/libCom/yajl/yajl_alloc.h b/src/libCom/yajl/yajl_alloc.h
index cc1e5cf43..3935eef58 100644
--- a/src/libCom/yajl/yajl_alloc.h
+++ b/src/libCom/yajl/yajl_alloc.h
@@ -45,6 +45,6 @@
#define YA_FREE(afs, ptr) (afs)->free((afs)->ctx, (ptr))
#define YA_REALLOC(afs, ptr, sz) (afs)->realloc((afs)->ctx, (ptr), (sz))
-void yajl_set_default_alloc_funcs(yajl_alloc_funcs * yaf);
+YAJL_API void yajl_set_default_alloc_funcs(yajl_alloc_funcs * yaf);
#endif
diff --git a/src/std/Makefile b/src/std/Makefile
index 2065f1f4e..dcfcb6a22 100644
--- a/src/std/Makefile
+++ b/src/std/Makefile
@@ -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
diff --git a/src/std/dev/devAaiSoft.c b/src/std/dev/devAaiSoft.c
index 586483c92..e2e014efb 100644
--- a/src/std/dev/devAaiSoft.c
+++ b/src/std/dev/devAaiSoft.c
@@ -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;
}
diff --git a/src/std/dev/devAaoSoft.c b/src/std/dev/devAaoSoft.c
index 6925b1ba1..3331ec1bf 100644
--- a/src/std/dev/devAaoSoft.c
+++ b/src/std/dev/devAaoSoft.c
@@ -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;
}
diff --git a/src/std/dev/devAiSoft.c b/src/std/dev/devAiSoft.c
index 2f6445f2b..0ecc1b13f 100644
--- a/src/std/dev/devAiSoft.c
+++ b/src/std/dev/devAiSoft.c
@@ -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;
}
diff --git a/src/std/dev/devAiSoftRaw.c b/src/std/dev/devAiSoftRaw.c
index ee697ff57..f2cfd5df5 100644
--- a/src/std/dev/devAiSoftRaw.c
+++ b/src/std/dev/devAiSoftRaw.c
@@ -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;
}
diff --git a/src/std/dev/devAoSoftCallback.c b/src/std/dev/devAoSoftCallback.c
index 35c3f6fe2..c1fb72f13 100644
--- a/src/std/dev/devAoSoftCallback.c
+++ b/src/std/dev/devAoSoftCallback.c
@@ -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;
}
+
diff --git a/src/std/dev/devBiDbState.c b/src/std/dev/devBiDbState.c
index c0d7120b2..fcb6c8f63 100644
--- a/src/std/dev/devBiDbState.c
+++ b/src/std/dev/devBiDbState.c
@@ -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;
}
diff --git a/src/std/dev/devBiSoft.c b/src/std/dev/devBiSoft.c
index 27e8d7c20..12640ad0c 100644
--- a/src/std/dev/devBiSoft.c
+++ b/src/std/dev/devBiSoft.c
@@ -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;
}
diff --git a/src/std/dev/devBiSoftRaw.c b/src/std/dev/devBiSoftRaw.c
index 47d2d8007..a71bf89cb 100644
--- a/src/std/dev/devBiSoftRaw.c
+++ b/src/std/dev/devBiSoftRaw.c
@@ -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;
}
diff --git a/src/std/dev/devBoSoftCallback.c b/src/std/dev/devBoSoftCallback.c
index 4cdb51a29..ffb68e525 100644
--- a/src/std/dev/devBoSoftCallback.c
+++ b/src/std/dev/devBoSoftCallback.c
@@ -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;
}
+
diff --git a/src/std/dev/devCalcoutSoftCallback.c b/src/std/dev/devCalcoutSoftCallback.c
index 0552df826..94f9d4f99 100644
--- a/src/std/dev/devCalcoutSoftCallback.c
+++ b/src/std/dev/devCalcoutSoftCallback.c
@@ -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;
}
+
diff --git a/src/std/dev/devEventSoft.c b/src/std/dev/devEventSoft.c
index 06717fa57..a748dda66 100644
--- a/src/std/dev/devEventSoft.c
+++ b/src/std/dev/devEventSoft.c
@@ -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;
}
diff --git a/src/std/dev/devHistogramSoft.c b/src/std/dev/devHistogramSoft.c
index c019f1849..3b46b5d99 100644
--- a/src/std/dev/devHistogramSoft.c
+++ b/src/std/dev/devHistogramSoft.c
@@ -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;
}
diff --git a/src/std/dev/devLiSoft.c b/src/std/dev/devLiSoft.c
index 07c4a1900..6d7b7fda1 100644
--- a/src/std/dev/devLiSoft.c
+++ b/src/std/dev/devLiSoft.c
@@ -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;
}
diff --git a/src/std/dev/devLoSoftCallback.c b/src/std/dev/devLoSoftCallback.c
index 2bc4a7e46..f211957b5 100644
--- a/src/std/dev/devLoSoftCallback.c
+++ b/src/std/dev/devLoSoftCallback.c
@@ -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;
}
+
diff --git a/src/std/dev/devLsiSoft.c b/src/std/dev/devLsiSoft.c
index a5547cba8..3076c9900 100644
--- a/src/std/dev/devLsiSoft.c
+++ b/src/std/dev/devLsiSoft.c
@@ -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;
}
diff --git a/src/std/dev/devLsoSoftCallback.c b/src/std/dev/devLsoSoftCallback.c
index fb95afafd..59579d558 100644
--- a/src/std/dev/devLsoSoftCallback.c
+++ b/src/std/dev/devLsoSoftCallback.c
@@ -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);
+
diff --git a/src/std/dev/devMbbiDirectSoft.c b/src/std/dev/devMbbiDirectSoft.c
index 6d10d1a51..9c929b2b6 100644
--- a/src/std/dev/devMbbiDirectSoft.c
+++ b/src/std/dev/devMbbiDirectSoft.c
@@ -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;
}
diff --git a/src/std/dev/devMbbiDirectSoftRaw.c b/src/std/dev/devMbbiDirectSoftRaw.c
index b27210a29..f6172cdec 100644
--- a/src/std/dev/devMbbiDirectSoftRaw.c
+++ b/src/std/dev/devMbbiDirectSoftRaw.c
@@ -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);
}
diff --git a/src/std/dev/devMbbiSoft.c b/src/std/dev/devMbbiSoft.c
index 9f084b7fc..b0b57144f 100644
--- a/src/std/dev/devMbbiSoft.c
+++ b/src/std/dev/devMbbiSoft.c
@@ -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;
}
diff --git a/src/std/dev/devMbbiSoftRaw.c b/src/std/dev/devMbbiSoftRaw.c
index 5b38549c7..3bd6b21da 100644
--- a/src/std/dev/devMbbiSoftRaw.c
+++ b/src/std/dev/devMbbiSoftRaw.c
@@ -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;
}
diff --git a/src/std/dev/devMbboDirectSoftCallback.c b/src/std/dev/devMbboDirectSoftCallback.c
index 8c71ef77e..e64c3d41c 100644
--- a/src/std/dev/devMbboDirectSoftCallback.c
+++ b/src/std/dev/devMbboDirectSoftCallback.c
@@ -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 */
diff --git a/src/std/dev/devMbboSoftCallback.c b/src/std/dev/devMbboSoftCallback.c
index 705308163..fd5fe405a 100644
--- a/src/std/dev/devMbboSoftCallback.c
+++ b/src/std/dev/devMbboSoftCallback.c
@@ -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;
}
+
diff --git a/src/std/dev/devPrintfSoftCallback.c b/src/std/dev/devPrintfSoftCallback.c
index 333ee1c70..e89afd53b 100644
--- a/src/std/dev/devPrintfSoftCallback.c
+++ b/src/std/dev/devPrintfSoftCallback.c
@@ -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 = {
diff --git a/src/std/dev/devSASoft.c b/src/std/dev/devSASoft.c
index 6c0c876f0..01a076e6d 100644
--- a/src/std/dev/devSASoft.c
+++ b/src/std/dev/devSASoft.c
@@ -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;
}
diff --git a/src/std/dev/devSiSoft.c b/src/std/dev/devSiSoft.c
index 46877de23..5141c1038 100644
--- a/src/std/dev/devSiSoft.c
+++ b/src/std/dev/devSiSoft.c
@@ -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;
}
diff --git a/src/std/dev/devSoSoftCallback.c b/src/std/dev/devSoSoftCallback.c
index f6d2c554e..df8c5d819 100644
--- a/src/std/dev/devSoSoftCallback.c
+++ b/src/std/dev/devSoSoftCallback.c
@@ -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;
}
+
diff --git a/src/std/dev/devWfSoft.c b/src/std/dev/devWfSoft.c
index f1754f78e..8d8295696 100644
--- a/src/std/dev/devWfSoft.c
+++ b/src/std/dev/devWfSoft.c
@@ -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;
}
diff --git a/src/std/link/Makefile b/src/std/link/Makefile
new file mode 100644
index 000000000..31d14b825
--- /dev/null
+++ b/src/std/link/Makefile
@@ -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
+
diff --git a/src/std/link/links.dbd.pod b/src/std/link/links.dbd.pod
new file mode 100644
index 000000000..ceb6ced77
--- /dev/null
+++ b/src/std/link/links.dbd.pod
@@ -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
+
+=item * L
+
+=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) 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 routine is called. Most record types
+support the use of constant links by calling C 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 and C 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, C, C, ... C. 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 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 field of a record.
+
+=back
+
+=head4 Example
+
+ {calc: {expr:"A*B", args:[{db:"record.VAL"}, 1.5], prec:3}}
+
+=cut
diff --git a/src/std/link/lnkCalc.c b/src/std/link/lnkCalc.c
new file mode 100644
index 000000000..286b61702
--- /dev/null
+++ b/src/std/link/lnkCalc.c
@@ -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
+#include
+#include
+
+#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);
+
diff --git a/src/std/link/lnkConst.c b/src/std/link/lnkConst.c
new file mode 100644
index 000000000..db824abbc
--- /dev/null
+++ b/src/std/link/lnkConst.c
@@ -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
+#include
+#include
+
+#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; inElems; 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);
+
diff --git a/src/std/rec/aSubRecord.c b/src/std/rec/aSubRecord.c
index 727c369ac..b513df17a 100644
--- a/src/std/rec/aSubRecord.c
+++ b/src/std/rec/aSubRecord.c
@@ -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);
diff --git a/src/std/rec/aaiRecord.c b/src/std/rec/aaiRecord.c
index 43cb73b10..0dfed346c 100644
--- a/src/std/rec/aaiRecord.c
+++ b/src/std/rec/aaiRecord.c
@@ -141,10 +141,7 @@ static long init_record(struct dbCommon *pcommon, int pass)
return 0;
}
- /* SIML must be a CONSTANT or a PV_LINK or a DB_LINK */
- if (prec->siml.type == CONSTANT) {
- recGblInitConstantLink(&prec->siml,DBF_USHORT,&prec->simm);
- }
+ recGblInitConstantLink(&prec->siml,DBF_USHORT,&prec->simm);
/* must have read_aai function defined */
if (pdset->number < 5 || pdset->read_aai == NULL) {
diff --git a/src/std/rec/aaoRecord.c b/src/std/rec/aaoRecord.c
index 853b92cfa..1cc2541eb 100644
--- a/src/std/rec/aaoRecord.c
+++ b/src/std/rec/aaoRecord.c
@@ -141,10 +141,7 @@ static long init_record(struct dbCommon *pcommon, int pass)
return 0;
}
- /* SIML must be a CONSTANT or a PV_LINK or a DB_LINK */
- if (prec->siml.type == CONSTANT) {
- recGblInitConstantLink(&prec->siml,DBF_USHORT,&prec->simm);
- }
+ recGblInitConstantLink(&prec->siml,DBF_USHORT,&prec->simm);
/* must have write_aao function defined */
if (pdset->number < 5 || pdset->write_aao == NULL) {
diff --git a/src/std/rec/aiRecord.c b/src/std/rec/aiRecord.c
index 31ce25ac6..81f730f6d 100644
--- a/src/std/rec/aiRecord.c
+++ b/src/std/rec/aiRecord.c
@@ -110,15 +110,8 @@ static long init_record(struct dbCommon *pcommon, int pass)
if (pass==0) return(0);
- /* ai.siml must be a CONSTANT or a PV_LINK or a DB_LINK */
- if (prec->siml.type == CONSTANT) {
- recGblInitConstantLink(&prec->siml,DBF_USHORT,&prec->simm);
- }
-
- /* ai.siol must be a CONSTANT or a PV_LINK or a DB_LINK */
- if (prec->siol.type == CONSTANT) {
- recGblInitConstantLink(&prec->siol,DBF_DOUBLE,&prec->sval);
- }
+ recGblInitConstantLink(&prec->siml,DBF_USHORT,&prec->simm);
+ recGblInitConstantLink(&prec->siol,DBF_DOUBLE,&prec->sval);
if(!(pdset = (aidset *)(prec->dset))) {
recGblRecordError(S_dev_noDSET,(void *)prec,"ai: init_record");
diff --git a/src/std/rec/aoRecord.c b/src/std/rec/aoRecord.c
index 3851de97f..0d923af5c 100644
--- a/src/std/rec/aoRecord.c
+++ b/src/std/rec/aoRecord.c
@@ -110,20 +110,15 @@ static long init_record(struct dbCommon *pcommon, int pass)
if (pass==0) return(0);
- /* ao.siml must be a CONSTANT or a PV_LINK or a DB_LINK */
- if (prec->siml.type == CONSTANT) {
- recGblInitConstantLink(&prec->siml,DBF_USHORT,&prec->simm);
- }
+ recGblInitConstantLink(&prec->siml,DBF_USHORT,&prec->simm);
if(!(pdset = (struct aodset *)(prec->dset))) {
recGblRecordError(S_dev_noDSET,(void *)prec,"ao: init_record");
return(S_dev_noDSET);
}
/* get the initial value if dol is a constant*/
- if (prec->dol.type == CONSTANT) {
- if(recGblInitConstantLink(&prec->dol,DBF_DOUBLE,&prec->val))
- prec->udf = isnan(prec->val);
- }
+ if (recGblInitConstantLink(&prec->dol,DBF_DOUBLE,&prec->val))
+ prec->udf = isnan(prec->val);
/* must have write_ao function defined */
if ((pdset->number < 6) || (pdset->write_ao ==NULL)) {
@@ -191,8 +186,8 @@ static long process(struct dbCommon *pcommon)
/* fetch value and convert*/
if (prec->pact == FALSE) {
- if ((prec->dol.type != CONSTANT)
- && (prec->omsl == menuOmslclosed_loop)) {
+ if (!dbLinkIsConstant(&prec->dol) &&
+ prec->omsl == menuOmslclosed_loop) {
status = fetch_value(prec, &value);
}
else {
diff --git a/src/std/rec/boRecord.c b/src/std/rec/boRecord.c
index 537362cf3..8f60a0be5 100644
--- a/src/std/rec/boRecord.c
+++ b/src/std/rec/boRecord.c
@@ -131,44 +131,40 @@ static void myCallbackFunc(CALLBACK *arg)
static long init_record(struct dbCommon *pcommon,int pass)
{
struct boRecord *prec = (struct boRecord *)pcommon;
- struct bodset *pdset;
- long status=0;
+ struct bodset *pdset = (struct bodset *) prec->dset;
+ unsigned short ival = 0;
+ long status = 0;
myCallback *pcallback;
- if (pass==0) return(0);
+ if (pass == 0)
+ return 0;
- /* bo.siml must be a CONSTANT or a PV_LINK or a DB_LINK */
- if (prec->siml.type == CONSTANT) {
- recGblInitConstantLink(&prec->siml,DBF_USHORT,&prec->simm);
+ recGblInitConstantLink(&prec->siml, DBF_USHORT, &prec->simm);
+
+ if (!pdset) {
+ recGblRecordError(S_dev_noDSET, prec, "bo: init_record");
+ return S_dev_noDSET;
}
- if(!(pdset = (struct bodset *)(prec->dset))) {
- recGblRecordError(S_dev_noDSET,(void *)prec,"bo: init_record");
- return(S_dev_noDSET);
- }
/* must have write_bo functions defined */
- if( (pdset->number < 5) || (pdset->write_bo == NULL) ) {
- recGblRecordError(S_dev_missingSup,(void *)prec,"bo: init_record");
- return(S_dev_missingSup);
+ if ((pdset->number < 5) || (pdset->write_bo == NULL)) {
+ recGblRecordError(S_dev_missingSup, prec, "bo: init_record");
+ return S_dev_missingSup;
}
+
/* get the initial value */
- if (prec->dol.type == CONSTANT) {
- unsigned short ival = 0;
-
- if(recGblInitConstantLink(&prec->dol,DBF_USHORT,&ival)) {
- if (ival == 0) prec->val = 0;
- else prec->val = 1;
- prec->udf = FALSE;
- }
+ if (recGblInitConstantLink(&prec->dol, DBF_USHORT, &ival)) {
+ prec->val = !!ival;
+ prec->udf = FALSE;
}
- pcallback = (myCallback *)(calloc(1,sizeof(myCallback)));
- prec->rpvt = (void *)pcallback;
- callbackSetCallback(myCallbackFunc,&pcallback->callback);
- callbackSetUser(pcallback,&pcallback->callback);
- pcallback->precord = (struct dbCommon *)prec;
+ pcallback = (myCallback *) calloc(1, sizeof(myCallback));
+ prec->rpvt = pcallback;
+ callbackSetCallback(myCallbackFunc, &pcallback->callback);
+ callbackSetUser(pcallback, &pcallback->callback);
+ pcallback->precord = (struct dbCommon *) prec;
- if( pdset->init_record ) {
+ if (pdset->init_record) {
status=(*pdset->init_record)(prec);
if(status==0) {
if(prec->rval==0) prec->val = 0;
@@ -203,7 +199,8 @@ static long process(struct dbCommon *pcommon)
return(S_dev_missingSup);
}
if (!prec->pact) {
- if ((prec->dol.type != CONSTANT) && (prec->omsl == menuOmslclosed_loop)){
+ if (!dbLinkIsConstant(&prec->dol) &&
+ prec->omsl == menuOmslclosed_loop) {
unsigned short val;
prec->pact = TRUE;
diff --git a/src/std/rec/calcRecord.c b/src/std/rec/calcRecord.c
index 9c4e02317..12a58d1c8 100644
--- a/src/std/rec/calcRecord.c
+++ b/src/std/rec/calcRecord.c
@@ -99,9 +99,7 @@ static long init_record(struct dbCommon *pcommon, int pass)
plink = &prec->inpa;
pvalue = &prec->a;
for (i = 0; i < CALCPERFORM_NARGS; i++, plink++, pvalue++) {
- if (plink->type == CONSTANT) {
- recGblInitConstantLink(plink, DBF_DOUBLE, pvalue);
- }
+ recGblInitConstantLink(plink, DBF_DOUBLE, pvalue);
}
if (postfix(prec->calc, prec->rpcl, &error_number)) {
recGblRecordError(S_db_badField, (void *)prec,
diff --git a/src/std/rec/calcoutRecord.c b/src/std/rec/calcoutRecord.c
index da6cf42d7..8b5525527 100644
--- a/src/std/rec/calcoutRecord.c
+++ b/src/std/rec/calcoutRecord.c
@@ -25,6 +25,7 @@
#include "dbDefs.h"
#include "dbAccess.h"
#include "dbEvent.h"
+#include "dbLink.h"
#include "dbScan.h"
#include "cantProceed.h"
#include "epicsMath.h"
@@ -99,15 +100,15 @@ typedef struct calcoutDSET {
}calcoutDSET;
-/* To provide feedback to the user as to the connection status of the
+/* To provide feedback to the user as to the connection status of the
* links (.INxV and .OUTV), the following algorithm has been implemented ...
*
- * A new PV_LINK [either in init() or special()] is searched for using
- * dbNameToAddr. If local, it is so indicated. If not, a checkLinkCb
- * callback is scheduled to check the connectivity later using
- * dbCaIsLinkConnected(). Anytime there are unconnected CA_LINKs, another
+ * A new PV_LINK is checked [in both init() and special()] to see if the
+ * target is local -- if so it is marked as such. If not, a checkLinkCb
+ * callback is scheduled to check the connection status later by calling
+ * dbIsLinkConnected(). Anytime there are unconnected CA_LINKs, another
* callback is scheduled. Once all connections are established, the CA_LINKs
- * are checked whenever the record processes.
+ * are checked whenever the record processes.
*
*/
@@ -142,42 +143,57 @@ static long init_record(struct dbCommon *pcommon, int pass)
epicsEnum16 *plinkValid;
short error_number;
calcoutDSET *pcalcoutDSET;
-
- DBADDR dbaddr;
- DBADDR *pAddr = &dbaddr;
rpvtStruct *prpvt;
if (pass == 0) {
prec->rpvt = (rpvtStruct *) callocMustSucceed(1, sizeof(rpvtStruct), "calcoutRecord");
return 0;
}
+
if (!(pcalcoutDSET = (calcoutDSET *)prec->dset)) {
recGblRecordError(S_dev_noDSET, (void *)prec, "calcout:init_record");
return S_dev_noDSET;
}
+
/* must have write defined */
if ((pcalcoutDSET->number < 5) || (pcalcoutDSET->write ==NULL)) {
recGblRecordError(S_dev_missingSup, (void *)prec, "calcout:init_record");
return S_dev_missingSup;
}
+
prpvt = prec->rpvt;
plink = &prec->inpa;
pvalue = &prec->a;
plinkValid = &prec->inav;
+
for (i = 0; i <= CALCPERFORM_NARGS; i++, plink++, pvalue++, plinkValid++) {
- if (plink->type == CONSTANT) {
- /* Don't InitConstantLink the .OUT link */
- if (i < CALCPERFORM_NARGS) {
- recGblInitConstantLink(plink, DBF_DOUBLE, pvalue);
- }
+ /* Don't InitConstantLink the .OUT link */
+ if (i < CALCPERFORM_NARGS) {
+ recGblInitConstantLink(plink, DBF_DOUBLE, pvalue);
+ }
+
+ if (dbLinkIsConstant(plink)) {
*plinkValid = calcoutINAV_CON;
- } else if (!dbNameToAddr(plink->value.pv_link.pvname, pAddr)) {
- /* PV resides on this ioc */
+ }
+ else if (dbLinkIsVolatile(plink)) {
+ int conn = dbIsLinkConnected(plink);
+
+ if (conn)
+ *plinkValid = calcoutINAV_EXT;
+ else {
+ /* Monitor for connection */
+ *plinkValid = calcoutINAV_EXT_NC;
+ prpvt->caLinkStat = CA_LINKS_NOT_OK;
+ }
+ }
+ else {
+ /* PV must reside on this ioc */
*plinkValid = calcoutINAV_LOC;
- } else {
- /* pv is not on this ioc. Callback later for connection stat */
- *plinkValid = calcoutINAV_EXT_NC;
- prpvt->caLinkStat = CA_LINKS_NOT_OK;
+
+ if (!dbIsLinkConnected(plink)) {
+ errlogPrintf("calcout: %s.INP%c in no-vo disco state\n",
+ prec->name, i+'A');
+ }
}
}
@@ -300,8 +316,6 @@ static long special(DBADDR *paddr, int after)
{
calcoutRecord *prec = (calcoutRecord *)paddr->precord;
rpvtStruct *prpvt = prec->rpvt;
- DBADDR dbaddr;
- DBADDR *pAddr = &dbaddr;
short error_number;
int fieldIndex = dbGetFieldIndex(paddr);
int lnkIndex;
@@ -349,23 +363,36 @@ static long special(DBADDR *paddr, int after)
plink = &prec->inpa + lnkIndex;
pvalue = &prec->a + lnkIndex;
plinkValid = &prec->inav + lnkIndex;
- if (plink->type == CONSTANT) {
- if (fieldIndex != calcoutRecordOUT) {
- recGblInitConstantLink(plink, DBF_DOUBLE, pvalue);
- db_post_events(prec, pvalue, DBE_VALUE);
- }
+
+ if (fieldIndex != calcoutRecordOUT)
+ recGblInitConstantLink(plink, DBF_DOUBLE, pvalue);
+
+ if (dbLinkIsConstant(plink)) {
+ db_post_events(prec, pvalue, DBE_VALUE);
*plinkValid = calcoutINAV_CON;
- } else if (!dbNameToAddr(plink->value.pv_link.pvname, pAddr)) {
- /* if the PV resides on this ioc */
+ } else if (dbLinkIsVolatile(plink)) {
+ int conn = dbIsLinkConnected(plink);
+
+ if (conn)
+ *plinkValid = calcoutINAV_EXT;
+ else {
+ /* Monitor for connection */
+ *plinkValid = calcoutINAV_EXT_NC;
+ /* DO_CALLBACK, if not already scheduled */
+ if (!prpvt->cbScheduled) {
+ callbackRequestDelayed(&prpvt->checkLinkCb, .5);
+ prpvt->cbScheduled = 1;
+ prpvt->caLinkStat = CA_LINKS_NOT_OK;
+ }
+ }
+ }
+ else {
+ /* PV must reside on this ioc */
*plinkValid = calcoutINAV_LOC;
- } else {
- /* pv is not on this ioc. Callback later for connection stat */
- *plinkValid = calcoutINAV_EXT_NC;
- /* DO_CALLBACK, if not already scheduled */
- if (!prpvt->cbScheduled) {
- callbackRequestDelayed(&prpvt->checkLinkCb, .5);
- prpvt->cbScheduled = 1;
- prpvt->caLinkStat = CA_LINKS_NOT_OK;
+
+ if (!dbIsLinkConnected(plink)) {
+ errlogPrintf("calcout: %s.INP%c in no-vo diso state\n",
+ prec->name, lnkIndex);
}
}
db_post_events(prec, plinkValid, DBE_VALUE);
@@ -708,9 +735,9 @@ static void checkLinks(calcoutRecord *prec)
plinkValid = &prec->inav;
for (i = 0; itype == CA_LINK) {
+ if (dbLinkIsVolatile(plink)) {
caLink = 1;
- stat = dbCaIsLinkConnected(plink);
+ stat = dbIsLinkConnected(plink);
if (!stat && (*plinkValid == calcoutINAV_EXT_NC)) {
caLinkNc = 1;
}
diff --git a/src/std/rec/dfanoutRecord.c b/src/std/rec/dfanoutRecord.c
index 76ee31bf3..4672fc413 100644
--- a/src/std/rec/dfanoutRecord.c
+++ b/src/std/rec/dfanoutRecord.c
@@ -94,13 +94,16 @@ static void push_values(dfanoutRecord *);
static long init_record(struct dbCommon *pcommon, int pass)
{
struct dfanoutRecord *prec = (struct dfanoutRecord *)pcommon;
- if (pass==0) return(0);
+ if (pass==0)
+ return 0;
+
+ recGblInitConstantLink(&prec->sell, DBF_USHORT, &prec->seln);
- recGblInitConstantLink(&prec->sell,DBF_USHORT,&prec->seln);
/* get the initial value dol is a constant*/
- if(recGblInitConstantLink(&prec->dol,DBF_DOUBLE,&prec->val))
- prec->udf = isnan(prec->val);
- return(0);
+ if (recGblInitConstantLink(&prec->dol, DBF_DOUBLE, &prec->val))
+ prec->udf = isnan(prec->val);
+
+ return 0;
}
static long process(struct dbCommon *pcommon)
@@ -108,11 +111,11 @@ static long process(struct dbCommon *pcommon)
struct dfanoutRecord *prec = (struct dfanoutRecord *)pcommon;
long status=0;
- if (!prec->pact
- && (prec->dol.type != CONSTANT)
- && (prec->omsl == menuOmslclosed_loop)){
- status = dbGetLink(&(prec->dol),DBR_DOUBLE,&(prec->val),0,0);
- if(prec->dol.type!=CONSTANT && RTN_SUCCESS(status))
+ if (!prec->pact &&
+ !dbLinkIsConstant(&prec->dol) &&
+ prec->omsl == menuOmslclosed_loop) {
+ status = dbGetLink(&prec->dol, DBR_DOUBLE, &prec->val, 0, 0);
+ if (!dbLinkIsConstant(&prec->dol) && !status)
prec->udf = isnan(prec->val);
}
prec->pact = TRUE;
diff --git a/src/std/rec/eventRecord.c b/src/std/rec/eventRecord.c
index 602023a53..38a4ad62f 100644
--- a/src/std/rec/eventRecord.c
+++ b/src/std/rec/eventRecord.c
@@ -99,18 +99,14 @@ static long init_record(struct dbCommon *pcommon, int pass)
if (pass==0) return(0);
- if (prec->siml.type == CONSTANT) {
- recGblInitConstantLink(&prec->siml,DBF_USHORT,&prec->simm);
- }
-
- if (prec->siol.type == CONSTANT) {
- recGblInitConstantLink(&prec->siol,DBF_STRING,&prec->sval);
- }
-
- prec->epvt = eventNameToHandle(prec->val);
+ recGblInitConstantLink(&prec->siml, DBF_USHORT, &prec->simm);
+ recGblInitConstantLink(&prec->siol, DBF_STRING, &prec->sval);
if( (pdset=(struct eventdset *)(prec->dset)) && (pdset->init_record) )
status=(*pdset->init_record)(prec);
+
+ prec->epvt = eventNameToHandle(prec->val);
+
return(status);
}
diff --git a/src/std/rec/fanoutRecord.c b/src/std/rec/fanoutRecord.c
index b5c1e72ae..3ac6b66c6 100644
--- a/src/std/rec/fanoutRecord.c
+++ b/src/std/rec/fanoutRecord.c
@@ -106,8 +106,7 @@ static long process(struct dbCommon *pcommon)
case fanoutSELM_All:
plink = &prec->lnk0;
for (i = 0; i < NLINKS; i++, plink++) {
- if (plink->type != CONSTANT)
- dbScanFwdLink(plink);
+ dbScanFwdLink(plink);
}
break;
@@ -134,7 +133,7 @@ static long process(struct dbCommon *pcommon)
break;
plink = &prec->lnk0;
for (i = 0; i < NLINKS; i++, seln >>= 1, plink++) {
- if (seln & 1 && plink->type != CONSTANT)
+ if (seln & 1)
dbScanFwdLink(plink);
}
break;
diff --git a/src/std/rec/histogramRecord.c b/src/std/rec/histogramRecord.c
index 84010ee24..a9563500f 100644
--- a/src/std/rec/histogramRecord.c
+++ b/src/std/rec/histogramRecord.c
@@ -182,13 +182,8 @@ static long init_record(struct dbCommon *pcommon, int pass)
wdogInit(prec);
- if (prec->siml.type == CONSTANT) {
- recGblInitConstantLink(&prec->siml, DBF_USHORT, &prec->simm);
- }
-
- if (prec->siol.type == CONSTANT) {
- recGblInitConstantLink(&prec->siol, DBF_DOUBLE, &prec->sval);
- }
+ recGblInitConstantLink(&prec->siml, DBF_USHORT, &prec->simm);
+ recGblInitConstantLink(&prec->siol, DBF_DOUBLE, &prec->sval);
/* must have device support defined */
pdset = (struct histogramdset *) prec->dset;
diff --git a/src/std/rec/longinRecord.c b/src/std/rec/longinRecord.c
index dc7eb1b43..bd3c24ab9 100644
--- a/src/std/rec/longinRecord.c
+++ b/src/std/rec/longinRecord.c
@@ -97,37 +97,36 @@ static long readValue(longinRecord *prec);
static long init_record(struct dbCommon *pcommon, int pass)
{
struct longinRecord *prec = (struct longinRecord *)pcommon;
- struct longindset *pdset;
- long status;
+ struct longindset *pdset = (struct longindset *) prec->dset;
- if (pass==0) return(0);
+ if (pass==0)
+ return(0);
- /* longin.siml must be a CONSTANT or a PV_LINK or a DB_LINK */
- if (prec->siml.type == CONSTANT) {
- recGblInitConstantLink(&prec->siml,DBF_USHORT,&prec->simm);
+ recGblInitConstantLink(&prec->siml, DBF_USHORT, &prec->simm);
+ recGblInitConstantLink(&prec->siol, DBF_LONG, &prec->sval);
+
+ if (!pdset) {
+ recGblRecordError(S_dev_noDSET, prec, "longin: init_record");
+ return S_dev_noDSET;
}
- /* longin.siol must be a CONSTANT or a PV_LINK or a DB_LINK */
- if (prec->siol.type == CONSTANT) {
- recGblInitConstantLink(&prec->siol,DBF_LONG,&prec->sval);
- }
-
- if(!(pdset = (struct longindset *)(prec->dset))) {
- recGblRecordError(S_dev_noDSET,(void *)prec,"longin: init_record");
- return(S_dev_noDSET);
- }
/* must have read_longin function defined */
- if( (pdset->number < 5) || (pdset->read_longin == NULL) ) {
- recGblRecordError(S_dev_missingSup,(void *)prec,"longin: init_record");
- return(S_dev_missingSup);
+ if ((pdset->number < 5) || (pdset->read_longin == NULL)) {
+ recGblRecordError(S_dev_missingSup, prec, "longin: init_record");
+ return S_dev_missingSup;
}
- if( pdset->init_record ) {
- if((status=(*pdset->init_record)(prec))) return(status);
+
+ if (pdset->init_record) {
+ long status = pdset->init_record(prec);
+
+ if (status)
+ return status;
}
+
prec->mlst = prec->val;
prec->alst = prec->val;
prec->lalm = prec->val;
- return(0);
+ return 0;
}
static long process(struct dbCommon *pcommon)
diff --git a/src/std/rec/longoutRecord.c b/src/std/rec/longoutRecord.c
index 8a9c1cf4e..6062ad741 100644
--- a/src/std/rec/longoutRecord.c
+++ b/src/std/rec/longoutRecord.c
@@ -94,33 +94,38 @@ static void convert(longoutRecord *prec, epicsInt32 value);
static long init_record(struct dbCommon *pcommon, int pass)
{
struct longoutRecord *prec = (struct longoutRecord *)pcommon;
- struct longoutdset *pdset;
- long status=0;
+ struct longoutdset *pdset = (struct longoutdset *) prec->dset;
- if (pass==0) return(0);
- if (prec->siml.type == CONSTANT) {
- recGblInitConstantLink(&prec->siml,DBF_USHORT,&prec->simm);
- }
- if(!(pdset = (struct longoutdset *)(prec->dset))) {
- recGblRecordError(S_dev_noDSET,(void *)prec,"longout: init_record");
- return(S_dev_noDSET);
+ if (pass==0)
+ return 0;
+
+ recGblInitConstantLink(&prec->siml, DBF_USHORT, &prec->simm);
+
+ if (!pdset) {
+ recGblRecordError(S_dev_noDSET, prec, "longout: init_record");
+ return S_dev_noDSET;
}
+
/* must have write_longout functions defined */
- if( (pdset->number < 5) || (pdset->write_longout == NULL) ) {
- recGblRecordError(S_dev_missingSup,(void *)prec,"longout: init_record");
- return(S_dev_missingSup);
+ if ((pdset->number < 5) || (pdset->write_longout == NULL)) {
+ recGblRecordError(S_dev_missingSup, prec, "longout: init_record");
+ return S_dev_missingSup;
}
- if (prec->dol.type == CONSTANT) {
- if(recGblInitConstantLink(&prec->dol,DBF_LONG,&prec->val))
- prec->udf=FALSE;
- }
- if( pdset->init_record ) {
- if((status=(*pdset->init_record)(prec))) return(status);
+
+ if (recGblInitConstantLink(&prec->dol, DBF_LONG, &prec->val))
+ prec->udf=FALSE;
+
+ if (pdset->init_record) {
+ long status = pdset->init_record(prec);
+
+ if (status)
+ return status;
}
+
prec->mlst = prec->val;
prec->alst = prec->val;
prec->lalm = prec->val;
- return(0);
+ return 0;
}
static long process(struct dbCommon *pcommon)
@@ -137,11 +142,10 @@ static long process(struct dbCommon *pcommon)
return(S_dev_missingSup);
}
if (!prec->pact) {
- if((prec->dol.type != CONSTANT)
- && (prec->omsl == menuOmslclosed_loop)) {
- status = dbGetLink(&(prec->dol),DBR_LONG,
- &value,0,0);
- if (prec->dol.type!=CONSTANT && RTN_SUCCESS(status))
+ if (!dbLinkIsConstant(&prec->dol) &&
+ prec->omsl == menuOmslclosed_loop) {
+ status = dbGetLink(&prec->dol, DBR_LONG, &value, 0, 0);
+ if (!dbLinkIsConstant(&prec->dol) && !status)
prec->udf=FALSE;
}
else {
diff --git a/src/std/rec/mbbiDirectRecord.c b/src/std/rec/mbbiDirectRecord.c
index 3d1b3f8b6..112272925 100644
--- a/src/std/rec/mbbiDirectRecord.c
+++ b/src/std/rec/mbbiDirectRecord.c
@@ -113,11 +113,8 @@ static long init_record(struct dbCommon *pcommon, int pass)
return S_dev_missingSup;
}
- if (prec->siml.type == CONSTANT)
- recGblInitConstantLink(&prec->siml, DBF_USHORT, &prec->simm);
-
- if (prec->siol.type == CONSTANT)
- recGblInitConstantLink(&prec->siol, DBF_USHORT, &prec->sval);
+ recGblInitConstantLink(&prec->siml, DBF_USHORT, &prec->simm);
+ recGblInitConstantLink(&prec->siol, DBF_USHORT, &prec->sval);
/* Initialize MASK if the user set NOBT instead */
if (prec->mask == 0 && prec->nobt <= 32)
diff --git a/src/std/rec/mbbiRecord.c b/src/std/rec/mbbiRecord.c
index 72000f45f..ad5a8ce06 100644
--- a/src/std/rec/mbbiRecord.c
+++ b/src/std/rec/mbbiRecord.c
@@ -131,11 +131,8 @@ static long init_record(struct dbCommon *pcommon, int pass)
return S_dev_missingSup;
}
- if (prec->siml.type == CONSTANT)
- recGblInitConstantLink(&prec->siml, DBF_USHORT, &prec->simm);
-
- if (prec->siol.type == CONSTANT)
- recGblInitConstantLink(&prec->siol, DBF_USHORT, &prec->sval);
+ recGblInitConstantLink(&prec->siml, DBF_USHORT, &prec->simm);
+ recGblInitConstantLink(&prec->siol, DBF_USHORT, &prec->sval);
/* Initialize MASK if the user set NOBT instead */
if (prec->mask == 0 && prec->nobt <= 32)
diff --git a/src/std/rec/mbboDirectRecord.c b/src/std/rec/mbboDirectRecord.c
index b807a98ea..0a6e3391e 100644
--- a/src/std/rec/mbboDirectRecord.c
+++ b/src/std/rec/mbboDirectRecord.c
@@ -115,12 +115,9 @@ static long init_record(struct dbCommon *pcommon, int pass)
return S_dev_missingSup;
}
- if (prec->siml.type == CONSTANT)
- recGblInitConstantLink(&prec->siml, DBF_USHORT, &prec->simm);
-
- if (prec->dol.type == CONSTANT)
- if (recGblInitConstantLink(&prec->dol, DBF_USHORT, &prec->val))
- prec->udf = FALSE;
+ recGblInitConstantLink(&prec->siml, DBF_USHORT, &prec->simm);
+ if (recGblInitConstantLink(&prec->dol, DBF_USHORT, &prec->val))
+ prec->udf = FALSE;
/* Initialize MASK if the user set NOBT instead */
if (prec->mask == 0 && prec->nobt <= 32)
@@ -175,7 +172,7 @@ static long process(struct dbCommon *pcommon)
}
if (!pact) {
- if (prec->dol.type != CONSTANT &&
+ if (!dbLinkIsConstant(&prec->dol) &&
prec->omsl == menuOmslclosed_loop) {
epicsUInt16 val;
diff --git a/src/std/rec/mbboRecord.c b/src/std/rec/mbboRecord.c
index c3e89a1ca..0ae6a815a 100644
--- a/src/std/rec/mbboRecord.c
+++ b/src/std/rec/mbboRecord.c
@@ -135,12 +135,9 @@ static long init_record(struct dbCommon *pcommon, int pass)
return S_dev_missingSup;
}
- if (prec->siml.type == CONSTANT)
- recGblInitConstantLink(&prec->siml, DBF_USHORT, &prec->simm);
-
- if (prec->dol.type == CONSTANT)
- if (recGblInitConstantLink(&prec->dol, DBF_USHORT, &prec->val))
- prec->udf = FALSE;
+ recGblInitConstantLink(&prec->siml, DBF_USHORT, &prec->simm);
+ if (recGblInitConstantLink(&prec->dol, DBF_USHORT, &prec->val))
+ prec->udf = FALSE;
/* Initialize MASK if the user set NOBT instead */
if (prec->mask == 0 && prec->nobt <= 32)
@@ -206,7 +203,7 @@ static long process(struct dbCommon *pcommon)
}
if (!pact) {
- if (prec->dol.type != CONSTANT &&
+ if (!dbLinkIsConstant(&prec->dol) &&
prec->omsl == menuOmslclosed_loop) {
epicsUInt16 val;
diff --git a/src/std/rec/printfRecord.c b/src/std/rec/printfRecord.c
index 79afb1078..7011453d4 100644
--- a/src/std/rec/printfRecord.c
+++ b/src/std/rec/printfRecord.c
@@ -46,7 +46,7 @@
VALTYPE val; \
int ok; \
\
- if (plink->type == CONSTANT) \
+ if (dbLinkIsConstant(plink)) \
ok = recGblInitConstantLink(plink++, DBRTYPE, &val); \
else \
ok = ! dbGetLink(plink++, DBRTYPE, &val, 0, 0); \
@@ -113,7 +113,7 @@ static void doPrintf(printfRecord *prec)
epicsInt16 i;
int ok;
- if (plink->type == CONSTANT)
+ if (dbLinkIsConstant(plink))
ok = recGblInitConstantLink(plink++, DBR_SHORT, &i);
else
ok = ! dbGetLink(plink++, DBR_SHORT, &i, 0, 0);
@@ -204,8 +204,9 @@ static void doPrintf(printfRecord *prec)
break;
case 's':
- if (flags & F_LONG && plink->type != CONSTANT) {
+ if (flags & F_LONG) {
long n = vspace + 1;
+ long status;
if (precision && n > precision)
n = precision + 1;
@@ -213,7 +214,14 @@ static void doPrintf(printfRecord *prec)
* characters to be printed from the string.
* It does not limit the field width however.
*/
- if (dbGetLink(plink++, DBR_CHAR, pval, 0, &n))
+ if (dbLinkIsConstant(plink)) {
+ epicsUInt32 len = n;
+ status = dbLoadLinkLS(plink++, pval, n, &len);
+ n = len;
+ }
+ else
+ status = dbGetLink(plink++, DBR_CHAR, pval, 0, &n);
+ if (status)
flags |= F_BADLNK;
else {
int padding;
@@ -252,7 +260,7 @@ static void doPrintf(printfRecord *prec)
char val[MAX_STRING_SIZE];
int ok;
- if (plink->type == CONSTANT)
+ if (dbLinkIsConstant(plink))
ok = recGblInitConstantLink(plink++, DBR_STRING, val);
else
ok = ! dbGetLink(plink++, DBR_STRING, val, 0, 0);
diff --git a/src/std/rec/selRecord.c b/src/std/rec/selRecord.c
index de5e01712..56a995c8f 100644
--- a/src/std/rec/selRecord.c
+++ b/src/std/rec/selRecord.c
@@ -91,22 +91,19 @@ static long init_record(struct dbCommon *pcommon, int pass)
int i;
double *pvalue;
- if (pass==0) return(0);
+ if (pass==0)
+ return 0;
/* get seln initial value if nvl is a constant*/
- if (prec->nvl.type == CONSTANT ) {
- recGblInitConstantLink(&prec->nvl,DBF_USHORT,&prec->seln);
- }
+ recGblInitConstantLink(&prec->nvl, DBF_USHORT, &prec->seln);
plink = &prec->inpa;
pvalue = &prec->a;
- for(i=0; itype==CONSTANT) {
- recGblInitConstantLink(plink,DBF_DOUBLE,pvalue);
- }
+ for (i=0; icallback);
prec->dpvt = pseqRecPvt;
- if (prec->sell.type == CONSTANT)
- recGblInitConstantLink(&prec->sell, DBF_USHORT, &prec->seln);
+ recGblInitConstantLink(&prec->sell, DBF_USHORT, &prec->seln);
grp = (linkGrp *) &prec->dly0;
for (index = 0; index < NUM_LINKS; index++, grp++) {
- if (grp->dol.type == CONSTANT)
- recGblInitConstantLink(&grp->dol, DBF_DOUBLE, &grp->dov);
+ recGblInitConstantLink(&grp->dol, DBF_DOUBLE, &grp->dov);
}
prec->oldn = prec->seln;
@@ -150,8 +148,7 @@ static long process(struct dbCommon *pcommon)
lmask = (1 << NUM_LINKS) - 1;
else {
/* Get SELN value */
- if (prec->sell.type != CONSTANT)
- dbGetLink(&prec->sell, DBR_USHORT, &prec->seln, 0, 0);
+ dbGetLink(&prec->sell, DBR_USHORT, &prec->seln, 0, 0);
if (prec->selm == seqSELM_Specified) {
int grpn = prec->seln + prec->offs;
@@ -185,7 +182,8 @@ static long process(struct dbCommon *pcommon)
pgrp = (linkGrp *) &prec->dly0;
for (i = 0; lmask; lmask >>= 1) {
if ((lmask & 1) &&
- (pgrp->lnk.type != CONSTANT || pgrp->dol.type != CONSTANT)) {
+ (!dbLinkIsConstant(&pgrp->lnk) ||
+ !dbLinkIsConstant(&pgrp->dol))) {
pcb->grps[i++] = pgrp;
}
pgrp++;
diff --git a/src/std/rec/stringinRecord.c b/src/std/rec/stringinRecord.c
index 28d4aaed0..163b23a49 100644
--- a/src/std/rec/stringinRecord.c
+++ b/src/std/rec/stringinRecord.c
@@ -94,34 +94,33 @@ static long init_record(struct dbCommon *pcommon, int pass)
{
struct stringinRecord *prec = (struct stringinRecord *)pcommon;
STATIC_ASSERT(sizeof(prec->oval)==sizeof(prec->val));
- struct stringindset *pdset;
- long status;
+ struct stringindset *pdset = (struct stringindset *) prec->dset;
- if (pass==0) return(0);
+ if (pass==0)
+ return 0;
- if (prec->siml.type == CONSTANT) {
- recGblInitConstantLink(&prec->siml,DBF_USHORT,&prec->simm);
+ recGblInitConstantLink(&prec->siml, DBF_USHORT, &prec->simm);
+ recGblInitConstantLink(&prec->siol, DBF_STRING, prec->sval);
+
+ if (!pdset) {
+ recGblRecordError(S_dev_noDSET, prec, "stringin: init_record");
+ return S_dev_noDSET;
}
- /* stringin.siol must be a CONSTANT or a PV_LINK or a DB_LINK */
- if (prec->siol.type == CONSTANT) {
- recGblInitConstantLink(&prec->siol,DBF_STRING,prec->sval);
- }
-
- if(!(pdset = (struct stringindset *)(prec->dset))) {
- recGblRecordError(S_dev_noDSET,(void *)prec,"stringin: init_record");
- return(S_dev_noDSET);
- }
/* must have read_stringin function defined */
- if( (pdset->number < 5) || (pdset->read_stringin == NULL) ) {
- recGblRecordError(S_dev_missingSup,(void *)prec,"stringin: init_record");
- return(S_dev_missingSup);
+ if ((pdset->number < 5) || (pdset->read_stringin == NULL)) {
+ recGblRecordError(S_dev_missingSup, prec, "stringin: init_record");
+ return S_dev_missingSup;
}
- if( pdset->init_record ) {
- if((status=(*pdset->init_record)(prec))) return(status);
+
+ if (pdset->init_record) {
+ long status = pdset->init_record(prec);
+
+ if (status)
+ return status;
}
- strcpy(prec->oval,prec->val);
- return(0);
+ strcpy(prec->oval, prec->val);
+ return 0;
}
/*
diff --git a/src/std/rec/stringoutRecord.c b/src/std/rec/stringoutRecord.c
index 7ea2c4c11..416a6db5e 100644
--- a/src/std/rec/stringoutRecord.c
+++ b/src/std/rec/stringoutRecord.c
@@ -96,34 +96,37 @@ static long init_record(struct dbCommon *pcommon, int pass)
{
struct stringoutRecord *prec = (struct stringoutRecord *)pcommon;
STATIC_ASSERT(sizeof(prec->oval)==sizeof(prec->val));
- struct stringoutdset *pdset;
- long status=0;
+ struct stringoutdset *pdset = (struct stringoutdset *) prec->dset;
- if (pass==0) return(0);
+ if (pass==0)
+ return 0;
- if (prec->siml.type == CONSTANT) {
- recGblInitConstantLink(&prec->siml,DBF_USHORT,&prec->simm);
+ recGblInitConstantLink(&prec->siml, DBF_USHORT, &prec->simm);
+
+ if (!pdset) {
+ recGblRecordError(S_dev_noDSET, prec, "stringout: init_record");
+ return S_dev_noDSET;
}
- if(!(pdset = (struct stringoutdset *)(prec->dset))) {
- recGblRecordError(S_dev_noDSET,(void *)prec,"stringout: init_record");
- return(S_dev_noDSET);
- }
/* must have write_stringout functions defined */
- if( (pdset->number < 5) || (pdset->write_stringout == NULL) ) {
- recGblRecordError(S_dev_missingSup,(void *)prec,"stringout: init_record");
- return(S_dev_missingSup);
+ if ((pdset->number < 5) || (pdset->write_stringout == NULL)) {
+ recGblRecordError(S_dev_missingSup, prec, "stringout: init_record");
+ return S_dev_missingSup;
}
+
/* get the initial value dol is a constant*/
- if (prec->dol.type == CONSTANT){
- if(recGblInitConstantLink(&prec->dol,DBF_STRING,prec->val))
- prec->udf=FALSE;
+ if (recGblInitConstantLink(&prec->dol, DBF_STRING, prec->val))
+ prec->udf = FALSE;
+
+ if (pdset->init_record) {
+ long status = pdset->init_record(prec);
+
+ if(status)
+ return status;
}
- if( pdset->init_record ) {
- if((status=(*pdset->init_record)(prec))) return(status);
- }
- strcpy(prec->oval,prec->val);
- return(0);
+
+ strcpy(prec->oval, prec->val);
+ return 0;
}
static long process(struct dbCommon *pcommon)
@@ -138,13 +141,13 @@ static long process(struct dbCommon *pcommon)
recGblRecordError(S_dev_missingSup,(void *)prec,"write_stringout");
return(S_dev_missingSup);
}
- if (!prec->pact
- && (prec->dol.type != CONSTANT)
- && (prec->omsl == menuOmslclosed_loop)) {
- status = dbGetLink(&(prec->dol),
- DBR_STRING,prec->val,0,0);
- if(prec->dol.type!=CONSTANT && RTN_SUCCESS(status)) prec->udf=FALSE;
- }
+ if (!prec->pact &&
+ !dbLinkIsConstant(&prec->dol) &&
+ prec->omsl == menuOmslclosed_loop) {
+ status = dbGetLink(&prec->dol, DBR_STRING, prec->val, 0, 0);
+ if (!dbLinkIsConstant(&prec->dol) && !status)
+ prec->udf=FALSE;
+ }
if(prec->udf == TRUE ){
recGblSetSevr(prec,UDF_ALARM,prec->udfs);
diff --git a/src/std/rec/subArrayRecord.c b/src/std/rec/subArrayRecord.c
index 3de610c26..d9d1c33e1 100644
--- a/src/std/rec/subArrayRecord.c
+++ b/src/std/rec/subArrayRecord.c
@@ -108,6 +108,8 @@ static long init_record(struct dbCommon *pcommon, int pass)
prec->bptr = callocMustSucceed(prec->malm, dbValueSize(prec->ftvl),
"subArrayRecord calloc failed");
prec->nord = 0;
+ if (prec->nelm > prec->malm)
+ prec->nelm = prec->malm;
return 0;
}
diff --git a/src/std/rec/subRecord.c b/src/std/rec/subRecord.c
index 32cd8abd4..1fc007034 100644
--- a/src/std/rec/subRecord.c
+++ b/src/std/rec/subRecord.c
@@ -100,9 +100,7 @@ static long init_record(struct dbCommon *pcommon, int pass)
plink = &prec->inpa;
pvalue = &prec->a;
for (i = 0; i < INP_ARG_MAX; i++, plink++, pvalue++) {
- if (plink->type == CONSTANT) {
- recGblInitConstantLink(plink, DBF_DOUBLE, pvalue);
- }
+ recGblInitConstantLink(plink, DBF_DOUBLE, pvalue);
}
if (prec->inam[0]) {
diff --git a/src/std/rec/test/Makefile b/src/std/rec/test/Makefile
index 793312f9a..90c7bab16 100644
--- a/src/std/rec/test/Makefile
+++ b/src/std/rec/test/Makefile
@@ -39,6 +39,13 @@ testHarness_SRCS += linkRetargetLinkTest.c
TESTFILES += ../linkRetargetLink.db
TESTS += linkRetargetLinkTest
+TESTPROD_HOST += linkInitTest
+linkInitTest_SRCS += linkInitTest.c
+linkInitTest_SRCS += recTestIoc_registerRecordDeviceDriver.cpp
+testHarness_SRCS += linkInitTest.c
+TESTFILES += ../linkInitTest.db
+TESTS += linkInitTest
+
TESTPROD_HOST += compressTest
compressTest_SRCS += compressTest.c
compressTest_SRCS += recTestIoc_registerRecordDeviceDriver.cpp
diff --git a/src/std/rec/test/arrayOpTest.c b/src/std/rec/test/arrayOpTest.c
index 27c0becdd..b37c725e2 100644
--- a/src/std/rec/test/arrayOpTest.c
+++ b/src/std/rec/test/arrayOpTest.c
@@ -8,6 +8,7 @@
#include
#include "dbAccess.h"
+#include "dbTest.h"
#include "dbUnitTest.h"
#include "errlog.h"
@@ -20,7 +21,7 @@ void recTestIoc_registerRecordDeviceDriver(struct dbBase *);
static void testGetPutArray(void)
{
- double data[4] = {1, 2, 3, 4};
+ double data[4] = {11, 12, 13, 14};
DBADDR addr, save;
long nreq;
epicsInt32 *pbtr;
@@ -45,16 +46,18 @@ static void testGetPutArray(void)
testAbort("Failed to find record wfrec");
memcpy(&save, &addr, sizeof(save));
- testDiag("Fetch initial value");
+ testDiag("Fetch initial value of %s", prec->name);
dbScanLock(addr.precord);
- testOk1(prec->nord==0);
+ testOk(prec->nord==3, "prec->nord==3 (got %d)", prec->nord);
nreq = NELEMENTS(data);
- if(dbGet(&addr, DBF_DOUBLE, &data, NULL, &nreq, NULL))
+ if(dbGet(&addr, DBF_DOUBLE, &data, NULL, &nreq, NULL)) {
testFail("dbGet fails");
- else {
- testOk(nreq==0, "nreq==0 (got %ld)", nreq);
+ testSkip(1, "failed get");
+ } else {
+ testOk(nreq==3, "nreq==3 (got %ld)", nreq);
+ testOk1(data[0]==1.0 && data[1]==2.0 && data[2]==3.0);
}
dbScanUnlock(addr.precord);
@@ -101,10 +104,10 @@ static void testGetPutArray(void)
testAbort("Failed to find record wfrec1");
memcpy(&save, &addr, sizeof(save));
- testDiag("Fetch initial value");
+ testDiag("Fetch initial value of %s", prec->name);
dbScanLock(addr.precord);
- testOk1(prec->nord==0);
+ testOk(prec->nord==0, "prec->nord==0 (got %d)", prec->nord);
nreq = NELEMENTS(data);
if(dbGet(&addr, DBF_DOUBLE, &data, NULL, &nreq, NULL))
@@ -157,7 +160,7 @@ static void testGetPutArray(void)
MAIN(arrayOpTest)
{
- testPlan(20);
+ testPlan(21);
testGetPutArray();
return testDone();
}
diff --git a/src/std/rec/test/arrayOpTest.db b/src/std/rec/test/arrayOpTest.db
index fc809fbe9..1c011e755 100644
--- a/src/std/rec/test/arrayOpTest.db
+++ b/src/std/rec/test/arrayOpTest.db
@@ -1,6 +1,7 @@
record(waveform, "wfrec") {
field(NELM, "10")
field(FTVL, "LONG")
+ field(INP, [1, 2, 3])
}
record(waveform, "wfrec1") {
field(NELM, "1")
diff --git a/src/std/rec/test/epicsRunRecordTests.c b/src/std/rec/test/epicsRunRecordTests.c
index 5612917cc..8b1d30d59 100644
--- a/src/std/rec/test/epicsRunRecordTests.c
+++ b/src/std/rec/test/epicsRunRecordTests.c
@@ -17,6 +17,7 @@ int compressTest(void);
int arrayOpTest(void);
int asTest(void);
int linkRetargetLinkTest(void);
+int linkInitTest(void);
void epicsRunRecordTests(void)
{
@@ -32,5 +33,7 @@ void epicsRunRecordTests(void)
runTest(linkRetargetLinkTest);
+ runTest(linkInitTest);
+
epicsExit(0); /* Trigger test harness */
}
diff --git a/src/std/rec/test/linkInitTest.c b/src/std/rec/test/linkInitTest.c
new file mode 100644
index 000000000..cc17c07c5
--- /dev/null
+++ b/src/std/rec/test/linkInitTest.c
@@ -0,0 +1,212 @@
+/*************************************************************************\
+* Copyright (c) 2015 Michael Davidsaver
+* EPICS BASE is distributed subject to a Software License Agreement found
+* in file LICENSE that is included with this distribution.
+\*************************************************************************/
+
+#include
+
+#include "dbAccess.h"
+#include "alarm.h"
+#include "dbUnitTest.h"
+#include "errlog.h"
+#include "epicsThread.h"
+
+#include "testMain.h"
+
+void recTestIoc_registerRecordDeviceDriver(struct dbBase *);
+
+static void startTestIoc(const char *dbfile)
+{
+ testdbPrepare();
+ testdbReadDatabase("recTestIoc.dbd", NULL, NULL);
+ recTestIoc_registerRecordDeviceDriver(pdbbase);
+ testdbReadDatabase(dbfile, NULL, NULL);
+
+ eltc(0);
+ testIocInitOk();
+ eltc(1);
+}
+
+static void testLongStringInit()
+{
+ testDiag("testLongStringInit");
+
+ startTestIoc("linkInitTest.db");
+
+ {
+ const char buf[] = "!----------------------------------------------!";
+ testdbGetArrFieldEqual("longstr1.VAL$", DBF_CHAR, NELEMENTS(buf)+2, NELEMENTS(buf), buf);
+ testdbGetFieldEqual("longstr1.VAL", DBR_STRING, "!--------------------------------------");
+ }
+
+ {
+ const char buf[] = "!----------------------------------------------!";
+ testdbGetArrFieldEqual("longstr2.VAL$", DBF_CHAR, NELEMENTS(buf)+2, NELEMENTS(buf), buf);
+ testdbGetFieldEqual("longstr2.VAL", DBR_STRING, "!--------------------------------------");
+ }
+
+ {
+ const char buf[] = "!----------------------------------------------!";
+ testdbGetArrFieldEqual("longstr3.VAL$", DBF_CHAR, NELEMENTS(buf)+2, NELEMENTS(buf), buf);
+ testdbGetFieldEqual("longstr3.VAL", DBR_STRING, "!--------------------------------------");
+ }
+
+ testdbGetFieldEqual("longstr4.VAL", DBR_STRING, "One");
+
+ testIocShutdownOk();
+
+ testdbCleanup();
+}
+
+static void testCalcInit()
+{
+ testDiag("testCalcInit");
+
+ startTestIoc("linkInitTest.db");
+
+ testdbGetFieldEqual("emptylink.VAL", DBR_DOUBLE, 0.0);
+ testdbGetFieldEqual("emptylink.SEVR", DBR_LONG, INVALID_ALARM);
+
+ testdbPutFieldOk("emptylink.PROC", DBF_LONG, 1);
+
+ testdbGetFieldEqual("emptylink.VAL", DBR_DOUBLE, 0.0);
+ testdbGetFieldEqual("emptylink.SEVR", DBR_LONG, 0);
+
+ testdbGetFieldEqual("emptylink1.VAL", DBR_DOUBLE, 0.0);
+ testdbGetFieldEqual("emptylink1.SEVR", DBR_LONG, INVALID_ALARM);
+
+ testdbPutFieldOk("emptylink1.PROC", DBF_LONG, 1);
+
+ testdbGetFieldEqual("emptylink1.VAL", DBR_DOUBLE, 1.0);
+ testdbGetFieldEqual("emptylink1.SEVR", DBR_LONG, 0);
+
+ testIocShutdownOk();
+
+ testdbCleanup();
+}
+
+static void testPrintfStrings()
+{
+ testDiag("testPrintfStrings");
+
+ startTestIoc("linkInitTest.db");
+
+ {
+ const char buf1[] = "Test string, exactly 40 characters long";
+ const char buf2[] = "Longer test string, more that 40 characters long";
+ const char buf2t[] = "Longer test string, more that 40 charac";
+
+ /* The FMT field is pp(TRUE), so this put triggers processing */
+ testdbPutFieldOk("printf1.FMT", DBF_STRING, "%s");
+ testdbGetArrFieldEqual("printf1.VAL$", DBF_CHAR, NELEMENTS(buf1)+2,
+ NELEMENTS(buf1), buf1);
+ testdbGetFieldEqual("printf1.VAL", DBR_STRING, buf1);
+
+ testdbPutFieldOk("printf1.FMT", DBF_STRING, "%ls");
+ testdbGetArrFieldEqual("printf1.VAL$", DBF_CHAR, NELEMENTS(buf1)+2,
+ NELEMENTS(buf1), buf1);
+ testdbGetFieldEqual("printf1.VAL", DBR_STRING, buf1);
+
+ testdbPutFieldOk("printf2.FMT", DBF_STRING, "%s");
+ testdbGetArrFieldEqual("printf2.VAL$", DBF_CHAR, NELEMENTS(buf2)+2,
+ NELEMENTS(buf2t), buf2t);
+ testdbGetFieldEqual("printf2.VAL", DBR_STRING, buf2t);
+
+ testdbPutFieldOk("printf2.FMT", DBF_STRING, "%ls");
+ testdbGetArrFieldEqual("printf2.VAL$", DBF_CHAR, NELEMENTS(buf2)+2,
+ NELEMENTS(buf2), buf2);
+ testdbGetFieldEqual("printf2.VAL", DBR_STRING, buf2t);
+
+ testdbPutFieldOk("printf2.FMT", DBF_STRING, "%.39ls");
+ testdbGetArrFieldEqual("printf2.VAL$", DBF_CHAR, NELEMENTS(buf2)+2,
+ NELEMENTS(buf2t), buf2t);
+ }
+
+ testIocShutdownOk();
+
+ testdbCleanup();
+}
+
+static void testArrayInputs()
+{
+ epicsInt32 oneToTwelve[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};
+
+ testDiag("testArrayInputs");
+
+ startTestIoc("linkInitTest.db");
+
+ testdbGetFieldEqual("aai1.NORD", DBR_LONG, 10);
+ testdbGetFieldEqual("aai1.UDF", DBR_UCHAR, 0);
+ testdbGetFieldEqual("sa1.NORD", DBR_LONG, 10);
+ testdbGetFieldEqual("sa1.UDF", DBR_UCHAR, 0);
+ testdbGetFieldEqual("sa2.NORD", DBR_LONG, 0);
+ testdbGetFieldEqual("sa2.UDF", DBR_UCHAR, 1);
+ testdbGetFieldEqual("wf1.NORD", DBR_LONG, 10);
+ testdbGetFieldEqual("wf1.UDF", DBR_UCHAR, 0);
+
+ testdbGetArrFieldEqual("aai1.VAL", DBF_LONG, 12, 10, &oneToTwelve[0]);
+ testdbGetArrFieldEqual("sa1.VAL", DBF_LONG, 12, 10, &oneToTwelve[2]);
+ testdbGetArrFieldEqual("sa2.VAL", DBF_LONG, 10, 0, NULL);
+ testdbGetArrFieldEqual("wf1.VAL", DBF_LONG, 12, 10, &oneToTwelve[0]);
+
+ testdbPutFieldOk("sa1.INDX", DBF_LONG, 3);
+ testdbGetArrFieldEqual("sa1.VAL", DBF_LONG, 12, 9, &oneToTwelve[3]);
+
+ testdbPutFieldOk("sa1.NELM", DBF_LONG, 3);
+ testdbGetArrFieldEqual("sa1.VAL", DBF_LONG, 12, 3, &oneToTwelve[3]);
+
+ testdbPutFieldOk("sa2.VAL", DBF_LONG, 1);
+ testdbGetArrFieldEqual("sa2.VAL", DBF_LONG, 10, 1, &oneToTwelve[0]);
+
+ testIocShutdownOk();
+ testdbCleanup();
+}
+
+static void testEventRecord()
+{
+ testMonitor *countmon;
+
+ testDiag("testEventRecord");
+
+ startTestIoc("linkInitTest.db");
+ countmon = testMonitorCreate("count1.VAL", DBR_LONG, 0);
+
+ testdbGetFieldEqual("ev1.VAL", DBR_STRING, "soft event 1");
+ testdbGetFieldEqual("ev1.UDF", DBR_UCHAR, 0);
+ testdbGetFieldEqual("ev2.VAL", DBR_STRING, "");
+ testdbGetFieldEqual("ev2.UDF", DBR_UCHAR, 1);
+ testdbGetFieldEqual("count1.VAL", DBR_LONG, 0);
+
+ testdbPutFieldOk("ev1.PROC", DBF_UCHAR, 1);
+ testMonitorWait(countmon);
+ testdbGetFieldEqual("count1.VAL", DBR_LONG, 1);
+
+ testdbPutFieldOk("ev2.PROC", DBF_UCHAR, 1);
+ testMonitorWait(countmon);
+ testdbGetFieldEqual("ev2.UDF", DBR_UCHAR, 0);
+ testdbGetFieldEqual("count1.VAL", DBR_LONG, 2);
+
+ testdbPutFieldOk("count1.EVNT", DBF_STRING, "Tock");
+ testdbPutFieldOk("ev2.PROC", DBF_UCHAR, 1);
+ testMonitorWait(countmon);
+ testdbGetFieldEqual("count1.VAL", DBR_LONG, 3);
+
+ testMonitorDestroy(countmon);
+ testIocShutdownOk();
+ testdbCleanup();
+}
+
+
+MAIN(linkInitTest)
+{
+ testPlan(62);
+
+ testLongStringInit();
+ testCalcInit();
+ testPrintfStrings();
+ testArrayInputs();
+ testEventRecord();
+
+ return testDone();
+}
diff --git a/src/std/rec/test/linkInitTest.db b/src/std/rec/test/linkInitTest.db
new file mode 100644
index 000000000..6887b56ba
--- /dev/null
+++ b/src/std/rec/test/linkInitTest.db
@@ -0,0 +1,67 @@
+record(lsi, "longstr1") {
+ field(SIZV, "100")
+ field(INP, ["!----------------------------------------------!"])
+}
+record(lsi, "longstr2") {
+ field(SIZV, "100")
+ field(INP, {const: ["!----------------------------------------------!"]})
+}
+record(lsi, "longstr3") {
+ field(SIZV, "100")
+ field(INP, {const: "!----------------------------------------------!"})
+}
+record(lsi, "longstr4") {
+ field(SIZV, "100")
+ field(INP, ["One","Two","Three","Four"])
+}
+
+record(ai, "emptylink" ) {
+ field(INP, {calc: {expr:"0"}})
+}
+record(ai, "emptylink1" ) {
+ field(INP, {calc: {expr:"1"}})
+}
+
+record(printf, "printf1") {
+ field(SIZV, "100")
+ field(INP0, ["Test string, exactly 40 characters long"])
+}
+record(printf, "printf2") {
+ field(SIZV, "100")
+ field(INP0, ["Longer test string, more that 40 characters long"])
+}
+
+record(waveform, "aai1") {
+ field(NELM, 10)
+ field(FTVL, "LONG")
+ field(INP, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12])
+}
+record(subArray, "sa1") {
+ field(FTVL, "LONG")
+ field(MALM, 12)
+ field(INP, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12])
+ field(INDX, 2)
+ field(NELM, 10)
+}
+record(subArray, "sa2") {
+ field(FTVL, "LONG")
+ field(MALM, 10)
+ field(NELM, 1)
+}
+record(waveform, "wf1") {
+ field(NELM, 10)
+ field(FTVL, "LONG")
+ field(INP, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12])
+}
+
+record(longin, "count1" ) {
+ field(INP, {calc: {expr:"VAL+1"}})
+ field(SCAN, "Event")
+ field(EVNT, "soft event 1")
+}
+record(event, "ev1") {
+ field(INP, ["soft event 1"])
+}
+record(event, "ev2") {
+ field(INP, "count1.EVNT")
+}
diff --git a/src/std/rec/test/linkRetargetLink.db b/src/std/rec/test/linkRetargetLink.db
index 3306e43ea..148e2d50b 100644
--- a/src/std/rec/test/linkRetargetLink.db
+++ b/src/std/rec/test/linkRetargetLink.db
@@ -15,3 +15,11 @@ record(stringout, "rec:link2") {
field(VAL, "rec:src2 CP")
field(OUT, "rec:ai.INP CA")
}
+
+record(ai, "rec:j1") {
+ field(INP, {calc:{
+ expr:"A+5",
+ args:5
+ }})
+ field(PINI, "YES")
+}
diff --git a/src/std/rec/test/linkRetargetLinkTest.c b/src/std/rec/test/linkRetargetLinkTest.c
index 894c94587..2a37696aa 100644
--- a/src/std/rec/test/linkRetargetLinkTest.c
+++ b/src/std/rec/test/linkRetargetLinkTest.c
@@ -27,24 +27,7 @@ static void testRetarget(void)
{
testMonitor *lnkmon, *valmon;
- testdbPrepare();
-
- testdbReadDatabase("recTestIoc.dbd", NULL, NULL);
-
- recTestIoc_registerRecordDeviceDriver(pdbbase);
-
- testdbReadDatabase("linkRetargetLink.db", NULL, NULL);
-
- eltc(0);
- testIocInitOk();
- eltc(1);
- /* wait for local CA links to be connected or dbPutField() will fail */
- /* wait for initial CA_CONNECT actions to be processed.
- * Assume that local CA links deliver callbacks synchronously
- * eg. that ca_create_channel() will invoke the connection callback
- * before returning.
- */
- dbCaSync();
+ testDiag("In testRetarget");
lnkmon = testMonitorCreate("rec:ai.INP", DBE_VALUE, 0);
valmon = testMonitorCreate("rec:ai", DBE_VALUE, 0);
@@ -80,15 +63,60 @@ static void testRetarget(void)
testMonitorDestroy(lnkmon);
testMonitorDestroy(valmon);
+}
- testIocShutdownOk();
+#define testLongStrEq(PV, VAL) testdbGetArrFieldEqual(PV, DBF_CHAR, sizeof(VAL)+2, sizeof(VAL), VAL)
+#define testPutLongStr(PV, VAL) testdbPutArrFieldOk(PV, DBF_CHAR, sizeof(VAL), VAL);
- testdbCleanup();
+static void testRetargetJLink(void)
+{
+ testDiag("In testRetargetJLink");
+
+ testdbGetFieldEqual("rec:j1", DBF_DOUBLE, 10.0);
+ /* minimal args */
+ testLongStrEq("rec:j1.INP$", "{\"calc\":{\"expr\":\"A+5\",\"args\":5}}");
+
+ /* with [] */
+ testPutLongStr("rec:j1.INP$", "{\"calc\":{\"expr\":\"A+5\",\"args\":[7]}}");
+ testdbPutFieldOk("rec:j1.PROC", DBF_LONG, 1);
+
+ /* with const */
+ testPutLongStr("rec:j1.INP$", "{\"calc\":{\"expr\":\"A+5\",\"args\":[{\"const\":7}]}}");
+ testdbPutFieldOk("rec:j1.PROC", DBF_LONG, 1);
+
+ testdbGetFieldEqual("rec:j1", DBF_DOUBLE, 12.0);
+ testLongStrEq("rec:j1.INP$", "{\"calc\":{\"expr\":\"A+5\",\"args\":[{\"const\":7}]}}");
}
MAIN(linkRetargetLinkTest)
{
- testPlan(10);
+ testPlan(18);
+
+ testdbPrepare();
+
+ testdbReadDatabase("recTestIoc.dbd", NULL, NULL);
+
+ recTestIoc_registerRecordDeviceDriver(pdbbase);
+
+ testdbReadDatabase("linkRetargetLink.db", NULL, NULL);
+
+ eltc(0);
+ testIocInitOk();
+ eltc(1);
+ /* wait for local CA links to be connected or dbPutField() will fail */
+ /* wait for initial CA_CONNECT actions to be processed.
+ * Assume that local CA links deliver callbacks synchronously
+ * eg. that ca_create_channel() will invoke the connection callback
+ * before returning.
+ */
+ dbCaSync();
+
testRetarget();
+ testRetargetJLink();
+
+ testIocShutdownOk();
+
+ testdbCleanup();
+
return testDone();
}
diff --git a/src/std/rec/waveformRecord.c b/src/std/rec/waveformRecord.c
index 5d3963e3c..c06c48001 100644
--- a/src/std/rec/waveformRecord.c
+++ b/src/std/rec/waveformRecord.c
@@ -110,10 +110,7 @@ static long init_record(struct dbCommon *pcommon, int pass)
return 0;
}
- /* wf.siml must be a CONSTANT or a PV_LINK or a DB_LINK */
- if (prec->siml.type == CONSTANT) {
- recGblInitConstantLink(&prec->siml,DBF_USHORT,&prec->simm);
- }
+ recGblInitConstantLink(&prec->siml,DBF_USHORT,&prec->simm);
/* must have dset defined */
if (!(pdset = (struct wfdset *)(prec->dset))) {
@@ -314,7 +311,7 @@ static long readValue(waveformRecord *prec)
return (*pdset->read_wf)(prec);
}
- status = dbGetLink(&(prec->siml), DBR_ENUM, &(prec->simm),0,0);
+ status = dbGetLink(&prec->siml, DBR_ENUM, &prec->simm, 0, 0);
if (status)
return status;
@@ -330,9 +327,9 @@ static long readValue(waveformRecord *prec)
if (prec->simm == menuYesNoYES){
long nRequest = prec->nelm;
- status = dbGetLink(&(prec->siol), prec->ftvl, prec->bptr, 0, &nRequest);
+ status = dbGetLink(&prec->siol, prec->ftvl, prec->bptr, 0, &nRequest);
/* nord set only for db links: needed for old db_access */
- if (prec->siol.type != CONSTANT) {
+ if (!dbLinkIsConstant(&prec->siol)) {
prec->nord = nRequest;
db_post_events(prec, &prec->nord, DBE_VALUE | DBE_LOG);
if (status == 0)
diff --git a/src/std/softIoc/RULES b/src/std/softIoc/RULES
index cb540ad3c..fc1c6236e 100644
--- a/src/std/softIoc/RULES
+++ b/src/std/softIoc/RULES
@@ -9,8 +9,10 @@
softIoc.dbd$(DEP): $(COMMON_DIR)/stdRecords.dbd
softIoc.dbd$(DEP): $(COMMON_DIR)/filters.dbd
+softIoc.dbd$(DEP): $(COMMON_DIR)/links.dbd
$(COMMON_DIR)/softIoc.dbd: $(COMMON_DIR)/stdRecords.dbd
$(COMMON_DIR)/softIoc.dbd: $(COMMON_DIR)/filters.dbd
+$(COMMON_DIR)/softIoc.dbd: $(COMMON_DIR)/links.dbd
$(COMMON_DIR)/softIoc.dbd: $(STDDIR)/softIoc/Makefile
softMain$(DEP): epicsInstallDir.h
diff --git a/src/std/softIoc/base.dbd b/src/std/softIoc/base.dbd
index 46fc57690..58f4884ea 100644
--- a/src/std/softIoc/base.dbd
+++ b/src/std/softIoc/base.dbd
@@ -14,6 +14,9 @@ include "stdRecords.dbd"
# Channel filters & plugins
include "filters.dbd"
+# Link types
+include "links.dbd"
+
# Standard device support
include "devSoft.dbd"
diff --git a/src/tools/DBD.pm b/src/tools/DBD.pm
index 3a2a85ee4..aabe26d26 100644
--- a/src/tools/DBD.pm
+++ b/src/tools/DBD.pm
@@ -6,6 +6,7 @@ use warnings;
use DBD::Base;
use DBD::Breaktable;
use DBD::Driver;
+use DBD::Link;
use DBD::Menu;
use DBD::Recordtype;
use DBD::Recfield;
@@ -21,6 +22,7 @@ sub new {
my $this = {
'DBD::Breaktable' => {},
'DBD::Driver' => {},
+ 'DBD::Link' => {},
'DBD::Function' => {},
'DBD::Menu' => {},
'DBD::Recordtype' => {},
@@ -80,6 +82,10 @@ sub drivers {
return shift->{'DBD::Driver'};
}
+sub links {
+ return shift->{'DBD::Link'};
+}
+
sub functions {
return shift->{'DBD::Function'};
}
diff --git a/src/tools/DBD/Device.pm b/src/tools/DBD/Device.pm
index 5d13a9655..2fa1777d9 100644
--- a/src/tools/DBD/Device.pm
+++ b/src/tools/DBD/Device.pm
@@ -5,6 +5,7 @@ use DBD::Base;
my %link_types = (
CONSTANT => qr/$RXnum/o,
PV_LINK => qr/$RXname \s+ [.NPCAMS ]*/ox,
+ JSON_LINK => qr/\{ .* \}/ox,
VME_IO => qr/\# (?: \s* [CS] \s* $RXintx)* \s* (?: @ .*)?/ox,
CAMAC_IO => qr/\# (?: \s* [BCNAF] \s* $RXintx)* \s* (?: @ .*)?/ox,
RF_IO => qr/\# (?: \s* [RMDE] \s* $RXintx)*/ox,
diff --git a/src/tools/DBD/Link.pm b/src/tools/DBD/Link.pm
new file mode 100644
index 000000000..4a4568e82
--- /dev/null
+++ b/src/tools/DBD/Link.pm
@@ -0,0 +1,22 @@
+package DBD::Link;
+use DBD::Base;
+@ISA = qw(DBD::Base);
+
+sub init {
+ my ($this, $name, $jlif) = @_;
+ $this->SUPER::init($jlif, "link support (jlif)");
+ $this->{KEY} = $name;
+ return $this;
+}
+
+sub key {
+ return shift->{KEY};
+}
+
+sub equals {
+ my ($a, $b) = @_;
+ return $a->SUPER::equals($b)
+ && $a->{KEY} eq $b->{KEY};
+}
+
+1;
diff --git a/src/tools/DBD/Output.pm b/src/tools/DBD/Output.pm
index 77386f0bd..c3bce9e58 100644
--- a/src/tools/DBD/Output.pm
+++ b/src/tools/DBD/Output.pm
@@ -13,6 +13,7 @@ use DBD::Base;
use DBD::Breaktable;
use DBD::Device;
use DBD::Driver;
+use DBD::Link;
use DBD::Menu;
use DBD::Recordtype;
use DBD::Recfield;
@@ -26,6 +27,7 @@ sub OutputDBD {
OutputMenus($out, $dbd->menus);
OutputRecordtypes($out, $dbd->recordtypes);
OutputDrivers($out, $dbd->drivers);
+ OutputLinks($out, $dbd->links);
OutputRegistrars($out, $dbd->registrars);
OutputFunctions($out, $dbd->functions);
OutputVariables($out, $dbd->variables);
@@ -78,6 +80,13 @@ sub OutputDrivers {
foreach keys %{$drivers};
}
+sub OutputLinks {
+ my ($out, $links) = @_;
+ while (my ($name, $link) = each %{$links}) {
+ printf $out "link(%s, %s)\n", $link->key, $name;
+ }
+}
+
sub OutputRegistrars {
my ($out, $registrars) = @_;
printf $out "registrar(%s)\n", $_
diff --git a/src/tools/DBD/Parser.pm b/src/tools/DBD/Parser.pm
index b7cc7b376..c5f83d6d0 100644
--- a/src/tools/DBD/Parser.pm
+++ b/src/tools/DBD/Parser.pm
@@ -13,6 +13,7 @@ use DBD::Base;
use DBD::Breaktable;
use DBD::Device;
use DBD::Driver;
+use DBD::Link;
use DBD::Menu;
use DBD::Recordtype;
use DBD::Recfield;
@@ -37,6 +38,11 @@ sub ParseDBD {
my ($driver_name) = unquote($1);
$dbd->add(DBD::Driver->new($driver_name));
}
+ elsif (m/\G link \s* \( \s* $RXstr \s*, \s* $RXstr \s* \)/oxgc) {
+ print "Link $1, $2\n" if $debug;
+ my ($key, $lset) = unquote($1, $2);
+ $dbd->add(DBD::Link->new($key, $lset));
+ }
elsif (m/\G registrar \s* \( \s* $RXstr \s* \)/oxgc) {
print "Registrar: $1\n" if $debug;
my ($registrar_name) = unquote($1);
diff --git a/src/tools/Makefile b/src/tools/Makefile
index 99820d567..8f5aacbed 100644
--- a/src/tools/Makefile
+++ b/src/tools/Makefile
@@ -23,6 +23,7 @@ PERL_MODULES += DBD/Base.pm
PERL_MODULES += DBD/Breaktable.pm
PERL_MODULES += DBD/Device.pm
PERL_MODULES += DBD/Driver.pm
+PERL_MODULES += DBD/Link.pm
PERL_MODULES += DBD/Function.pm
PERL_MODULES += DBD/Menu.pm
PERL_MODULES += DBD/Output.pm
diff --git a/src/tools/registerRecordDeviceDriver.pl b/src/tools/registerRecordDeviceDriver.pl
index 5d56ecb3e..c6ab607e6 100644
--- a/src/tools/registerRecordDeviceDriver.pl
+++ b/src/tools/registerRecordDeviceDriver.pl
@@ -160,6 +160,20 @@ if (%drivers) {
print $out "};\n\n";
}
+my %links = %{$dbd->links};
+if (%links) {
+ my @links = sort keys %links;
+
+ # Declare the link interfaces
+ print $out wrap('epicsShareExtern jlif ', ' ',
+ join(', ', map {"*pvar_jlif_$_"} @links)), ";\n\n";
+
+ # List of pointers to each link interface
+ print $out "static struct jlif *jlifsl[] = {\n";
+ print $out join(",\n", map {" pvar_jlif_$_"} @links);
+ print $out "};\n\n";
+}
+
my @registrars = sort keys %{$dbd->registrars};
my @functions = sort keys %{$dbd->functions};
push @registrars, map {"register_func_$_"} @functions;
@@ -235,6 +249,10 @@ print $out (<< 'END') if %drivers;
registerDrivers(pbase, NELEMENTS(drvsl), driverSupportNames, drvsl);
END
+print $out (<< 'END') if %links;
+ registerJLinks(pbase, NELEMENTS(jlifsl), jlifsl);
+END
+
print $out (<< "END") for @registrars;
pvar_func_$_();
END