Merge Dirk Zimoch's fix_zero_size_arrays branch

This commit is contained in:
Andrew Johnson
2020-10-29 17:07:35 -05:00
12 changed files with 106 additions and 42 deletions

View File

@@ -17,11 +17,49 @@ should also be read to understand what has changed since earlier releases.
<!-- Insert new items immediately below here ... -->
### Support for empty arrays
Several problems with empty arrays have been fixed.
#### Changed dbr_size_n(TYPE,COUNT) macro
When called with COUNT=0 the macro no longer returns the number of bytes
required for a scalar (1 element) but for an empty array (0 elements).
Make sure you don't call it with COUNT=0 when you really mean COUNT=1.
#### Array records
The soft supports of array records aai, waveform, and subArray as well as
the aSub record type have been fixed to correctly report 0 elements read
when reading empty arrays from an input link.
#### Array support for dbpf
The dbpf function now accepts arrays, including empty arrays as a JSON string.
#### Scalar records reading from empty arrays
Records reading scalar fields from empty arrays are now set to INVALID/LINK
alarm status.
Links have to call dbGetLink with pnRequest=NULL to be recognized as requests
for scalars.
This changes the semantics of pnRequest=NULL. It is now different from
requesting up to 1 array element, which may return a valid empty array.
### Writing empty arrays to scalar records
Witing an empty array to a scalar field now sets the target record to
INVALID/LINK alarm but does not modify the value. Before, the value used
to be set to 0 (without any alarm).
A target field needs to have the SPC_DBADDR tag to be recognized as an array
field and the record support must define a put_array_info method.
### Add registerAllRecordDeviceDrivers()
Addition of registerAllRecordDeviceDrivers() as an iocsh function
and in iocshRegisterCommon.h. This function uses dynamic lookup with epicsFindSymbol()
to perform the same function as a generated \*_registerRecordDeviceDriver() function.
and in iocshRegisterCommon.h. This function uses dynamic lookup with
`epicsFindSymbol()` to perform the same function as a generated
`*_registerRecordDeviceDriver()` function.
This allows dynamic loading/linking of support modules without code generation.
This feature is not intended for use by IOCs constructed using the standard EPICS application

View File

@@ -517,7 +517,7 @@ struct dbr_ctrl_double{
};
#define dbr_size_n(TYPE,COUNT)\
((unsigned)((COUNT)<=0?dbr_size[TYPE]:dbr_size[TYPE]+((COUNT)-1)*dbr_value_size[TYPE]))
((unsigned)((COUNT)<0?dbr_size[TYPE]:dbr_size[TYPE]+((COUNT)-1)*dbr_value_size[TYPE]))
/* size for each type - array indexed by the DBR_ type code */
LIBCA_API extern const unsigned short dbr_size[];

View File

