Merge ~dirk.zimoch/epics-base:fix_zero_size_arrays into ~epics-core/epics-base/+git/epics-base:7.0

Proposed by Dirk Zimoch
Status: Merged
Approved by: Andrew Johnson
Approved revision: d1491e0860efc2ea3bd85a9f68cd83f18b575ae0
Merged at revision: d108a1ff113319aed89fd1c5ef7f397a82eca707
Proposed branch: ~dirk.zimoch/epics-base:fix_zero_size_arrays
Merge into: ~epics-core/epics-base/+git/epics-base:7.0
Diff against target: 349 lines (+102/-40)
12 files modified
documentation/RELEASE_NOTES.md (+36/-0)
modules/ca/src/client/db_access.h (+1/-1)
modules/ca/src/client/nciu.cpp (+2/-2)
modules/database/src/ioc/db/dbAccess.c (+16/-15)
modules/database/src/ioc/db/dbCa.c (+7/-1)
modules/database/src/ioc/db/dbTest.c (+21/-10)
modules/database/src/std/dev/devAaiSoft.c (+8/-3)
modules/database/src/std/dev/devAiSoft.c (+2/-1)
modules/database/src/std/dev/devSASoft.c (+2/-2)
modules/database/src/std/dev/devWfSoft.c (+5/-2)
modules/database/src/std/rec/aSubRecord.c (+1/-2)
modules/database/test/std/rec/regressTest.c (+1/-1)
Reviewer Review Type Date Requested Status
Andrew Johnson Approve
mdavidsaver Approve
Review via email: mp+386175@code.launchpad.net
To post a comment you must log in.
Revision history for this message
mdavidsaver (mdavidsaver) wrote :

Looks good.

wrt. dbpf. So this would look like:

> dbpf "some:pv" "1 2 3 4"

Did you consider switching dbpf() to use iocshArgArgv to capture the value element(s)?
This doesn't have to be the only argument. This seems more natural to me, and potentially
avoids some of the added string parsing code. I suppose this would be a disadvantage for
vxworks though?

Also, an example of this should be added to the help message for dbpf.
This is in dbIocRegister.c. I can do this after merge if needed.

review: Approve
Revision history for this message
Dirk Zimoch (dirk.zimoch) wrote :

Yes, 'dbpf some:pv 1 2 3 4' would look more natural. For VxWorks this would be limited to 9 array elements though. RTEMS? Let's see what I can do.

field (INP, "empty:array CP") does not give the desired result yet. I am still working on that. Maybe it will change the output of 'caget partially_filled:array" though (which would them become compatible with camonitor partially_filled:array). As I said, still working on it ...

Revision history for this message
Andrew Johnson (anj) wrote :

Speculating here I admit so this comment is probably going a bit far for this MR: I wonder how hard it would be to integrate yajl into iocsh and add an iocshArgJSON type? The yajl JSON-5 parser would make that slightly easier to use (no need to "quote":"keys").

I'm not too concerned about trying to keep compatibility with the VxWorks target shell, there are enough places where we already have to accept arguments as strings (for float and double values), and VxWorks users can and often do switch to using iocsh anyway.

However my point is this could change the dbpf of arrays to become 'dbpf some:pv [1,2,3,4]' which is in line with our use of JSON in the database files.

Revision history for this message
Andrew Johnson (anj) wrote :

On changing the semantics of passing pnRequest=NULL to dbGet(): I like this as a solution, it probably just clarifies existing practice but I haven't thought through if and how any other code might have to change (if at all). Presumably similar changes apply to the semantics of the dbGetField(), dbGetLink(), lset::getValue() etc. since they normally pass that argument through to dbGet().

Has anyone done a search through the epics-modules repo's on GitHub to see if there is any code there which might need changing? Hopefully there isn't any such code, but I would like to be proactive about that just in case. If there might need to be any changes to external code we should create a HAS_EMPTY_ARRAY_SUPPORT macro in dbAccessDefs.h to allow for easy compile-time detection.

Do recent versions of autosave properly save & restore an empty array field? There are other tools out there for doing the same thing, but I believe the synapps autosave module has the widest usage.

