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:

+ + + +
+  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:

+ + + +
+ + +

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:

+ + + +

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