Merged dbca-test branch
This commit is contained in:
+54
-31
@@ -74,6 +74,7 @@ static void dbCaTask(void *);
|
||||
errlogPrintf("%s has DB CA link to %s\n",\
|
||||
pcaLink->plink->value.pv_link.precord->name, pcaLink->pvname)
|
||||
|
||||
static int dbca_chan_count;
|
||||
|
||||
/* caLink locking
|
||||
*
|
||||
@@ -164,6 +165,32 @@ static void addAction(caLink *pca, short link_action)
|
||||
epicsEventSignal(workListEvent);
|
||||
}
|
||||
|
||||
static void dbCaLinkFree(caLink *pca)
|
||||
{
|
||||
dbCaCallback callback;
|
||||
struct link *plinkPutCallback = 0;
|
||||
|
||||
if (pca->chid) {
|
||||
ca_clear_channel(pca->chid);
|
||||
--dbca_chan_count;
|
||||
}
|
||||
callback = pca->putCallback;
|
||||
if (callback) {
|
||||
plinkPutCallback = pca->plinkPutCallback;
|
||||
pca->plinkPutCallback = 0;
|
||||
pca->putCallback = 0;
|
||||
pca->putType = 0;
|
||||
}
|
||||
free(pca->pgetNative);
|
||||
free(pca->pputNative);
|
||||
free(pca->pgetString);
|
||||
free(pca->pputString);
|
||||
free(pca->pvname);
|
||||
epicsMutexDestroy(pca->lock);
|
||||
free(pca);
|
||||
if (callback) callback(plinkPutCallback);
|
||||
}
|
||||
|
||||
void dbCaCallbackProcess(void *usrPvt)
|
||||
{
|
||||
struct link *plink = (struct link *)usrPvt;
|
||||
@@ -181,7 +208,18 @@ void dbCaShutdown(void)
|
||||
epicsEventSignal(workListEvent);
|
||||
epicsEventMustWait(startStopEvent);
|
||||
epicsEventDestroy(startStopEvent);
|
||||
epicsEventDestroy(workListEvent);
|
||||
} else {
|
||||
/* manually cleanup queue since dbCa thread isn't running
|
||||
* which only happens in unit tests
|
||||
*/
|
||||
caLink *pca;
|
||||
epicsMutexMustLock(workListLock);
|
||||
while((pca=(caLink*)ellGet(&workList))!=NULL) {
|
||||
if(pca->link_action&CA_CLEAR_CHANNEL) {
|
||||
dbCaLinkFree(pca);
|
||||
}
|
||||
}
|
||||
epicsMutexUnlock(workListLock);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -190,11 +228,20 @@ static void dbCaExit(void *arg)
|
||||
dbCaShutdown();
|
||||
}
|
||||
|
||||
void dbCaLinkInitIsolated(void)
|
||||
{
|
||||
if (!workListLock)
|
||||
workListLock = epicsMutexMustCreate();
|
||||
if (!workListEvent)
|
||||
workListEvent = epicsEventMustCreate(epicsEventEmpty);
|
||||
dbCaCtl = ctlExit;
|
||||
epicsAtExit(dbCaExit, NULL);
|
||||
}
|
||||
|
||||
void dbCaLinkInit(void)
|
||||
{
|
||||
dbServiceIOInit();
|
||||
workListLock = epicsMutexMustCreate();
|
||||
workListEvent = epicsEventMustCreate(epicsEventEmpty);
|
||||
dbCaLinkInitIsolated();
|
||||
startStopEvent = epicsEventMustCreate(epicsEventEmpty);
|
||||
dbCaCtl = ctlPause;
|
||||
|
||||
@@ -202,7 +249,6 @@ void dbCaLinkInit(void)
|
||||
epicsThreadGetStackSize(epicsThreadStackBig),
|
||||
dbCaTask, NULL);
|
||||
epicsEventMustWait(startStopEvent);
|
||||
epicsAtExit(dbCaExit, NULL);
|
||||
}
|
||||
|
||||
void dbCaRun(void)
|
||||
@@ -847,8 +893,6 @@ static void getAttribEventCallback(struct event_handler_args arg)
|
||||
|
||||
static void dbCaTask(void *arg)
|
||||
{
|
||||
int chan_count = 0;
|
||||
|
||||
taskwdInsert(0, NULL, NULL);
|
||||
SEVCHK(ca_context_create(ca_enable_preemptive_callback),
|
||||
"dbCaTask calling ca_context_create");
|
||||
@@ -878,29 +922,8 @@ static void dbCaTask(void *arg)
|
||||
if (link_action & CA_CLEAR_CHANNEL) --removesOutstanding;
|
||||
epicsMutexUnlock(workListLock); /* Give back immediately */
|
||||
if (link_action & CA_CLEAR_CHANNEL) { /* This must be first */
|
||||
dbCaCallback callback;
|
||||
struct link *plinkPutCallback = 0;
|
||||
|
||||
if (pca->chid) {
|
||||
ca_clear_channel(pca->chid);
|
||||
--chan_count;
|
||||
}
|
||||
callback = pca->putCallback;
|
||||
if (callback) {
|
||||
plinkPutCallback = pca->plinkPutCallback;
|
||||
pca->plinkPutCallback = 0;
|
||||
pca->putCallback = 0;
|
||||
pca->putType = 0;
|
||||
}
|
||||
free(pca->pgetNative);
|
||||
free(pca->pputNative);
|
||||
free(pca->pgetString);
|
||||
free(pca->pputString);
|
||||
free(pca->pvname);
|
||||
epicsMutexDestroy(pca->lock);
|
||||
free(pca);
|
||||
dbCaLinkFree(pca);
|
||||
/* No alarm is raised. Since link is changing so what? */
|
||||
if (callback) callback(plinkPutCallback);
|
||||
continue; /* No other link_action makes sense */
|
||||
}
|
||||
if (link_action & CA_CONNECT) {
|
||||
@@ -913,7 +936,7 @@ static void dbCaTask(void *arg)
|
||||
printLinks(pca);
|
||||
continue;
|
||||
}
|
||||
chan_count++;
|
||||
dbca_chan_count++;
|
||||
status = ca_replace_access_rights_event(pca->chid,
|
||||
accessRightsCallback);
|
||||
if (status != ECA_NORMAL) {
|
||||
@@ -1015,9 +1038,9 @@ static void dbCaTask(void *arg)
|
||||
}
|
||||
shutdown:
|
||||
taskwdRemove(0);
|
||||
if (chan_count == 0)
|
||||
if (dbca_chan_count == 0)
|
||||
ca_context_destroy();
|
||||
else
|
||||
fprintf(stderr, "dbCa: chan_count = %d at shutdown\n", chan_count);
|
||||
fprintf(stderr, "dbCa: chan_count = %d at shutdown\n", dbca_chan_count);
|
||||
epicsEventSignal(startStopEvent);
|
||||
}
|
||||
|
||||
+2
-1
@@ -23,7 +23,8 @@ extern "C" {
|
||||
typedef void (*dbCaCallback)(void *userPvt);
|
||||
epicsShareFunc void dbCaCallbackProcess(void *usrPvt);
|
||||
|
||||
epicsShareFunc void dbCaLinkInit(void);
|
||||
epicsShareFunc void dbCaLinkInit(void); /* internal initialization for iocBuild() */
|
||||
epicsShareFunc void dbCaLinkInitIsolated(void); /* internal initialization for iocBuildIsolated() */
|
||||
epicsShareFunc void dbCaRun(void);
|
||||
epicsShareFunc void dbCaPause(void);
|
||||
epicsShareFunc void dbCaShutdown(void);
|
||||
|
||||
@@ -12,13 +12,15 @@ include $(TOP)/configure/CONFIG
|
||||
|
||||
TESTLIBRARY = dbTestIoc
|
||||
|
||||
dbTestIoc_SRCS = xRecord.c
|
||||
dbTestIoc_SRCS += xRecord.c
|
||||
dbTestIoc_SRCS += dbLinkdset.c
|
||||
dbTestIoc_LIBS = dbCore
|
||||
|
||||
TARGETS += $(COMMON_DIR)/dbTestIoc.dbd
|
||||
dbTestIoc_DBD += menuGlobal.dbd
|
||||
dbTestIoc_DBD += menuConvert.dbd
|
||||
dbTestIoc_DBD += xRecord.dbd
|
||||
dbTestIoc_DBD += dbLinkdset.dbd
|
||||
TESTFILES += $(COMMON_DIR)/dbTestIoc.dbd ../xRecord.db
|
||||
|
||||
testHarness_SRCS += dbTestIoc_registerRecordDeviceDriver.cpp
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
# The records in this file have intentional
|
||||
# syntax error in their input links
|
||||
|
||||
record(x, "eVME_IO1") {
|
||||
field(DTYP, "Unit Test VME_IO")
|
||||
field(INP, "C100 S101 @parm VME_IO")
|
||||
}
|
||||
record(x, "eVME_IO2") {
|
||||
field(DTYP, "Unit Test VME_IO")
|
||||
field(INP, "#C200 201 @parm VME_IO")
|
||||
}
|
||||
|
||||
record(x, "eINST_IO") {
|
||||
field(DTYP, "Unit Test INST_IO")
|
||||
field(INP, "hello")
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <devSup.h>
|
||||
|
||||
#include <epicsExport.h>
|
||||
|
||||
static
|
||||
long link_test_extend(struct dbCommon *junk)
|
||||
{ return 0; }
|
||||
|
||||
static dsxt xrecextend = {&link_test_extend, &link_test_extend};
|
||||
|
||||
static
|
||||
long link_test_init(int junk)
|
||||
{
|
||||
devExtend(&xrecextend);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static
|
||||
long link_test_noop(void *junk)
|
||||
{ return 0; }
|
||||
|
||||
|
||||
|
||||
#define DEFDSET(LTYPE) \
|
||||
static dset devxLTest ## LTYPE = {4, NULL, &link_test_init, &link_test_noop, &link_test_noop}; \
|
||||
epicsExportAddress(dset, devxLTest ## LTYPE);
|
||||
|
||||
DEFDSET(Soft)
|
||||
DEFDSET(VME_IO)
|
||||
DEFDSET(CAMAC_IO)
|
||||
DEFDSET(AB_IO)
|
||||
DEFDSET(GPIB_IO)
|
||||
DEFDSET(BITBUS_IO)
|
||||
DEFDSET(INST_IO)
|
||||
DEFDSET(BBGPIB_IO)
|
||||
DEFDSET(RF_IO)
|
||||
DEFDSET(VXI_IO)
|
||||
@@ -0,0 +1,11 @@
|
||||
device(x, CONSTANT,devxLTestSoft,"Soft Channel")
|
||||
|
||||
device(x, VME_IO, devxLTestVME_IO, "Unit Test VME_IO")
|
||||
device(x, CAMAC_IO, devxLTestCAMAC_IO, "Unit Test CAMAC_IO")
|
||||
device(x, AB_IO, devxLTestAB_IO, "Unit Test AB_IO")
|
||||
device(x, GPIB_IO, devxLTestGPIB_IO, "Unit Test GPIB_IO")
|
||||
device(x, BITBUS_IO, devxLTestBITBUS_IO, "Unit Test BITBUS_IO")
|
||||
device(x, INST_IO, devxLTestINST_IO, "Unit Test INST_IO")
|
||||
device(x, BBGPIB_IO, devxLTestBBGPIB_IO, "Unit Test BBGPIB_IO")
|
||||
device(x, RF_IO, devxLTestRF_IO, "Unit Test RF_IO")
|
||||
device(x, VXI_IO, devxLTestVXI_IO, "Unit Test VXI_IO")
|
||||
@@ -31,14 +31,15 @@
|
||||
void dbTestIoc_registerRecordDeviceDriver(struct dbBase *);
|
||||
|
||||
static const struct testDataT {
|
||||
const char *linkstring;
|
||||
const char * const linkstring;
|
||||
short linkType;
|
||||
unsigned int pvlMask;
|
||||
const char *linkback;
|
||||
const char * const linkback;
|
||||
} testSetData[] = {
|
||||
{"", CONSTANT, 0},
|
||||
{"0", CONSTANT, 0},
|
||||
{"42", CONSTANT, 0},
|
||||
|
||||
{"x1", DB_LINK, 0, "x1 NPP NMS"},
|
||||
{"x1.VAL", DB_LINK, 0, "x1.VAL NPP NMS"},
|
||||
{"x1.TIME", DB_LINK, 0, "x1.TIME NPP NMS"},
|
||||
@@ -46,15 +47,21 @@ static const struct testDataT {
|
||||
{"x1 PP MSS", DB_LINK, pvlOptPP|pvlOptMSS, "x1 PP MSS"},
|
||||
{"x1 PPMSS", DB_LINK, pvlOptPP|pvlOptMSS, "x1 PP MSS"},
|
||||
{"x1 PPMSI", DB_LINK, pvlOptPP|pvlOptMSI, "x1 PP MSI"},
|
||||
/*TODO: testing doesn't support CA_LINK yet */
|
||||
|
||||
{"qq", CA_LINK, pvlOptInpNative, "qq NPP NMS"},
|
||||
{"qq MSI", CA_LINK, pvlOptInpNative|pvlOptMSI, "qq NPP MSI"},
|
||||
{"qq MSICA", CA_LINK, pvlOptInpNative|pvlOptCA|pvlOptMSI, "qq CA MSI"},
|
||||
|
||||
{"x1 CA", CA_LINK, pvlOptInpNative|pvlOptCA, "x1 CA NMS"},
|
||||
{NULL}
|
||||
};
|
||||
|
||||
static void testSet(void)
|
||||
static void testCADBSet(void)
|
||||
{
|
||||
const struct testDataT *td = testSetData;
|
||||
xRecord *prec;
|
||||
DBLINK *plink;
|
||||
testDiag("DB/CA link retargeting");
|
||||
testdbPrepare();
|
||||
|
||||
testdbReadDatabase("dbTestIoc.dbd", NULL, NULL);
|
||||
@@ -71,6 +78,7 @@ static void testSet(void)
|
||||
plink = &prec->lnk;
|
||||
|
||||
for(;td->linkstring;td++) {
|
||||
testDiag("x1.LNK <- \"%s\"", td->linkstring);
|
||||
|
||||
testdbPutFieldOk("x1.LNK", DBF_STRING, td->linkstring);
|
||||
if(td->linkback)
|
||||
@@ -91,7 +99,9 @@ static void testSet(void)
|
||||
break;
|
||||
|
||||
case DB_LINK:
|
||||
testOk1(plink->value.pv_link.pvlMask==td->pvlMask);
|
||||
case CA_LINK:
|
||||
testOk(plink->value.pv_link.pvlMask==td->pvlMask,
|
||||
"pvlMask %x == %x", plink->value.pv_link.pvlMask, td->pvlMask);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -102,9 +112,286 @@ static void testSet(void)
|
||||
testdbCleanup();
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
const char * const recname;
|
||||
short ltype;
|
||||
const char * const wval;
|
||||
short vals[5];
|
||||
const char * const parm;
|
||||
} testHWDataT;
|
||||
|
||||
static const testHWDataT testHWData[] = {
|
||||
{"rVME_IO", VME_IO, "#C100 S101 @parm VME_IO", {100, 101}, "parm VME_IO"},
|
||||
{"rCAMAC_IO", CAMAC_IO, "#B11 C12 N13 A14 F15 @parm CAMAC_IO", {11, 12, 13, 14, 15}, "parm CAMAC_IO"},
|
||||
{"rAB_IO", AB_IO, "#L21 A22 C23 S24 @parm AB_IO", {21, 22, 23, 24}, "parm AB_IO"},
|
||||
{"rGPIB_IO", GPIB_IO, "#L31 A32 @parm GPIB_IO", {31, 32}, "parm GPIB_IO"},
|
||||
{"rBITBUS_IO", BITBUS_IO, "#L41 N42 P43 S44 @parm BITBUS_IO", {41, 42, 43, 44}, "parm BITBUS_IO"},
|
||||
{"rINST_IO", INST_IO, "@parm INST_IO", {0}, "parm INST_IO"},
|
||||
{"rBBGPIB_IO", BBGPIB_IO, "#L51 B52 G53 @parm BBGPIB_IO", {51, 52, 53}, "parm BBGPIB_IO"},
|
||||
{"rRF_IO", RF_IO, "#R61 M62 D63 E64", {61, 62, 63, 64}, NULL},
|
||||
{"rVXI_IO1", VXI_IO, "#V71 C72 S73 @parm1 VXI_IO", {71, 72, 73}, "parm1 VXI_IO"},
|
||||
{"rVXI_IO2", VXI_IO, "#V81 S82 @parm2 VXI_IO", {81, 82}, "parm2 VXI_IO"},
|
||||
{NULL}
|
||||
};
|
||||
|
||||
static void testLink(DBLINK *plink, const testHWDataT *td)
|
||||
{
|
||||
switch(td->ltype) {
|
||||
case VME_IO:
|
||||
testOk1(plink->value.vmeio.card==td->vals[0]);
|
||||
testOk1(plink->value.vmeio.signal==td->vals[1]);
|
||||
testOk1(strcmp(plink->value.vmeio.parm, td->parm)==0);
|
||||
break;
|
||||
case CAMAC_IO:
|
||||
testOk1(plink->value.camacio.b==td->vals[0]);
|
||||
testOk1(plink->value.camacio.c==td->vals[1]);
|
||||
testOk1(plink->value.camacio.n==td->vals[2]);
|
||||
testOk1(plink->value.camacio.a==td->vals[3]);
|
||||
testOk1(plink->value.camacio.f==td->vals[4]);
|
||||
testOk1(strcmp(plink->value.camacio.parm, td->parm)==0);
|
||||
break;
|
||||
case AB_IO:
|
||||
testOk1(plink->value.abio.link==td->vals[0]);
|
||||
testOk1(plink->value.abio.adapter==td->vals[1]);
|
||||
testOk1(plink->value.abio.card==td->vals[2]);
|
||||
testOk1(plink->value.abio.signal==td->vals[3]);
|
||||
testOk1(strcmp(plink->value.abio.parm, td->parm)==0);
|
||||
break;
|
||||
case GPIB_IO:
|
||||
testOk1(plink->value.gpibio.link==td->vals[0]);
|
||||
testOk1(plink->value.gpibio.addr==td->vals[1]);
|
||||
testOk1(strcmp(plink->value.gpibio.parm, td->parm)==0);
|
||||
break;
|
||||
case BITBUS_IO:
|
||||
testOk1(plink->value.bitbusio.link==td->vals[0]);
|
||||
testOk1(plink->value.bitbusio.node==td->vals[1]);
|
||||
testOk1(plink->value.bitbusio.port==td->vals[2]);
|
||||
testOk1(plink->value.bitbusio.signal==td->vals[3]);
|
||||
testOk1(strcmp(plink->value.bitbusio.parm, td->parm)==0);
|
||||
break;
|
||||
case INST_IO:
|
||||
testOk1(strcmp(plink->value.instio.string, td->parm)==0);
|
||||
break;
|
||||
case BBGPIB_IO:
|
||||
testOk1(plink->value.bbgpibio.link==td->vals[0]);
|
||||
testOk1(plink->value.bbgpibio.bbaddr==td->vals[1]);
|
||||
testOk1(plink->value.bbgpibio.gpibaddr==td->vals[2]);
|
||||
testOk1(strcmp(plink->value.bbgpibio.parm, td->parm)==0);
|
||||
break;
|
||||
case RF_IO:
|
||||
testOk1(plink->value.rfio.cryo==td->vals[0]);
|
||||
testOk1(plink->value.rfio.micro==td->vals[1]);
|
||||
testOk1(plink->value.rfio.dataset==td->vals[2]);
|
||||
testOk1(plink->value.rfio.element==td->vals[3]);
|
||||
break;
|
||||
case VXI_IO:
|
||||
if(plink->value.vxiio.flag==VXIDYNAMIC) {
|
||||
testOk1(plink->value.vxiio.frame==td->vals[0]);
|
||||
testOk1(plink->value.vxiio.slot==td->vals[1]);
|
||||
testOk1(plink->value.vxiio.signal==td->vals[2]);
|
||||
} else {
|
||||
testOk1(plink->value.vxiio.la==td->vals[0]);
|
||||
testOk1(plink->value.vxiio.signal==td->vals[1]);
|
||||
}
|
||||
testOk1(strcmp(plink->value.vxiio.parm, td->parm)==0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void testHWInitSet(void)
|
||||
{
|
||||
const testHWDataT *td = testHWData;
|
||||
testDiag("HW link parsing during initialization");
|
||||
testdbPrepare();
|
||||
|
||||
testdbReadDatabase("dbTestIoc.dbd", NULL, NULL);
|
||||
|
||||
dbTestIoc_registerRecordDeviceDriver(pdbbase);
|
||||
|
||||
testdbReadDatabase("dbPutLinkTest.db", NULL, NULL);
|
||||
|
||||
eltc(0);
|
||||
testIocInitOk();
|
||||
eltc(1);
|
||||
|
||||
for(;td->recname;td++) {
|
||||
char buf[MAX_STRING_SIZE];
|
||||
xRecord *prec;
|
||||
DBLINK *plink;
|
||||
testDiag("%s == \"%s\"", td->recname, td->wval);
|
||||
|
||||
prec = (xRecord*)testdbRecordPtr(td->recname);
|
||||
plink = &prec->inp;
|
||||
|
||||
strcpy(buf, td->recname);
|
||||
strcat(buf, ".INP");
|
||||
|
||||
testdbGetFieldEqual(buf, DBR_STRING, td->wval);
|
||||
|
||||
testOk(plink->type==td->ltype, "link type %d == %d",
|
||||
plink->type, td->ltype);
|
||||
if(plink->type==td->ltype) {
|
||||
testLink(plink, td);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
testIocShutdownOk();
|
||||
|
||||
testdbCleanup();
|
||||
}
|
||||
|
||||
static const testHWDataT testHWData2[] = {
|
||||
{"rVME_IO", VME_IO, "#C200 S201 @another VME_IO", {200, 201}, "another VME_IO"},
|
||||
{"rCAMAC_IO", CAMAC_IO, "#B111 C112 N113 A114 F115 @CAMAC_IO", {111, 112, 113, 114, 115}, "CAMAC_IO"},
|
||||
{"rAB_IO", AB_IO, "#L121 A122 C123 S124 @another AB_IO", {121, 122, 123, 124}, "another AB_IO"},
|
||||
{"rGPIB_IO", GPIB_IO, "#L131 A132 @another GPIB_IO", {131, 132}, "another GPIB_IO"},
|
||||
{"rBITBUS_IO", BITBUS_IO, "#L141 N142 P143 S144 @BITBUS_IO", {141, 142, 143, 144}, "BITBUS_IO"},
|
||||
{"rINST_IO", INST_IO, "@another INST_IO", {0}, "another INST_IO"},
|
||||
{"rBBGPIB_IO", BBGPIB_IO, "#L151 B152 G153 @another BBGPIB_IO", {151, 152, 153}, "another BBGPIB_IO"},
|
||||
{"rRF_IO", RF_IO, "#R161 M162 D163 E164", {161, 162, 163, 164}, NULL},
|
||||
{"rVXI_IO1", VXI_IO, "#V171 C172 S173 @another1 VXI_IO", {171, 172, 173}, "another1 VXI_IO"},
|
||||
{"rVXI_IO2", VXI_IO, "#V181 S182 @another2 VXI_IO", {181, 182}, "another2 VXI_IO"},
|
||||
{NULL}
|
||||
};
|
||||
|
||||
static void testHWMod(void)
|
||||
{
|
||||
const testHWDataT *td = testHWData2;
|
||||
testDiag("HW link parsing during retarget");
|
||||
testdbPrepare();
|
||||
|
||||
testdbReadDatabase("dbTestIoc.dbd", NULL, NULL);
|
||||
|
||||
dbTestIoc_registerRecordDeviceDriver(pdbbase);
|
||||
|
||||
testdbReadDatabase("dbPutLinkTest.db", NULL, NULL);
|
||||
|
||||
eltc(0);
|
||||
testIocInitOk();
|
||||
eltc(1);
|
||||
|
||||
for(;td->recname;td++) {
|
||||
char buf[MAX_STRING_SIZE];
|
||||
xRecord *prec;
|
||||
DBLINK *plink;
|
||||
testDiag("%s -> \"%s\"", td->recname, td->wval);
|
||||
|
||||
prec = (xRecord*)testdbRecordPtr(td->recname);
|
||||
plink = &prec->inp;
|
||||
|
||||
strcpy(buf, td->recname);
|
||||
strcat(buf, ".INP");
|
||||
|
||||
testdbPutFieldOk(buf, DBR_STRING, td->wval);
|
||||
|
||||
testdbGetFieldEqual(buf, DBR_STRING, td->wval);
|
||||
|
||||
testOk(plink->type==td->ltype, "link type %d == %d",
|
||||
plink->type, td->ltype);
|
||||
if(plink->type==td->ltype) {
|
||||
testLink(plink, td);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
testIocShutdownOk();
|
||||
|
||||
testdbCleanup();
|
||||
}
|
||||
|
||||
static void testLinkInitFail(void)
|
||||
{
|
||||
xRecord *prec;
|
||||
DBLINK *plink;
|
||||
testDiag("Link parsing failures during initialization");
|
||||
testdbPrepare();
|
||||
|
||||
testdbReadDatabase("dbTestIoc.dbd", NULL, NULL);
|
||||
|
||||
dbTestIoc_registerRecordDeviceDriver(pdbbase);
|
||||
|
||||
|
||||
/* this load will fail */
|
||||
eltc(0);
|
||||
testOk1(dbReadDatabase(&pdbbase, "dbBadLink.db", "." OSI_PATH_LIST_SEPARATOR ".." OSI_PATH_LIST_SEPARATOR
|
||||
"../O.Common" OSI_PATH_LIST_SEPARATOR "O.Common", NULL)!=0);
|
||||
|
||||
testIocInitOk();
|
||||
eltc(1);
|
||||
|
||||
testdbGetFieldEqual("eVME_IO1.INP", DBR_STRING, "#C0 S0 @");
|
||||
|
||||
prec = (xRecord*)testdbRecordPtr("eVME_IO1");
|
||||
plink = &prec->inp;
|
||||
testOk1(plink->type==VME_IO);
|
||||
testOk1(plink->value.vmeio.parm!=NULL);
|
||||
|
||||
testdbGetFieldEqual("eVME_IO2.INP", DBR_STRING, "#C200 S0 @");
|
||||
|
||||
prec = (xRecord*)testdbRecordPtr("eVME_IO2");
|
||||
plink = &prec->inp;
|
||||
testOk1(plink->type==VME_IO);
|
||||
testOk1(plink->value.vmeio.parm!=NULL);
|
||||
|
||||
testdbGetFieldEqual("eINST_IO.INP", DBR_STRING, "@");
|
||||
|
||||
prec = (xRecord*)testdbRecordPtr("eINST_IO");
|
||||
plink = &prec->inp;
|
||||
testOk1(plink->type==INST_IO);
|
||||
testOk1(plink->value.instio.string!=NULL);
|
||||
|
||||
testIocShutdownOk();
|
||||
|
||||
testdbCleanup();
|
||||
}
|
||||
|
||||
static void testLinkFail(void)
|
||||
{
|
||||
testDiag("Link parsing failures");
|
||||
testdbPrepare();
|
||||
|
||||
testdbReadDatabase("dbTestIoc.dbd", NULL, NULL);
|
||||
|
||||
dbTestIoc_registerRecordDeviceDriver(pdbbase);
|
||||
|
||||
testdbReadDatabase("dbPutLinkTest.db", NULL, NULL);
|
||||
|
||||
eltc(0);
|
||||
testIocInitOk();
|
||||
eltc(1);
|
||||
|
||||
/* 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, "@");
|
||||
|
||||
/* syntax errors */
|
||||
testdbPutFieldFail(S_dbLib_badField, "rVME_IO.INP", DBR_STRING, "#S201 C200 @another VME_IO");
|
||||
testdbPutFieldFail(S_dbLib_badField, "rVME_IO.INP", DBR_STRING, "C200 #S201");
|
||||
|
||||
/* VME_IO doesn't accept empty string */
|
||||
testdbPutFieldFail(S_dbLib_badField, "rVME_IO.INP", DBR_STRING, "");
|
||||
|
||||
testdbPutFieldOk("rVME_IO.INP", DBR_STRING, "#C1 S2 @hello");
|
||||
|
||||
/* VME_IO fails invalid input */
|
||||
testdbPutFieldFail(S_dbLib_badField, "rVME_IO.INP", DBR_STRING, "abc");
|
||||
|
||||
testIocShutdownOk();
|
||||
|
||||
testdbCleanup();
|
||||
}
|
||||
|
||||
MAIN(dbPutLinkTest)
|
||||
{
|
||||
testPlan(40);
|
||||
testSet();
|
||||
testPlan(200);
|
||||
testCADBSet();
|
||||
testHWInitSet();
|
||||
testHWMod();
|
||||
testLinkInitFail();
|
||||
testLinkFail();
|
||||
return testDone();
|
||||
}
|
||||
|
||||
@@ -2,3 +2,44 @@ record(x, "x1") {}
|
||||
record(x, "x2") {}
|
||||
record(x, "x3") {}
|
||||
record(x, "x4") {}
|
||||
|
||||
record(x, "rVME_IO") {
|
||||
field(DTYP, "Unit Test VME_IO")
|
||||
field(INP, "#C100 S101 @parm VME_IO")
|
||||
}
|
||||
record(x, "rCAMAC_IO") {
|
||||
field(DTYP, "Unit Test CAMAC_IO")
|
||||
field(INP, "#B11 C12 N13 A14 F15 @parm CAMAC_IO")
|
||||
}
|
||||
record(x, "rAB_IO") {
|
||||
field(DTYP, "Unit Test AB_IO")
|
||||
field(INP, "#L21 A22 C23 S24 @parm AB_IO")
|
||||
}
|
||||
record(x, "rGPIB_IO") {
|
||||
field(DTYP, "Unit Test GPIB_IO")
|
||||
field(INP, "#L31 A32 @parm GPIB_IO")
|
||||
}
|
||||
record(x, "rBITBUS_IO") {
|
||||
field(DTYP, "Unit Test BITBUS_IO")
|
||||
field(INP, "#L41 N42 P43 S44 @parm BITBUS_IO")
|
||||
}
|
||||
record(x, "rINST_IO") {
|
||||
field(DTYP, "Unit Test INST_IO")
|
||||
field(INP, "@parm INST_IO")
|
||||
}
|
||||
record(x, "rBBGPIB_IO") {
|
||||
field(DTYP, "Unit Test BBGPIB_IO")
|
||||
field(INP, "#L51 B52 G53 @parm BBGPIB_IO")
|
||||
}
|
||||
record(x, "rRF_IO") {
|
||||
field(DTYP, "Unit Test RF_IO")
|
||||
field(INP, "#R61 M62 D63 E64")
|
||||
}
|
||||
record(x, "rVXI_IO1") {
|
||||
field(DTYP, "Unit Test VXI_IO")
|
||||
field(INP, "#V71 C72 S73 @parm1 VXI_IO")
|
||||
}
|
||||
record(x, "rVXI_IO2") {
|
||||
field(DTYP, "Unit Test VXI_IO")
|
||||
field(INP, "#V81 S82 @parm2 VXI_IO")
|
||||
}
|
||||
|
||||
@@ -8,4 +8,7 @@ recordtype(x) {
|
||||
field(LNK, DBF_INLINK) {
|
||||
prompt("Link")
|
||||
}
|
||||
field(INP, DBF_INLINK) {
|
||||
prompt("Input Link")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -227,9 +227,10 @@ void dbFreeLinkContents(struct link *plink)
|
||||
case BITBUS_IO: parm = plink->value.bitbusio.parm;break;
|
||||
case INST_IO: parm = plink->value.instio.string; break;
|
||||
case BBGPIB_IO: parm = plink->value.bbgpibio.parm;break;
|
||||
case RF_IO: break;
|
||||
case VXI_IO: parm = plink->value.vxiio.parm; break;
|
||||
default:
|
||||
epicsPrintf("dbFreeLink called but link type unknown\n");
|
||||
epicsPrintf("dbFreeLink called but link type %d unknown\n", plink->type);
|
||||
}
|
||||
if(parm && (parm != pNullString)) free((void *)parm);
|
||||
if(plink->text) free(plink->text);
|
||||
|
||||
@@ -53,6 +53,7 @@
|
||||
#include "dbNotify.h"
|
||||
#include "dbScan.h"
|
||||
#include "dbStaticLib.h"
|
||||
#include "dbStaticPvt.h"
|
||||
#include "devSup.h"
|
||||
#include "drvSup.h"
|
||||
#include "epicsRelease.h"
|
||||
@@ -192,6 +193,8 @@ int iocBuildIsolated(void)
|
||||
status = iocBuild_1();
|
||||
if (status) return status;
|
||||
|
||||
dbCaLinkInitIsolated();
|
||||
|
||||
status = iocBuild_2();
|
||||
if (status) return status;
|
||||
|
||||
@@ -621,6 +624,11 @@ static void doCloseLinks(dbRecordType *pdbRecordType, dbCommon *precord,
|
||||
}
|
||||
dbCaRemoveLink(plink);
|
||||
plink->type = CONSTANT;
|
||||
|
||||
} else if (plink->type == DB_LINK) {
|
||||
/* free link, but don't split lockset like dbDbRemoveLink() */
|
||||
free(plink->value.pv_link.pvt);
|
||||
plink->type = CONSTANT;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -644,11 +652,20 @@ static void doCloseLinks(dbRecordType *pdbRecordType, dbCommon *precord,
|
||||
static void doFreeRecord(dbRecordType *pdbRecordType, dbCommon *precord,
|
||||
void *user)
|
||||
{
|
||||
int j;
|
||||
struct rset *prset = pdbRecordType->prset;
|
||||
|
||||
if (!prset) return; /* unlikely */
|
||||
|
||||
epicsMutexDestroy(precord->mlok);
|
||||
|
||||
for (j = 0; j < pdbRecordType->no_links; j++) {
|
||||
dbFldDes *pdbFldDes =
|
||||
pdbRecordType->papFldDes[pdbRecordType->link_ind[j]];
|
||||
DBLINK *plink = (DBLINK *)((char *)precord + pdbFldDes->offset);
|
||||
|
||||
dbFreeLinkContents(plink);
|
||||
}
|
||||
}
|
||||
|
||||
int iocShutdown(void)
|
||||
|
||||
Reference in New Issue
Block a user