Merge Dirk Zimoch's fix_zero_size_arrays branch
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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[];
|
||||
|
||||
@@ -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 ) {
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user