I see the change to modules/ca to allow the ca_array_put() API to write a 0-length array, do we need to make any changes to the caput program to allow that to be exercised from the command-line? That might be reasonable as a separate development for later, but we should support it.

Finally a minor code complaint: I don't like the use of the epicsOldString type. I know it's just a typedef but it isn't by accident that there is only one existing use of this data type in the IOC code and that's in newer code where sizeof(epicsOldString) should be replaced by MAX_STRING_SIZE anyway. The epicsTypes.h header even has a comment block above tye typedef which says "Dont use this - it may vanish in the future". I read that imperative as applying to the typedef and not so much to the MAX_STRING_SIZE macro (which would be much harder to get rid of) but there are several other ancient typedefs, enums and arrays in epicsTypes.h which should really get dumped.

Revision history for this message
Dirk Zimoch (dirk.zimoch) wrote :

I had checked EPICS base for uses of pnRequest and found no problems. All scalar records use NULL.

I have not checked autosave yet.

Caput „just works“ writing zero size arrays.

I willchange the code which uses epicsOldString and will use MAX_STRING_SIZE instead.

Revision history for this message
Dirk Zimoch (dirk.zimoch) wrote :

I have removed epicsOldString.

From looking at the code, I would say autosave saves as scalar if curr_elements<=1. This may even now produce problematic results and should be fixed. It better always saves arrays as arrays, even with 0 or 1 elements. I need to do some actual tests.

But first I would like to find out if I can do something about CA links.

Revision history for this message
Dirk Zimoch (dirk.zimoch) wrote :

CA links now work as well. It was not necessary to change anything in CA, thus caget did not change.

In dbCa.c dbCaGetLink() are several occations where sevr/stat is set to INVALID/LINK alarm in case of errors. I think that is not necessary. Returning -1 is sufficient. The calling function dbGetLink() already sets INVALID/LINK alarm on error.

Revision history for this message
Dirk Zimoch (dirk.zimoch) wrote :

Writing empty arrays to scalars via output links still needs to be fixed.

Revision history for this message
Dirk Zimoch (dirk.zimoch) wrote :

Finally put is fixed as well. I changed dbPut to set the target to INVALID/LINK if the target is a scalar and 0 elements are put. Only the target record goes to alarm state, not the sending record.
The target is then processed normally if the link is PP, as before only that before if was processed with no alarm and value 0.

If the target happens to be an array of max length 1, it is set to an empty array and not to alarm. For that I always use now the array implementation if the target is a SPC_DBADDR field with array_info(), even if the target has only 1 element. That allowed me to streamline the dbPut code a bit.

This change works for db links, CA links and as well for `caput -a 0` (empty array put) to a scalar field. Is it ok to call this case a "LINK_ALARM"? I cannot really distinguish between caput and any other put in dbPut. An alternative would be to check the target in the CA server and report an error to the caller when trying to put an empty array into a scalar. But that would probably affect CA links as well and generate an error in the sending record.

Revision history for this message
Dirk Zimoch (dirk.zimoch) wrote :

For the record: I consider this work complete. Any opinions on the final version?

Revision history for this message
Andrew Johnson (anj) wrote :

Thanks for working on this Dirk. The Core group will need some time to review it again, and I suspect we won't want to merge it before the 7.0.4.1 patch release, which will be coming out soon to fix some problems discovered in 7.0.4

Revision history for this message
Andrew Johnson (anj) wrote :

Some comments inline, not quite ready. We aren't 100% sure about the dbpf change but it's probably Okay. The code change in dbPut() needs fixing.

review: Needs Fixing
Revision history for this message
Andrew Johnson (anj) wrote :

The previous comment was from a Core Group review of this code, sorry for not making that clear.

Revision history for this message
Dirk Zimoch (dirk.zimoch) wrote :

Replies inline.

Revision history for this message
Dirk Zimoch (dirk.zimoch) wrote :

more inline comment.

Revision history for this message
Andrew Johnson (anj) wrote :

Replies inline.

Revision history for this message
Dirk Zimoch (dirk.zimoch) :
Revision history for this message
Andrew Johnson (anj) :
Revision history for this message
Dirk Zimoch (dirk.zimoch) wrote :

