From 8c60e7e2cb40efc1f1ea300b03bd327b6bd500d0 Mon Sep 17 00:00:00 2001
From: Michael Davidsaver
Date: Fri, 1 Aug 2014 11:28:10 -0400
Subject: [PATCH 001/204] track INP/OUT field in struct dbFldDes
avoid string comparisons
---
src/ioc/db/dbAccess.c | 3 +--
src/ioc/dbStatic/dbBase.h | 1 +
src/ioc/dbStatic/dbLexRoutines.c | 2 ++
src/ioc/dbStatic/dbStaticLib.c | 3 +--
src/ioc/misc/iocInit.c | 3 +--
5 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/src/ioc/db/dbAccess.c b/src/ioc/db/dbAccess.c
index bf0f00dec..2abbe8ea2 100644
--- a/src/ioc/db/dbAccess.c
+++ b/src/ioc/db/dbAccess.c
@@ -942,8 +942,7 @@ static long dbPutFieldLink(DBADDR *paddr,
if (status) goto finish;
isDevLink = ellCount(&precord->rdes->devList) > 0 &&
- (strcmp(pfldDes->name, "INP") == 0 ||
- strcmp(pfldDes->name, "OUT") == 0);
+ pfldDes->isDevLink;
dbLockSetGblLock();
dbLockSetRecordLock(precord);
diff --git a/src/ioc/dbStatic/dbBase.h b/src/ioc/dbStatic/dbBase.h
index 691953442..2aa9a6084 100644
--- a/src/ioc/dbStatic/dbBase.h
+++ b/src/ioc/dbStatic/dbBase.h
@@ -81,6 +81,7 @@ typedef struct dbFldDes{ /* field description */
ctType base; /*base for integer to string conversions*/
short promptgroup; /*prompt, i.e. gui group */
short interest; /*interest level */
+ short isDevLink; /* true for INP/OUT fields */
asLevel as_level; /*access security level */
char *initial; /*initial value */
/*If (DBF_MENU,DBF_DEVICE) ftPvt is (pdbMenu,pdbDeviceMenu) */
diff --git a/src/ioc/dbStatic/dbLexRoutines.c b/src/ioc/dbStatic/dbLexRoutines.c
index cf2406556..063701751 100644
--- a/src/ioc/dbStatic/dbLexRoutines.c
+++ b/src/ioc/dbStatic/dbLexRoutines.c
@@ -484,6 +484,8 @@ static void dbRecordtypeFieldHead(char *name,char *type)
allocTemp(pdbFldDes);
pdbFldDes->name = epicsStrDup(name);
pdbFldDes->as_level = ASL1;
+ pdbFldDes->isDevLink = strcmp(pdbFldDes->name, "INP")==0 ||
+ strcmp(pdbFldDes->name, "OUT")==0;
for(i=0; ifield_type = pamapdbfType[i].value;
diff --git a/src/ioc/dbStatic/dbStaticLib.c b/src/ioc/dbStatic/dbStaticLib.c
index 43a371b7a..9f953a673 100644
--- a/src/ioc/dbStatic/dbStaticLib.c
+++ b/src/ioc/dbStatic/dbStaticLib.c
@@ -2106,8 +2106,7 @@ long dbPutString(DBENTRY *pdbentry,const char *pstring)
goto done;
}
- if (strcmp(pflddes->name, "INP") == 0 ||
- strcmp(pflddes->name, "OUT") == 0) {
+ if (pflddes->isDevLink) {
status = setLinkType(pdbentry); /* This uses DTYP to look up and set plink->type, necessary for default DTYP */
if (status) {
errMessage(status,"in dbPutString from setLinkType");
diff --git a/src/ioc/misc/iocInit.c b/src/ioc/misc/iocInit.c
index 73f8ebd00..1e224d2f5 100644
--- a/src/ioc/misc/iocInit.c
+++ b/src/ioc/misc/iocInit.c
@@ -484,8 +484,7 @@ static void doResolveLinks(dbRecordType *pdbRecordType, dbCommon *precord,
dbFldDes *pdbFldDes = papFldDes[link_ind[j]];
DBLINK *plink = (DBLINK *)((char *)precord + pdbFldDes->offset);
- if (ellCount(&precord->rdes->devList) > 0 &&
- (strcmp(pdbFldDes->name, "INP") == 0 || strcmp(pdbFldDes->name, "OUT") == 0)) {
+ if (ellCount(&precord->rdes->devList) > 0 && pdbFldDes->isDevLink) {
devSup *pdevSup = dbDTYPtoDevSup(pdbRecordType, precord->dtyp);
if (pdevSup) {
From 99626f03cd30550f3ca822fa1a0a1d4428f7e649 Mon Sep 17 00:00:00 2001
From: Michael Davidsaver
Date: Fri, 1 Aug 2014 11:28:10 -0400
Subject: [PATCH 002/204] dbLink: remove struct lset and dbDb_lset
unnecessary exposure of internal API
---
src/ioc/db/dbLink.c | 6 ------
src/ioc/db/dbLink.h | 24 ------------------------
2 files changed, 30 deletions(-)
diff --git a/src/ioc/db/dbLink.c b/src/ioc/db/dbLink.c
index be0a3b70a..af23d7615 100644
--- a/src/ioc/db/dbLink.c
+++ b/src/ioc/db/dbLink.c
@@ -379,12 +379,6 @@ static void dbDbScanFwdLink(struct link *plink)
dbScanPassive(precord, paddr->precord);
}
-lset dbDb_lset = { dbDbInitLink, dbDbAddLink, NULL, dbDbRemoveLink,
- dbDbIsLinkConnected, dbDbGetDBFtype, dbDbGetElements, dbDbGetValue,
- dbDbGetControlLimits, dbDbGetGraphicLimits, dbDbGetAlarmLimits,
- dbDbGetPrecision, dbDbGetUnits, dbDbGetAlarm, dbDbGetTimeStamp,
- dbDbPutValue, dbDbScanFwdLink };
-
/***************************** Generic Link API *****************************/
void dbInitLink(struct dbCommon *precord, struct link *plink, short dbfType)
diff --git a/src/ioc/db/dbLink.h b/src/ioc/db/dbLink.h
index ff0de3003..ee097757f 100644
--- a/src/ioc/db/dbLink.h
+++ b/src/ioc/db/dbLink.h
@@ -25,30 +25,6 @@
extern "C" {
#endif
-typedef struct lset {
- long (*initLink)(struct link *plink, short dbfType);
- long (*addLink)(struct link *plink, short dbfType);
- long (*loadLink)(struct link *plink, short dbrType, void *pbuffer);
- void (*removeLink)(struct link *plink);
- int (*isLinkConnected)(const struct link *plink);
- int (*getDBFtype)(const struct link *plink);
- long (*getElements)(const struct link *plink, long *nelements);
- long (*getValue)(struct link *plink, short dbrType, void *pbuffer,
- epicsEnum16 *pstat, epicsEnum16 *psevr, 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,
- double *hi, double *hihi);
- long (*getPrecision)(const struct link *plink, short *precision);
- long (*getUnits)(const struct link *plink, char *units, int unitsSize);
- long (*getAlarm)(const struct link *plink, epicsEnum16 *status,
- epicsEnum16 *severity);
- long (*getTimeStamp)(const struct link *plink, epicsTimeStamp *pstamp);
- long (*putValue)(struct link *plink, short dbrType,
- const void *pbuffer, long nRequest);
- void (*scanFwdLink)(struct link *plink);
-} lset;
-
#define dbGetSevr(PLINK, PSEVERITY) \
dbGetAlarm((PLINK), NULL, (PSEVERITY));
From 470a9f9fc975dd6e7ea9383d772e7a66d2de2027 Mon Sep 17 00:00:00 2001
From: Michael Davidsaver
Date: Fri, 1 Aug 2014 11:28:10 -0400
Subject: [PATCH 003/204] Introduce struct dbLinkInfo and dbParseLink()
This structure holds the processed form of any link type.
It is populated by dbParseLink(), which can be called w/o
record locking.
dbCanSetLink() checks the the parsed link, and a DBLINK field
have compatible types for assignment.
Record locking is required, but this does not modify the field.
dbSetLink() makes the actual assignment.
---
src/ioc/dbStatic/dbStaticLib.c | 295 +++++++++++++++++++++++++++++++++
src/ioc/dbStatic/dbStaticPvt.h | 31 ++++
2 files changed, 326 insertions(+)
diff --git a/src/ioc/dbStatic/dbStaticLib.c b/src/ioc/dbStatic/dbStaticLib.c
index 9f953a673..5ca23493b 100644
--- a/src/ioc/dbStatic/dbStaticLib.c
+++ b/src/ioc/dbStatic/dbStaticLib.c
@@ -2024,6 +2024,301 @@ static void cvtDecimalOrHexToShort(char *from,short *value)
}
}
+long dbParseLink(const char *str, short ftype, dbLinkInfo *pinfo)
+{
+ char *pstr, *pend;
+ size_t len, i;
+
+ /* Strip leading blanks and tabs */
+ while (*str && (*str == ' ' || *str == '\t')) str++;
+
+ memset(pinfo, 0, sizeof(*pinfo));
+
+ len = strlen(str);
+ pinfo->target = pstr = malloc(len+1);
+ if(!pstr)
+ return S_dbLib_outMem;
+ strcpy(pstr, str);
+
+ /* Strip trailing blanks and tabs */
+ for(i=len; i>0 && (pstr[i-1]==' ' || pstr[i-1]=='\t'); i--) {}
+ pstr[i]='\0';
+
+ /* check for HW link types */
+ if(pstr[0]=='@') {
+ pinfo->ltype = INST_IO; /* can only be INST_IO */
+
+ /* move string up by one to overwrite '@'
+ * so it can be re-used later in struct instio
+ */
+ ssize_t N = strlen(pstr)-1;
+ memmove(pstr, pstr+1, N);
+ pstr[N] = '\0';
+
+ return 0;
+
+ } else if(pstr[0]=='#') { /* some other HW link type */
+ int ret;
+ char junk = 0;
+ char *patstr = strchr(pstr, '@'); /* find start of parm string */
+
+ if(patstr)
+ *patstr++ = '\0'; /* isolate the parm string for later processing */
+
+ /* generalized extraction of ID charactor and integer pairs (eg. "#C15 S14") */
+ ret = sscanf(pinfo->target, "# %c%d %c%d %c%d %c%d %c%d %c",
+ &pinfo->hwid[0], &pinfo->hwnums[0],
+ &pinfo->hwid[1], &pinfo->hwnums[1],
+ &pinfo->hwid[2], &pinfo->hwnums[2],
+ &pinfo->hwid[3], &pinfo->hwnums[3],
+ &pinfo->hwid[4], &pinfo->hwnums[4],
+ &junk);
+
+ /* ret<0 when pattern not matched
+ * ret==11 when extra non-space before '@'.
+ * ret is odd when a number is missing
+ */
+ if(ret<0 || ret>10 || ret%2==1) goto fail;
+
+ if(strcmp(pinfo->hwid, "CS")==0) pinfo->ltype = VME_IO;
+ else if(strcmp(pinfo->hwid, "BCNAF")==0)pinfo->ltype = CAMAC_IO;
+ else if(strcmp(pinfo->hwid, "RMDE")==0) pinfo->ltype = RF_IO;
+ else if(strcmp(pinfo->hwid, "LACS")==0) pinfo->ltype = AB_IO;
+ else if(strcmp(pinfo->hwid, "LA")==0) pinfo->ltype = GPIB_IO;
+ else if(strcmp(pinfo->hwid, "LNPS")==0) pinfo->ltype = BITBUS_IO;
+ else if(strcmp(pinfo->hwid, "LBG")==0) pinfo->ltype = BBGPIB_IO;
+ else if(strcmp(pinfo->hwid, "VCS")==0) pinfo->ltype = VXI_IO;
+ else if(strcmp(pinfo->hwid, "VS")==0) pinfo->ltype = VXI_IO;
+ else goto fail;
+
+ if(patstr && pinfo->ltype!=RF_IO) {
+ /* move the parm string forward.
+ * pinfo->target will be re-used as ->parm
+ */
+ memmove(pinfo->target, patstr, strlen(patstr)+1);
+ } else if(!patstr && pinfo->ltype==RF_IO) {
+ /* RF_IO only. the string isn't needed anymore */
+ 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;
+ }
+
+ return 0;
+ }
+
+ /* A string is a constant if epicsStrtod or strtol consumes entire string */
+ (void)epicsStrtod(pstr, &pend);
+ if(*pend!='\0')
+ (void)strtol(pstr, &pend, 0);
+ if(*pend=='\0') {
+ pinfo->ltype = CONSTANT;
+ return 0; /* This is a CONSTANT link */
+ }
+
+ pinfo->ltype = PV_LINK;
+ pstr = strchr(pstr, ' '); /* find start of link modifiers (can't be seperated by tabs) */
+ if(pstr) {
+ *pstr++ = '\0'; /* isolate modifiers. pinfo->target is PV name only for re-use in struct pv_link */
+
+ /* Space seperation of modifiers isn't required, and other chars are ignored.
+ * Order of comparisons resolves ambiguity by checking for
+ * longer matches first.
+ * eg. "QQCPPXMSITT" is pvlOptCPP|pvlOptMS
+ */
+
+ if (strstr(pstr, "NPP")) pinfo->modifiers = 0;
+ else if (strstr(pstr, "CPP")) pinfo->modifiers = pvlOptCPP;
+ else if (strstr(pstr, "PP")) pinfo->modifiers = pvlOptPP;
+ else if (strstr(pstr, "CA")) pinfo->modifiers = pvlOptCA;
+ else if (strstr(pstr, "CP")) pinfo->modifiers = pvlOptCP;
+
+ if (strstr(pstr, "NMS")) pinfo->modifiers |= pvlOptNMS;
+ else if (strstr(pstr, "MSI")) pinfo->modifiers |= pvlOptMSI;
+ else if (strstr(pstr, "MSS")) pinfo->modifiers |= pvlOptMSS;
+ else if (strstr(pstr, "MS")) pinfo->modifiers |= pvlOptMS;
+
+ /* filter modifiers based on link type */
+ switch(ftype) {
+ case DBF_INLINK: /* accept all */ break;
+ case DBF_OUTLINK: pinfo->modifiers &= ~pvlOptCPP; break;
+ case DBF_FWDLINK: pinfo->modifiers &= pvlOptCA; break;
+ }
+ }
+
+ return 0;
+fail:
+ free(pinfo->target);
+ return S_dbLib_badField;
+}
+
+long dbCanSetLink(DBLINK *plink, dbLinkInfo *pinfo, devSup *devsup)
+{
+ /* consume allocated string pinfo->target on failure */
+
+ int link_type = CONSTANT;
+ if(devsup)
+ link_type = devsup->link_type;
+ if(link_type==pinfo->ltype)
+ return 0;
+ switch(pinfo->ltype) {
+ case CONSTANT:
+ case PV_LINK:
+ if(link_type==CONSTANT || link_type==PV_LINK)
+ return 0;
+ else if(link_type==INST_IO && pinfo->target[0]!='\0')
+ /* for compatibility. Invalid non-empty INST_IO is treated as empty string */
+ return 0;
+ default:
+ free(pinfo->target);
+ pinfo->target = NULL;
+ return 1;
+ }
+}
+
+static
+void dbSetLinkConst(DBLINK *plink, dbLinkInfo *pinfo)
+{
+ plink->type = CONSTANT;
+ plink->value.constantStr = pinfo->target;
+
+ pinfo->target = NULL;
+}
+
+static
+void dbSetLinkPV(DBLINK *plink, dbLinkInfo *pinfo)
+{
+ plink->type = PV_LINK;
+ plink->value.pv_link.pvname = pinfo->target;
+ plink->value.pv_link.pvlMask = pinfo->modifiers;
+
+ pinfo->target = NULL;
+}
+
+static
+void dbSetLinkHW(DBLINK *plink, dbLinkInfo *pinfo)
+{
+
+ switch(pinfo->ltype) {
+ case INST_IO:
+ plink->value.instio.string = pinfo->target;
+ break;
+ case VME_IO:
+ plink->value.vmeio.card = pinfo->hwnums[0];
+ plink->value.vmeio.signal = pinfo->hwnums[1];
+ plink->value.vmeio.parm = pinfo->target;
+ break;
+ case CAMAC_IO:
+ plink->value.camacio.b = pinfo->hwnums[0];
+ plink->value.camacio.c = pinfo->hwnums[1];
+ plink->value.camacio.n = pinfo->hwnums[2];
+ plink->value.camacio.a = pinfo->hwnums[3];
+ plink->value.camacio.f = pinfo->hwnums[4];
+ plink->value.camacio.parm = pinfo->target;
+ break;
+ case RF_IO:
+ plink->value.rfio.cryo = pinfo->hwnums[0];
+ plink->value.rfio.micro = pinfo->hwnums[1];
+ plink->value.rfio.dataset = pinfo->hwnums[2];
+ plink->value.rfio.element = pinfo->hwnums[3];
+ break;
+ case AB_IO:
+ plink->value.abio.link = pinfo->hwnums[0];
+ plink->value.abio.adapter = pinfo->hwnums[1];
+ plink->value.abio.card = pinfo->hwnums[2];
+ plink->value.abio.signal = pinfo->hwnums[3];
+ plink->value.abio.parm = pinfo->target;
+ break;
+ case GPIB_IO:
+ plink->value.gpibio.link = pinfo->hwnums[0];
+ plink->value.gpibio.addr = pinfo->hwnums[1];
+ plink->value.gpibio.parm = pinfo->target;
+ break;
+ case BITBUS_IO:
+ plink->value.bitbusio.link = pinfo->hwnums[0];
+ plink->value.bitbusio.node = pinfo->hwnums[1];
+ plink->value.bitbusio.port = pinfo->hwnums[2];
+ plink->value.bitbusio.signal = pinfo->hwnums[3];
+ plink->value.bitbusio.parm = pinfo->target;
+ break;
+ case BBGPIB_IO:
+ plink->value.bbgpibio.link = pinfo->hwnums[0];
+ plink->value.bbgpibio.bbaddr = pinfo->hwnums[1];
+ plink->value.bbgpibio.gpibaddr = pinfo->hwnums[2];
+ plink->value.bbgpibio.parm = pinfo->target;
+ break;
+ case VXI_IO:
+ if(strcmp(pinfo->hwid, "VCS")==0) {
+ plink->value.vxiio.flag=VXIDYNAMIC;
+ plink->value.vxiio.frame = pinfo->hwnums[0];
+ plink->value.vxiio.slot = pinfo->hwnums[1];
+ plink->value.vxiio.signal = pinfo->hwnums[2];
+ } else if(strcmp(pinfo->hwid, "VS")==0) {
+ plink->value.vxiio.flag=VXISTATIC;
+ plink->value.vxiio.la = pinfo->hwnums[0];
+ plink->value.vxiio.signal = pinfo->hwnums[1];
+ } else {
+ cantProceed("dbSetLinkHW: logic error, unknown VXI_IO variant");
+ }
+ plink->value.vxiio.parm = pinfo->target;
+ break;
+
+ default:
+ cantProceed("dbSetLinkHW: logic error, unhandled link type");
+ return;
+ }
+
+ plink->type = pinfo->ltype;
+
+ pinfo->target = NULL; /* now owned by link field */
+}
+
+long dbSetLink(DBLINK *plink, dbLinkInfo *pinfo, devSup *devsup)
+{
+ int ret = 0;
+ int link_type = CONSTANT;
+
+ if(devsup)
+ link_type = devsup->link_type;
+
+ if(link_type==CONSTANT || link_type==PV_LINK) {
+ switch(pinfo->ltype) {
+ case CONSTANT:
+ dbFreeLinkContents(plink);
+ dbSetLinkConst(plink, pinfo); break;
+ case PV_LINK:
+ dbFreeLinkContents(plink);
+ dbSetLinkPV(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) {
+ dbFreeLinkContents(plink);
+ dbSetLinkHW(plink, pinfo);
+
+ } else if(link_type==INST_IO && (pinfo->ltype==CONSTANT || pinfo->ltype==PV_LINK) && pinfo->target[0]!='\0') {
+ /* for compatibility. Invalid non-empty INST_IO is treated as empty string */
+ pinfo->target[0] = '\0';
+ pinfo->ltype = INST_IO;
+
+ dbFreeLinkContents(plink);
+ dbSetLinkHW(plink, pinfo);
+
+ } else
+ goto fail;
+
+ return ret;
+fail:
+ free(pinfo->target);
+ pinfo->target = NULL;
+ return S_dbLib_badField;
+}
+
long dbPutString(DBENTRY *pdbentry,const char *pstring)
{
dbFldDes *pflddes = pdbentry->pflddes;
diff --git a/src/ioc/dbStatic/dbStaticPvt.h b/src/ioc/dbStatic/dbStaticPvt.h
index 8f8664dc1..f3e6a932e 100644
--- a/src/ioc/dbStatic/dbStaticPvt.h
+++ b/src/ioc/dbStatic/dbStaticPvt.h
@@ -36,6 +36,37 @@ char *dbRecordName(DBENTRY *pdbentry);
char *dbGetStringNum(DBENTRY *pdbentry);
long dbPutStringNum(DBENTRY *pdbentry,const char *pstring);
+typedef struct dbLinkInfo {
+ short ltype;
+
+ /* full link string for CONSTANT and PV_LINK,
+ * parm string for HW links*/
+ char *target;
+
+ /* for PV_LINK */
+ short modifiers;
+
+ /* HW links */
+ char hwid[6]; /* one extra element for a nil */
+ int hwnums[5];
+} dbLinkInfo;
+
+/* Parse link string. no record locks needed.
+ * on success caller must free pinfo->target
+ */
+long dbParseLink(const char *str, short ftype, dbLinkInfo *pinfo);
+/* Check if link type allow the parsed link value pinfo
+ * to be assigned to the given link.
+ * Record containing plink must be locked.
+ * Frees pinfo->target on failure.
+ */
+long dbCanSetLink(DBLINK *plink, dbLinkInfo *pinfo, devSup *devsup);
+/* Set link field. source record must be locked (target record too
+ * when a DB_LINK is created)
+ * Unconditionally takes ownership of pinfo->target
+ */
+long dbSetLink(DBLINK *plink, dbLinkInfo *pinfo, devSup *dset);
+
/* The following is for path */
typedef struct dbPathNode {
ELLNODE node;
From 480797c0e001fb6d27df8b07b5f76de16e0b6b6e Mon Sep 17 00:00:00 2001
From: Michael Davidsaver
Date: Fri, 1 Aug 2014 11:28:10 -0400
Subject: [PATCH 004/204] db/test: extend dbPutLinkTest to cover dbParseLink()
---
src/ioc/db/test/dbPutLinkTest.c | 122 +++++++++++++++++++++++++++++++-
1 file changed, 121 insertions(+), 1 deletion(-)
diff --git a/src/ioc/db/test/dbPutLinkTest.c b/src/ioc/db/test/dbPutLinkTest.c
index 75e01b2d7..31563466f 100644
--- a/src/ioc/db/test/dbPutLinkTest.c
+++ b/src/ioc/db/test/dbPutLinkTest.c
@@ -26,10 +26,128 @@
#include "xRecord.h"
+#include "dbStaticPvt.h"
+
#include "testMain.h"
+static
+int testStrcmp(int expect, const char *A, const char *B) {
+ static const char op[] = "<=>";
+ int ret = strcmp(A,B);
+ testOk(ret==expect, "\"%s\" %c= \"%s\"",
+ A, op[expect+1], B);
+ return ret==expect;
+}
+
void dbTestIoc_registerRecordDeviceDriver(struct dbBase *);
+#define TEST_CONSTANT(SET, EXPECT) {SET, {CONSTANT, EXPECT}}
+#define TEST_PV_LINK(SET, PV, MOD) {SET, {PV_LINK, PV, MOD}}
+#define TEST_HW(SET, TYPE, ID, PARM, ...) {SET, {TYPE, PARM, 0, ID, {__VA_ARGS__}}}
+
+static const struct testParseDataT {
+ const char * const str;
+ dbLinkInfo info;
+} testParseData[] = {
+ TEST_CONSTANT("", ""),
+ TEST_CONSTANT("0.1", "0.1"),
+ TEST_CONSTANT(" 0.2\t ", "0.2"),
+
+ TEST_PV_LINK("0.1a", "0.1a", 0),
+ TEST_PV_LINK(" hello ", "hello", 0),
+ TEST_PV_LINK(" hellox MSI", "hellox", pvlOptMSI),
+ TEST_PV_LINK(" world MSICP", "world", pvlOptMSI|pvlOptCP),
+
+ TEST_HW("#C14 S145 @testing", VME_IO, "CS", "testing", 14, 145),
+ TEST_HW("#B11 C12 N13 A14 F15 @cparam", CAMAC_IO, "BCNAF", "cparam", 11, 12, 13, 14, 15),
+ TEST_HW("@hello world", INST_IO, "", "hello world"),
+ {NULL}
+};
+
+static void testLinkParse(void)
+{
+ const struct testParseDataT *td = testParseData;
+ dbLinkInfo info;
+ testDiag("link parsing");
+ testdbPrepare();
+
+ testdbReadDatabase("dbTestIoc.dbd", NULL, NULL);
+
+ dbTestIoc_registerRecordDeviceDriver(pdbbase);
+
+ testdbReadDatabase("dbPutLinkTest.db", NULL, NULL);
+
+ eltc(0);
+ testIocInitOk();
+ eltc(1);
+
+ 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)
+ testStrcmp(0, info.target, td->info.target);
+ if(info.ltype==td->info.ltype) {
+ switch(info.ltype) {
+ case PV_LINK:
+ testOk1(info.modifiers==td->info.modifiers);
+ break;
+ case VME_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);
+ }
+
+ testIocShutdownOk();
+
+ testdbCleanup();
+}
+
+static const char *testParseFailData[] = {
+ "#",
+ "#S",
+ "#ABC",
+ "#A0 B",
+ "#A0 B @",
+ "#A0 B C @",
+ "#R1 M2 D3 E4 @oops", /* RF_IO has no parm */
+ "#C1 S2", /* VME_IO needs parm */
+ NULL
+};
+
+static void testLinkFailParse(void)
+{
+ const char * const *td = testParseFailData;
+ dbLinkInfo info;
+ testDiag("link parsing of invalid input");
+ testdbPrepare();
+
+ testdbReadDatabase("dbTestIoc.dbd", NULL, NULL);
+
+ dbTestIoc_registerRecordDeviceDriver(pdbbase);
+
+ testdbReadDatabase("dbPutLinkTest.db", NULL, NULL);
+
+ eltc(0);
+ testIocInitOk();
+ eltc(1);
+
+ for(;*td; td++) {
+ testDiag("Expect failure \"%s\"", *td);
+ testOk1(dbParseLink(*td, DBF_INLINK, &info)==S_dbLib_badField);
+ }
+
+ testIocShutdownOk();
+
+ testdbCleanup();
+}
+
static const struct testDataT {
const char * const linkstring;
short linkType;
@@ -387,7 +505,9 @@ static void testLinkFail(void)
MAIN(dbPutLinkTest)
{
- testPlan(200);
+ testPlan(245);
+ testLinkParse();
+ testLinkFailParse();
testCADBSet();
testHWInitSet();
testHWMod();
From 200355bc0030267431161018547180c292378fff Mon Sep 17 00:00:00 2001
From: Michael Davidsaver
Date: Fri, 1 Aug 2014 11:28:10 -0400
Subject: [PATCH 005/204] dbStatic: re-write dbPutString() to use new link
parsing
dbPutString() now has two behaviors. If link_type==CONSTANT
and constantStr==NULL, the link has not yet been initialized.
Just set the ->text field.
If the link has been initialized, then use dbSetLink() to
make the assignment.
Eliminate special handling for DBF_DEVICE.
---
src/ioc/dbStatic/dbStaticLib.c | 489 ++-------------------------------
1 file changed, 21 insertions(+), 468 deletions(-)
diff --git a/src/ioc/dbStatic/dbStaticLib.c b/src/ioc/dbStatic/dbStaticLib.c
index 5ca23493b..e7807bc7a 100644
--- a/src/ioc/dbStatic/dbStaticLib.c
+++ b/src/ioc/dbStatic/dbStaticLib.c
@@ -44,6 +44,8 @@
#include "dbStaticLib.h"
#include "dbStaticPvt.h"
+#include "dbCommon.h"
+
int dbStaticDebug = 0;
static char *pNullString = "";
#define messagesize 100
@@ -84,12 +86,9 @@ static int mapDBFtoDCT[DBF_NOACCESS+1] = {
/*forward references for private routines*/
static FILE *openOutstream(const char *filename);
static void finishOutstream(FILE *stream);
-static long setLinkType(DBENTRY *pdbentry);
-static long putParmString(char **pparm,const char *pstring);
static void entryErrMessage(DBENTRY *pdbentry,long status,char *mess);
static void zeroDbentry(DBENTRY *pdbentry);
static char *getpMessage(DBENTRY *pdbentry);
-static long putPvLink(DBENTRY *pdbentry,short pvlMask,const char *pvname);
static long dbAddOnePath (DBBASE *pdbbase, const char *path, unsigned length);
/* internal routines*/
@@ -114,103 +113,6 @@ static void finishOutstream(FILE *stream)
}
}
-static long setLinkType(DBENTRY *pdbentry)
-{
- DBENTRY dbEntry;
- dbRecordType *precordType;
- devSup *pdevSup;
- DBLINK *plink;
- long status=0;
- int link_type,ind,type;
-
- dbCopyEntryContents(pdbentry, &dbEntry);
- status = dbFindField(&dbEntry, "DTYP");
- if (status) {
- epicsPrintf("field DTYP does not exist for recordtype %s\n",
- dbGetRecordTypeName(&dbEntry));
- status = S_dbLib_fieldNotFound;
- goto done;
- }
-
- precordType = dbEntry.precordType;
- if (!precordType) {
- status = S_dbLib_badField;
- goto done;
- }
-
- if (ellCount(&precordType->devList) == 0) goto done;
-
- ind = dbGetMenuIndex(&dbEntry);
- if (ind == -1) {
- char *pstring;
-
- pstring = dbGetString(&dbEntry);
- if (strstr(pstring, "$(") || strstr(pstring, "${")) {
- link_type = MACRO_LINK;
- } else {
- status = S_dbLib_badField;
- goto done;
- }
- } else {
- pdevSup = (devSup *)ellNth(&precordType->devList, ind + 1);
- if (!pdevSup) {
- status = S_dbLib_badField;
- goto done;
- }
- link_type = pdevSup->link_type;
- }
-
- plink = (DBLINK *)pdbentry->pfield;
- if (plink->type == link_type) goto done;
-
- if (plink->text)
- {
- /* re-parse link text when DTYP has changed */
- char * link_text;
- link_text = plink->text;
- plink->text = NULL;
- dbFreeLinkContents(plink);
- plink->type = link_type;
- dbPutString(pdbentry, link_text);
- free(link_text);
- goto done;
- }
-
- type = plink->type;
- if ((type == CONSTANT || type == PV_LINK ||
- type == PN_LINK || type == DB_LINK || type == CA_LINK) &&
- (link_type == CONSTANT || link_type == PV_LINK)) goto done;
-
- dbFreeLinkContents(plink);
- plink->type = link_type;
- switch (plink->type) {
- 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;
- case GPIB_IO: plink->value.gpibio.parm = pNullString; break;
- case BITBUS_IO: plink->value.bitbusio.parm = pNullString; break;
- case INST_IO: plink->value.instio.string = pNullString; break;
- case BBGPIB_IO: plink->value.bbgpibio.parm = pNullString; break;
- case VXI_IO: plink->value.vxiio.parm = pNullString; break;
- }
-done:
- dbFinishEntry(&dbEntry);
- return(status);
-}
-
-static long putParmString(char **pparm,const char *pstring)
-{
- if (*pparm && *pparm != pNullString) {
- free(*pparm);
- *pparm = pNullString;
- }
- if (!pstring) return 0;
- pstring = strchr(pstring, '@');
- if (!pstring || !*++pstring) return 0;
- *pparm = epicsStrDup(pstring);
- return 0;
-}
-
void dbFreeLinkContents(struct link *plink)
{
char *parm = NULL;
@@ -308,35 +210,6 @@ static char *getpMessage(DBENTRY *pdbentry)
return msg;
}
-static long putPvLink(DBENTRY *pdbentry,short pvlMask,const char *pvname)
-{
- dbFldDes *pflddes;
- DBLINK *plink;
- char *pname;
-
- 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 != PV_LINK) return(S_dbLib_badLink);
- pname = plink->value.pv_link.pvname;
- if(pname) free((void *)pname);
- pname = dbCalloc(strlen(pvname)+1,sizeof(char));
- plink->value.pv_link.pvname = pname;
- strcpy(pname,pvname);
- plink->value.pv_link.pvlMask = pvlMask;
- return(0);
- default:
- errPrintf(-1,__FILE__, __LINE__,"Logic Error\n");
- }
- return(S_dbLib_badLink);
-}
-
/*Public only for dbStaticNoRun*/
dbDeviceMenu *dbGetDeviceMenu(DBENTRY *pdbentry)
{
@@ -2015,15 +1888,6 @@ char * dbGetString(DBENTRY *pdbentry)
return (message);
}
-static void cvtDecimalOrHexToShort(char *from,short *value)
-{
- if(strspn(from,"0x")==2 || strspn(from,"0X")==2) {
- sscanf(from,"%hi",value);
- } else {
- sscanf(from,"%hd",value);
- }
-}
-
long dbParseLink(const char *str, short ftype, dbLinkInfo *pinfo)
{
char *pstr, *pend;
@@ -2362,350 +2226,39 @@ long dbPutString(DBENTRY *pdbentry,const char *pstring)
case DBF_FLOAT:
case DBF_DOUBLE:
case DBF_MENU:
+ case DBF_DEVICE:
status = dbPutStringNum(pdbentry,pstring);
break;
- case DBF_DEVICE: {
- DBENTRY dbEntry;
- char *name;
-
- status = dbPutStringNum(pdbentry, pstring);
- if (status) return status;
-
- name = dbGetRelatedField(pdbentry);
- if (!name) return 0;
-
- dbCopyEntryContents(pdbentry, &dbEntry);
- status = dbFindField(&dbEntry, name);
- if (!status)
- status = setLinkType(&dbEntry);
- dbFinishEntry(&dbEntry);
- return status;
- }
-
case DBF_INLINK:
case DBF_OUTLINK:
case DBF_FWDLINK: {
- DBLINK *plink;
- char string[80];
- char *pstr = string;
+ dbLinkInfo link_info;
+ DBLINK *plink = (DBLINK *)pfield;
- if (!pfield)
- return S_dbLib_fieldNotFound;
+ status = dbParseLink(pstring, pflddes->field_type, &link_info);
- plink = (DBLINK *)pfield;
- dbFreeLinkContents(plink);
- if (stringHasMacro) {
- plink->type = MACRO_LINK;
- plink->value.macro_link.macroStr = epicsStrDup(pstring);
- goto done;
- }
+ if(status==0 && plink->type==CONSTANT && plink->value.constantStr==NULL) {
+ /* links not yet initialized by dbInitRecordLinks() */
+ free(plink->text);
+ plink->text = epicsStrDup(pstring);
+ free(link_info.target);
- if (pflddes->isDevLink) {
- status = setLinkType(pdbentry); /* This uses DTYP to look up and set plink->type, necessary for default DTYP */
- if (status) {
- errMessage(status,"in dbPutString from setLinkType");
- return status;
- }
- /* store link text in case DTYP changes later */
- plink->text = malloc(strlen(pstring) + 1);
- if (plink->text)
- strcpy(plink->text, pstring);
- }
- if (strlen(pstring) >= sizeof(string)) {
- status = S_dbLib_badField;
- errMessage(status,
- "dbPutString received a string that is too long");
- return status;
- }
- strcpy(pstr, pstring);
- /* Strip leading blanks and tabs */
- while (*pstr && (*pstr == ' ' || *pstr == '\t')) pstr++;
- /* Strip trailing blanks and tabs */
- if (pstr) {
- int ind;
+ } else if(status==0) {
+ /* assignment after init (eg. autosave restore) */
+ struct dbCommon *prec = pdbentry->precnode->precord;
+ devSup *devsup = (devSup *)ellNth(&pdbentry->precordType->devList, prec->dtyp+1);
+ status = dbCanSetLink(plink, &link_info, devsup);
+ if(status)
+ status = dbSetLink(plink, &link_info, devsup);
+ }
- for (ind = (int) strlen(pstr) - 1; ind >= 0; ind--) {
- if (pstr[ind] != ' ' && pstr[ind] != '\t') break;
- pstr[ind] = '\0';
- }
- }
- if (!pstr || !*pstr) {
- if (plink->type == PV_LINK) dbCvtLinkToConstant(pdbentry);
- if (plink->type != CONSTANT) return S_dbLib_badField;
- free((void *)plink->value.constantStr);
- plink->value.constantStr = NULL;
- goto done;
- }
- switch (plink->type) {
- case CONSTANT:
- case PV_LINK: {
- short ppOpt = 0;
- short msOpt = 0;
- char *end;
- char *enddouble;
- char *endlong;
-
- /* Check first to see if string is a constant*/
- /*It is a string if epicsStrtod or strtol eats entire string*/
- /*leading and trailing blanks have already been stripped*/
- (void) epicsStrtod(pstr, &enddouble);
- (void) strtol(pstr, &endlong, 0);
-
- if (*enddouble == 0 || *endlong == 0) {
- if (plink->type == PV_LINK) dbCvtLinkToConstant(pdbentry);
- if (!plink->value.constantStr ||
- strlen(plink->value.constantStr) < strlen(pstr)) {
- free(plink->value.constantStr);
- plink->value.constantStr =
- dbCalloc(strlen(pstr) + 1, sizeof(char));
- }
- strcpy(plink->value.constantStr, pstr);
- goto done;
- }
-
- if (plink->type==CONSTANT) dbCvtLinkToPvlink(pdbentry);
- end = strchr(pstr,' ');
- if (end) {
- switch (pflddes->field_type) {
- case DBF_INLINK: {
- if (strstr(end, "NPP")) ppOpt = 0;
- else if (strstr(end, "CPP")) ppOpt = pvlOptCPP;
- else if (strstr(end, "PP")) ppOpt = pvlOptPP;
- else if (strstr(end, "CA")) ppOpt = pvlOptCA;
- else if (strstr(end, "CP")) ppOpt = pvlOptCP;
- else ppOpt = 0;
- if (strstr(end, "NMS")) msOpt = pvlOptNMS;
- else if (strstr(end, "MSI")) msOpt = pvlOptMSI;
- else if (strstr(end, "MSS")) msOpt = pvlOptMSS;
-/*must be the last one:*/ else if (strstr(end, "MS")) msOpt = pvlOptMS;
- else msOpt = 0;
- *end = 0;
- }
- break;
-
- case DBF_OUTLINK: {
- if (strstr(end, "NPP")) ppOpt = 0;
- else if(strstr(end, "PP")) ppOpt = pvlOptPP;
- else if(strstr(end, "CA")) ppOpt = pvlOptCA;
- else ppOpt = 0;
- if (strstr(end, "NMS")) msOpt = pvlOptNMS;
- else if(strstr(end, "MSI")) msOpt = pvlOptMSI;
- else if(strstr(end, "MSS")) msOpt = pvlOptMSS;
-/*must be the last one:*/ else if(strstr(end, "MS")) msOpt = pvlOptMS;
- else msOpt = 0;
- *end = 0;
- }
- break;
-
- case DBF_FWDLINK: {
- if (strstr(end, "NPP")) ppOpt = 0;
- else if (strstr(end, "CA")) ppOpt = pvlOptCA;
- else ppOpt = 0;
- msOpt = 0;
- *end = 0;
- }
- break;
-
- default:
- epicsPrintf("dbPutString: Logic Error\n");
- }
- }
- status = putPvLink(pdbentry,ppOpt|msOpt,pstr);
- goto done;
- }
- /*break; is unnecessary*/
- case VME_IO: {
- char *end;
-
- if (!(end = strchr(pstr,'#'))) return S_dbLib_badField;
- pstr = end + 1;
- if (!(end = strchr(pstr,'C'))) return S_dbLib_badField;
- pstr = end + 1;
- cvtDecimalOrHexToShort(pstr,&plink->value.vmeio.card);
- if (!(end = strchr(pstr,'S'))) return S_dbLib_badField;
- pstr = end + 1;
- cvtDecimalOrHexToShort(pstr, &plink->value.vmeio.signal);
- status = putParmString(&plink->value.vmeio.parm, pstr);
- }
- break;
-
- case CAMAC_IO: {
- char *end;
-
- if (!(end = strchr(pstr,'#'))) return (S_dbLib_badField);
- pstr = end + 1;
- if (!(end = strchr(pstr,'B'))) return (S_dbLib_badField);
- pstr = end + 1;
- cvtDecimalOrHexToShort(pstr,&plink->value.camacio.b);
- if (!(end = strchr(pstr,'C'))) return (S_dbLib_badField);
- pstr = end + 1;
- cvtDecimalOrHexToShort(pstr,&plink->value.camacio.c);
- if (!(end = strchr(pstr,'N'))) return (S_dbLib_badField);
- pstr = end + 1;
- cvtDecimalOrHexToShort(pstr,&plink->value.camacio.n);
- if (!(end = strchr(pstr,'A'))) {
- plink->value.camacio.a = 0;
- } else {
- pstr = end + 1;
- cvtDecimalOrHexToShort(pstr,&plink->value.camacio.a);
- }
- if (!(end = strchr(pstr,'F'))) {
- plink->value.camacio.f = 0;
- } else {
- pstr = end + 1;
- cvtDecimalOrHexToShort(pstr,&plink->value.camacio.f);
- }
- status = putParmString(&plink->value.camacio.parm,pstr);
- }
- break;
-
- case RF_IO: {
- char *end;
-
- if(!(end = strchr(pstr,'#'))) return (S_dbLib_badField);
- pstr = end + 1;
- if(!(end = strchr(pstr,'R'))) return (S_dbLib_badField);
- pstr = end + 1;
- cvtDecimalOrHexToShort(pstr,&plink->value.rfio.cryo);
- if(!(end = strchr(pstr,'M'))) return (S_dbLib_badField);
- pstr = end + 1;
- cvtDecimalOrHexToShort(pstr,&plink->value.rfio.micro);
- if(!(end = strchr(pstr,'D'))) return (S_dbLib_badField);
- pstr = end + 1;
- cvtDecimalOrHexToShort(pstr,&plink->value.rfio.dataset);
- if(!(end = strchr(pstr,'E'))) return (S_dbLib_badField);
- pstr = end + 1;
- cvtDecimalOrHexToShort(pstr,&plink->value.rfio.element);
- }
- break;
- case AB_IO: {
- char *end;
-
- if(!(end = strchr(pstr,'#'))) return (S_dbLib_badField);
- pstr = end + 1;
- if(!(end = strchr(pstr,'L'))) return (S_dbLib_badField);
- pstr = end + 1;
- cvtDecimalOrHexToShort(pstr,&plink->value.abio.link);
- if(!(end = strchr(pstr,'A'))) return (S_dbLib_badField);
- pstr = end + 1;
- cvtDecimalOrHexToShort(pstr,&plink->value.abio.adapter);
- if(!(end = strchr(pstr,'C'))) return (S_dbLib_badField);
- pstr = end + 1;
- cvtDecimalOrHexToShort(pstr,&plink->value.abio.card);
- if(!(end = strchr(pstr,'S'))) return (S_dbLib_badField);
- pstr = end + 1;
- cvtDecimalOrHexToShort(pstr,&plink->value.abio.signal);
- status = putParmString(&plink->value.abio.parm,pstr);
- }
- break;
- case GPIB_IO: {
- char *end;
-
- if(!(end = strchr(pstr,'#'))) return (S_dbLib_badField);
- pstr = end + 1;
- if(!(end = strchr(pstr,'L'))) return (S_dbLib_badField);
- pstr = end + 1;
- cvtDecimalOrHexToShort(pstr,&plink->value.gpibio.link);
- if(!(end = strchr(pstr,'A'))) return (S_dbLib_badField);
- pstr = end + 1;
- cvtDecimalOrHexToShort(pstr,&plink->value.gpibio.addr);
- status = putParmString(&plink->value.gpibio.parm,pstr);
- }
- break;
- case BITBUS_IO: {
- /* jbk - the bbgpib struct uses unsigned char's instead
- of short, so read values into short and then convert */
-
- char *end;
- short tmp_val;
-
- if(!(end = strchr(pstr,'#'))) return (S_dbLib_badField);
- pstr = end + 1;
- if(!(end = strchr(pstr,'L'))) return (S_dbLib_badField);
- pstr = end + 1;
- cvtDecimalOrHexToShort(pstr,&tmp_val);
- plink->value.bitbusio.link=(unsigned char)tmp_val;
- if(!(end = strchr(pstr,'N'))) return (S_dbLib_badField);
- pstr = end + 1;
- cvtDecimalOrHexToShort(pstr,&tmp_val);
- plink->value.bitbusio.node=(unsigned char)tmp_val;
- if(!(end = strchr(pstr,'P'))) return (S_dbLib_badField);
- pstr = end + 1;
- cvtDecimalOrHexToShort(pstr,&tmp_val);
- plink->value.bitbusio.port=(unsigned char)tmp_val;
- if(!(end = strchr(pstr,'S'))) return (S_dbLib_badField);
- pstr = end + 1;
- cvtDecimalOrHexToShort(pstr,&tmp_val);
- plink->value.bitbusio.signal=(unsigned char)tmp_val;
- status = putParmString(&plink->value.bitbusio.parm,pstr);
- }
- break;
- case BBGPIB_IO: {
- /* jbk - the bbgpib struct uses unsigned char's instead
- of short, so read values into short and then convert */
-
- char *end;
- short tmp_val;
-
- if(!(end = strchr(pstr,'#'))) return (S_dbLib_badField);
- pstr = end + 1;
- if(!(end = strchr(pstr,'L'))) return (S_dbLib_badField);
- pstr = end + 1;
- cvtDecimalOrHexToShort(pstr,&tmp_val);
- plink->value.bbgpibio.link=(unsigned char)tmp_val;
- if(!(end = strchr(pstr,'B'))) return (S_dbLib_badField);
- pstr = end + 1;
- cvtDecimalOrHexToShort(pstr,&tmp_val);
- plink->value.bbgpibio.bbaddr=(unsigned char)tmp_val;
- if(!(end = strchr(pstr,'G'))) return (S_dbLib_badField);
- pstr = end + 1;
- cvtDecimalOrHexToShort(pstr,&tmp_val);
- plink->value.bbgpibio.gpibaddr=(unsigned char)tmp_val;
- status = putParmString(&plink->value.bbgpibio.parm,pstr);
- }
- break;
- case VXI_IO: {
- char *end;
-
- if(!(end = strchr(pstr,'#'))) return (S_dbLib_badField);
- pstr = end + 1;
- memset((char *)&plink->value.vxiio,0,sizeof(struct vxiio));
- plink->value.vxiio.parm = pNullString;
- if(!((end = strchr(pstr,'C'))&&(end < strchr(pstr,'@')) )) {
- plink->value.vxiio.flag = VXISTATIC;
- if(!(end = strchr(pstr,'V'))) return (S_dbLib_badField);
- pstr = end + 1;
- cvtDecimalOrHexToShort(pstr,&plink->value.vxiio.la);
- } else {
- plink->value.vxiio.flag = VXIDYNAMIC;
- if(!(end = strchr(pstr,'V'))) return (S_dbLib_badField);
- pstr = end + 1;
- cvtDecimalOrHexToShort(pstr,&plink->value.vxiio.frame);
- if(!(end = strchr(pstr,'C'))) return (S_dbLib_badField);
- pstr = end + 1;
- cvtDecimalOrHexToShort(pstr,&plink->value.vxiio.slot);
- }
- if((end = strchr(pstr,'S'))) {
- pstr = end + 1;
- cvtDecimalOrHexToShort(pstr,&plink->value.vxiio.signal);
- } else {
- plink->value.vxiio.signal = 0;
- }
- status = putParmString(&plink->value.vxiio.parm,pstr);
- }
- break;
- case INST_IO: {
- status = putParmString(&plink->value.instio.string, pstr);
- }
- break;
- }
- }
+ }
break;
default:
return S_dbLib_badField;
}
-done:
+
if (!status && strcmp(pflddes->name, "VAL") == 0) {
DBENTRY dbentry;
From 33804bd7ead99c23549b3fa43504366c12900bec Mon Sep 17 00:00:00 2001
From: Michael Davidsaver
Date: Fri, 1 Aug 2014 11:28:10 -0400
Subject: [PATCH 006/204] iocInit: initialize links in iocBuild
Done before any driver, record, or device supports run.
Also before the hooks used by autosave.
---
src/ioc/dbStatic/dbStaticLib.c | 59 ++++++++++++++++++++++++++++++++++
src/ioc/dbStatic/dbStaticPvt.h | 2 ++
src/ioc/misc/iocInit.c | 23 +++++++++----
3 files changed, 77 insertions(+), 7 deletions(-)
diff --git a/src/ioc/dbStatic/dbStaticLib.c b/src/ioc/dbStatic/dbStaticLib.c
index e7807bc7a..ca7dfff83 100644
--- a/src/ioc/dbStatic/dbStaticLib.c
+++ b/src/ioc/dbStatic/dbStaticLib.c
@@ -1888,6 +1888,65 @@ char * dbGetString(DBENTRY *pdbentry)
return (message);
}
+long dbInitRecordLinks(dbRecordType *rtyp, struct dbCommon *prec)
+{
+ short i;
+
+ for (i=0; ino_links; i++) {
+ dbLinkInfo link_info;
+ dbFldDes *pflddes = rtyp->papFldDes[rtyp->link_ind[i]];
+ DBLINK *plink = (DBLINK *)(((char *)prec) + pflddes->offset);
+ devSup *devsup = NULL;
+
+ /* link fields are zero'd on allocation.
+ * so are effectively CONSTANT, but with constantStr==NULL.
+ * Here we initialize them to have the correct link type,
+ * with zero values and empty (but non-NULL) strings.
+ */
+
+ if(pflddes->isDevLink) {
+ devsup = (devSup *)ellNth(&rtyp->devList, prec->dtyp+1);
+ }
+ if(devsup)
+ plink->type = devsup->link_type;
+ else
+ plink->type = CONSTANT;
+
+ switch (plink->type) {
+ case CONSTANT: plink->value.constantStr = callocMustSucceed(1, 1, "init CONSTANT link"); break;
+ case PV_LINK: plink->value.pv_link.pvname = callocMustSucceed(1, 1, "init PV_LINK"); 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;
+ case GPIB_IO: plink->value.gpibio.parm = pNullString; break;
+ case BITBUS_IO: plink->value.bitbusio.parm = pNullString; break;
+ case INST_IO: plink->value.instio.string = pNullString; break;
+ case BBGPIB_IO: plink->value.bbgpibio.parm = pNullString; break;
+ case VXI_IO: plink->value.vxiio.parm = pNullString; break;
+ }
+
+ if(!plink->text)
+ continue;
+
+ if(dbParseLink(plink->text, pflddes->field_type, &link_info)!=0) {
+ /* This was already parsed once when ->text was set.
+ * Any syntax error messages were printed at that time.
+ */
+
+ } else if(dbCanSetLink(plink, &link_info, devsup)!=0) {
+ errlogPrintf("Error: %s.%s: can't initialize link type %d with \"%s\" (type %d)\n",
+ prec->name, pflddes->name, plink->type, plink->text, link_info.ltype);
+
+ } else if(dbSetLink(plink, &link_info, devsup)) {
+ errlogPrintf("Error: %s.%s: failed to initialize link type %d with \"%s\" (type %d)\n",
+ prec->name, pflddes->name, plink->type, plink->text, link_info.ltype);
+ }
+ free(plink->text);
+ plink->text = NULL;
+ }
+ return 0;
+}
+
long dbParseLink(const char *str, short ftype, dbLinkInfo *pinfo)
{
char *pstr, *pend;
diff --git a/src/ioc/dbStatic/dbStaticPvt.h b/src/ioc/dbStatic/dbStaticPvt.h
index f3e6a932e..c249e73bb 100644
--- a/src/ioc/dbStatic/dbStaticPvt.h
+++ b/src/ioc/dbStatic/dbStaticPvt.h
@@ -51,6 +51,8 @@ typedef struct dbLinkInfo {
int hwnums[5];
} dbLinkInfo;
+long dbInitRecordLinks(dbRecordType *rtyp, struct dbCommon *prec);
+
/* Parse link string. no record locks needed.
* on success caller must free pinfo->target
*/
diff --git a/src/ioc/misc/iocInit.c b/src/ioc/misc/iocInit.c
index 1e224d2f5..32b7fc524 100644
--- a/src/ioc/misc/iocInit.c
+++ b/src/ioc/misc/iocInit.c
@@ -56,6 +56,8 @@
#include "dbCommon.h"
#include "dbLock.h"
#include "dbAccess.h"
+#include "dbStaticLib.h"
+#include "dbStaticPvt.h"
#include "recGbl.h"
#include "dbNotify.h"
#include "dbCa.h"
@@ -82,7 +84,14 @@ static void initDatabase(void);
static void initialProcess(void);
static void exitDatabase(void *dummy);
-
+/*
+ * Iterate through all record instances (but not aliases),
+ * calling a function for each one.
+ */
+typedef void (*recIterFunc)(dbRecordType *rtyp, dbCommon *prec, void *user);
+
+static void iterateRecords(recIterFunc func, void *user);
+
/*
* Initialize EPICS on the IOC.
*/
@@ -122,8 +131,14 @@ static int iocBuild_1(void)
return 0;
}
+static void prepareLinks(dbRecordType *rtyp, dbCommon *prec, void *junk)
+{
+ dbInitRecordLinks(rtyp, prec);
+}
+
static int iocBuild_2(void)
{
+ iterateRecords(prepareLinks, NULL);
initHookAnnounce(initHookAfterCaLinkInit);
initDrvSup();
@@ -413,12 +428,6 @@ static void finishDevSup(void)
}
}
}
-
-/*
- * Iterate through all record instances (but not aliases),
- * calling a function for each one.
- */
-typedef void (*recIterFunc)(dbRecordType *rtyp, dbCommon *prec, void *user);
static void iterateRecords(recIterFunc func, void *user)
{
From c5da1e3f89b6a54219e2096aea5f3678cbbf83ec Mon Sep 17 00:00:00 2001
From: Michael Davidsaver
Date: Fri, 1 Aug 2014 11:28:10 -0400
Subject: [PATCH 007/204] dbAccess: update dbPutFieldLink() to use
dbParseLink()/dbSetLink()
Now returns early for invalid link strings w/o modifying the
field, or detaching device support.
---
src/ioc/db/dbAccess.c | 46 +++++++++++++++++++++++++++++++++----------
1 file changed, 36 insertions(+), 10 deletions(-)
diff --git a/src/ioc/db/dbAccess.c b/src/ioc/db/dbAccess.c
index 2abbe8ea2..cd69c97ec 100644
--- a/src/ioc/db/dbAccess.c
+++ b/src/ioc/db/dbAccess.c
@@ -35,6 +35,7 @@
#include "errMdef.h"
#define epicsExportSharedSymbols
#include "dbStaticLib.h"
+#include "dbStaticPvt.h"
#include "dbBase.h"
#include "link.h"
#include "dbFldTypes.h"
@@ -909,15 +910,17 @@ devSup* dbDSETtoDevSup(dbRecordType *prdes, struct dset *pdset) {
static long dbPutFieldLink(DBADDR *paddr,
short dbrType, const void *pbuffer, long nRequest)
{
+ dbLinkInfo link_info;
+ DBADDR *pdbaddr = NULL;
dbCommon *precord = paddr->precord;
dbFldDes *pfldDes = paddr->pfldDes;
long special = paddr->special;
struct link *plink = (struct link *)paddr->pfield;
const char *pstring = (const char *)pbuffer;
- DBENTRY dbEntry;
struct dsxt *old_dsxt = NULL;
struct dset *new_dset = NULL;
struct dsxt *new_dsxt = NULL;
+ devSup *new_devsup = NULL;
long status;
int isDevLink;
short scan;
@@ -936,16 +939,30 @@ static long dbPutFieldLink(DBADDR *paddr,
return S_db_badDbrtype;
}
- dbInitEntry(pdbbase, &dbEntry);
- status = dbFindRecord(&dbEntry, precord->name);
- if (!status) status = dbFindField(&dbEntry, pfldDes->name);
- if (status) goto finish;
+ status = dbParseLink(pstring, pfldDes->field_type, &link_info);
+ if (status)
+ return status;
+
+ if (link_info.ltype==PV_LINK && !(link_info.modifiers&(pvlOptCA|pvlOptCP|pvlOptCPP))) {
+ DBADDR tempaddr;
+ if(dbNameToAddr(link_info.target, &tempaddr)==0) {
+ /* we will create a DB link. */
+ pdbaddr = malloc(sizeof(*pdbaddr));
+ if(!pdbaddr) {
+ status = S_db_noMemory;
+ goto cleanup;
+ }
+ *pdbaddr = tempaddr; /* struct copy */
+ }
+ }
isDevLink = ellCount(&precord->rdes->devList) > 0 &&
pfldDes->isDevLink;
dbLockSetGblLock();
dbLockSetRecordLock(precord);
+ if(pdbaddr)
+ dbLockSetRecordLock(pdbaddr->precord);
scan = precord->scan;
@@ -954,10 +971,19 @@ static long dbPutFieldLink(DBADDR *paddr,
if (pdevSup) {
new_dset = pdevSup->pdset;
new_dsxt = pdevSup->pdsxt;
+ new_devsup = pdevSup;
}
+ }
+ if (dbCanSetLink(plink, &link_info, new_devsup)) {
+ /* link type mis-match prevents assignment */
+ status = S_dbLib_badField;
+ goto unlock;
+ }
+
+ if (isDevLink) {
if (precord->dset) {
- pdevSup = dbDSETtoDevSup(precord->rdes, precord->dset);
+ devSup *pdevSup = dbDSETtoDevSup(precord->rdes, precord->dset);
if (pdevSup)
old_dsxt = pdevSup->pdsxt;
}
@@ -1005,7 +1031,7 @@ static long dbPutFieldLink(DBADDR *paddr,
if (special) status = dbPutSpecial(paddr, 0);
- if (!status) status = dbPutString(&dbEntry, pstring);
+ if (!status) status = dbSetLink(plink, &link_info, new_devsup);
if (!status && special) status = dbPutSpecial(paddr, 1);
@@ -1032,7 +1058,7 @@ static long dbPutFieldLink(DBADDR *paddr,
switch (plink->type) { /* New link type */
case PV_LINK:
- dbAddLink(precord, plink, pfldDes->field_type);
+ dbAddLink(precord, plink, pfldDes->field_type, pdbaddr);
break;
case CONSTANT:
@@ -1063,8 +1089,8 @@ postScanEvent:
db_post_events(precord, &precord->scan, DBE_VALUE | DBE_LOG);
unlock:
dbLockSetGblUnlock();
-finish:
- dbFinishEntry(&dbEntry);
+cleanup:
+ free(link_info.target);
return status;
}
From a919d7e09d17bd4a0b49d4af58ee566a5fe58479 Mon Sep 17 00:00:00 2001
From: Michael Davidsaver
Date: Fri, 1 Aug 2014 11:28:10 -0400
Subject: [PATCH 008/204] dbLink: pass DBADDR* to dbAddLink()
Now we know whether a DB or CA link is created
before calling dbAddLink().
---
src/ioc/db/dbLink.c | 36 +++++++++++-------------------------
src/ioc/db/dbLink.h | 3 ++-
2 files changed, 13 insertions(+), 26 deletions(-)
diff --git a/src/ioc/db/dbLink.c b/src/ioc/db/dbLink.c
index af23d7615..bc4de8ec6 100644
--- a/src/ioc/db/dbLink.c
+++ b/src/ioc/db/dbLink.c
@@ -123,26 +123,6 @@ static long dbDbInitLink(struct link *plink, short dbfType)
return 0;
}
-static long dbDbAddLink(struct link *plink, short dbfType)
-{
- DBADDR dbaddr;
- long status;
- DBADDR *pdbAddr;
-
- status = dbNameToAddr(plink->value.pv_link.pvname, &dbaddr);
- if (status)
- return status;
-
- plink->type = DB_LINK;
- pdbAddr = dbCalloc(1, sizeof(struct dbAddr));
- *pdbAddr = dbaddr; /* structure copy */
- plink->value.pv_link.pvt = pdbAddr;
-
- dbLockSetRecordLock(pdbAddr->precord);
- dbLockSetMerge(plink->value.pv_link.precord, pdbAddr->precord);
- return 0;
-}
-
static void dbDbRemoveLink(struct link *plink)
{
free(plink->value.pv_link.pvt);
@@ -411,17 +391,23 @@ void dbInitLink(struct dbCommon *precord, struct link *plink, short dbfType)
}
}
-void dbAddLink(struct dbCommon *precord, struct link *plink, short dbfType)
+void dbAddLink(struct dbCommon *precord, struct link *plink, short dbfType, DBADDR *ptargetaddr)
{
plink->value.pv_link.precord = precord;
if (plink == &precord->tsel)
recGblTSELwasModified(plink);
- if (!(plink->value.pv_link.pvlMask & (pvlOptCA | pvlOptCP | pvlOptCPP))) {
- /* Can we make it a DB link? */
- if (!dbDbAddLink(plink, dbfType))
- return;
+ if (ptargetaddr) {
+ /* make a DB link */
+
+ plink->type = DB_LINK;
+ plink->value.pv_link.pvt = ptargetaddr;
+
+ /* target record is already locked in dbPutFieldLink() */
+ dbLockSetMerge(plink->value.pv_link.precord, ptargetaddr->precord);
+
+ return;
}
/* Make it a CA link */
diff --git a/src/ioc/db/dbLink.h b/src/ioc/db/dbLink.h
index ee097757f..68a803dbb 100644
--- a/src/ioc/db/dbLink.h
+++ b/src/ioc/db/dbLink.h
@@ -20,6 +20,7 @@
#include "shareLib.h"
#include "epicsTypes.h"
#include "epicsTime.h"
+#include "dbAddr.h"
#ifdef __cplusplus
extern "C" {
@@ -31,7 +32,7 @@ extern "C" {
epicsShareFunc void dbInitLink(struct dbCommon *precord, struct link *plink,
short dbfType);
epicsShareFunc void dbAddLink(struct dbCommon *precord, struct link *plink,
- short dbfType);
+ short dbfType, DBADDR *ptargetaddr);
epicsShareFunc long dbLoadLink(struct link *plink, short dbrType,
void *pbuffer);
epicsShareFunc void dbRemoveLink(struct link *plink);
From 5a8915c051320c861244bb9041bac86aa30901bd Mon Sep 17 00:00:00 2001
From: Michael Davidsaver
Date: Fri, 1 Aug 2014 11:28:10 -0400
Subject: [PATCH 009/204] INST_IO no longer accept invalid string as empty
---
src/ioc/db/test/dbPutLinkTest.c | 7 +++----
src/ioc/dbStatic/dbStaticLib.c | 11 -----------
2 files changed, 3 insertions(+), 15 deletions(-)
diff --git a/src/ioc/db/test/dbPutLinkTest.c b/src/ioc/db/test/dbPutLinkTest.c
index 31563466f..52ae41fb5 100644
--- a/src/ioc/db/test/dbPutLinkTest.c
+++ b/src/ioc/db/test/dbPutLinkTest.c
@@ -482,9 +482,8 @@ static void testLinkFail(void)
/* INST_IO doesn't accept empty string */
testdbPutFieldFail(S_dbLib_badField, "rINST_IO.INP", DBR_STRING, "");
- /* INST_IO accepts invalid input as empty string */
- testdbPutFieldOk("rINST_IO.INP", DBR_STRING, "abc");
- testdbGetFieldEqual("rINST_IO.INP", DBR_STRING, "@");
+ /* INST_IO doesn't accept empty string */
+ testdbPutFieldFail(S_dbLib_badField, "rINST_IO.INP", DBR_STRING, "abc");
/* syntax errors */
testdbPutFieldFail(S_dbLib_badField, "rVME_IO.INP", DBR_STRING, "#S201 C200 @another VME_IO");
@@ -505,7 +504,7 @@ static void testLinkFail(void)
MAIN(dbPutLinkTest)
{
- testPlan(245);
+ testPlan(244);
testLinkParse();
testLinkFailParse();
testCADBSet();
diff --git a/src/ioc/dbStatic/dbStaticLib.c b/src/ioc/dbStatic/dbStaticLib.c
index ca7dfff83..ac4276e5d 100644
--- a/src/ioc/dbStatic/dbStaticLib.c
+++ b/src/ioc/dbStatic/dbStaticLib.c
@@ -2092,9 +2092,6 @@ long dbCanSetLink(DBLINK *plink, dbLinkInfo *pinfo, devSup *devsup)
case PV_LINK:
if(link_type==CONSTANT || link_type==PV_LINK)
return 0;
- else if(link_type==INST_IO && pinfo->target[0]!='\0')
- /* for compatibility. Invalid non-empty INST_IO is treated as empty string */
- return 0;
default:
free(pinfo->target);
pinfo->target = NULL;
@@ -2224,14 +2221,6 @@ long dbSetLink(DBLINK *plink, dbLinkInfo *pinfo, devSup *devsup)
dbFreeLinkContents(plink);
dbSetLinkHW(plink, pinfo);
- } else if(link_type==INST_IO && (pinfo->ltype==CONSTANT || pinfo->ltype==PV_LINK) && pinfo->target[0]!='\0') {
- /* for compatibility. Invalid non-empty INST_IO is treated as empty string */
- pinfo->target[0] = '\0';
- pinfo->ltype = INST_IO;
-
- dbFreeLinkContents(plink);
- dbSetLinkHW(plink, pinfo);
-
} else
goto fail;
From 6ed997aaccfe26aaef5c0413b99e42f35640c88c Mon Sep 17 00:00:00 2001
From: Michael Davidsaver
Date: Fri, 1 Aug 2014 11:28:10 -0400
Subject: [PATCH 010/204] Invalid HW links no longer partially initialized
---
src/ioc/db/test/dbPutLinkTest.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/ioc/db/test/dbPutLinkTest.c b/src/ioc/db/test/dbPutLinkTest.c
index 52ae41fb5..e6580b0a1 100644
--- a/src/ioc/db/test/dbPutLinkTest.c
+++ b/src/ioc/db/test/dbPutLinkTest.c
@@ -445,7 +445,7 @@ static void testLinkInitFail(void)
testOk1(plink->type==VME_IO);
testOk1(plink->value.vmeio.parm!=NULL);
- testdbGetFieldEqual("eVME_IO2.INP", DBR_STRING, "#C200 S0 @");
+ testdbGetFieldEqual("eVME_IO2.INP", DBR_STRING, "#C0 S0 @");
prec = (xRecord*)testdbRecordPtr("eVME_IO2");
plink = &prec->inp;
From 5847f98f8922b5ea17c1dd7403c609a6195f3297 Mon Sep 17 00:00:00 2001
From: Michael Davidsaver
Date: Fri, 1 Aug 2014 13:55:33 -0400
Subject: [PATCH 011/204] oops
---
src/ioc/db/test/dbLinkdset.c | 5 +++--
src/ioc/dbStatic/dbStaticLib.c | 2 +-
2 files changed, 4 insertions(+), 3 deletions(-)
diff --git a/src/ioc/db/test/dbLinkdset.c b/src/ioc/db/test/dbLinkdset.c
index dce258a3f..c1de4cf00 100644
--- a/src/ioc/db/test/dbLinkdset.c
+++ b/src/ioc/db/test/dbLinkdset.c
@@ -12,9 +12,10 @@ long link_test_extend(struct dbCommon *junk)
static dsxt xrecextend = {&link_test_extend, &link_test_extend};
static
-long link_test_init(int junk)
+long link_test_init(int pass)
{
- devExtend(&xrecextend);
+ if(pass==0)
+ devExtend(&xrecextend);
return 0;
}
diff --git a/src/ioc/dbStatic/dbStaticLib.c b/src/ioc/dbStatic/dbStaticLib.c
index ac4276e5d..bf1c65580 100644
--- a/src/ioc/dbStatic/dbStaticLib.c
+++ b/src/ioc/dbStatic/dbStaticLib.c
@@ -2297,7 +2297,7 @@ long dbPutString(DBENTRY *pdbentry,const char *pstring)
struct dbCommon *prec = pdbentry->precnode->precord;
devSup *devsup = (devSup *)ellNth(&pdbentry->precordType->devList, prec->dtyp+1);
status = dbCanSetLink(plink, &link_info, devsup);
- if(status)
+ if(status==0)
status = dbSetLink(plink, &link_info, devsup);
}
From 3885c809a659282c334a8ca8b26a483c269f33a0 Mon Sep 17 00:00:00 2001
From: Michael Davidsaver
Date: Fri, 22 Aug 2014 15:23:51 -0400
Subject: [PATCH 012/204] introduce genVersionHeader.pl
---
configure/RULES_BUILD | 13 ++++++
src/tools/Makefile | 1 +
src/tools/genVersionHeader.pl | 74 +++++++++++++++++++++++++++++++++++
3 files changed, 88 insertions(+)
create mode 100644 src/tools/genVersionHeader.pl
diff --git a/configure/RULES_BUILD b/configure/RULES_BUILD
index 711da3434..0af9b6e55 100644
--- a/configure/RULES_BUILD
+++ b/configure/RULES_BUILD
@@ -359,6 +359,19 @@ tapfiles: $(TESTSCRIPTS) $(TAPFILES)
@$(RM) $@
$(PERL) $(TOOLS)/makeTestfile.pl $@ $<
+#---------------------------------------------------------------
+# Generate header with version number from VCS
+
+ifneq ($(VERHEADER),)
+GENVERSIONHEADER ?= $(PERL) $(TOOLS)/genVersionHeader.pl
+MODVERSION ?= undefined
+MODMACRO ?= MODULEVERSION
+
+$(VERHEADER)::
+ $(GENVERSIONHEADER) -t $(TOP) -V $(MODVERSION) -N $(MODMACRO) $@
+
+endif
+
#---------------------------------------------------------------
# Install rules for BIN_INSTALLS and LIB_INSTALLS
diff --git a/src/tools/Makefile b/src/tools/Makefile
index 496b0e4d1..ff5c55e39 100644
--- a/src/tools/Makefile
+++ b/src/tools/Makefile
@@ -49,6 +49,7 @@ PERL_SCRIPTS += dbdToMenuH.pl
PERL_SCRIPTS += dbdToRecordtypeH.pl
PERL_SCRIPTS += dbdExpand.pl
PERL_SCRIPTS += dbdToHtml.pl
+PERL_SCRIPTS += genVersionHeader.pl
PERL_SCRIPTS += registerRecordDeviceDriver.pl
include $(TOP)/configure/RULES
diff --git a/src/tools/genVersionHeader.pl b/src/tools/genVersionHeader.pl
new file mode 100644
index 000000000..0fd6db2df
--- /dev/null
+++ b/src/tools/genVersionHeader.pl
@@ -0,0 +1,74 @@
+#!/usr/bin/env perl
+#
+# Generate a C header file which
+# defines a macro with a string
+# describing the VCS revision
+#
+
+use EPICS::Getopts;
+
+use strict;
+
+our($opt_v,$opt_t,$opt_N,$opt_V);
+
+$opt_N = "MODVERSION";
+$opt_V = "undefined";
+$opt_t = ".";
+my $foundvcs = 0;
+my $result;
+
+getopts("vt:N:V:") or
+ die "Usage: genVersionHeader.pl [-t top] [-N NAME] [-V VERSION] output.h";
+
+my ($outfile) = @ARGV;
+
+if(-d "$opt_t/.hg") { # Mercurial
+ # v1-4-abcdef-dirty
+ # is 4 commits after tag 'v1' with short hash abcdef
+ # with uncommited modifications
+ $result = `cd "$opt_t" && hg tip --template '{latesttag}-{latesttagdistance}-{node|short}\n'`;
+ chomp($result);
+ if(length($result)>1) {
+ $opt_V = $result;
+ $foundvcs = 1;
+ }
+ # see if working copy has modifications, additions, removals, or missing files
+ my $hasmod = `cd "$opt_t" && hg status -m -a -r -d`;
+ chomp($hasmod);
+ if(length($hasmod)>0) {
+ $opt_V = "$opt_V-dirty";
+ }
+}
+if(!$foundvcs && -d "$opt_t/.git") {
+ # same format as Mercurial
+ $result = `git --git-dir="$opt_t/.git" describe --tags --dirty`;
+ chomp($result);
+ if(length($result)>1) {
+ $opt_V = $result;
+ $foundvcs = 1;
+ }
+}
+
+my $output = "#ifndef $opt_N\n# define $opt_N \"$opt_V\"\n#endif\n";
+print "== would\n$output" if $opt_v;
+
+my $DST;
+if(open($DST, '+<', $outfile)) {
+ my $actual = join("", <$DST>);
+ print "== have\n$actual" if $opt_v;
+
+ if($actual eq $output) {
+ print "keep existing $outfile\n";
+ exit(0)
+ }
+ print "updating $outfile\n";
+} else {
+ print "create $outfile\n";
+ open($DST, '>', $outfile) or die "Unable to open or create $outfile";
+}
+
+seek($DST,0,0);
+truncate($DST,0);
+print $DST $output;
+
+close($DST);
From 55cf45364c69fc5d15d733ac24d2bc23c8641894 Mon Sep 17 00:00:00 2001
From: Ralph Lange
Date: Wed, 27 Aug 2014 11:03:54 -0700
Subject: [PATCH 013/204] tools: Make genVersionHeader.pl log more detailed and
streamlined with other EPICS build output
---
src/tools/genVersionHeader.pl | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/src/tools/genVersionHeader.pl b/src/tools/genVersionHeader.pl
index 0fd6db2df..357c17acc 100644
--- a/src/tools/genVersionHeader.pl
+++ b/src/tools/genVersionHeader.pl
@@ -58,13 +58,13 @@ if(open($DST, '+<', $outfile)) {
print "== have\n$actual" if $opt_v;
if($actual eq $output) {
- print "keep existing $outfile\n";
+ print "Keeping existing VCS version header $outfile\n";
exit(0)
}
- print "updating $outfile\n";
+ print "Updating VCS version header $outfile\n";
} else {
- print "create $outfile\n";
- open($DST, '>', $outfile) or die "Unable to open or create $outfile";
+ print "Creating VCS version header $outfile\n";
+ open($DST, '>', $outfile) or die "Unable to open or create VCS version header $outfile";
}
seek($DST,0,0);
From 6a018dce35268a2ac5f9f8c337873dfd2082efe9 Mon Sep 17 00:00:00 2001
From: Ralph Lange
Date: Wed, 27 Aug 2014 11:06:57 -0700
Subject: [PATCH 014/204] tools: Add support for Darcs to genVersionHeader.pl
---
src/tools/genVersionHeader.pl | 16 ++++++++++++++++
1 file changed, 16 insertions(+)
diff --git a/src/tools/genVersionHeader.pl b/src/tools/genVersionHeader.pl
index 357c17acc..d4f1c52dc 100644
--- a/src/tools/genVersionHeader.pl
+++ b/src/tools/genVersionHeader.pl
@@ -22,6 +22,22 @@ getopts("vt:N:V:") or
my ($outfile) = @ARGV;
+if(-d "$opt_t/_darcs") { # Darcs
+ # v1-4-dirty
+ # is tag 'v1' plus 4 patches
+ # with uncommited modifications
+ $result = `cd "$opt_t" && echo "\$(darcs show tags | head -1)-\$((\$(darcs changes --count --from-tag .)-1))"`;
+ chomp($result);
+ if(length($result)>1) {
+ $opt_V = $result;
+ $foundvcs = 1;
+ }
+ # see if working copy has modifications, additions, removals, or missing files
+ my $hasmod = `darcs whatsnew --repodir="$opt_t" -l`;
+ if(!$?) {
+ $opt_V = "$opt_V-dirty";
+ }
+}
if(-d "$opt_t/.hg") { # Mercurial
# v1-4-abcdef-dirty
# is 4 commits after tag 'v1' with short hash abcdef
From 3f462bd032f499d4ff52ecf30380b7a6699e6675 Mon Sep 17 00:00:00 2001
From: Michael Davidsaver
Date: Wed, 27 Aug 2014 17:24:44 -0700
Subject: [PATCH 015/204] check errors
---
src/tools/genVersionHeader.pl | 32 ++++++++++++++++----------------
1 file changed, 16 insertions(+), 16 deletions(-)
diff --git a/src/tools/genVersionHeader.pl b/src/tools/genVersionHeader.pl
index d4f1c52dc..42c0ee531 100644
--- a/src/tools/genVersionHeader.pl
+++ b/src/tools/genVersionHeader.pl
@@ -22,44 +22,44 @@ getopts("vt:N:V:") or
my ($outfile) = @ARGV;
-if(-d "$opt_t/_darcs") { # Darcs
+if(!$foundvcs && -d "$opt_t/_darcs") { # Darcs
# v1-4-dirty
# is tag 'v1' plus 4 patches
# with uncommited modifications
$result = `cd "$opt_t" && echo "\$(darcs show tags | head -1)-\$((\$(darcs changes --count --from-tag .)-1))"`;
chomp($result);
- if(length($result)>1) {
+ if(!$? && length($result)>1) {
$opt_V = $result;
$foundvcs = 1;
- }
- # see if working copy has modifications, additions, removals, or missing files
- my $hasmod = `darcs whatsnew --repodir="$opt_t" -l`;
- if(!$?) {
- $opt_V = "$opt_V-dirty";
+ # see if working copy has modifications, additions, removals, or missing files
+ my $hasmod = `darcs whatsnew --repodir="$opt_t" -l`;
+ if(!$?) {
+ $opt_V = "$opt_V-dirty";
+ }
}
}
-if(-d "$opt_t/.hg") { # Mercurial
+if(!$foundvcs && -d "$opt_t/.hg") { # Mercurial
# v1-4-abcdef-dirty
# is 4 commits after tag 'v1' with short hash abcdef
# with uncommited modifications
$result = `cd "$opt_t" && hg tip --template '{latesttag}-{latesttagdistance}-{node|short}\n'`;
chomp($result);
- if(length($result)>1) {
+ if(!$? && length($result)>1) {
$opt_V = $result;
$foundvcs = 1;
- }
- # see if working copy has modifications, additions, removals, or missing files
- my $hasmod = `cd "$opt_t" && hg status -m -a -r -d`;
- chomp($hasmod);
- if(length($hasmod)>0) {
- $opt_V = "$opt_V-dirty";
+ # see if working copy has modifications, additions, removals, or missing files
+ my $hasmod = `cd "$opt_t" && hg status -m -a -r -d`;
+ chomp($hasmod);
+ if(length($hasmod)>0) {
+ $opt_V = "$opt_V-dirty";
+ }
}
}
if(!$foundvcs && -d "$opt_t/.git") {
# same format as Mercurial
$result = `git --git-dir="$opt_t/.git" describe --tags --dirty`;
chomp($result);
- if(length($result)>1) {
+ if(!$? && length($result)>1) {
$opt_V = $result;
$foundvcs = 1;
}
From bc9255ba1c9015270538a8e43b40853d2ba9cf39 Mon Sep 17 00:00:00 2001
From: Michael Davidsaver
Date: Thu, 28 Aug 2014 10:38:24 -0700
Subject: [PATCH 016/204] add version header to example template
---
src/template/base/Makefile | 2 +
src/template/base/top/exampleApp/Db/Makefile | 1 +
.../top/exampleApp/Db/dbVersionExample.db | 5 ++
src/template/base/top/exampleApp/src/Makefile | 12 +++++
.../top/exampleApp/src/_APPNAME_Hello.dbd | 1 +
.../base/top/exampleApp/src/devStringIn.c | 52 +++++++++++++++++++
.../base/top/exampleBoot/ioc/st.cmd@Common | 1 +
.../base/top/exampleBoot/ioc/st.cmd@RTEMS | 1 +
.../base/top/exampleBoot/ioc/st.cmd@vxWorks | 1 +
9 files changed, 76 insertions(+)
create mode 100644 src/template/base/top/exampleApp/Db/dbVersionExample.db
create mode 100644 src/template/base/top/exampleApp/src/devStringIn.c
diff --git a/src/template/base/Makefile b/src/template/base/Makefile
index 3cb048b0f..8e3aae45e 100644
--- a/src/template/base/Makefile
+++ b/src/template/base/Makefile
@@ -28,9 +28,11 @@ TEMPLATES += top/exampleApp/Makefile
TEMPLATES += top/exampleApp/Db/Makefile
TEMPLATES += top/exampleApp/Db/dbExample1.db
TEMPLATES += top/exampleApp/Db/dbExample2.db
+TEMPLATES += top/exampleApp/Db/dbVersionExample.db
TEMPLATES += top/exampleApp/Db/dbSubExample.db
TEMPLATES += top/exampleApp/Db/user.substitutions
TEMPLATES += top/exampleApp/src/Makefile
+TEMPLATES += top/exampleApp/src/devStringIn.c
TEMPLATES += top/exampleApp/src/xxxRecord.dbd
TEMPLATES += top/exampleApp/src/xxxRecord.c
TEMPLATES += top/exampleApp/src/devXxxSoft.c
diff --git a/src/template/base/top/exampleApp/Db/Makefile b/src/template/base/top/exampleApp/Db/Makefile
index 3adbf09a5..86bc0d620 100644
--- a/src/template/base/top/exampleApp/Db/Makefile
+++ b/src/template/base/top/exampleApp/Db/Makefile
@@ -12,6 +12,7 @@ include $(TOP)/configure/CONFIG
# databases, templates, substitutions like this
DB += dbExample1.db
DB += dbExample2.db
+DB += dbVersionExample.db
DB += dbSubExample.db
DB += user.substitutions
diff --git a/src/template/base/top/exampleApp/Db/dbVersionExample.db b/src/template/base/top/exampleApp/Db/dbVersionExample.db
new file mode 100644
index 000000000..bd1c175df
--- /dev/null
+++ b/src/template/base/top/exampleApp/Db/dbVersionExample.db
@@ -0,0 +1,5 @@
+record(stringin, "$(user):version") {
+ field(DTYP, "My Version")
+ field(DESC, "Module version")
+ field(PINI, "YES")
+}
diff --git a/src/template/base/top/exampleApp/src/Makefile b/src/template/base/top/exampleApp/src/Makefile
index 243cd926f..4d6b21485 100644
--- a/src/template/base/top/exampleApp/src/Makefile
+++ b/src/template/base/top/exampleApp/src/Makefile
@@ -17,6 +17,7 @@ DBD += xxxSupport.dbd
# Compile and add the code to the support library
_APPNAME_Support_SRCS += xxxRecord.c
_APPNAME_Support_SRCS += devXxxSoft.c
+_APPNAME_Support_SRCS += devStringIn.c
# Link locally-provided code into the support library,
# rather than directly into the IOC application.
@@ -26,6 +27,14 @@ _APPNAME_Support_SRCS += initTrace.c
_APPNAME_Support_LIBS += $(EPICS_BASE_IOC_LIBS)
+# Generate a header which defines a macro with a version string
+# from VCS system, or the default
+VERHEADER = mymodversion.h
+# The macro name to use (default is MODULEVERSION)
+MODMACRO = MYMODVERSION
+# Default version string if no VCS system in use
+MODVERSION = defaultversionstring
+
#=============================
# Build the IOC application
@@ -81,3 +90,6 @@ include $(TOP)/configure/RULES
#----------------------------------------
# ADD RULES AFTER THIS LINE
+# Dependency for the generated header
+# must be explicit
+devStringIn$(DEP): $(VERHEADER)
diff --git a/src/template/base/top/exampleApp/src/_APPNAME_Hello.dbd b/src/template/base/top/exampleApp/src/_APPNAME_Hello.dbd
index 64eb0389a..5be6cdc04 100644
--- a/src/template/base/top/exampleApp/src/_APPNAME_Hello.dbd
+++ b/src/template/base/top/exampleApp/src/_APPNAME_Hello.dbd
@@ -1 +1,2 @@
registrar(helloRegister)
+device(stringin,INST_IO,devSiMyModVersion,"My Version")
diff --git a/src/template/base/top/exampleApp/src/devStringIn.c b/src/template/base/top/exampleApp/src/devStringIn.c
new file mode 100644
index 000000000..3aa3fc94a
--- /dev/null
+++ b/src/template/base/top/exampleApp/src/devStringIn.c
@@ -0,0 +1,52 @@
+/* devStringIn.c */
+/* Example device support module for stringinRecord */
+
+#include
+#include
+#include
+#include
+
+#include "recGbl.h"
+#include "alarm.h"
+#include "recSup.h"
+#include "devSup.h"
+#include "stringinRecord.h"
+
+#include "mymodversion.h"
+
+
+static long init_record(stringinRecord *prec)
+{
+ return 0; /* no initialization needed */
+}
+
+static long read_si(stringinRecord *prec)
+{
+ size_t N = strlen(MYMODVERSION);
+ if(Nval, MYMODVERSION);
+ } else {
+ /* Not enough space, set an alarm */
+ (void)recGblSetSevr(prec, READ_ALARM, INVALID_ALARM);
+ }
+ return 0;
+}
+
+#include "epicsExport.h"
+
+static struct {
+ long number;
+ DEVSUPFUN report;
+ DEVSUPFUN init;
+ DEVSUPFUN init_record;
+ DEVSUPFUN get_ioint_info;
+ DEVSUPFUN read_si;
+}devSiMyModVersion={
+ 5,
+ NULL,
+ NULL,
+ init_record,
+ NULL,
+ read_si,
+};
+epicsExportAddress(dset,devSiMyModVersion);
diff --git a/src/template/base/top/exampleBoot/ioc/st.cmd@Common b/src/template/base/top/exampleBoot/ioc/st.cmd@Common
index 76360dd08..c4eb081ba 100644
--- a/src/template/base/top/exampleBoot/ioc/st.cmd@Common
+++ b/src/template/base/top/exampleBoot/ioc/st.cmd@Common
@@ -13,6 +13,7 @@ _CSAFEAPPNAME__registerRecordDeviceDriver pdbbase
## Load record instances
dbLoadTemplate "db/user.substitutions"
+dbLoadRecords "db/dbVersionExample.db", "user=_USER_"
dbLoadRecords "db/dbSubExample.db", "user=_USER_"
## Set this to see messages from mySub
diff --git a/src/template/base/top/exampleBoot/ioc/st.cmd@RTEMS b/src/template/base/top/exampleBoot/ioc/st.cmd@RTEMS
index 4addc5c29..7b6319608 100644
--- a/src/template/base/top/exampleBoot/ioc/st.cmd@RTEMS
+++ b/src/template/base/top/exampleBoot/ioc/st.cmd@RTEMS
@@ -11,6 +11,7 @@ _CSAFEAPPNAME__registerRecordDeviceDriver(pdbbase)
## Load record instances
dbLoadTemplate("db/user.substitutions")
+dbLoadRecords("db/dbVersionExample.db", "user=_USER_")
dbLoadRecords("db/dbSubExample.db", "user=_USER_")
## Set this to see messages from mySub
diff --git a/src/template/base/top/exampleBoot/ioc/st.cmd@vxWorks b/src/template/base/top/exampleBoot/ioc/st.cmd@vxWorks
index 27ad0572b..d8a2077ce 100644
--- a/src/template/base/top/exampleBoot/ioc/st.cmd@vxWorks
+++ b/src/template/base/top/exampleBoot/ioc/st.cmd@vxWorks
@@ -20,6 +20,7 @@ _CSAFEAPPNAME__registerRecordDeviceDriver pdbbase
## Load record instances
dbLoadTemplate "db/user.substitutions"
+dbLoadRecords "db/dbVersionExample.db", "user=_USER_"
dbLoadRecords "db/dbSubExample.db", "user=_USER_"
## Set this to see messages from mySub
From e5b354708e5060674eec89c2aabb7cb906335ea9 Mon Sep 17 00:00:00 2001
From: Kukhee Kim
Date: Thu, 28 Aug 2014 12:11:51 -0700
Subject: [PATCH 017/204] Implement LIFO behavior in compress record
---
src/std/rec/compressRecord.c | 52 ++++++++++++++++++++++++++----
src/std/rec/compressRecord.dbd.pod | 11 +++++++
2 files changed, 57 insertions(+), 6 deletions(-)
diff --git a/src/std/rec/compressRecord.c b/src/std/rec/compressRecord.c
index 0ecfbd4e5..84a93f82f 100644
--- a/src/std/rec/compressRecord.c
+++ b/src/std/rec/compressRecord.c
@@ -106,7 +106,7 @@ static void monitor(compressRecord *prec)
db_post_events(prec, prec->bptr, monitor_mask);
}
-static void put_value(compressRecord *prec, double *psource, int n)
+static void put_value_FIFO(compressRecord *prec, double *psource, int n)
{
epicsInt32 offset = prec->off;
epicsInt32 nuse = prec->nuse;
@@ -130,6 +130,42 @@ static void put_value(compressRecord *prec, double *psource, int n)
prec->nuse = nuse;
return;
}
+
+static void put_value_LIFO(compressRecord *prec, double *psource, int n)
+{
+ epicsInt32 offset = prec->off;
+ epicsInt32 nuse = prec->nuse;
+ epicsInt32 nsam = prec->nsam;
+ epicsInt32 start = offset - nuse;
+ double *pdest;
+ int i;
+ if(start<0) start += nsam;
+
+ for(i = 0; i < n; i++) {
+ if(--start < 0) start += nsam;
+ pdest = prec->bptr + start;
+ *pdest = *psource++;
+ }
+ nuse += n;
+ if(nuse > nsam) nuse = nsam;
+ offset = start + nuse;
+ if(offset>=nsam) offset -= nsam;
+
+ prec->off = offset;
+ prec->nuse = nuse;
+
+ return;
+}
+
+static void put_value(compressRecord *prec, double *psource, int n)
+{
+ switch(prec->balg) {
+ case bufferingALG_FIFO: put_value_FIFO(prec, psource, n); break;
+ case bufferingALG_LIFO: put_value_LIFO(prec, psource, n); break;
+ }
+ return;
+}
+
/* qsort comparison function (for median calculation) */
static int compare(const void *arg1, const void *arg2)
@@ -305,6 +341,7 @@ static long init_record(compressRecord *prec, int pass)
prec->bptr = calloc(prec->nsam, sizeof(double));
reset(prec);
}
+
return 0;
}
@@ -388,18 +425,21 @@ static long cvt_dbaddr(DBADDR *paddr)
paddr->field_type = DBF_DOUBLE;
paddr->field_size = sizeof(double);
paddr->dbr_field_type = DBF_DOUBLE;
+
+ if(prec->balg == bufferingALG_LIFO) paddr->special = SPC_NOMOD;
return 0;
}
static long get_array_info(DBADDR *paddr, long *no_elements, long *offset)
{
compressRecord *prec = (compressRecord *) paddr->precord;
+ epicsInt32 start = prec->off - prec->nuse;
+ if(start<0) start += prec->nsam;
+ if(prec->balg == bufferingALG_FIFO)
+ *no_elements = prec->nuse;
+ else *no_elements = prec->nsam;
+ *offset = start;
- *no_elements = prec->nuse;
- if (prec->nuse == prec->nsam)
- *offset = prec->off;
- else
- *offset = 0;
return 0;
}
diff --git a/src/std/rec/compressRecord.dbd.pod b/src/std/rec/compressRecord.dbd.pod
index 97b84a9a0..14f1bd407 100644
--- a/src/std/rec/compressRecord.dbd.pod
+++ b/src/std/rec/compressRecord.dbd.pod
@@ -41,6 +41,10 @@ menu(compressALG) {
choice(compressALG_Circular_Buffer,"Circular Buffer")
choice(compressALG_N_to_1_Median,"N to 1 Median")
}
+menu(bufferingALG) {
+ choice(bufferingALG_FIFO, "FIFO Buffer")
+ choice(bufferingALG_LIFO, "LIFO Buffer")
+}
recordtype(compress) {
=fields VAL
@@ -76,6 +80,13 @@ recordtype(compress) {
interest(1)
menu(compressALG)
}
+ field(BALG,DBF_MENU) {
+ prompt("Buffering Algorithm")
+ promptgroup(GUI_ALARMS)
+ special(SPC_RESET)
+ interest(1)
+ menu(bufferingALG)
+ }
field(NSAM,DBF_ULONG) {
prompt("Number of Values")
promptgroup(GUI_COMPRESS)
From e24d4637c9271f8d4c37c927e999658da11f08e7 Mon Sep 17 00:00:00 2001
From: Michael Davidsaver
Date: Fri, 29 Aug 2014 09:56:32 -0700
Subject: [PATCH 018/204] genVersionHeader: use date+time as the default
version number
---
configure/RULES_BUILD | 4 ++--
src/tools/genVersionHeader.pl | 8 +++++++-
2 files changed, 9 insertions(+), 3 deletions(-)
diff --git a/configure/RULES_BUILD b/configure/RULES_BUILD
index 0af9b6e55..516c1eaaf 100644
--- a/configure/RULES_BUILD
+++ b/configure/RULES_BUILD
@@ -364,11 +364,11 @@ tapfiles: $(TESTSCRIPTS) $(TAPFILES)
ifneq ($(VERHEADER),)
GENVERSIONHEADER ?= $(PERL) $(TOOLS)/genVersionHeader.pl
-MODVERSION ?= undefined
+#MODVERSION = defaultversionnumber
MODMACRO ?= MODULEVERSION
$(VERHEADER)::
- $(GENVERSIONHEADER) -t $(TOP) -V $(MODVERSION) -N $(MODMACRO) $@
+ $(GENVERSIONHEADER) -t "$(TOP)" -V "$(MODVERSION)" -N "$(MODMACRO)" $@
endif
diff --git a/src/tools/genVersionHeader.pl b/src/tools/genVersionHeader.pl
index 42c0ee531..19c28edd7 100644
--- a/src/tools/genVersionHeader.pl
+++ b/src/tools/genVersionHeader.pl
@@ -6,13 +6,13 @@
#
use EPICS::Getopts;
+use POSIX qw(strftime);
use strict;
our($opt_v,$opt_t,$opt_N,$opt_V);
$opt_N = "MODVERSION";
-$opt_V = "undefined";
$opt_t = ".";
my $foundvcs = 0;
my $result;
@@ -22,6 +22,12 @@ getopts("vt:N:V:") or
my ($outfile) = @ARGV;
+chomp($opt_V);
+if(!$opt_V) {
+ # RFC 8601 date+time w/ zone (eg "2014-08-29T09:42:47-0700")
+ $opt_V = strftime "%Y-%m-%dT%H:%M:%S%z", localtime;
+}
+
if(!$foundvcs && -d "$opt_t/_darcs") { # Darcs
# v1-4-dirty
# is tag 'v1' plus 4 patches
From 95d0d97f6af5d347323c59163a90d95c78b9ad51 Mon Sep 17 00:00:00 2001
From: Michael Davidsaver
Date: Fri, 29 Aug 2014 10:58:18 -0700
Subject: [PATCH 019/204] more consistent macro names
---
configure/RULES_BUILD | 14 ++++++++------
1 file changed, 8 insertions(+), 6 deletions(-)
diff --git a/configure/RULES_BUILD b/configure/RULES_BUILD
index 516c1eaaf..2b836ce2c 100644
--- a/configure/RULES_BUILD
+++ b/configure/RULES_BUILD
@@ -362,13 +362,15 @@ tapfiles: $(TESTSCRIPTS) $(TAPFILES)
#---------------------------------------------------------------
# Generate header with version number from VCS
-ifneq ($(VERHEADER),)
-GENVERSIONHEADER ?= $(PERL) $(TOOLS)/genVersionHeader.pl
-#MODVERSION = defaultversionnumber
-MODMACRO ?= MODULEVERSION
+ifneq ($(GENVERSION),)
+GENERATEVERSIONHEADERCMD ?= $(PERL) $(TOOLS)/genVersionHeader.pl
+# C macro name
+GENVERSIONMACRO ?= MODULEVERSION
+# C macro default (empty uses date+time)
+#GENVERSIONDEFAULT = defaultversionnumber
-$(VERHEADER)::
- $(GENVERSIONHEADER) -t "$(TOP)" -V "$(MODVERSION)" -N "$(MODMACRO)" $@
+$(GENVERSION)::
+ $(GENERATEVERSIONHEADERCMD) -t "$(TOP)" -V "$(GENVERSIONDEFAULT)" -N "$(GENVERSIONMACRO)" $@
endif
From 75321140ef05d1510b135120df7819cc1ba9fb17 Mon Sep 17 00:00:00 2001
From: Michael Davidsaver
Date: Fri, 29 Aug 2014 10:58:18 -0700
Subject: [PATCH 020/204] update example template
---
src/template/base/Makefile | 2 +-
src/template/base/top/exampleApp/src/Makefile | 12 ++++--------
...{devStringIn.c => devStringInGenVersion.c} | 19 +++++++------------
3 files changed, 12 insertions(+), 21 deletions(-)
rename src/template/base/top/exampleApp/src/{devStringIn.c => devStringInGenVersion.c} (72%)
diff --git a/src/template/base/Makefile b/src/template/base/Makefile
index 8e3aae45e..f2292c4e5 100644
--- a/src/template/base/Makefile
+++ b/src/template/base/Makefile
@@ -32,7 +32,7 @@ TEMPLATES += top/exampleApp/Db/dbVersionExample.db
TEMPLATES += top/exampleApp/Db/dbSubExample.db
TEMPLATES += top/exampleApp/Db/user.substitutions
TEMPLATES += top/exampleApp/src/Makefile
-TEMPLATES += top/exampleApp/src/devStringIn.c
+TEMPLATES += top/exampleApp/src/devStringInGenVersion.c
TEMPLATES += top/exampleApp/src/xxxRecord.dbd
TEMPLATES += top/exampleApp/src/xxxRecord.c
TEMPLATES += top/exampleApp/src/devXxxSoft.c
diff --git a/src/template/base/top/exampleApp/src/Makefile b/src/template/base/top/exampleApp/src/Makefile
index 4d6b21485..a419167b8 100644
--- a/src/template/base/top/exampleApp/src/Makefile
+++ b/src/template/base/top/exampleApp/src/Makefile
@@ -17,7 +17,7 @@ DBD += xxxSupport.dbd
# Compile and add the code to the support library
_APPNAME_Support_SRCS += xxxRecord.c
_APPNAME_Support_SRCS += devXxxSoft.c
-_APPNAME_Support_SRCS += devStringIn.c
+_APPNAME_Support_SRCS += devStringInGenVersion.c
# Link locally-provided code into the support library,
# rather than directly into the IOC application.
@@ -28,12 +28,8 @@ _APPNAME_Support_SRCS += initTrace.c
_APPNAME_Support_LIBS += $(EPICS_BASE_IOC_LIBS)
# Generate a header which defines a macro with a version string
-# from VCS system, or the default
-VERHEADER = mymodversion.h
-# The macro name to use (default is MODULEVERSION)
-MODMACRO = MYMODVERSION
-# Default version string if no VCS system in use
-MODVERSION = defaultversionstring
+# with the date and time, or a revision id from VCS system if available
+GENVERSION = mymodversion.h
#=============================
# Build the IOC application
@@ -92,4 +88,4 @@ include $(TOP)/configure/RULES
# Dependency for the generated header
# must be explicit
-devStringIn$(DEP): $(VERHEADER)
+devStringInGenVersion$(DEP): $(GENVERSION)
diff --git a/src/template/base/top/exampleApp/src/devStringIn.c b/src/template/base/top/exampleApp/src/devStringInGenVersion.c
similarity index 72%
rename from src/template/base/top/exampleApp/src/devStringIn.c
rename to src/template/base/top/exampleApp/src/devStringInGenVersion.c
index 3aa3fc94a..21fd09bfc 100644
--- a/src/template/base/top/exampleApp/src/devStringIn.c
+++ b/src/template/base/top/exampleApp/src/devStringInGenVersion.c
@@ -14,26 +14,21 @@
#include "mymodversion.h"
-
-static long init_record(stringinRecord *prec)
-{
- return 0; /* no initialization needed */
-}
+/* must be last include */
+#include "epicsExport.h"
static long read_si(stringinRecord *prec)
{
- size_t N = strlen(MYMODVERSION);
- if(Nval, MYMODVERSION);
+ size_t N = strlen(MODULEVERSION);
+ if(Nval)) {
+ strcpy(prec->val, MODULEVERSION);
} else {
- /* Not enough space, set an alarm */
+ /* Not enough space, so signal an alarm */
(void)recGblSetSevr(prec, READ_ALARM, INVALID_ALARM);
}
return 0;
}
-#include "epicsExport.h"
-
static struct {
long number;
DEVSUPFUN report;
@@ -45,7 +40,7 @@ static struct {
5,
NULL,
NULL,
- init_record,
+ NULL,
NULL,
read_si,
};
From 79497c359c133c6ce48e6637a3145e8e5fdf8e7e Mon Sep 17 00:00:00 2001
From: Michael Davidsaver
Date: Fri, 29 Aug 2014 11:05:56 -0700
Subject: [PATCH 021/204] add release note
---
documentation/RELEASE_NOTES.html | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/documentation/RELEASE_NOTES.html b/documentation/RELEASE_NOTES.html
index 3db8233c0..99a32458e 100644
--- a/documentation/RELEASE_NOTES.html
+++ b/documentation/RELEASE_NOTES.html
@@ -15,6 +15,16 @@ EPICS Base 3.15.0.x releases are not intended for use in production systems.
Changes between 3.15.0.1 and 3.15.0.2
+Generate Version Header
+
+A Perl script and Makefile rules are added to allow modules
+to generate a C header file with a macro defined with an automatically updated identifier.
+This is a VCS revision ID (Darcs, Git, and Mercurial supported) or
+the time of the build if not VCS system is in use.
+
+The example template is updated with a demonstration which makes this
+identifier visible with a stringin record.
+
Implement EPICS_CAS_INTF_ADDR_LIST in rsrv
The IOC server can now bind to a single IP address (and optional port number)
From 42a3cf8e110a5e261ab7b6198a27491412f1f4bd Mon Sep 17 00:00:00 2001
From: Andrew Johnson
Date: Mon, 1 Dec 2014 15:31:18 -0600
Subject: [PATCH 022/204] Set version numbers for 3.16.0.0-DEV
---
configure/CONFIG_BASE_VERSION | 25 +-
documentation/RELEASE_NOTES.html | 1005 +-----------------------------
2 files changed, 26 insertions(+), 1004 deletions(-)
diff --git a/configure/CONFIG_BASE_VERSION b/configure/CONFIG_BASE_VERSION
index f15f47449..a87b2ce72 100644
--- a/configure/CONFIG_BASE_VERSION
+++ b/configure/CONFIG_BASE_VERSION
@@ -17,26 +17,37 @@
# EPICS_SITE_VERSION is defined in CONFIG_SITE for sites that want a local
# version number to be included in the reported version string.
-# In 3.15 we still define BASE_3_14 so "ifdef BASE_3_14" means
-# 3.14 or later, but "ifeq ($(BASE_3_14),YES)" means 3.14 only.
+# We define BASE_3_14 and BASE_3_15 as NO and BASE_3_16 as YES, so
+# ifdef BASE_3_14
+# true for 3.14 or later
+# ifdef BASE_3_15
+# true for 3.15 or later
+# ifeq ($(BASE_3_14),YES)
+# true for 3.14.x only
+# ifeq ($(BASE_3_15),YES)
+# true for 3.15 only
+# ifeq ($(BASE_3_16),YES)
+# true for 3.16 only.
+
BASE_3_14 = NO
-BASE_3_15 = YES
+BASE_3_15 = NO
+BASE_3_16 = YES
# EPICS_VERSION must be a number >0 and <256
EPICS_VERSION = 3
# EPICS_REVISION must be a number >=0 and <256
-EPICS_REVISION = 15
+EPICS_REVISION = 16
# EPICS_MODIFICATION must be a number >=0 and <256
-EPICS_MODIFICATION = 1
+EPICS_MODIFICATION = 0
# EPICS_PATCH_LEVEL must be a number (win32 resource file requirement)
# Not included if zero
EPICS_PATCH_LEVEL = 0
# This will end in -DEV between official releases
-#EPICS_DEV_SNAPSHOT=-DEV
+EPICS_DEV_SNAPSHOT=-DEV
#EPICS_DEV_SNAPSHOT=-pre1
#EPICS_DEV_SNAPSHOT=-pre1-DEV
#EPICS_DEV_SNAPSHOT=-pre2
@@ -45,7 +56,7 @@ EPICS_PATCH_LEVEL = 0
#EPICS_DEV_SNAPSHOT=-rc1-DEV
#EPICS_DEV_SNAPSHOT=-rc2
#EPICS_DEV_SNAPSHOT=-rc2-DEV
-EPICS_DEV_SNAPSHOT=
+#EPICS_DEV_SNAPSHOT=
# No changes should be needed below here
diff --git a/documentation/RELEASE_NOTES.html b/documentation/RELEASE_NOTES.html
index 269b6210c..8361621c8 100644
--- a/documentation/RELEASE_NOTES.html
+++ b/documentation/RELEASE_NOTES.html
@@ -3,1012 +3,23 @@
- EPICS Base R3.15.1 Release Notes
+ EPICS Base R3.16.0 Release Notes
-EPICS Base Release 3.15.1
+EPICS Base Release 3.16.0
-Changes between 3.15.0.2 and 3.15.1
+This version of EPICS Base has not been released yet.
-epicsStrnEscapedFromRaw() and epicsStrnRawFromEscaped()
+Changes between 3.15.x and 3.16.0
+
-Hooking into dbLoadRecords
-
-A function pointer hook has been added to the dbLoadRecords() routine, to
-allow external modules such as autosave to be notified when new records have
-been loaded during IOC initialization. The hook is called dbLoadRecordsHook and
-follows the model of the recGblAlarmHook pointer in that modules that wish to
-use it must save the current value of the pointer before installing their own
-function pointer, and must call the original function from their own
-routine.
-
-The hook is activiated from the dbLoadRecords() routine and gets called only
-after a database instance file has been read in without error. Note that the
-dbLoadTemplates() routine directly calls dbLoadRecords() so this hook also
-provides information about instantiated database templates. It is still possible
-to load record instances using dbLoadDatabase() though, and doing this will not
-result in the hook routines being called.
-
-Code to use this hook should look something like this:
-
-
-#include "dbAccessDefs.h"
-
-static DB_LOAD_RECORDS_HOOK_ROUTINE previousHook;
-
-static void myRoutine(const char* file, const char* subs) {
- if (previousHook)
- previousHook(file, subs);
-
- /* Do whatever ... */
-}
-
-void myInit(void) {
- static int done = 0;
-
- if (!done) {
- previousHook = dbLoadRecordsHook;
- dbLoadRecordsHook = myRoutine;
- done = 1;
- }
-}
-
-
-As with many other parts of the static database access library there is no
-mutex to protect the function pointer. Initialization is expected to take place
-in the context of the IOC's main thread, from either a static C++ constructor or
-an EPICS registrar routine.
-
-
-Changes between 3.15.0.1 and 3.15.0.2
-
-New iocshLoad command
-
-A new command iocshLoad has been added to iocsh which executes a
-named iocsh script and can also set one or more shell macro variables at the
-same time, the values of which will be forgotten immediately after the named
-script finishes executing. The following example shows the syntax:
-
-
-iocshLoad "serial.cmd", "DEV=/dev/ttyS0,PORT=com1,TYPE=RS485"
-iocshLoad "radmon.cmd", "PORT=com1,ADDR=0"
-
-
-Support routines for 64-bit integers
-
-The libCom library now provides support for 64-bit integer types on all
-supported architectures. The epicsTypes.h header file defines epicsInt64 and
-epicsUInt64 type definitions for both C and C++ code. The epicsStdlib.h header
-also declares the following for parsing strings into the relevent sized integer
-variables: Functions epicsParseLLong(), epicsParseULLong() with related macros
-epicsScanLLong() and epicsScanULLong(), and the functions epicsParseInt64()
-and epicsParseUInt64(). Use the first two functions and the macros for long long
-and unsigned long long integer types, and the last two functions for the
-epicsInt64 and epicsUInt64 types. Note that the latter can map to the types long
-and unsigned long on some 64-bit architectures such as linux-x86_64, not to the
-two long long types.
-
-This version does not provide the ability to define 64-bit record fields, the
-use of the 64-bit types in the IOC database will come in a later release of
-EPICS Base.
-
-Full support for loadable support modules
-
-Apparently later versions of Base 3.14 permitted support modules to be loaded
-from a shared library at runtime without the IOC having been linked against that
-shared library; the registerRecordDeviceDriver.pl program would accept a partial
-DBD file containing just the entries needed for the library and generate the
-appropriate registration code. In 3.15 however the registerRecordDeviceDriver.pl
-program was replaced by one using the new DBD file parser, and in this a device
-support entry would only be accepted after first loading the record type that it
-depended on.
-
-The parser has been modified to accept device entries without having seen the
-record type first, although a warning is given when that happens. To remove the
-warning the DBD file can provide a record type declaration instead (no fields
-can be defined, so the braces must be empty), before the device() entry. The
-result will generate the correct registration code for the device entry without
-including anything for any merely declared record types. The generated code can
-be linked into a shared library and loaded by an IOC at runtime using dlload.
-
-
-Parallel callback threads
-
-The general purpose callback facility can run multiple parallel callback
-threads per priority level. This makes better use of SMP architectures (e.g.
-processors with multiple cores), as callback work - which includes second
-stage processing of records with asynchronuous device support and I/O
-scanned processing - can be distributed over the available CPUs.
-
-Note that by using parallel callback threads the order of scan callback
-requests in the queue is not retained. If a device support needs to be
-informed when scanIoRequest processing has finished, it should use the new
-scanIoSetComplete() feature to add a user function that will be called after
-the scanIoRequest record processing has finished.
-
-Parallel callback threads have to be explicitly configured, by default
-the IOC keeps the old behavior of running one callback thread per priority.
-
-Merge MMIO API from devLib2
-
-Added calls to handle 8, 16, and 32 bit Memory Mapped I/O reads and writes.
-The calls added include X_iowriteY() and
-X_ioreadY()
-where X is nat (native), be or le,
-and Y is 16 or 32.
-Also added are ioread8() and iowrite8().
-
-Added optional dbServer API to database
-
-A server layer that sits on top of the IOC database may now register itself
-as such by calling dbRegisterServer() and providing optional routines
-that other components can use. The initial purpose of this API allows the Trace
-Processing implementation in dbProcess() to identify a client that
-causes a record to process when TPRO is set.
-
-To support the client idenfication, the server provides a routine that
-returns that identity string when called by one of its own processing
-threads.
-
-Concatenated database definition files
-
-A series of database definition (dbd) files can now be concatenated during
-the build process into a newly-created dbd file with result being installed into
-$(INSTALL_LOCATION)/dbd without expanding it.
-
-The following lines in an EPICS Makefile will create a file name.dbd in the
-O.Common build directory containing the contents of file1.dbd followed by
-file2.dbd then file3.dbd. The new file will then be installed into
-$(INSTALL_LOCATION)/dbd without expanding any of its include statements.
-
-
-DBDCAT += name.dbd
-name_DBD += file1.dbd file2.dbd file3.dbd
-
-
-The source files file1.dbd, file2.dbd and file3.dbd may be created by the
-current Makefile, be located in the parent directory or any other directory in
-the SRC_DIRS list, be specified by their full pathname, exist in the install dbd
-directory, or be found in any dbd directory linked from the application's
-RELEASE files.
-
-Posix: Drop SCHED_FIFO before exec() in child process
-
-If Base is compiled with USE_POSIX_THREAD_PRIORITY_SCHEDULING = YES
-in configure/CONFIG_SITE or related files, the Posix implementation of the
-libCom osiSpawnDetachedProcess() routine will switch the child process
-to use the normal SCHED_OTHER (non real-time) scheduler before executing the
-named executable program. If it needs to use the real-time scheduler the new
-program can request that for itself.
-
-Posix: Lock all memory when running with FIFO scheduler
-
-On Posix systems, an IOC application's ability to meet timing deadlines is
-often dependent on its ability to lock part or all of the process's virtual
-address space into RAM, preventing that memory from being paged to the swap
-area. This change will attempt to lock the process's virtual address space into
-RAM if the process has the ability to run threads with different priorities. If
-unsuccessful, it prints an message to stderr and continues.
-
-On Linux, one can grant a process the ability to run threads with different
-priorities by using the command ulimit -r unlimited. To use the
-FIFO scheduler for an IOC, use a command like this:
-
-chrt -f 1 softIoc -d test.db
-
-On Linux, one can grant a process the ability to lock itself into memory
-using the command ulimit -l unlimited. These limits can also be
-configured on a per user/per group basis by changing /etc/security/limits.conf
-or its equivalent.
-
-A child process created via fork() normally inherits its parent's resource
-limits, so a child of a real-time soft-IOC will get its parent's real-time
-priority and memlock limits. The memory locks themselves however are not
-inherited by child processes.
-
-Implement EPICS_CAS_INTF_ADDR_LIST in rsrv
-
-The IOC server can now bind to a single IP address (and optional port number)
-read from the standard environment parameter EPICS_CAS_INTF_ADDR_LIST.
-Additional addresses included in that parameter after the first will be ignored
-and a warning message displayed at iocInit time.
-
-alarmString.h deprecated again
-
-The string arrays that provide string versions of the alarm status and
-severity values have been moved into libCom and the header file that used to
-instanciate them is no longer required, although a copy is still provided for
-backwards compatibility reasons. Only the alarm.h header needs to be included
-now to declare the epicsAlarmSeverityStrings and epicsAlarmConditionStrings
-arrays.
-
-General purpose thread pool
-
-
-A general purpose threaded work queue API epicsThreadPool is added.
-Multiple pools can be created with controlable priority and number
-of worker threads. Lazy worker startup is supported.
-
-Database field setting updates
-
-A database (.db) file loaded by an IOC does not have to repeat the record
-type of a record that has already been loaded. It may replace the first
-parameter of the record(type, name) statement with an asterisk
-character inside double-quotes, "*" instead. Thus the following is a
-legal database file:
-
-record(ao, "ao1") {}
-record("*", "ao1") {
- field(VAL, 10)
-}
-
-Note that database configuration tools will not be expected to have to
-understand this syntax, which is provided for scripted and hand-coded database
-and template instantiation only. Setting the IOC's dbRecordsOnceOnly
-flag also makes this syntax illegal, since its purpose is to prevent
-multiply-defined records from being collapsed into a single instance.
-
-Added echo command to iocsh
-
-The single argument string may contain escaped characters, which will be
-translated to their raw form before being printed (enclose the string in quotes
-to avoid double-translation). A newline is always appended to the output, and
-output stream redirection is supported.
-
-Added macro EPICS_UNUSED to compilerDependencies.h
-
-To prevent the compiler from warning about a known-unused variable, mark it
-with the macro EPICS_UNUSED. On gcc and clang this will expand to
-__attribute__((unused)) to prevent the warning.
-
-User specified db substitution file suffix
-
-Per Dirk Zimoch's suggestion, a user specified db substitution file suffix is
-now allowed by setting the variable SUBST_SUFFIX in a configuration directory
-CONFIG_SITE file or in a Makefile before the include $(TOP)/configure/RULES
-line. The default for SUBST_SUFFIX is .substitutions
-
-NTP Time Provider adjusts to OS tick rate changes
-
-Dirk Zimoch provided code that allows the NTP Time provider (used on VxWorks
-and RTEMS only) to adapt to changes in the OS clock tick rate after the provider
-has been initialized. Note that changing the tick rate after iocInit() is not
-advisable, and that other software might still misbehave if initialized before
-an OS tick rate change.
-
-Added newEpicsMutex macro
-
-Internal C++ uses of new epicsMutex() have been replaced with a new
-macro which calls a new constructor, passing it the file name and line number of
-the mutex creation code. C code that creates mutexes has been using a similar
-macro for a long time, but there was no equivalent constructor for the C++
-wrapper class, so identifying a specific mutex was much harder to do.
-
-Post DBE_PROPERTY events automatically
-
-A new record field attribute "prop(YES)" has been added to identify fields
-holding meta-data. External changes to these fields will cause a CA monitor
-event to be sent to all record subscribers who have asked for DBE_PROPERTY
-updates. Meta-data fields have been marked for all Base record types.
-
-errlogRemoveListener() routine changed
-
-Code that calls errlogRemoveListener(myfunc) must be modified to use
-the new, safer routine errlogRemoveListeners(myfunc, &pvt) instead.
-The replacement routine takes a second argument which must be the same private
-pointer that was passed to errlogAddListener() when adding that
-listener. It also deletes all matching listeners (hence the new plural name) and
-returns how many were actually deleted, whereas the previous routine only
-removed the first listener that matched.
-
-Simplified generation of .dbd files
-
-The Perl script makeIncludeDbd.pl has been removed and the rules
-that created an intermediate xxxInclude.dbd file from the
-Makefile variable xxx_DBD have been modified to generate the target
-xxx.dbd file directly. This should simplify applications that
-might have had to provide dependency rules for the intermediate files in 3.15.
-Applications which provide their own xxxInclude.dbd source file
-will continue to have it expanded as before.
-
-New Undefined Severity field UDFS
-
-A new field has been added to dbCommon which configures the alarm severity
-associated with the record being undefined (when UDF=TRUE). The default value is
-INVALID so old databases will not be affected, but now individual records can be
-configured to have a lower severity or even no alarm when undefined. Be careful
-when changing this on applications where the IVOA field of output records is
-used, IVOA still requires an INVALID severity to trigger value replacement.
-
-New build target tapfiles
-
-This new make target runs the same tests as the runtests
target, but
-instead of summarizing or displaying the output for each test script it creates
-a .tap
file inside the architecture build directory which contains the
-detailed test output. The output file can be parsed by continuous integration
-packages such as Jenkins to show the
-test results.
-
-Array field double-buffering
-
-Array data can now be moved, without copying, into and out of the VAL field
-of the waveform, aai, and aao record types by replacing the pointer in BPTR.
-The basic rules which device support must follow are:
-
-
- - BPTR, and the memory it is currently pointing to, can only be accessed
- while the record is locked.
- - NELM may not be changed; NORD should be updated whenever the number of
- valid data elements changes.
- - When BPTR is replaced it must always point to a block of memory large
- enough to hold the maximum number of elements, as given by the NELM and
- FTVL fields.
-
-
-Spin-locks API added
-
-The new header file epicsSpin.h adds a portable spin-locks API which is
-intended for locking very short sections of code (typically one or two lines of
-C or C++) to provide a critical section that protects against race conditions.
-On Posix platforms this uses the pthread_spinlock_t type if it's available and
-the build is not configured to use Posix thread priorities, but otherwise it
-falls back to a pthread_mutex_t. On the UP VxWorks and RTEMS platforms the
-implementations lock out CPU interrupts and disable task preemption while a
-spin-lock is held. The default implementation (used when no other implementation
-is provided) uses an epicsMutex. Spin-locks may not be taken recursively, and
-the code inside the critical section should be short and deterministic.
-
-Improvements to aToIPAddr()
-
-The libCom routine aToIPAddr() and the vxWorks implementation of the
-associated hostToIPAddr() function have been modified to be able to look up
-hostnames that begin with one or more digits. The epicsSockResolveTest program
-was added to check this functionality.
-
-mbboDirect and mbbiDirect records
-
-These record types have undergone some significant rework, and will behave
-slightly differently than they did in their 3.14 versions. The externally
-visible changes are as follows:
-
-mbbiDirect
-
-
- - If the MASK field is set in a database file, it will not be over-written
- when the record is initialized. This allows non-contiguous masks to be set,
- although only the device support actually uses the MASK field.
- - If process() finds the UDF field to be set, the record will raise a
- UDF/INVALID alarm.
-
-
-mbboDirect
-
-
- - If the MASK field is set in a database file, it will not be over-written
- when the record is initialized. This allows non-contiguous masks to be set,
- although only the device support actually uses the MASK field.
- - After the device support's init_record() routine returns during record
- initialization, if OMSL is
supervisory
and UDF is clear the fields
- B0-BF will be set from the current VAL field.
- - When a put to the OMSL field sets it to
supervisory
, the fields
- B0-BF will be set from the current VAL field. This did not used to happen,
- the individual bit fields were previously never modified by the record.
- Note that this change may require some databases to be modified, if they
- were designed to take advantage of the previous behavior.
-
-
-Redirection of the errlog console stream
-
-A new routine has been added to the errlog facility which allows the console
-error message stream to be redirected from stderr to some other already open
-file stream:
-
-int errlogSetConsole(FILE *stream);
-
-
-The stream argument must be a FILE* pointer as returned by fopen() that is
-open for output. If NULL is passed in, the errlog thread's stderr output stream
-will be used instead. Note that messages to the console can be disabled and
-re-enabled using the eltc routine which is also an iocsh command, but there is
-no iocsh command currently provided for calling errlogSetConsole.
-
-Add cleanup subroutine to aSub record
-
-An aSub routine may set the CADR field with a function pointer which will be
-run before a new routine in the event that a change to the SNAM field changes
-the record's process subroutine.
-
-This can be used to free any resources the routine needs to allocate. It can
-also be used to determine if this is the first time this routine has been called
-by this record instance. The CADR field is set to NULL immediately after the
-routine it points to is called.
-
-Example:
-
-void cleanup(aSubRecord* prec) {
- free(prec->dpvt);
- prec->dpvt = NULL;
-}
-
-long myAsubRoutine(aSubRecord* prec) {
- if (!prec->cadr) {
- /* check types of inputs and outputs */
- if (prec->ftva != menuFtypeDOUBLE)
- return 1; /* oops */
-
- dpvt = malloc(42);
- prec->cadr = &cleanup;
- }
-
- /* normal processing */
-}
-epicsRegisterFunction(myAsubRoutine);
-
-
-Sequence record enhancements
-
-The sequence record type now has 16 link groups numbered 0 through 9 and A
-through F, instead of the previous 10 groups numbered 1 through 9 and A. The
-changes to this record are directly equivalent to those described below for the
-fanout record. The fields OFFS and SHFT have been added and operate on the SELN
-value exactly the same way. The result is backwards compatible with the 3.14
-version of the sequence record as long as none of the new fields are modified
-and the application does not rely on the SOFT/INVALID alarm that was generated
-when the selection number exceeded 10. The record also now posts monitors on the
-SELN field at the end of the sequence if its value changed when read through the
-SELL link.
-
-
Fanout record enhancements
-
-The fanout record type now has 16 output links LNK0-LNK9 and LNKA-LNKF, plus
-two additional fields which make the result backwards compatible with 3.14
-databases, but also allow the link selection to be shifted without having to
-process the SELN value through a calc or calcout record first.
-
-Previously there was no LNK0 field, so when SELM is Mask
bit 0 of SELN
-controls whether the LNK1 link field was activated; bit 1 controls LNK2 and so
-on. When SELM is Specified
and SELN is zero no output link would be
-activated at all; LNK1 gets activated when SELN is 1 and so on. Only 6 links
-were provided, LNK1 through LNK6. The updated record type maintains the original
-behavior when the new fields are not configured, except that the SOFT/INVALID
-alarm is not generated when SELN is 7 through 15.
-
-The update involved adding a LNK0 field, as well as fields LNK7 through LNK9
-and LNKA through LNKF. To add flexibility and maintain backwards compatibility,
-two additional fields have been added:
-
-
-- OFFS
-
-- This field holds a signed offset which is added to SELN to select which link
-to activate when SELM is
Specified
. If the resulting value is outside the
-range 0 .. 15 the record will go into a SOFT/INVALID alarm state. The default
-value of OFFS is zero, so if it is not explicitly set and SELN is 1 the LNK1
-link will be activated.
-
-- SHFT
-
-- When SELM is
Mask
the signed field SHFT is used to shift the SELN
-value by SHFT bits (positive means right-wards, values outside the range -15 ..
-15 will result in a SOFT/INVALID alarm), before using the resulting bit-pattern
-to control which links to activate. The default value is -1, so if SHFT is not
-explicitly set bit 0 of SELN will be used to control whether LNK1 gets
-activated.
-
-
-
-The record also now posts monitors on the SELN field if it changes as a
-result of record processing (i.e. when read through the SELL link).
-
-Deleted Java build rules
-
-Java has its own build systems now, so we've deleted the rules and associated
-variables from Base, although they might get added to the Extensions build rules
-for a while in case anyone still needs them.
-
-Changes between 3.14.x and 3.15.0.1
-
-Application clean rules
-
-The clean Makefile target has changed between a single-colon rule
-and a double-colon rule more than once in the life of the EPICS build rules, and
-it just changed back to a single-colon rule, but now we recommend that
-applications that wish to provide a Makefile that is backwards compatible with
-the 3.14 build rules use the construct shown below. The 3.15 rules now support
-a variable called CLEANS to which a Makefile can add a list of files to
-be deleted when the user does a make clean like this:
-
-CLEANS += <list of files to be cleaned>
-
-ifndef BASE_3_15
-clean::
- $(RM) $(CLEANS)
-endif
-
-The conditional rule provides compatibility for use with the 3.14 build
-system.
-
-MSI included with Base
-
-An enhanced version of the Macro Substitution and Include program msi
-has been included with Base. Both this new version of msi and the IOC's
-dbLoadTemplates command now support setting global macros in
-substitution files, and dbLoadTemplates can now take a list of global
-macro settings as the second argument on its command line. The substitution file
-syntax is documented in the Application Developers Guide.
-
-Cross-builds targeting win32-x86-mingw
-
-Some Linux distributions now package the MinGW cross-compiler which makes it
-possible to cross-build the win32-x86-mingw target from a linux-x86 host. Build
-configuration files for this combination are now included; adjust the settings
-in configure/os/CONFIG_SITE.linux-x86.win32-x86-mingw and add win32-x86-mingw to
-the CROSS_COMPILER_TARGET_ARCHS variable in configure/CONFIG_SITE or in
-configure/os/CONFIG_SITE.linux-x86.Common.
-
-Architecture win32-x86-cygwin Removed
-
-The ability to compile non-cygwin binaries using the Cygwin build tools is no
-longer supported by current versions of Cygwin, so this architecture has been
-removed. Use the MinWG tools and the win32-x86-mingw architecture instead.
-
-RTEMS and VxWorks Test Harnesses
-
-The original libCom test harness has been renamed libComTestHarness,
-and two additional test harnesses have been created dbTestHarness and
-filterTestHarness which are all built for RTEMS and vxWorks targets.
-The new ones include tests in src/ioc/db/test and src/std/filters/test.
-
-Running the new tests requires additional .db and .dbd files to be loaded at
-runtime, which can be found in the relevant source directory or its O.Common
-subdirectory. If the target can access the Base source tree directly it may be
-simplest to cd to the relevant source directory before running the test. If not,
-the files needed are listed in the generated 'testspec' file found in the
-associated build (O.arch) directory.
-
-For RTEMS users the current directory is determined in a BSP specific way.
-See rtems_init.c and setBootConfigFromNVRAM.c in src/libCom/RTEMS.
-
-New API to hook into thread creation
-
-A hook API has been added allowing user-supplied functions to be called
-whenever a thread starts. The calls are made from the thread's context,
-and can be used to control additional thread properties not handled inside
-EPICS base, e.g. setting the scheduling policy or CPU affinity (on SMP
-systems).
-
-The API also supports a mapping operation, calling a user-supplied function
-for every thread that is currently running.
-
-New scan rate units
-
-Scan rates defined in the menuScan.dbd file may now be specified in seconds,
-minutes, hours or Hertz, and plural time units will also be accepted (seconds
-are used if no unit is mentioned in the choice string). At iocInit each
-scan rate is compared with the OS's clock tick and a warning printed if the
-rate is too fast or likely to be more than 10% different to the requested rate.
-For example the rates given below are all valid, although non-standard (the
-default menuScan choices that come with Base have not been changed):
-
-
-menu(menuScan) {
- choice(menuScanPassive, "Passive")
- choice(menuScanEvent, "Event")
- choice(menuScanI_O_Intr, "I/O Intr")
- choice(menuScan1_hour, "1 hour")
- choice(menuScan0_5_hours, "0.5 hours")
- choice(menuScan15_minutes, "15 minutes")
- choice(menuScan5_minutes, "5 minutes")
- choice(menuScan1_minute, "1 minute")
- choice(menuScan10_seconds, "10 seconds")
- choice(menuScan5_seconds, "5 seconds")
- choice(menuScan2_seconds, "2 seconds")
- choice(menuScan1_second, "1 second")
- choice(menuScan2_Hertz, "2 Hertz")
- choice(menuScan5_Hertz, "5 Hertz")
- choice(menuScan10_Hertz, "10 Hz")
-}
-
-Alarm filtering added to input record types
-
-The record types ai, calc, longin and mbbi have a new alarm filter added to
-them. This provides a low-pass filter that can be used to delay the reporting of
-alarms caused by the input level passing the HIGH, HIHI, LOW or LOLO values. The
-filter is controlled with a new AFTC field that sets the filter's time constant.
-The default value for this field is zero, which keeps the record's original
-alarm behaviour.
-
-The record must be scanned often enough for the filtering action to work
-effectively and the alarm severity can only change when the record is processed,
-but that processing does not have to be regular; the filter uses the time since
-the record last processed in its calculation. Setting AFTC to a positive number
-of seconds will delay the record going into or out of a minor alarm severity or
-from minor to major severity until the input signal has been in that range for
-that number of seconds.
-
-Post events on Waveform record's NORD field
-
-When the record type or device support modify the NORD field of a waveform
-record, the record support code now posts DBE_VALUE and DBE_LOG events for that
-field, signalling the array length change to any client monitoring the NORD
-field.
-
-Attributes of Non-VAL Fields
-
-Non-VAL fields now report meaningful information for precision, units,
-graphic limits, control limits, and alarm limits instead of simply using
-PREC, EGU, HOPR, LOPR, DRVL, DRVH, HIHI, HIGH, LOW, and LOLO. All delay
-fields have a default precision of 2 digits, units "s" and control limits
-of 0 to 100,000 seconds (these precision and limit values can be changed
-for each record type as a whole at runtime by updating a registered global
-variable). Input fields like A-L of the calc record read their metadata
-from the corresponding INPn link if possible.
-epicsStdioRedirect.h merged into epicsStdio.h
-
-The definitions from the header file epicsStdioRedirect.h have been moved
-into epicsStdio.h so all calls to printf(), puts() and putchar() in files that
-include that OSI header will now be subject to stdout redirection. In past
-releases (3.14.7 and later) it was necessary to request the redirection support
-by including the epicsStdioRedirect.h header file. The header file is still
-provided, but now it just includes epicsStdio.h.
-
-Named Soft Events
-
-Soft events can now be given meaningful names instead of just using the
-numbers 1-255. The EVNT field is now a DBF_STRING. The post_event() API
-is now deprecated but still works. It should be replaced by code that in advance
-looks up the EVNTPVT event handle associated with the named event by
-calling eventNameToHandle(char *), and when that event occurs passes
-that handle to the new postEvent(EVNTPVT) routine (which may be called
-from interrupt level). A new iocsh command postEvent name will
-trigger a named event from the command-line or a startup script (on vxWorks the
-expression postEvent(eventNameToHandle("name")) must be used
-instead though).
-
-Parallel Builds
-
-
-As EPICS sites get computers with more CPUs they report additional bugs in our
-parallel build rules. Various issues have been fixed by separating out the build
-rules that generate dependency (.d) files, ensuring that they are constructed at
-the appropriate time in the build.
-
-
-These rule changes can cause additional warning messages to appear when building
-support modules. Where an application provides its own Makefile rules it may now
-have to add rules to construct an associated dependency file. In many cases
-though the change needed is just to replace a dependency for a
-target$(OBJ) with the target$(DEP) so this
-
-
- myLib$(OBJ): myLib_lex.c
-
-
-becomes
-
-
- myLib$(DEP): myLib_lex.c
-
-
-To debug build issues assocated with dependency files, use the command make
---debug=m which tells GNUmake to display information about what it is doing
-during the first pass when it updates its makefiles.
-
-
-Removed tsDefs.h
-
-
-The deprecated tsDefs API was provided for 3.13 compatibility only, and has now
-been removed. Convert any remaining code that used it to call the epicsTime API
-instead.
-
-
-Changes to epicsVersion.h
-
-
-The two macros EPICS_UPDATE_LEVEL and EPICS_CVS_SNAPSHOT have
-been deleted from the epicsVersion.h file; they were deprecated in R3.14 and can
-be replaced with EPICS_PATCH_LEVEL and EPICS_DEV_SNAPSHOT
-respectively.
-
-
-A new pair of macros has been added to make version number comparisons easier.
-Code that will not work with a version of Base before 3.15.0 can now be
-written like this to prevent it from compiling:
-
-
-#if defined(VERSION_INT) && EPICS_VERSION_INT < VERSION_INT(3,15,0,0)
-# error EPICS Base R3.15.0 or later is required
-#endif
-
-
-
-Added support for iocLogPrefix
-
-
-Added a iocLogPrefix command to iocsh. This adds a
-prefix to all messages from this IOC (or other log client) as they get sent to the
-iocLogServer. This lets sites use the "fac=<facility>" syntax for
-displaying the facility, process name etc. in log viewers like the
-cmlogviewer.
-
-
-Reworked the epicsEvent C & C++ APIs
-
-
- - Renamed the enum epicsEventWaitStatus to epicsEventStatus
- - Defined epicsEventWaitStatus as a macro for epicsEventStatus
- - Renamed epicsEventWaitOk to epicsEventOk
- - Renamed epicsEventWaitError to epicsEventError
- - Defined epicsEventWaitOK and epicsEventWaitError as macros
- - Added epicsEventTrigger(id) which triggers an event and returns OK or an
- error status if the underlying OS primitives report an error
- - Added epicsEventMustTrigger(id) which halts on error
- - Defined epicsEventSignal(id) as a macro for epicsEventMustTrigger(id)
- - Added a new C++ method epicsEvent::trigger() which throws an
- epicsEvent::invalidSemaphore in the event of an error
- - epicsEvent::signal() makes an inline call to epicsEvent::trigger()
- - epicsEventWait() and epicsEventWaitWithTimeout() now return an error
- status if the underlying OS primitives report an error
- - All the epicsEventMust...() routines are now implemented in the common
- libCom/osi/epicsEvent.cpp source file, and call cantProceed() instead of
- mis-using assert()
- - Implemented epicsEventShow() on Posix
- - Win32: Removed all epicsShareAPI decorations
-
-
-
-Enabled histogram record type
-
-
-The histogram record was not included in the base.dbd file in any 3.14 release,
-but has now been added along with its associated soft device support. The build
-system now generates the list of all the record.dbd files in base automatically
-in src/std/rec/Makefile.
-
-
-Reorganization of src/
-
-Reorganization of subdirectories of src/ to better represent the relation
-between different parts as described in the following table.
-
-This change also allows the number of libraries built to be reduced to:
-libCap5.so, libca.so, libdbCore.so, libdbStaticHost.so,
-libCom.so, libcas.so, libdbRecStd.so, and libgdd.so
-
-
-
- | Component |
- Dependency |
- Library name |
- Description |
-
-
- | src/tools |
- |
- |
- Build system scripts |
-
-
- | src/libCom |
- src/tools |
- Com |
- Utility routines and OS-independant API |
-
-
- | src/template |
- src/tools |
- |
- User application templates (e.g. makeBaseApp) |
-
-
- | src/ca/client |
- src/libCom |
- ca |
- Channel Access client |
-
-
- | src/ca/legacy/gdd |
- src/ca/client |
- gdd |
- Generic data layer for PCAS |
-
-
- | src/ca/legacy/pcas |
- src/ca/legacy/gdd |
- cas |
- Portable Channel Access Server |
-
-
- | src/ioc |
- src/ca |
- dbCore |
- Core database processing functions |
-
-
- | src/std |
- src/ioc |
- dbRecStd |
- Standard records, soft device support and the softIoc |
-
-
-
-
-In order to better reflect these relations the following
-directories and files were moved as described:
-
-
-
- | Relocations |
-
-
- | Previous | New |
-
-
- | libCom |
-
-
- | src/RTEMS |
- src/libCom/RTEMS |
-
-
- | src/toolsComm/flex |
- src/libCom/flex |
-
-
- | src/toolsComm/antelope |
- src/libCom/yacc |
-
-
- src/dbStatic/alarm.h .../alarmString.h |
- src/libCom/misc/ |
-
-
- | IOC Core Components |
-
-
- | src/bpt |
- src/ioc/bpt |
-
-
- | src/db |
- src/ioc/db |
-
-
- | src/dbStatic |
- src/ioc/dbStatic |
-
-
- | src/dbtools |
- src/ioc/dbtemplate |
-
-
- | src/misc |
- src/ioc/misc |
-
-
- | src/registry |
- src/ioc/registry |
-
-
- | src/rsrv |
- src/ioc/rsrv 1 |
-
-
- | Standard Record Definitions |
-
-
- | src/dev/softDev |
- src/std/dev |
-
-
- | src/rec |
- src/std/rec |
-
-
- | src/softIoc |
- src/std/softIoc |
-
-
- | Channel Access |
-
-
- | src/ca |
- src/ca/client |
-
-
- | src/catools |
- src/ca/client/tools |
-
-
- | src/cap5 |
- src/ca/client/perl |
-
-
- | src/gdd |
- src/ca/legacy/gdd |
-
-
- | src/cas |
- src/ca/legacy/pcas |
-
-
- | src/excas |
- src/ca/legacy/pcas/ex |
-
-
- | User Templates |
-
-
- | src/makeBaseApp |
- src/template/base |
-
-
- | src/makeBaseExt |
- src/template/ext |
-
-
- | Dispersed |
-
-
- | src/util 2 |
- src/ca/client |
-
-
- | src/ca/client/test |
-
-
- | src/libCom/log |
-
-
- | src/as 3 |
- src/libCom/as |
-
-
- | src/ioc/as |
-
-
-
-1
-RSRV is built as part of dbCore due to its tight (bidirectional) coupling
-with the other database code.
-
-2
-The contents for src/util/ moved to three locations. The caRepeater init script
-was moved to src/ca/client/. ca_test is now in src/ca/client/test/.
-The iocLogServer was moved into the same directory (src/libCom/log) as
-the log client code.
-
-3
-The Access Security code has been divided, with the parts not related to the
-database (lexer/parser and trap registration) becoming part of libCom.
-The remaining components are included in the dbCore library
-
-
-Moved src/RTEMS/base directory
-
-
-These files are now found under src/RTEMS.
-
-
-Removed 3.13 compatibility
-
-
-Removed the 3.13 <top>/config directory and build compatibility rules and
-variables, and various conversion documents.
From 0f23784b32f641943d785bbe8288a0cead3e660e Mon Sep 17 00:00:00 2001
From: Andrew Johnson
Date: Tue, 2 Dec 2014 11:30:37 -0600
Subject: [PATCH 023/204] Tweak to callback API.
---
documentation/RELEASE_NOTES.html | 5 +++++
src/ioc/db/callback.h | 18 ++++++++++--------
src/ioc/db/dbScan.c | 6 ++++--
3 files changed, 19 insertions(+), 10 deletions(-)
diff --git a/documentation/RELEASE_NOTES.html b/documentation/RELEASE_NOTES.html
index 8361621c8..b2e5fdc65 100644
--- a/documentation/RELEASE_NOTES.html
+++ b/documentation/RELEASE_NOTES.html
@@ -20,6 +20,11 @@
-->
+Callback subsystem API
+
+Added a new macro callbackGetPriority(prio, callback) to the
+callback.h header and removed the need for dbScan.c to reach into the internals
+of its CALLBACK objects.