diff --git a/src/ioc/db/dbLink.c b/src/ioc/db/dbLink.c index 9279b91fb..c90dcb3df 100644 --- a/src/ioc/db/dbLink.c +++ b/src/ioc/db/dbLink.c @@ -66,6 +66,26 @@ static const char * link_field_name(const struct link *plink) return "????"; } +/* Special TSEL handler for PV links */ +/* FIXME: Generalize for new link types... */ +static void TSEL_modified(struct link *plink) +{ + struct pv_link *ppv_link; + char *pfieldname; + + if (plink->type != PV_LINK) { + errlogPrintf("dbLink::TSEL_modified called for non PV_LINK\n"); + return; + } + /* If pvname contains .TIME truncate it to point to VAL instead */ + ppv_link = &plink->value.pv_link; + pfieldname = strstr(ppv_link->pvname, ".TIME"); + if (pfieldname) { + *pfieldname = 0; + plink->flags |= DBLINK_FLAG_TSELisTIME; + } +} + /***************************** Generic Link API *****************************/ @@ -93,7 +113,7 @@ void dbInitLink(struct link *plink, short dbfType) return; if (plink == &precord->tsel) - recGblTSELwasModified(plink); + TSEL_modified(plink); if (!(plink->value.pv_link.pvlMask & (pvlOptCA | pvlOptCP | pvlOptCPP))) { /* Make it a DB link if possible */ @@ -127,6 +147,9 @@ void dbAddLink(struct dbLocker *locker, struct link *plink, short dbfType, { struct dbCommon *precord = plink->precord; + /* Clear old TSELisTIME flag */ + plink->flags &= ~DBLINK_FLAG_TSELisTIME; + if (plink->type == CONSTANT) { dbConstAddLink(plink); return; @@ -145,7 +168,7 @@ void dbAddLink(struct dbLocker *locker, struct link *plink, short dbfType, return; if (plink == &precord->tsel) - recGblTSELwasModified(plink); + TSEL_modified(plink); if (ptarget) { /* It's a DB link */ @@ -470,4 +493,3 @@ long dbPutLinkLS(struct link *plink, char *pbuffer, epicsUInt32 len) return dbPutLink(plink, DBR_STRING, pbuffer, 1); } - diff --git a/src/ioc/db/recGbl.c b/src/ioc/db/recGbl.c index a614b5268..337ed7875 100644 --- a/src/ioc/db/recGbl.c +++ b/src/ioc/db/recGbl.c @@ -4,7 +4,7 @@ * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found -* in file LICENSE that is included with this distribution. +* in file LICENSE that is included with this distribution. \*************************************************************************/ /* recGbl.c */ /* @@ -259,15 +259,13 @@ void recGblGetTimeStamp(void *pvoid) struct link *plink = &prec->tsel; if (!dbLinkIsConstant(plink)) { - struct pv_link *ppv_link = &plink->value.pv_link; - - if (ppv_link->pvlMask & pvlOptTSELisTime) { + if (plink->flags & DBLINK_FLAG_TSELisTIME) { if (dbGetTimeStamp(plink, &prec->time)) - errlogPrintf("recGblGetTimeStamp: dbGetTimeStamp failed, %s.TSEL = %s\n", - prec->name, ppv_link->pvname); + errlogPrintf("recGblGetTimeStamp: dbGetTimeStamp failed for %s.TSEL", + prec->name); return; } - dbGetLink(&prec->tsel, DBR_SHORT, &prec->tse, 0, 0); + dbGetLink(plink, DBR_SHORT, &prec->tse, 0, 0); } if (prec->tse != epicsTimeEventDeviceTime) { if (epicsTimeGetEvent(&prec->time, prec->tse)) @@ -276,24 +274,6 @@ void recGblGetTimeStamp(void *pvoid) } } -void recGblTSELwasModified(struct link *plink) -{ - struct pv_link *ppv_link = &plink->value.pv_link; - char *pfieldname; - - if (plink->type != PV_LINK) { - errlogPrintf("recGblTSELwasModified called for non PV_LINK\n"); - return; - } - /*If pvname ends in .TIME then just ask for VAL*/ - /*Note that the VAL value will not be used*/ - pfieldname = strstr(ppv_link->pvname, ".TIME"); - if (pfieldname) { - *pfieldname = 0; - ppv_link->pvlMask |= pvlOptTSELisTime; - } -} - void recGblCheckDeadband(epicsFloat64 *poldval, const epicsFloat64 newval, const epicsFloat64 deadband, unsigned *monitor_mask, const unsigned add_mask) { diff --git a/src/ioc/db/recGbl.h b/src/ioc/db/recGbl.h index 8eb589450..171c00964 100644 --- a/src/ioc/db/recGbl.h +++ b/src/ioc/db/recGbl.h @@ -4,7 +4,7 @@ * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found -* in file LICENSE that is included with this distribution. +* in file LICENSE that is included with this distribution. \*************************************************************************/ /* recGbl.h */ /* Record Global @@ -63,7 +63,6 @@ epicsShareFunc void recGblInheritSevr(int msMode, void *precord, epicsEnum16 sta epicsEnum16 sevr); epicsShareFunc void recGblFwdLink(void *precord); epicsShareFunc void recGblGetTimeStamp(void *precord); -epicsShareFunc void recGblTSELwasModified(struct link *plink); epicsShareFunc void recGblCheckDeadband(epicsFloat64 *poldval, const epicsFloat64 newval, const epicsFloat64 deadband, unsigned *monitor_mask, const unsigned add_mask); diff --git a/src/ioc/db/test/dbPutLinkTest.c b/src/ioc/db/test/dbPutLinkTest.c index b39b98db2..941c38852 100644 --- a/src/ioc/db/test/dbPutLinkTest.c +++ b/src/ioc/db/test/dbPutLinkTest.c @@ -16,6 +16,7 @@ #include "epicsThread.h" #include "iocInit.h" #include "dbBase.h" +#include "dbDefs.h" #include "link.h" #include "dbAccess.h" #include "registry.h" @@ -565,7 +566,7 @@ void testJLink(void) testIocInitOk(); eltc(1); - testNumZ(3); + testNumZ(6); testdbPutFieldOk("j1.PROC", DBF_LONG, 1); testdbPutFieldOk("j2.PROC", DBF_LONG, 1); @@ -576,13 +577,16 @@ void testJLink(void) testdbGetFieldEqual("j2.VAL", DBF_LONG, 2); testdbGetFieldEqual("j3.VAL", DBF_LONG, 3); - testNumZ(3); + testNumZ(6); testdbPutFieldOk("j1.INP", DBF_STRING, "{\"z\":{\"good\":4}}"); testdbPutFieldOk("j1.PROC", DBF_LONG, 1); testdbGetFieldEqual("j1.VAL", DBF_LONG, 4); - testNumZ(3); + testdbPutFieldOk("j2.TSEL", DBF_STRING, "{\"z\":{\"good\":0}}"); + testdbPutFieldOk("j2.PROC", DBF_LONG, 1); + + testNumZ(7); testdbPutFieldFail(S_dbLib_badField, "j1.INP", DBF_STRING, "{\"z\":{\"fail\":5}}"); testdbPutFieldOk("j1.PROC", DBF_LONG, 1); @@ -590,7 +594,13 @@ void testJLink(void) /* put failure in parsing stage doesn't modify link */ testdbGetFieldEqual("j1.INP", DBF_STRING, "{\"z\":{\"good\":4}}"); - testNumZ(3); + testNumZ(7); + + /* Check SDIS using a JSON link prevents processing */ + testdbPutFieldOk("j1.SDIS", DBF_STRING, "{\"z\":{\"good\":1}}"); + testdbPutFieldOk("j1.INP", DBF_STRING, "{\"z\":{\"good\":1}}"); + testdbPutFieldOk("j1.PROC", DBF_LONG, 1); + testdbGetFieldEqual("j1.VAL", DBF_LONG, 4); testIocShutdownOk(); @@ -599,9 +609,81 @@ void testJLink(void) testdbCleanup(); } +static +void testTSEL(void) +{ + dbCommon *rec[2]; + dbLocker *locker; + + testDiag("Test TSEL link to .TIME"); + + testdbPrepare(); + + testdbReadDatabase("dbTestIoc.dbd", NULL, NULL); + + dbTestIoc_registerRecordDeviceDriver(pdbbase); + + testdbReadDatabase("dbPutLinkTest.db", NULL, NULL); + + rec[0] = testdbRecordPtr("time:one"); + rec[1] = testdbRecordPtr("time:two"); + + eltc(0); + testIocInitOk(); + eltc(1); + + locker = dbLockerAlloc(rec, NELEMENTS(rec), 0); + if(!locker) + testAbort("dbLockerAlloc() fails"); + + testdbPutFieldOk("time:one.PROC", DBF_LONG, 1); + + /* wait a bit so that we would get different timestamps */ + epicsThreadSleep(0.001); + + testdbPutFieldOk("time:two.PROC", DBF_LONG, 1); + +#define COMPARE(MSG, C, TS1, TS2) testOk((C)^((TS1)->secPastEpoch == (TS2)->secPastEpoch && (TS1)->nsec == (TS2)->nsec), \ + MSG " %u:%u == %u:%u", (unsigned)(TS1)->secPastEpoch, (unsigned)(TS1)->nsec, \ + (unsigned)(TS2)->secPastEpoch, (unsigned)(TS2)->nsec) + + testDiag("Check initially connected TSEL link"); + dbScanLockMany(locker); + COMPARE("first", 0, &rec[0]->time, &rec[1]->time); + testOk1(rec[1]->tsel.flags & DBLINK_FLAG_TSELisTIME); + dbScanUnlockMany(locker); + + testdbPutFieldOk("time:two.TSEL", DBF_STRING, ""); + + testdbPutFieldOk("time:two.PROC", DBF_LONG, 1); + + testDiag("Check no TSEL link"); + dbScanLockMany(locker); + COMPARE("second", 1, &rec[0]->time, &rec[1]->time); + testOk1(!(rec[1]->tsel.flags & DBLINK_FLAG_TSELisTIME)); + dbScanUnlockMany(locker); + + testdbPutFieldOk("time:two.TSEL", DBF_STRING, "time:one.TIME"); + + testdbPutFieldOk("time:two.PROC", DBF_LONG, 1); + + testDiag("Check re-connected TSEL link"); + dbScanLockMany(locker); + COMPARE("third", 0, &rec[0]->time, &rec[1]->time); + testOk1(rec[1]->tsel.flags & DBLINK_FLAG_TSELisTIME); + dbScanUnlockMany(locker); + + dbLockerFree(locker); + + testIocShutdownOk(); + + testdbCleanup(); +#undef COMPARE +} + MAIN(dbPutLinkTest) { - testPlan(301); + testPlan(319); testLinkParse(); testLinkFailParse(); testCADBSet(); @@ -610,5 +692,6 @@ MAIN(dbPutLinkTest) testLinkInitFail(); testLinkFail(); testJLink(); + testTSEL(); return testDone(); } diff --git a/src/ioc/db/test/dbPutLinkTest.db b/src/ioc/db/test/dbPutLinkTest.db index 6742fcb39..ee657c5e4 100644 --- a/src/ioc/db/test/dbPutLinkTest.db +++ b/src/ioc/db/test/dbPutLinkTest.db @@ -47,3 +47,8 @@ record(x, "rVXI_IO2") { field(DTYP, "Unit Test VXI_IO") field(INP, "#V81 S82 @parm2 VXI_IO") } + +record(x, "time:one") {} +record(x, "time:two") { + field(TSEL, "time:one.TIME") +} diff --git a/src/ioc/db/test/dbPutLinkTestJ.db b/src/ioc/db/test/dbPutLinkTestJ.db index 25cf4c822..621557c2c 100644 --- a/src/ioc/db/test/dbPutLinkTestJ.db +++ b/src/ioc/db/test/dbPutLinkTestJ.db @@ -1,10 +1,14 @@ record(x, "j1") { field(INP, {z:{good:1}}) + field(TSEL, {z:{good:0}}) + field(SDIS, {z:{good:0}}) + field(FLNK, {z:{good:0}}) } record(x, "j2") { field(INP, {z:{good:2}}) + field(TSEL, "j1.TIME") } record(x, "j3") { diff --git a/src/ioc/dbStatic/dbStaticLib.c b/src/ioc/dbStatic/dbStaticLib.c index d647f787d..bcfa93727 100644 --- a/src/ioc/dbStatic/dbStaticLib.c +++ b/src/ioc/dbStatic/dbStaticLib.c @@ -1940,7 +1940,7 @@ char * dbGetString(DBENTRY *pdbentry) else ppind=0; dbMsgPrint(pdbentry, "%s%s%s%s", plink->value.pv_link.pvname ? plink->value.pv_link.pvname : "", - (pvlMask & pvlOptTSELisTime) ? ".TIME" : "", + (plink->flags & DBLINK_FLAG_TSELisTIME) ? ".TIME" : "", ppstring[ppind], msstring[plink->value.pv_link.pvlMask&pvlOptMsMode]); break; diff --git a/src/ioc/dbStatic/link.h b/src/ioc/dbStatic/link.h index f2b426308..2e3c77820 100644 --- a/src/ioc/dbStatic/link.h +++ b/src/ioc/dbStatic/link.h @@ -67,10 +67,10 @@ epicsShareExtern maplinkType pamaplinkType[]; #define pvlOptInpString 0x100 /*Input as string*/ #define pvlOptOutNative 0x200 /*Output native*/ #define pvlOptOutString 0x400 /*Output as string*/ -#define pvlOptTSELisTime 0x800 /*Field TSEL is getting timeStamp*/ /* DBLINK Flag bits */ #define DBLINK_FLAG_INITIALIZED 1 /* dbInitLink() called */ +#define DBLINK_FLAG_TSELisTIME 2 /* Use TSEL to get timeStamp */ struct macro_link { char *macroStr; diff --git a/src/std/link/links.dbd.pod b/src/std/link/links.dbd.pod index ceb6ced77..d94e9e8f0 100644 --- a/src/std/link/links.dbd.pod +++ b/src/std/link/links.dbd.pod @@ -114,6 +114,14 @@ expression. Equivalent to the C field of a record. An optional integer specifying the numeric precision with which the calculation result should be displayed. Equivalent to the C field of a record. +=item time + +An optional string containing a single upper or lower-case letter C ... C +which must correspond to an input provided in the c parameter. When the +record containing such a link has C set to -2 (epicsTimeEventDeviceTime) +the record's timestamp field C