@@ -329,7 +329,7 @@ void nciu::write (
if ( ! this->accessRightState.writePermit() ) {
throw cacChannel::noWriteAccess();
}
if ( countIn > this->count || countIn == 0 ) {
if ( countIn > this->count) {
throw cacChannel::outOfBounds();
}
if ( type == DBR_STRING ) {
@@ -350,7 +350,7 @@ cacChannel::ioStatus nciu::write (
if ( ! this->accessRightState.writePermit() ) {
throw cacChannel::noWriteAccess();
}
if ( countIn > this->count || countIn == 0 ) {
if ( countIn > this->count) {
throw cacChannel::outOfBounds();
}
if ( type == DBR_STRING ) {

View File

@@ -946,6 +946,11 @@ long dbGet(DBADDR *paddr, short dbrType,
if (offset == 0 && (!nRequest || no_elements == 1)) {
if (nRequest)
*nRequest = 1;
else if (no_elements < 1) {
status = S_db_onlyOne;
goto done;
}
if (!pfl || pfl->type == dbfl_type_rec) {
status = dbFastGetConvertRoutine[field_type][dbrType]
(paddr->pfield, pbuf, paddr);
@@ -1330,25 +1335,21 @@ long dbPut(DBADDR *paddr, short dbrType,
status = prset->get_array_info(paddr, &dummy, &offset);
/* paddr->pfield may be modified */
if (status) goto done;
} else
offset = 0;
if (no_elements <= 1) {
status = dbFastPutConvertRoutine[dbrType][field_type](pbuffer,
paddr->pfield, paddr);
nRequest = 1;
} else {
if (no_elements < nRequest)
nRequest = no_elements;
status = dbPutConvertRoutine[dbrType][field_type](paddr, pbuffer,
nRequest, no_elements, offset);
}
/* update array info */
if (!status &&
paddr->pfldDes->special == SPC_DBADDR &&
prset && prset->put_array_info) {
status = prset->put_array_info(paddr, nRequest);
/* update array info */
if (!status && prset->put_array_info)
status = prset->put_array_info(paddr, nRequest);
} else {
if (nRequest < 1) {
recGblSetSevr(precord, LINK_ALARM, INVALID_ALARM);
} else {
status = dbFastPutConvertRoutine[dbrType][field_type](pbuffer,
paddr->pfield, paddr);
nRequest = 1;
}
}
/* Always do special processing if needed */

View File

@@ -411,9 +411,15 @@ long dbCaGetLink(struct link *plink, short dbrType, void *pdest,
goto done;
}
newType = dbDBRoldToDBFnew[pca->dbrType];
if (!nelements || *nelements == 1) {
if (!nelements) {
long (*fConvert)(const void *from, void *to, struct dbAddr *paddr);
if (pca->usedelements < 1) {
pca->sevr = INVALID_ALARM;
pca->stat = LINK_ALARM;
status = -1;
goto done;
}
fConvert = dbFastGetConvertRoutine[newType][dbrType];
assert(pca->pgetNative);
status = fConvert(pca->pgetNative, pdest, 0);

View File

@@ -41,6 +41,7 @@
#include "recGbl.h"
#include "recSup.h"
#include "special.h"
#include "dbConvertJSON.h"
#define MAXLINE 80
#define MAXMESS 128
@@ -364,8 +365,9 @@ long dbpf(const char *pname,const char *pvalue)
{
DBADDR addr;
long status;
short dbrType;
size_t n = 1;
short dbrType = DBR_STRING;
long n = 1;
char *array = NULL;
if (!pname || !*pname || !pvalue) {
printf("Usage: dbpf \"pv name\", \"value\"\n");
@@ -380,16 +382,25 @@ long dbpf(const char *pname,const char *pvalue)
return -1;
}
if (addr.no_elements > 1 &&
(addr.dbr_field_type == DBR_CHAR || addr.dbr_field_type == DBR_UCHAR)) {
if (addr.no_elements > 1) {
dbrType = addr.dbr_field_type;
n = strlen(pvalue) + 1;
if (addr.dbr_field_type == DBR_CHAR || addr.dbr_field_type == DBR_UCHAR) {
n = (long)strlen(pvalue) + 1;
} else {
n = addr.no_elements;
array = calloc(n, dbValueSize(dbrType));
if (!array) {
printf("Out of memory\n");
return -1;
}
status = dbPutConvertJSON(pvalue, dbrType, array, &n);
if (status)
return status;
pvalue = array;
}
}
else {
dbrType = DBR_STRING;
}
status = dbPutField(&addr, dbrType, pvalue, (long) n);
status = dbPutField(&addr, dbrType, pvalue, n);
free(array);
dbgf(pname);
return status;
}

View File

@@ -61,9 +61,10 @@ static long init_record(dbCommon *pcommon)
}
status = dbLoadLinkArray(plink, prec->ftvl, prec->bptr, &nRequest);
if (!status && nRequest > 0) {
if (!status) {
prec->nord = nRequest;
prec->udf = FALSE;
return status;
}
}
return 0;
@@ -75,7 +76,7 @@ static long readLocked(struct link *pinp, void *dummy)
long nRequest = prec->nelm;
long status = dbGetLink(pinp, prec->ftvl, prec->bptr, 0, &nRequest);
if (!status && nRequest > 0) {
if (!status) {
prec->nord = nRequest;
prec->udf = FALSE;
@@ -90,8 +91,12 @@ static long read_aai(aaiRecord *prec)
{
epicsUInt32 nord = prec->nord;
struct link *pinp = prec->simm == menuYesNoYES ? &prec->siol : &prec->inp;
long status = dbLinkDoLocked(pinp, readLocked, NULL);
long status;
if (dbLinkIsConstant(pinp))
return 0;
status = dbLinkDoLocked(pinp, readLocked, NULL);
if (status == S_db_noLSET)
status = readLocked(pinp, NULL);

View File

@@ -86,9 +86,10 @@ static long read_ai(aiRecord *prec)
prec->udf = FALSE;
prec->dpvt = &devAiSoft; /* Any non-zero value */
return 2;
}
else
prec->dpvt = NULL;
return 2;
return status;
}

View File

@@ -66,7 +66,7 @@ static long init_record(dbCommon *pcommon)
status = dbLoadLinkArray(&prec->inp, prec->ftvl, prec->bptr, &nRequest);
if (!status && nRequest > 0)
if (!status)
subset(prec, nRequest);
return status;
@@ -116,7 +116,7 @@ static long read_sa(subArrayRecord *prec)
status = readLocked(&prec->inp, &rt);
}
if (!status && rt.nRequest > 0) {
if (!status) {
subset(prec, rt.nRequest);
if (nord != prec->nord)

View File

@@ -42,7 +42,7 @@ static long init_record(dbCommon *pcommon)
long nelm = prec->nelm;
long status = dbLoadLinkArray(&prec->inp, prec->ftvl, prec->bptr, &nelm);
if (!status && nelm > 0) {
if (!status) {
prec->nord = nelm;
prec->udf = FALSE;
}
@@ -78,11 +78,14 @@ static long read_wf(waveformRecord *prec)
rt.ptime = (dbLinkIsConstant(&prec->tsel) &&
prec->tse == epicsTimeEventDeviceTime) ? &prec->time : NULL;
if (dbLinkIsConstant(&prec->inp))
return 0;
status = dbLinkDoLocked(&prec->inp, readLocked, &rt);
if (status == S_db_noLSET)
status = readLocked(&prec->inp, &rt);
if (!status && rt.nRequest > 0) {
if (!status) {
prec->nord = rt.nRequest;
prec->udf = FALSE;
if (nord != prec->nord)

View File

@@ -278,10 +278,9 @@ static long fetch_values(aSubRecord *prec)
long nRequest = (&prec->noa)[i];
status = dbGetLink(&(&prec->inpa)[i], (&prec->fta)[i], (&prec->a)[i], 0,
&nRequest);
if (nRequest > 0)
(&prec->nea)[i] = nRequest;
if (status)
return status;
(&prec->nea)[i] = nRequest;
}
return 0;
}

View File

@@ -133,7 +133,7 @@ void testCADisconn(void)
startRegressTestIoc("badCaLink.db");
testdbPutFieldOk("ai:disconn.PROC", DBF_LONG, 1);
testdbPutFieldFail(-1, "ai:disconn.PROC", DBF_LONG, 1);
testdbGetFieldEqual("ai:disconn.SEVR", DBF_LONG, INVALID_ALARM);
testdbGetFieldEqual("ai:disconn.STAT", DBF_LONG, LINK_ALARM);
}