Ah, now I got it. I always mixed up get_array_info and put_array_info. I must have been blind.

Revision history for this message
Dirk Zimoch (dirk.zimoch) wrote :

epics> dbpf array_of_numbers "[1,2,3]"
DBF_DOUBLE[3]: 1 2 3
epics> dbpf string "[1,2,3]"
DBF_STRING: "[1,2,3]"
epics> dbpf array_of_strings "[1,2,3]"
DBF_STRING[3]: "1" "2" "3"
epics> dbpf array_of_strings '"[1,2,3]"'
DBF_STRING: "[1,2,3]"
epics> dbpf string "bla"
DBF_STRING: "bla"
epics> dbpf array_of_strings "bla"
dbConvertJSON: lexical error: invalid char in json text.
                                       bla
                     (right here) ------^
epics> dbpf array_of_strings '"bla"'
DBF_STRING: "bla"
epics> dbpf array_of_chars "bla"
DBF_CHAR[4]: "bla"
epics> dbpf array_of_chars "[0x41,0x42,0x43]"
DBF_CHAR[17]: "[0x41,0x42,0x43]"

Should I support
1) writing plain strings into arrays of string?
2) writing arrays of integers into arrays of char?

Revision history for this message
Andrew Johnson (anj) wrote :

I have developed some updates to this branch, for dbgf and the Release Notes, which I will apply after merging.

Looks good to me now.

review: Approve
Revision history for this message
Andrew Johnson (anj) wrote :
Download full text (5.9 KiB)

My changes:

commit 297f04bddc6765d7fc2b5941e16054b448fc994e
Author: Andrew Johnson <email address hidden>
Date: Fri Oct 30 13:37:50 2020 -0500

    Make dbgf display something for an empty array

    Also significantly expands on Dirk's Release Notes entries.

diff --git a/documentation/RELEASE_NOTES.md b/documentation/RELEASE_NOTES.md
index 0a54dc67f..f21fbdcec 100644
--- a/documentation/RELEASE_NOTES.md
+++ b/documentation/RELEASE_NOTES.md
@@ -17,42 +17,84 @@ should also be read to understand what has changed since earlier releases.

 <!-- Insert new items immediately below here ... -->

-### Support for empty arrays
+### Support for zero-length arrays

-Several problems with empty arrays have been fixed.
+Several modifications have been made to properly support zero-length
+array values inside the IOC and over Channel Access. Some of these changes
+may affect external code that interfaces with the IOC, either directly or
+over the CA client API so we recommend thorough testing of any external
+code that handles array fields when upgrading to this release.

-#### Changed dbr_size_n(TYPE,COUNT) macro
+Since these changes affect the Channel Access client-side API they will
+require rebuilding any CA Gateways against this version or Base to
+properly handle zero-length arrays. The `caget`, `caput` and `camonitor`
+client programs are known to work with empty arrays as long as they were
+built with this or a later version of EPICS.

-When called with COUNT=0 the macro no longer returns the number of bytes
+#### Change to the db_access.h `dbr_size_n(TYPE, COUNT)` macro
+
+When called with COUNT=0 this 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.
+Make sure code that uses this doesn't call it with COUNT=0 when it really
+means COUNT=1.
+
+Note that the db_access.h header file is included by cadef.h so the change
+can impact Channel Access client programs that use this macro.
+
+#### Channel Access support for zero-length arrays
+
+The `ca_array_put()` and `ca_array_put_callback()` routines now accept an
+element count of zero, and will write a zero-length array to the PV if
+possible. No error will be raised if the target is a scalar field though,
+and the field's value will not be changed.
+
+The `ca_array_get_callback()` and `ca_create_subscription()` routines
+still accept a count of zero to mean fetch as many elements as the PV
+currently holds.
+
+Client programs should be prepared for the `count` fields of any
+`struct event_handler_args` or `struct exception_handler_args` passed to
+their callback routines to be zero.

 #### 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.
+The soft device support for the array records aai, waveform, and subArray
+as well as the aSub record type now correctly report reading 0 elements
+when getting an empty array from an input link.

 #### Array support for dbpf

