diff --git a/src/ioc/db/dbAccess.c b/src/ioc/db/dbAccess.c index b7a650282..a45aa9b93 100644 --- a/src/ioc/db/dbAccess.c +++ b/src/ioc/db/dbAccess.c @@ -812,11 +812,10 @@ long dbGet(DBADDR *paddr, short dbrType, void *pbuffer, long *options, long *nRequest, void *pflin) { char *pbuf = pbuffer; - void *pfieldsave; + void *pfieldsave = paddr->pfield; db_field_log *pfl = (db_field_log *)pflin; short field_type; - long no_elements; - long offset; + long capacity, no_elements, offset; struct rset *prset; long status = 0; @@ -826,18 +825,33 @@ long dbGet(DBADDR *paddr, short dbrType, return 0; if (!pfl || pfl->type == dbfl_type_rec) { - field_type = paddr->field_type; - no_elements = paddr->no_elements; + field_type = paddr->field_type; + no_elements = capacity = paddr->no_elements; + + /* Update field info from record + * may modify paddr->pfield + */ + if (paddr->pfldDes->special == SPC_DBADDR && + (prset = dbGetRset(paddr)) && + prset->get_array_info) { + status = prset->get_array_info(paddr, &no_elements, &offset); + } else + offset = 0; } else { - field_type = pfl->field_type; - no_elements = pfl->no_elements; + field_type = pfl->field_type; + no_elements = capacity = pfl->no_elements; + offset = 0; } - if (field_type >= DBF_INLINK && field_type <= DBF_FWDLINK) - return getLinkValue(paddr, dbrType, pbuf, nRequest); + if (field_type >= DBF_INLINK && field_type <= DBF_FWDLINK) { + status = getLinkValue(paddr, dbrType, pbuf, nRequest); + goto done; + } - if (paddr->special == SPC_ATTRIBUTE) - return getAttrValue(paddr, dbrType, pbuf, nRequest); + if (paddr->special == SPC_ATTRIBUTE) { + status = getAttrValue(paddr, dbrType, pbuf, nRequest); + goto done; + } /* Check for valid request */ if (INVALID_DB_REQ(dbrType) || field_type > DBF_DEVICE) { @@ -845,26 +859,13 @@ long dbGet(DBADDR *paddr, short dbrType, sprintf(message, "dbGet: Request type is %d\n", dbrType); recGblDbaddrError(S_db_badDbrtype, paddr, message); - return S_db_badDbrtype; + status = S_db_badDbrtype; + goto done; } - /* For SPC_DBADDR fields, the rset function - * get_array_info() is allowed to modify - * paddr->pfield. So we store the original - * value and restore it later. - */ - pfieldsave = paddr->pfield; - - /* Update field info */ - if (paddr->pfldDes->special == SPC_DBADDR && - (prset = dbGetRset(paddr)) && - prset->get_array_info) { - status = prset->get_array_info(paddr, &no_elements, &offset); - } else - offset = 0; - if (offset == 0 && (!nRequest || no_elements == 1)) { - if (nRequest) *nRequest = 1; + if (nRequest) + *nRequest = 1; if (!pfl || pfl->type == dbfl_type_rec) { status = dbFastGetConvertRoutine[field_type][dbrType] (paddr->pfield, pbuf, paddr); @@ -883,10 +884,11 @@ long dbGet(DBADDR *paddr, short dbrType, } } else { long n; - long (*convert)(); + GETCONVERTFUNC convert; if (nRequest) { - if (no_elements<(*nRequest)) *nRequest = no_elements; + if (no_elements < *nRequest) + *nRequest = no_elements; n = *nRequest; } else { n = 1; @@ -901,11 +903,11 @@ long dbGet(DBADDR *paddr, short dbrType, status = S_db_badDbrtype; goto done; } - /* convert database field and place it in the buffer */ + /* convert data into the caller's buffer */ if (n <= 0) { ;/*do nothing*/ } else if (!pfl || pfl->type == dbfl_type_rec) { - status = convert(paddr, pbuf, n, no_elements, offset); + status = convert(paddr, pbuf, n, capacity, offset); } else { DBADDR localAddr = *paddr; /* Structure copy */ @@ -916,7 +918,7 @@ long dbGet(DBADDR *paddr, short dbrType, localAddr.pfield = (char *) &pfl->u.v.field; else localAddr.pfield = (char *) pfl->u.r.field; - status = convert(&localAddr, pbuf, n, no_elements, offset); + status = convert(&localAddr, pbuf, n, capacity, offset); } } done: diff --git a/src/ioc/db/test/Makefile b/src/ioc/db/test/Makefile index c16016378..b844a0bd6 100644 --- a/src/ioc/db/test/Makefile +++ b/src/ioc/db/test/Makefile @@ -12,6 +12,7 @@ include $(TOP)/configure/CONFIG TESTLIBRARY = dbTestIoc +dbTestIoc_SRCS += arrRecord.c dbTestIoc_SRCS += xRecord.c dbTestIoc_SRCS += dbLinkdset.c dbTestIoc_LIBS = dbCore ca Com @@ -20,6 +21,7 @@ TARGETS += $(COMMON_DIR)/dbTestIoc.dbd dbTestIoc_DBD += menuGlobal.dbd dbTestIoc_DBD += menuConvert.dbd dbTestIoc_DBD += menuScan.dbd +#dbTestIoc_DBD += arrRecord.dbd dbTestIoc_DBD += xRecord.dbd dbTestIoc_DBD += dbLinkdset.dbd TESTFILES += $(COMMON_DIR)/dbTestIoc.dbd ../xRecord.db @@ -95,6 +97,16 @@ dbChannelTest_SRCS += dbTestIoc_registerRecordDeviceDriver.cpp testHarness_SRCS += dbChannelTest.c TESTS += dbChannelTest +TARGETS += $(COMMON_DIR)/dbChArrTest.dbd +dbChArrTest_DBD += arrRecord.dbd +TESTPROD_HOST += dbChArrTest +dbChArrTest_SRCS += dbChArrTest.cpp +dbChArrTest_SRCS += dbChArrTest_registerRecordDeviceDriver.cpp +testHarness_SRCS += dbChArrTest.cpp +testHarness_SRCS += dbChArrTest_registerRecordDeviceDriver.cpp +TESTFILES += $(COMMON_DIR)/dbChArrTest.dbd ../dbChArrTest.db +TESTS += dbChArrTest + TESTPROD_HOST += chfPluginTest chfPluginTest_SRCS += chfPluginTest.c chfPluginTest_SRCS += dbTestIoc_registerRecordDeviceDriver.cpp @@ -132,6 +144,7 @@ TESTSCRIPTS_HOST += $(TESTS:%=%.t) include $(TOP)/configure/RULES +arrRecord$(DEP): $(COMMON_DIR)/arrRecord.h xRecord$(DEP): $(COMMON_DIR)/xRecord.h dbPutLinkTest$(DEP): $(COMMON_DIR)/xRecord.h scanIoTest$(DEP): $(COMMON_DIR)/yRecord.h diff --git a/src/ioc/db/test/arrRecord.c b/src/ioc/db/test/arrRecord.c new file mode 100644 index 000000000..5e9b2f02c --- /dev/null +++ b/src/ioc/db/test/arrRecord.c @@ -0,0 +1,135 @@ +/*************************************************************************\ +* Copyright (c) 2010 Brookhaven National Laboratory. +* Copyright (c) 2010 Helmholtz-Zentrum Berlin +* fuer Materialien und Energie GmbH. +* Copyright (c) 2008 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* arrRecord.c - minimal array record for test purposes: no processing */ + +/* + * Author: Ralph Lange + * + * vaguely implemented like parts of recWaveform.c by Bob Dalesio + * + */ + +#include + +#include "dbDefs.h" +#include "epicsPrint.h" +#include "dbAccess.h" +#include "dbEvent.h" +#include "dbFldTypes.h" +#include "recSup.h" +#include "recGbl.h" +#include "cantProceed.h" +#define GEN_SIZE_OFFSET +#include "arrRecord.h" +#undef GEN_SIZE_OFFSET +#include "epicsExport.h" + +/* Create RSET - Record Support Entry Table*/ +#define report NULL +#define initialize NULL +static long init_record(arrRecord *, int); +static long process(arrRecord *); +#define special NULL +#define get_value NULL +static long cvt_dbaddr(DBADDR *); +static long get_array_info(DBADDR *, long *, long *); +static long put_array_info(DBADDR *, long); +#define get_units NULL +#define get_precision NULL +#define get_enum_str NULL +#define get_enum_strs NULL +#define put_enum_str NULL +#define get_graphic_double NULL +#define get_control_double NULL +#define get_alarm_double NULL + +rset arrRSET = { + RSETNUMBER, + report, + initialize, + init_record, + process, + special, + get_value, + cvt_dbaddr, + get_array_info, + put_array_info, + get_units, + get_precision, + get_enum_str, + get_enum_strs, + put_enum_str, + get_graphic_double, + get_control_double, + get_alarm_double +}; +epicsExportAddress(rset, arrRSET); + +static long init_record(arrRecord *prec, int pass) +{ + if (pass == 0) { + if (prec->nelm <= 0) + prec->nelm = 1; + if (prec->ftvl > DBF_ENUM) + prec->ftvl = DBF_UCHAR; + prec->bptr = callocMustSucceed(prec->nelm, dbValueSize(prec->ftvl), + "arr calloc failed"); + + if (prec->nelm == 1) { + prec->nord = 1; + } else { + prec->nord = 0; + } + return 0; + } + return 0; +} + +static long process(arrRecord *prec) +{ + return 0; +} + +static long cvt_dbaddr(DBADDR *paddr) +{ + arrRecord *prec = (arrRecord *) paddr->precord; + + paddr->pfield = prec->bptr; + paddr->no_elements = prec->nelm; + paddr->field_type = prec->ftvl; + paddr->field_size = dbValueSize(prec->ftvl); + paddr->dbr_field_type = prec->ftvl; + + return 0; +} + +static long get_array_info(DBADDR *paddr, long *no_elements, long *offset) +{ + arrRecord *prec = (arrRecord *) paddr->precord; + + *no_elements = prec->nord; + *offset = prec->off; + + return 0; +} + +static long put_array_info(DBADDR *paddr, long nNew) +{ + arrRecord *prec = (arrRecord *) paddr->precord; + + prec->nord = nNew; + if (prec->nord > prec->nelm) + prec->nord = prec->nelm; + + return 0; +} diff --git a/src/ioc/db/test/arrRecord.dbd b/src/ioc/db/test/arrRecord.dbd new file mode 100644 index 000000000..2b115b500 --- /dev/null +++ b/src/ioc/db/test/arrRecord.dbd @@ -0,0 +1,34 @@ +include "menuGlobal.dbd" +include "menuConvert.dbd" +include "menuScan.dbd" +recordtype(arr) { + include "dbCommon.dbd" + field(VAL, DBF_NOACCESS) { + prompt("Value") + special(SPC_DBADDR) + pp(TRUE) + extra("void *val") + } + field(NELM, DBF_ULONG) { + prompt("Number of Elements") + special(SPC_NOMOD) + initial("1") + } + field(FTVL, DBF_MENU) { + prompt("Field Type of Value") + special(SPC_NOMOD) + menu(menuFtype) + } + field(NORD, DBF_ULONG) { + prompt("Number elements read") + special(SPC_NOMOD) + } + field(OFF, DBF_ULONG) { + prompt("Offset into array") + } + field(BPTR, DBF_NOACCESS) { + prompt("Buffer Pointer") + special(SPC_NOMOD) + extra("void *bptr") + } +} diff --git a/src/ioc/db/test/dbChArrTest.cpp b/src/ioc/db/test/dbChArrTest.cpp new file mode 100644 index 000000000..6451fa35a --- /dev/null +++ b/src/ioc/db/test/dbChArrTest.cpp @@ -0,0 +1,242 @@ +/*************************************************************************\ +* Copyright (c) 2010 Brookhaven National Laboratory. +* Copyright (c) 2010 Helmholtz-Zentrum Berlin +* fuer Materialien und Energie GmbH. +* Copyright (c) 2016 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2003 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE is distributed subject to the Software License Agreement +* found in the file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* + * Authors: Ralph Lange , + * Andrew Johnson + */ + +#include +#include +#include +#include +#include + +#include "registryFunction.h" +#include "epicsThread.h" +#include "epicsExit.h" +#include "epicsStdio.h" +#include "epicsString.h" +#include "envDefs.h" +#include "dbStaticLib.h" +#include "dbmf.h" +#include "registry.h" +#include "dbAddr.h" +#include "dbAccess.h" +#include "asDbLib.h" +#include "iocInit.h" +#include "iocsh.h" +#include "dbChannel.h" +#include "epicsUnitTest.h" +#include "testMain.h" +#include "osiFileName.h" + +extern "C" { + int dbChArrTest_registerRecordDeviceDriver(struct dbBase *pdbbase); +} + +#define CA_SERVER_PORT "65535" + +const char *server_port = CA_SERVER_PORT; + +static void createAndOpen(const char *name, dbChannel**pch) +{ + testOk(!!(*pch = dbChannelCreate(name)), "dbChannel %s created", name); + testOk(!(dbChannelOpen(*pch)), "dbChannel opened"); + testOk((ellCount(&(*pch)->pre_chain) == 0), "no filters in pre chain"); + testOk((ellCount(&(*pch)->post_chain) == 0), "no filters in post chain"); +} + +static void freeArray(db_field_log *pfl) { + if (pfl->type == dbfl_type_ref) { + free(pfl->u.r.field); + } +} + +static void testHead (const char *title, const char *typ = "") { + const char *line = "------------------------------------------------------------------------------"; + testDiag("%s", line); + testDiag(title, typ); + testDiag("%s", line); +} + +static void check(short dbr_type) { + dbChannel *pch; + db_field_log *pfl; + dbAddr valaddr; + dbAddr offaddr; + const char *offname = NULL, *valname = NULL, *typname = NULL; + epicsInt32 buf[26]; + long off, req; + int i; + + switch (dbr_type) { + case DBR_LONG: + offname = "i32.OFF"; + valname = "i32.VAL"; + typname = "long"; + break; + case DBR_DOUBLE: + offname = "f64.OFF"; + valname = "f64.VAL"; + typname = "double"; + break; + case DBR_STRING: + offname = "c40.OFF"; + valname = "c40.VAL"; + typname = "string"; + break; + default: + testDiag("Invalid data type %d", dbr_type); + } + + (void) dbNameToAddr(offname, &offaddr); + (void) dbNameToAddr(valname, &valaddr); + + testHead("Ten %s elements", typname); + + /* Fill the record's array field with data, 10..19 */ + + epicsInt32 ar[10] = {10,11,12,13,14,15,16,17,18,19}; + (void) dbPutField(&valaddr, DBR_LONG, ar, 10); + + /* Open a channel to it, make sure no filters present */ + + createAndOpen(valname, &pch); + testOk(pch->final_type == valaddr.field_type, + "final type unchanged (%d->%d)", valaddr.field_type, pch->final_type); + testOk(pch->final_no_elements == valaddr.no_elements, + "final no_elements unchanged (%ld->%ld)", valaddr.no_elements, pch->final_no_elements); + + /* TEST1 sets the record's OFF field, then requests 10 elements from the channel, + * passing in a transparent db_field_log and converting the data to LONG on the way in. + * It checks that it got back the expected data and the right number of elements. + */ + +#define TEST1(Size, Offset, Text, Expected) \ + testDiag("Reading from offset = %d (%s)", Offset, Text); \ + off = Offset; req = 10; \ + memset(buf, 0, sizeof(buf)); \ + (void) dbPutField(&offaddr, DBR_LONG, &off, 1); \ + pfl = db_create_read_log(pch); \ + testOk(pfl && pfl->type == dbfl_type_rec, "Valid pfl, type = rec"); \ + testOk(!dbChannelGetField(pch, DBR_LONG, buf, NULL, &req, pfl), "Got Field value"); \ + testOk(req == Size, "Got %ld elements (expected %d)", req, Size); \ + if (!testOk(!memcmp(buf, Expected, sizeof(Expected)), "Data correct")) \ + for (i=0; ifinal_type == valaddr.field_type, + "final type unchanged (%d->%d)", valaddr.field_type, pch->final_type); + testOk(pch->final_no_elements == valaddr.no_elements, + "final no_elements unchanged (%ld->%ld)", valaddr.no_elements, pch->final_no_elements); + + const epicsInt32 res_5_0[] = {15,16,17,18,19}; + TEST1(5, 0, "no offset", res_5_0); + + const epicsInt32 res_5_3[] = {18,19,15,16,17}; + TEST1(5, 3, "wrapped", res_5_3); + + /* TEST2 sets the record's OFF field, then requests 15 elements from the channel + * but passes in a db_field_log with alternate data, converting that data to LONG. + * It checks that it got back the expected data and the right number of elements. + */ + +#define TEST2(Size, Offset, Text, Expected) \ + testDiag("Reading from offset = %d (%s)", Offset, Text); \ + off = Offset; req = 15; \ + memset(buf, 0, sizeof(buf)); \ + (void) dbPutField(&offaddr, DBR_LONG, &off, 1); \ + pfl = db_create_read_log(pch); \ + pfl->type = dbfl_type_ref; \ + pfl->field_type = DBF_CHAR; \ + pfl->field_size = 1; \ + pfl->no_elements = 26; \ + pfl->u.r.dtor = freeArray; \ + pfl->u.r.field = epicsStrDup("abcdefghijklmnopqrsstuvwxyz"); \ + testOk(!dbChannelGetField(pch, DBR_LONG, buf, NULL, &req, pfl), "Got Field value"); \ + testOk(req == Size, "Got %ld elements (expected %d)", req, Size); \ + if (!testOk(!memcmp(buf, Expected, sizeof(Expected)), "Data correct")) \ + for (i=0; i