diff --git a/src/ioc/db/dbAccess.c b/src/ioc/db/dbAccess.c index 1c01da331..2b04280c0 100644 --- a/src/ioc/db/dbAccess.c +++ b/src/ioc/db/dbAccess.c @@ -42,7 +42,7 @@ #include "dbBase.h" #include "dbBkpt.h" #include "dbCa.h" -#include "dbCommon.h" +#include "dbCommonPvt.h" #include "dbConvertFast.h" #include "dbConvert.h" #include "dbEvent.h" @@ -682,6 +682,33 @@ finish: return status; } +void dbInitEntryFromAddr(struct dbAddr *paddr, DBENTRY *pdbentry) +{ + struct dbCommon *prec = paddr->precord; + dbCommonPvt *ppvt = CONTAINER(prec, dbCommonPvt, common); + + memset((char *)pdbentry,'\0',sizeof(DBENTRY)); + + pdbentry->pdbbase = pdbbase; + pdbentry->precordType = prec->rdes; + pdbentry->precnode = ppvt->recnode; + pdbentry->pflddes = paddr->pfldDes; + pdbentry->pfield = paddr->pfield; + pdbentry->indfield = -1; /* invalid */ +} + +void dbInitEntryFromRecord(struct dbCommon *prec, DBENTRY *pdbentry) +{ + dbCommonPvt *ppvt = CONTAINER(prec, dbCommonPvt, common); + + memset((char *)pdbentry,'\0',sizeof(DBENTRY)); + + pdbentry->pdbbase = pdbbase; + pdbentry->precordType = prec->rdes; + pdbentry->precnode = ppvt->recnode; + pdbentry->indfield = -1; /* invalid */ +} + long dbValueSize(short dbr_type) { /* sizes for value associated with each DBR request type */ diff --git a/src/ioc/db/dbCommonPvt.h b/src/ioc/db/dbCommonPvt.h new file mode 100644 index 000000000..3dfce8b27 --- /dev/null +++ b/src/ioc/db/dbCommonPvt.h @@ -0,0 +1,14 @@ +#ifndef DBCOMMONPVT_H +#define DBCOMMONPVT_H + +#include "dbCommon.h" + +/** Base internal additional information for every record + */ +typedef struct dbCommonPvt { + struct dbRecordNode *recnode; + + struct dbCommon common; +} dbCommonPvt; + +#endif // DBCOMMONPVT_H diff --git a/src/ioc/db/test/Makefile b/src/ioc/db/test/Makefile index 17941a2bc..e3d4164ff 100644 --- a/src/ioc/db/test/Makefile +++ b/src/ioc/db/test/Makefile @@ -159,6 +159,13 @@ testHarness_SRCS += dbPutGetTest.c TESTFILES += ../dbPutGetTest.db TESTS += testPutGetTest +TESTPROD_HOST += dbStaticTest +dbStaticTest_SRCS += dbStaticTest.c +dbStaticTest_SRCS += dbTestIoc_registerRecordDeviceDriver.cpp +testHarness_SRCS += dbStaticTest.c +TESTFILES += ../dbStaticTest.db +TESTS += dbStaticTest + # This runs all the test programs in a known working order: testHarness_SRCS += epicsRunDbTests.c diff --git a/src/ioc/db/test/dbStaticTest.c b/src/ioc/db/test/dbStaticTest.c new file mode 100644 index 000000000..e1d0a9aae --- /dev/null +++ b/src/ioc/db/test/dbStaticTest.c @@ -0,0 +1,160 @@ +#include + +#include +#include +#include +#include +#include +#include + +static void testEntry(const char *pv) +{ + DBENTRY entry; + + testDiag("testEntry(\"%s\")", pv); + + dbInitEntry(pdbbase, &entry); + + testOk1(dbFindRecord(&entry, pv)==0); + + testDiag("precordType=%p precnode=%p", entry.precordType, entry.precnode); + testOk1(entry.precordType && strcmp(entry.precordType->name, "x")==0); + testOk1(entry.precnode && strcmp(((dbCommon*)entry.precnode->precord)->name, "testrec")==0); + testOk1(entry.pflddes && strcmp(entry.pflddes->name, "VAL")==0); + + testOk1(dbFollowAlias(&entry)==0); + + testOk1(dbFindInfo(&entry, "A")==0 && strcmp(dbGetInfoString(&entry), "B")==0); + + dbFinishEntry(&entry); +} + +static void testAddr2Entry(const char *pv) +{ + DBENTRY entry, entry2; + dbAddr addr; + + testDiag("testAddr2Entry(\"%s\")", pv); + + memset(&entry, 0, sizeof(entry)); + memset(&entry2, 0, sizeof(entry2)); + + dbInitEntry(pdbbase, &entry); + + if(dbFindRecord(&entry, pv)!=0) + testAbort("no entry for %s", pv); + + if(dbFollowAlias(&entry)) + testAbort("Can't follow alias"); + + if(dbNameToAddr(pv, &addr)) + testAbort("no addr for %s", pv); + + dbInitEntryFromAddr(&addr, &entry2); + + testOk1(entry.pdbbase==entry2.pdbbase); + testOk1(entry.precordType==entry2.precordType); + testOk1(entry.pflddes==entry2.pflddes); + testOk1(entry.precnode==entry2.precnode); + testOk1(entry.pfield==entry2.pfield); + testOk1(entry2.indfield==-1); + testOk1(!entry2.pinfonode); + testOk1(!entry2.message); + + /* no way to look this up, so not set */ + entry.indfield = -1; + + testOk1(memcmp(&entry, &entry2, sizeof(entry))==0); + + dbFinishEntry(&entry); + dbFinishEntry(&entry2); +} + +static void testRec2Entry(const char *recname) +{ + DBENTRY entry, entry2; + dbCommon *prec; + + testDiag("testRec2Entry(\"%s\")", recname); + + memset(&entry, 0, sizeof(entry)); + memset(&entry2, 0, sizeof(entry2)); + + prec = testdbRecordPtr(recname); + + dbInitEntry(pdbbase, &entry); + + if(dbFindRecord(&entry, recname)!=0) + testAbort("no entry for %s", recname); + + if(dbFollowAlias(&entry)) + testAbort("Can't follow alias"); + + dbInitEntryFromRecord(prec, &entry2); + + testOk1(entry.pdbbase==entry2.pdbbase); + testOk1(entry.precordType==entry2.precordType); + testOk1(entry.pflddes==entry2.pflddes); + testOk1(entry.precnode==entry2.precnode); + testOk1(entry.pfield==entry2.pfield); + testOk1(entry2.indfield==-1); + testOk1(!entry2.pinfonode); + testOk1(!entry2.message); + + /* no way to look this up, so not set */ + entry.indfield = -1; + + testOk1(memcmp(&entry, &entry2, sizeof(entry))==0); + + dbFinishEntry(&entry); + dbFinishEntry(&entry2); +} + +void dbTestIoc_registerRecordDeviceDriver(struct dbBase *); + +MAIN(dbStaticTest) +{ + testPlan(192); + testdbPrepare(); + + testdbReadDatabase("dbTestIoc.dbd", NULL, NULL); + dbTestIoc_registerRecordDeviceDriver(pdbbase); + testdbReadDatabase("dbStaticTest.db", NULL, NULL); + + testEntry("testrec.VAL"); + testEntry("testalias.VAL"); + testEntry("testalias2.VAL"); + testEntry("testalias3.VAL"); + testAddr2Entry("testrec.VAL"); + testAddr2Entry("testalias.VAL"); + testAddr2Entry("testalias2.VAL"); + testAddr2Entry("testalias3.VAL"); + testRec2Entry("testrec"); + testRec2Entry("testalias"); + testRec2Entry("testalias2"); + testRec2Entry("testalias3"); + + eltc(0); + testIocInitOk(); + eltc(1); + + testEntry("testrec.VAL"); + testEntry("testalias.VAL"); + testEntry("testalias2.VAL"); + testEntry("testalias3.VAL"); + testAddr2Entry("testrec.VAL"); + testAddr2Entry("testalias.VAL"); + testAddr2Entry("testalias2.VAL"); + testAddr2Entry("testalias3.VAL"); + testRec2Entry("testrec"); + testRec2Entry("testalias"); + testRec2Entry("testalias2"); + testRec2Entry("testalias3"); + + testIocShutdownOk(); + + testdbCleanup(); + + return testDone(); +} + diff --git a/src/ioc/db/test/dbStaticTest.db b/src/ioc/db/test/dbStaticTest.db new file mode 100644 index 000000000..c35ff9a91 --- /dev/null +++ b/src/ioc/db/test/dbStaticTest.db @@ -0,0 +1,8 @@ + +record(x, "testrec") { + info("A", "B") + alias("testalias") +} + +alias("testrec", "testalias2") +alias("testalias2", "testalias3") diff --git a/src/ioc/db/test/epicsRunDbTests.c b/src/ioc/db/test/epicsRunDbTests.c index 10f10e267..69f6b082e 100644 --- a/src/ioc/db/test/epicsRunDbTests.c +++ b/src/ioc/db/test/epicsRunDbTests.c @@ -26,6 +26,7 @@ int dbScanTest(void); int scanIoTest(void); int dbLockTest(void); int dbPutLinkTest(void); +int dbStaticTest(void); int dbCaLinkTest(void); int testDbChannel(void); int chfPluginTest(void); @@ -46,6 +47,7 @@ void epicsRunDbTests(void) runTest(scanIoTest); runTest(dbLockTest); runTest(dbPutLinkTest); + runTest(dbStaticTest); runTest(dbCaLinkTest); runTest(testDbChannel); runTest(arrShorthandTest); diff --git a/src/ioc/dbStatic/dbBase.h b/src/ioc/dbStatic/dbBase.h index c7a7d7dc5..3dad551ba 100644 --- a/src/ioc/dbStatic/dbBase.h +++ b/src/ioc/dbStatic/dbBase.h @@ -109,6 +109,7 @@ typedef struct dbRecordNode { char *recordname; ELLLIST infoList; /*LIST head of info nodes*/ int flags; + struct dbRecordNode *aliasedRecord; /* NULL unless flags|DBRN_FLAGS_ISALIAS */ }dbRecordNode; /*dbRecordAttribute is for "psuedo" fields */ diff --git a/src/ioc/dbStatic/dbStaticLib.c b/src/ioc/dbStatic/dbStaticLib.c index 6d874398c..0a15e8bb3 100644 --- a/src/ioc/dbStatic/dbStaticLib.c +++ b/src/ioc/dbStatic/dbStaticLib.c @@ -1652,8 +1652,10 @@ long dbCreateAlias(DBENTRY *pdbentry, const char *alias) dbRecordNode *pnewnode; PVDENTRY *ppvd; ELLLIST *preclist = NULL; - if (!precordType) return S_dbLib_recordTypeNotFound; + /* alias of alias still references actual record */ + while(precnode && (precnode->flags&DBRN_FLAGS_ISALIAS)) + precnode = precnode->aliasedRecord; if (!precnode) return S_dbLib_recNotFound; zeroDbentry(pdbentry); if (!dbFindRecord(pdbentry, alias)) return S_dbLib_recExists; @@ -1663,9 +1665,9 @@ long dbCreateAlias(DBENTRY *pdbentry, const char *alias) pnewnode = dbCalloc(1, sizeof(dbRecordNode)); pnewnode->recordname = epicsStrDup(alias); pnewnode->precord = precnode->precord; + pnewnode->aliasedRecord = precnode; pnewnode->flags = DBRN_FLAGS_ISALIAS; - if (!(precnode->flags & DBRN_FLAGS_ISALIAS)) - precnode->flags |= DBRN_FLAGS_HASALIAS; + precnode->flags |= DBRN_FLAGS_HASALIAS; ellInit(&pnewnode->infoList); ellAdd(preclist, &pnewnode->node); precordType->no_aliases++; @@ -1675,6 +1677,15 @@ long dbCreateAlias(DBENTRY *pdbentry, const char *alias) return 0; } +int dbFollowAlias(DBENTRY *pdbentry) +{ + if(!pdbentry->precnode) + return S_dbLib_recNotFound; + if(pdbentry->precnode->aliasedRecord) + pdbentry->precnode = pdbentry->precnode->aliasedRecord; + return 0; +} + int dbIsAlias(DBENTRY *pdbentry) { dbRecordNode *precnode = pdbentry->precnode; diff --git a/src/ioc/dbStatic/dbStaticLib.h b/src/ioc/dbStatic/dbStaticLib.h index 1619a9090..bc40ae6a2 100644 --- a/src/ioc/dbStatic/dbStaticLib.h +++ b/src/ioc/dbStatic/dbStaticLib.h @@ -56,7 +56,10 @@ typedef struct{ char *message; short indfield; } DBENTRY; - + +struct dbAddr; +struct dbCommon; + /*dbDumpFldDes is obsolete. It is only provided for compatibility*/ #define dbDumpFldDes dbDumpField @@ -67,6 +70,17 @@ epicsShareFunc DBENTRY * dbAllocEntry(DBBASE *pdbbase); epicsShareFunc void dbFreeEntry(DBENTRY *pdbentry); epicsShareFunc void dbInitEntry(DBBASE *pdbbase, DBENTRY *pdbentry); +/** Initialize DBENTRY from a valid dbAddr*. + * Constant time equivalent of dbInitEntry() then dbFindRecord(), and finally dbFollowAlias() + * except that DBENTRY::indfield is not set + */ +epicsShareFunc void dbInitEntryFromAddr(struct dbAddr *paddr, DBENTRY *pdbentry); +/** Initialize DBENTRY from a valid record (dbCommon*). + * Constant time equivalent of dbInitEntry() then dbFindRecord(), and finally dbFollowAlias() + * when no field is specified (pflddes and pfield are NULL). + * except that DBENTRY::indfield is not set. + */ +epicsShareFunc void dbInitEntryFromRecord(struct dbCommon *prec, DBENTRY *pdbentry); epicsShareFunc void dbFinishEntry(DBENTRY *pdbentry); epicsShareFunc DBENTRY * dbCopyEntry(DBENTRY *pdbentry); epicsShareFunc void dbCopyEntryContents(DBENTRY *pfrom, @@ -156,6 +170,8 @@ epicsShareFunc int dbIsVisibleRecord(DBENTRY *pdbentry); epicsShareFunc long dbCreateAlias(DBENTRY *pdbentry, const char *paliasName); epicsShareFunc int dbIsAlias(DBENTRY *pdbentry); +/* Follow alias to actual record */ +epicsShareFunc int dbFollowAlias(DBENTRY *pdbentry); epicsShareFunc long dbDeleteAliases(DBENTRY *pdbentry); epicsShareFunc long dbFindFieldPart(DBENTRY *pdbentry, diff --git a/src/ioc/dbStatic/dbStaticRun.c b/src/ioc/dbStatic/dbStaticRun.c index 886fa3501..26bfa01d8 100644 --- a/src/ioc/dbStatic/dbStaticRun.c +++ b/src/ioc/dbStatic/dbStaticRun.c @@ -25,7 +25,7 @@ #define epicsExportSharedSymbols #include "dbBase.h" -#include "dbCommon.h" +#include "dbCommonPvt.h" #include "dbStaticLib.h" #include "dbStaticPvt.h" #include "devSup.h" @@ -69,90 +69,99 @@ long dbAllocRecord(DBENTRY *pdbentry,const char *precordName) dbRecordNode *precnode = pdbentry->precnode; dbFldDes *pflddes; int i; + dbCommonPvt *ppvt; dbCommon *precord; char *pfield; if(!pdbRecordType) return(S_dbLib_recordTypeNotFound); if(!precnode) return(S_dbLib_recNotFound); if(pdbRecordType->rec_size == 0) { - printf("\t*** Did you run x_RegisterRecordDeviceDriver(pdbbase) yet? ***\n"); - epicsPrintf("dbAllocRecord(%s) with %s rec_size = 0\n", - precordName, pdbRecordType->name); - return(S_dbLib_noRecSup); + printf("\t*** Did you run x_RegisterRecordDeviceDriver(pdbbase) yet? ***\n"); + epicsPrintf("dbAllocRecord(%s) with %s rec_size = 0\n", + precordName, pdbRecordType->name); + return(S_dbLib_noRecSup); + } else if(pdbRecordType->rec_sizename); + epicsPrintf("dbAllocRecord(%s) with %s rec_size = %d\n", + precordName, pdbRecordType->name, pdbRecordType->rec_size); + return(S_dbLib_noRecSup); } - precord = dbCalloc(1, pdbRecordType->rec_size); + ppvt = dbCalloc(1, offsetof(dbCommonPvt, common) + pdbRecordType->rec_size); + precord = &ppvt->common; + ppvt->recnode = precnode; + precord->rdes = pdbRecordType; precnode->precord = precord; pflddes = pdbRecordType->papFldDes[0]; if(!pflddes) { - epicsPrintf("dbAllocRecord pflddes for NAME not found\n"); - return(S_dbLib_flddesNotFound); + epicsPrintf("dbAllocRecord pflddes for NAME not found\n"); + return(S_dbLib_flddesNotFound); } assert(pflddes->offset == 0); assert(pflddes->size == sizeof(precord->name)); if(strlen(precordName) >= sizeof(precord->name)) { - epicsPrintf("dbAllocRecord: NAME(%s) too long\n",precordName); - return(S_dbLib_nameLength); + epicsPrintf("dbAllocRecord: NAME(%s) too long\n",precordName); + return(S_dbLib_nameLength); } strcpy(precord->name, precordName); for(i=1; ino_fields; i++) { - pflddes = pdbRecordType->papFldDes[i]; - if(!pflddes) continue; - pfield = (char*)precord + pflddes->offset; - pdbentry->pfield = (void *)pfield; - pdbentry->pflddes = pflddes; - pdbentry->indfield = i; - switch(pflddes->field_type) { - case DBF_STRING: - if(pflddes->initial) { - if(strlen(pflddes->initial) >= pflddes->size) { - epicsPrintf("initial size > size for %s.%s\n", - pdbRecordType->name,pflddes->name); - } else { - strcpy(pfield,pflddes->initial); - } - } - break; - case DBF_CHAR: - case DBF_UCHAR: - case DBF_SHORT: - case DBF_USHORT: - case DBF_LONG: - case DBF_ULONG: - case DBF_FLOAT: - case DBF_DOUBLE: - case DBF_ENUM: - case DBF_MENU: - if(pflddes->initial) { - long status; + pflddes = pdbRecordType->papFldDes[i]; + if(!pflddes) continue; + pfield = (char*)precord + pflddes->offset; + pdbentry->pfield = (void *)pfield; + pdbentry->pflddes = pflddes; + pdbentry->indfield = i; + switch(pflddes->field_type) { + case DBF_STRING: + if(pflddes->initial) { + if(strlen(pflddes->initial) >= pflddes->size) { + epicsPrintf("initial size > size for %s.%s\n", + pdbRecordType->name,pflddes->name); + } else { + strcpy(pfield,pflddes->initial); + } + } + break; + case DBF_CHAR: + case DBF_UCHAR: + case DBF_SHORT: + case DBF_USHORT: + case DBF_LONG: + case DBF_ULONG: + case DBF_FLOAT: + case DBF_DOUBLE: + case DBF_ENUM: + case DBF_MENU: + if(pflddes->initial) { + long status; - status = dbPutStringNum(pdbentry,pflddes->initial); - if(status) - epicsPrintf("Error initializing %s.%s initial %s\n", - pdbRecordType->name,pflddes->name,pflddes->initial); - } - break; - case DBF_DEVICE: - if(!pflddes->ftPvt) dbGetDeviceMenu(pdbentry); - break; - case DBF_INLINK: - case DBF_OUTLINK: - case DBF_FWDLINK: { - DBLINK *plink = (DBLINK *)pfield; + status = dbPutStringNum(pdbentry,pflddes->initial); + if(status) + epicsPrintf("Error initializing %s.%s initial %s\n", + pdbRecordType->name,pflddes->name,pflddes->initial); + } + break; + case DBF_DEVICE: + if(!pflddes->ftPvt) dbGetDeviceMenu(pdbentry); + break; + case DBF_INLINK: + case DBF_OUTLINK: + case DBF_FWDLINK: { + DBLINK *plink = (DBLINK *)pfield; - plink->type = CONSTANT; - if(pflddes->initial) { - plink->text = - dbCalloc(strlen(pflddes->initial)+1,sizeof(char)); - strcpy(plink->text,pflddes->initial); - } - } - break; - case DBF_NOACCESS: - break; - default: - epicsPrintf("dbAllocRecord: Illegal field type\n"); - } + plink->type = CONSTANT; + if(pflddes->initial) { + plink->text = + dbCalloc(strlen(pflddes->initial)+1,sizeof(char)); + strcpy(plink->text,pflddes->initial); + } + } + break; + case DBF_NOACCESS: + break; + default: + epicsPrintf("dbAllocRecord: Illegal field type\n"); + } } return(0); } @@ -165,7 +174,7 @@ long dbFreeRecord(DBENTRY *pdbentry) if(!pdbRecordType) return(S_dbLib_recordTypeNotFound); if(!precnode) return(S_dbLib_recNotFound); if(!precnode->precord) return(S_dbLib_recNotFound); - free(precnode->precord); + free(CONTAINER(precnode->precord, dbCommonPvt, common)); precnode->precord = NULL; return(0); } diff --git a/src/ioc/misc/iocInit.c b/src/ioc/misc/iocInit.c index 795ef5341..aefeb6154 100644 --- a/src/ioc/misc/iocInit.c +++ b/src/ioc/misc/iocInit.c @@ -493,7 +493,6 @@ static void doInitRecord0(dbRecordType *pdbRecordType, dbCommon *precord, if (!prset) return; /* unlikely */ precord->rset = prset; - precord->rdes = pdbRecordType; precord->mlok = epicsMutexMustCreate(); ellInit(&precord->mlis);