-The dbpf function now accepts arrays...

Read more...

Revision history for this message
Andrew Johnson (anj) wrote :

Core Group 11/4: Approved.

review: Approve
Revision history for this message
Dirk Zimoch (dirk.zimoch) wrote :

+The `ca_array_put()` and `ca_array_put_callback()` routines now accept an
+element count of zero, and will write a zero-length array to the PV if
+possible. No error will be raised if the target is a scalar field though,
+and the field's value will not be changed.

I thought I set the target scalar record to INVALID/LINK.

Revision history for this message
Andrew Johnson (anj) wrote :

That is possible with a DB link and that's probably what you're remembering, but not via the CA API. CA clients can't set record alarms, look at the code in dbChannel_put() in db_access.c which is what would do that.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
diff --git a/documentation/RELEASE_NOTES.md b/documentation/RELEASE_NOTES.md
index e78bb64..1b4e78d 100644
--- a/documentation/RELEASE_NOTES.md
+++ b/documentation/RELEASE_NOTES.md
@@ -17,6 +17,42 @@ should also be read to understand what has changed since earlier releases.
1717
18<!-- Insert new items immediately below here ... -->18<!-- Insert new items immediately below here ... -->
1919
20### Support for empty arrays
21
22Several problems with empty arrays have been fixed.
23
24#### Changed dbr_size_n(TYPE,COUNT) macro
25
26When called with COUNT=0 the macro no longer returns the number of bytes
27required for a scalar (1 element) but for an empty array (0 elements).
28Make sure you don't call it with COUNT=0 when you really mean COUNT=1.
29
30#### Array records
31
32The soft supports of array records aai, waveform, and subArray as well as
33the aSub record type have been fixed to correctly report 0 elements read
34when reading empty arrays from an input link.
35
36#### Array support for dbpf
37
38The dbpf function now accepts arrays, including empty arrays as a JSON string.
39
40#### Scalar records reading from empty arrays
41
42Records reading scalar fields from empty arrays are now set to INVALID/LINK
43alarm status.
44Links have to call dbGetLink with pnRequest=NULL to be recognized as requests
45for scalars.
46This changes the semantics of pnRequest=NULL. It is now different from
47requesting up to 1 array element, which may return a valid empty array.
48
49### Writing empty arrays to scalar records
50
51Witing an empty array to a scalar field now sets the target record to
52INVALID/LINK alarm but does not modify the value. Before, the value used
53to be set to 0 (without any alarm).
54A target field needs to have the SPC_DBADDR tag to be recognized as an array
55field and the record support must define a put_array_info method.
2056
21## EPICS Release 7.0.457## EPICS Release 7.0.4
2258
diff --git a/modules/ca/src/client/db_access.h b/modules/ca/src/client/db_access.h
index 810f82f..ad10a88 100644
--- a/modules/ca/src/client/db_access.h
+++ b/modules/ca/src/client/db_access.h
@@ -516,7 +516,7 @@ struct dbr_ctrl_double{
516};516};
517517
518#define dbr_size_n(TYPE,COUNT)\518#define dbr_size_n(TYPE,COUNT)\
519((unsigned)((COUNT)<=0?dbr_size[TYPE]:dbr_size[TYPE]+((COUNT)-1)*dbr_value_size[TYPE]))519((unsigned)((COUNT)<0?dbr_size[TYPE]:dbr_size[TYPE]+((COUNT)-1)*dbr_value_size[TYPE]))
520520
521/* size for each type - array indexed by the DBR_ type code */521/* size for each type - array indexed by the DBR_ type code */
522LIBCA_API extern const unsigned short dbr_size[];522LIBCA_API extern const unsigned short dbr_size[];
diff --git a/modules/ca/src/client/nciu.cpp b/modules/ca/src/client/nciu.cpp
index 0a7e708..ebf30fe 100644
--- a/modules/ca/src/client/nciu.cpp
+++ b/modules/ca/src/client/nciu.cpp
@@ -328,7 +328,7 @@ void nciu::write (
328 if ( ! this->accessRightState.writePermit() ) {328 if ( ! this->accessRightState.writePermit() ) {
329 throw cacChannel::noWriteAccess();329 throw cacChannel::noWriteAccess();
330 }330 }
331 if ( countIn > this->count || countIn == 0 ) {331 if ( countIn > this->count) {
332 throw cacChannel::outOfBounds();332 throw cacChannel::outOfBounds();
333 }333 }
334 if ( type == DBR_STRING ) {334 if ( type == DBR_STRING ) {
@@ -349,7 +349,7 @@ cacChannel::ioStatus nciu::write (
349 if ( ! this->accessRightState.writePermit() ) {349 if ( ! this->accessRightState.writePermit() ) {
350 throw cacChannel::noWriteAccess();350 throw cacChannel::noWriteAccess();
351 }351 }
352 if ( countIn > this->count || countIn == 0 ) {352 if ( countIn > this->count) {
353 throw cacChannel::outOfBounds();353 throw cacChannel::outOfBounds();
354 }354 }
355 if ( type == DBR_STRING ) {355 if ( type == DBR_STRING ) {
diff --git a/modules/database/src/ioc/db/dbAccess.c b/modules/database/src/ioc/db/dbAccess.c
index 9cda401..28273f6 100644
--- a/modules/database/src/ioc/db/dbAccess.c
+++ b/modules/database/src/ioc/db/dbAccess.c
@@ -945,6 +945,11 @@ long dbGet(DBADDR *paddr, short dbrType,
945 if (offset == 0 && (!nRequest || no_elements == 1)) {945 if (offset == 0 && (!nRequest || no_elements == 1)) {
946 if (nRequest)946 if (nRequest)
947 *nRequest = 1;947 *nRequest = 1;
948 else if (no_elements < 1) {
949 status = S_db_onlyOne;
950 goto done;
951 }
952
948 if (!pfl || pfl->type == dbfl_type_rec) {953 if (!pfl || pfl->type == dbfl_type_rec) {
949 status = dbFastGetConvertRoutine[field_type][dbrType]954 status = dbFastGetConvertRoutine[field_type][dbrType]
950 (paddr->pfield, pbuf, paddr);955 (paddr->pfield, pbuf, paddr);
@@ -1329,25 +1334,21 @@ long dbPut(DBADDR *paddr, short dbrType,
1329 status = prset->get_array_info(paddr, &dummy, &offset);1334 status = prset->get_array_info(paddr, &dummy, &offset);
1330 /* paddr->pfield may be modified */1335 /* paddr->pfield may be modified */
1331 if (status) goto done;1336 if (status) goto done;
1332 } else
1333 offset = 0;
1334
1335 if (no_elements <= 1) {
1336 status = dbFastPutConvertRoutine[dbrType][field_type](pbuffer,
1337 paddr->pfield, paddr);
1338 nRequest = 1;
1339 } else {
1340 if (no_elements < nRequest)1337 if (no_elements < nRequest)
1341 nRequest = no_elements;1338 nRequest = no_elements;
1342 status = dbPutConvertRoutine[dbrType][field_type](paddr, pbuffer,1339 status = dbPutConvertRoutine[dbrType][field_type](paddr, pbuffer,
1343 nRequest, no_elements, offset);1340 nRequest, no_elements, offset);
1344 }1341 /* update array info */
13451342 if (!status && prset->put_array_info)
1346 /* update array info */1343 status = prset->put_array_info(paddr, nRequest);
1347 if (!status &&1344 } else {
1348 paddr->pfldDes->special == SPC_DBADDR &&1345 if (nRequest < 1) {
1349 prset && prset->put_array_info) {1346 recGblSetSevr(precord, LINK_ALARM, INVALID_ALARM);
1350 status = prset->put_array_info(paddr, nRequest);1347 } else {
1348 status = dbFastPutConvertRoutine[dbrType][field_type](pbuffer,
1349 paddr->pfield, paddr);
1350 nRequest = 1;
1351 }
1351 }1352 }
13521353
1353 /* Always do special processing if needed */1354 /* Always do special processing if needed */
diff --git a/modules/database/src/ioc/db/dbCa.c b/modules/database/src/ioc/db/dbCa.c
index 4ae39bb..28e0df6 100644
--- a/modules/database/src/ioc/db/dbCa.c
+++ b/modules/database/src/ioc/db/dbCa.c
@@ -410,9 +410,15 @@ long dbCaGetLink(struct link *plink, short dbrType, void *pdest,
410 goto done;410 goto done;
411 }411 }
412 newType = dbDBRoldToDBFnew[pca->dbrType];412 newType = dbDBRoldToDBFnew[pca->dbrType];
413 if (!nelements || *nelements == 1) {413 if (!nelements) {
414 long (*fConvert)(const void *from, void *to, struct dbAddr *paddr);414 long (*fConvert)(const void *from, void *to, struct dbAddr *paddr);
415415
416 if (pca->usedelements < 1) {
417 pca->sevr = INVALID_ALARM;
418 pca->stat = LINK_ALARM;
419 status = -1;
420 goto done;
421 }
416 fConvert = dbFastGetConvertRoutine[newType][dbrType];422 fConvert = dbFastGetConvertRoutine[newType][dbrType];
417 assert(pca->pgetNative);423 assert(pca->pgetNative);
418 status = fConvert(pca->pgetNative, pdest, 0);424 status = fConvert(pca->pgetNative, pdest, 0);
diff --git a/modules/database/src/ioc/db/dbTest.c b/modules/database/src/ioc/db/dbTest.c
index 1193954..9368bfc 100644
--- a/modules/database/src/ioc/db/dbTest.c
+++ b/modules/database/src/ioc/db/dbTest.c
@@ -40,6 +40,7 @@
40#include "recGbl.h"40#include "recGbl.h"
41#include "recSup.h"41#include "recSup.h"
42#include "special.h"42#include "special.h"
43#include "dbConvertJSON.h"
4344
44#define MAXLINE 8045#define MAXLINE 80
45#define MAXMESS 12846#define MAXMESS 128
@@ -363,8 +364,9 @@ long dbpf(const char *pname,const char *pvalue)
363{364{
364 DBADDR addr;365 DBADDR addr;
365 long status;366 long status;
366 short dbrType;367 short dbrType = DBR_STRING;
367 size_t n = 1;368 long n = 1;
369 char *array = NULL;
368370
369 if (!pname || !*pname || !pvalue) {371 if (!pname || !*pname || !pvalue) {
370 printf("Usage: dbpf \"pv name\", \"value\"\n");372 printf("Usage: dbpf \"pv name\", \"value\"\n");
@@ -379,16 +381,25 @@ long dbpf(const char *pname,const char *pvalue)
379 return -1;381 return -1;
380 }382 }
381383
382 if (addr.no_elements > 1 &&384 if (addr.no_elements > 1) {
383 (addr.dbr_field_type == DBR_CHAR || addr.dbr_field_type == DBR_UCHAR)) {
384 dbrType = addr.dbr_field_type;385 dbrType = addr.dbr_field_type;
385 n = strlen(pvalue) + 1;386 if (addr.dbr_field_type == DBR_CHAR || addr.dbr_field_type == DBR_UCHAR) {
386 }387 n = (long)strlen(pvalue) + 1;
387 else {388 } else {
388 dbrType = DBR_STRING;389 n = addr.no_elements;
390 array = calloc(n, dbValueSize(dbrType));
391 if (!array) {
392 printf("Out of memory\n");
393 return -1;
394 }
395 status = dbPutConvertJSON(pvalue, dbrType, array, &n);
396 if (status)
397 return status;
398 pvalue = array;
399 }
389 }400 }
390401 status = dbPutField(&addr, dbrType, pvalue, n);
391 status = dbPutField(&addr, dbrType, pvalue, (long) n);402 free(array);
392 dbgf(pname);403 dbgf(pname);
393 return status;404 return status;
394}405}
diff --git a/modules/database/src/std/dev/devAaiSoft.c b/modules/database/src/std/dev/devAaiSoft.c
index 1f57656..0fbb114 100644
--- a/modules/database/src/std/dev/devAaiSoft.c
+++ b/modules/database/src/std/dev/devAaiSoft.c
@@ -60,9 +60,10 @@ static long init_record(dbCommon *pcommon)
60 }60 }
6161
62 status = dbLoadLinkArray(plink, prec->ftvl, prec->bptr, &nRequest);62 status = dbLoadLinkArray(plink, prec->ftvl, prec->bptr, &nRequest);
63 if (!status && nRequest > 0) {63 if (!status) {
64 prec->nord = nRequest;64 prec->nord = nRequest;
65 prec->udf = FALSE;65 prec->udf = FALSE;
66 return status;
66 }67 }
67 }68 }
68 return 0;69 return 0;
@@ -74,7 +75,7 @@ static long readLocked(struct link *pinp, void *dummy)
74 long nRequest = prec->nelm;75 long nRequest = prec->nelm;
75 long status = dbGetLink(pinp, prec->ftvl, prec->bptr, 0, &nRequest);76 long status = dbGetLink(pinp, prec->ftvl, prec->bptr, 0, &nRequest);
7677
77 if (!status && nRequest > 0) {78 if (!status) {
78 prec->nord = nRequest;79 prec->nord = nRequest;
79 prec->udf = FALSE;80 prec->udf = FALSE;
8081
@@ -89,8 +90,12 @@ static long read_aai(aaiRecord *prec)
89{90{
90 epicsUInt32 nord = prec->nord;91 epicsUInt32 nord = prec->nord;
91 struct link *pinp = prec->simm == menuYesNoYES ? &prec->siol : &prec->inp;92 struct link *pinp = prec->simm == menuYesNoYES ? &prec->siol : &prec->inp;
92 long status = dbLinkDoLocked(pinp, readLocked, NULL);93 long status;
9394
95 if (dbLinkIsConstant(pinp))
96 return 0;
97
98 status = dbLinkDoLocked(pinp, readLocked, NULL);
94 if (status == S_db_noLSET)99 if (status == S_db_noLSET)
95 status = readLocked(pinp, NULL);100 status = readLocked(pinp, NULL);
96101
diff --git a/modules/database/src/std/dev/devAiSoft.c b/modules/database/src/std/dev/devAiSoft.c
index e41d9fe..a9b32a3 100644
--- a/modules/database/src/std/dev/devAiSoft.c
+++ b/modules/database/src/std/dev/devAiSoft.c
@@ -85,9 +85,10 @@ static long read_ai(aiRecord *prec)
8585
86 prec->udf = FALSE;86 prec->udf = FALSE;
87 prec->dpvt = &devAiSoft; /* Any non-zero value */87 prec->dpvt = &devAiSoft; /* Any non-zero value */
88 return 2;
88 }89 }
89 else90 else
90 prec->dpvt = NULL;91 prec->dpvt = NULL;
9192
92 return 2;93 return status;
93}94}
diff --git a/modules/database/src/std/dev/devSASoft.c b/modules/database/src/std/dev/devSASoft.c
index be32af4..8db192f 100644
--- a/modules/database/src/std/dev/devSASoft.c
+++ b/modules/database/src/std/dev/devSASoft.c
@@ -65,7 +65,7 @@ static long init_record(dbCommon *pcommon)
6565
66 status = dbLoadLinkArray(&prec->inp, prec->ftvl, prec->bptr, &nRequest);66 status = dbLoadLinkArray(&prec->inp, prec->ftvl, prec->bptr, &nRequest);
6767
68 if (!status && nRequest > 0)68 if (!status)
69 subset(prec, nRequest);69 subset(prec, nRequest);
7070
71 return status;71 return status;
@@ -115,7 +115,7 @@ static long read_sa(subArrayRecord *prec)
115 status = readLocked(&prec->inp, &rt);115 status = readLocked(&prec->inp, &rt);
116 }116 }
117117
118 if (!status && rt.nRequest > 0) {118 if (!status) {
119 subset(prec, rt.nRequest);119 subset(prec, rt.nRequest);
120120
121 if (nord != prec->nord)121 if (nord != prec->nord)
diff --git a/modules/database/src/std/dev/devWfSoft.c b/modules/database/src/std/dev/devWfSoft.c
index 0a089b8..29a617b 100644
--- a/modules/database/src/std/dev/devWfSoft.c
+++ b/modules/database/src/std/dev/devWfSoft.c
@@ -41,7 +41,7 @@ static long init_record(dbCommon *pcommon)
41 long nelm = prec->nelm;41 long nelm = prec->nelm;
42 long status = dbLoadLinkArray(&prec->inp, prec->ftvl, prec->bptr, &nelm);42 long status = dbLoadLinkArray(&prec->inp, prec->ftvl, prec->bptr, &nelm);
4343
44 if (!status && nelm > 0) {44 if (!status) {
45 prec->nord = nelm;45 prec->nord = nelm;
46 prec->udf = FALSE;46 prec->udf = FALSE;
47 }47 }
@@ -77,11 +77,14 @@ static long read_wf(waveformRecord *prec)
77 rt.ptime = (dbLinkIsConstant(&prec->tsel) &&77 rt.ptime = (dbLinkIsConstant(&prec->tsel) &&
78 prec->tse == epicsTimeEventDeviceTime) ? &prec->time : NULL;78 prec->tse == epicsTimeEventDeviceTime) ? &prec->time : NULL;
7979
80 if (dbLinkIsConstant(&prec->inp))
81 return 0;
82
80 status = dbLinkDoLocked(&prec->inp, readLocked, &rt);83 status = dbLinkDoLocked(&prec->inp, readLocked, &rt);
81 if (status == S_db_noLSET)84 if (status == S_db_noLSET)
82 status = readLocked(&prec->inp, &rt);85 status = readLocked(&prec->inp, &rt);
8386
84 if (!status && rt.nRequest > 0) {87 if (!status) {
85 prec->nord = rt.nRequest;88 prec->nord = rt.nRequest;
86 prec->udf = FALSE;89 prec->udf = FALSE;
87 if (nord != prec->nord)90 if (nord != prec->nord)
diff --git a/modules/database/src/std/rec/aSubRecord.c b/modules/database/src/std/rec/aSubRecord.c
index 666558b..1bcbb63 100644
--- a/modules/database/src/std/rec/aSubRecord.c
+++ b/modules/database/src/std/rec/aSubRecord.c
@@ -277,10 +277,9 @@ static long fetch_values(aSubRecord *prec)
277 long nRequest = (&prec->noa)[i];277 long nRequest = (&prec->noa)[i];
278 status = dbGetLink(&(&prec->inpa)[i], (&prec->fta)[i], (&prec->a)[i], 0,278 status = dbGetLink(&(&prec->inpa)[i], (&prec->fta)[i], (&prec->a)[i], 0,
279 &nRequest);279 &nRequest);
280 if (nRequest > 0)
281 (&prec->nea)[i] = nRequest;
282 if (status)280 if (status)
283 return status;281 return status;
282 (&prec->nea)[i] = nRequest;
284 }283 }
285 return 0;284 return 0;
286}285}
diff --git a/modules/database/test/std/rec/regressTest.c b/modules/database/test/std/rec/regressTest.c
index 6614639..fd4f0a8 100644
--- a/modules/database/test/std/rec/regressTest.c
+++ b/modules/database/test/std/rec/regressTest.c
@@ -132,7 +132,7 @@ void testCADisconn(void)
132132
133 startRegressTestIoc("badCaLink.db");133 startRegressTestIoc("badCaLink.db");
134134
135 testdbPutFieldOk("ai:disconn.PROC", DBF_LONG, 1);135 testdbPutFieldFail(-1, "ai:disconn.PROC", DBF_LONG, 1);
136 testdbGetFieldEqual("ai:disconn.SEVR", DBF_LONG, INVALID_ALARM);136 testdbGetFieldEqual("ai:disconn.SEVR", DBF_LONG, INVALID_ALARM);
137 testdbGetFieldEqual("ai:disconn.STAT", DBF_LONG, LINK_ALARM);137 testdbGetFieldEqual("ai:disconn.STAT", DBF_LONG, LINK_ALARM);
138}138}

Subscribers

People subscribed via source and target branches