Merge ~dirk.zimoch/epics-base:fix_zero_size_arrays into ~epics-core/epics-base/+git/epics-base:7.0
- Git
- lp:~dirk.zimoch/epics-base
- fix_zero_size_arrays
- Merge into 7.0
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) |
||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Andrew Johnson | Approve | ||
mdavidsaver | Approve | ||
Review via email: mp+386175@code.launchpad.net |
Commit message
Description of the change
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_
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.
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_
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(
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.
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.
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.
Dirk Zimoch (dirk.zimoch) wrote : | # |
Writing empty arrays to scalars via output links still needs to be fixed.
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.
Dirk Zimoch (dirk.zimoch) wrote : | # |
For the record: I consider this work complete. Any opinions on the final version?
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
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.
Andrew Johnson (anj) wrote : | # |
The previous comment was from a Core Group review of this code, sorry for not making that clear.
Dirk Zimoch (dirk.zimoch) wrote : | # |
Replies inline.
Dirk Zimoch (dirk.zimoch) wrote : | # |
more inline comment.
Andrew Johnson (anj) wrote : | # |
Replies inline.
Dirk Zimoch (dirk.zimoch) : | # |
Andrew Johnson (anj) : | # |
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.
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.
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?
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.
Andrew Johnson (anj) wrote : | # |
My changes:
commit 297f04bddc6765d
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
index 0a54dc67f.
--- a/documentation
+++ b/documentation
@@ -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_
+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_
+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_
+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_
+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...
Andrew Johnson (anj) wrote : | # |
Core Group 11/4: Approved.
Dirk Zimoch (dirk.zimoch) wrote : | # |
+The `ca_array_put()` and `ca_array_
+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.
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
1 | diff --git a/documentation/RELEASE_NOTES.md b/documentation/RELEASE_NOTES.md | |||
2 | index e78bb64..1b4e78d 100644 | |||
3 | --- a/documentation/RELEASE_NOTES.md | |||
4 | +++ b/documentation/RELEASE_NOTES.md | |||
5 | @@ -17,6 +17,42 @@ should also be read to understand what has changed since earlier releases. | |||
6 | 17 | 17 | ||
7 | 18 | <!-- Insert new items immediately below here ... --> | 18 | <!-- Insert new items immediately below here ... --> |
8 | 19 | 19 | ||
9 | 20 | ### Support for empty arrays | ||
10 | 21 | |||
11 | 22 | Several problems with empty arrays have been fixed. | ||
12 | 23 | |||
13 | 24 | #### Changed dbr_size_n(TYPE,COUNT) macro | ||
14 | 25 | |||
15 | 26 | When called with COUNT=0 the macro no longer returns the number of bytes | ||
16 | 27 | required for a scalar (1 element) but for an empty array (0 elements). | ||
17 | 28 | Make sure you don't call it with COUNT=0 when you really mean COUNT=1. | ||
18 | 29 | |||
19 | 30 | #### Array records | ||
20 | 31 | |||
21 | 32 | The soft supports of array records aai, waveform, and subArray as well as | ||
22 | 33 | the aSub record type have been fixed to correctly report 0 elements read | ||
23 | 34 | when reading empty arrays from an input link. | ||
24 | 35 | |||
25 | 36 | #### Array support for dbpf | ||
26 | 37 | |||
27 | 38 | The dbpf function now accepts arrays, including empty arrays as a JSON string. | ||
28 | 39 | |||
29 | 40 | #### Scalar records reading from empty arrays | ||
30 | 41 | |||
31 | 42 | Records reading scalar fields from empty arrays are now set to INVALID/LINK | ||
32 | 43 | alarm status. | ||
33 | 44 | Links have to call dbGetLink with pnRequest=NULL to be recognized as requests | ||
34 | 45 | for scalars. | ||
35 | 46 | This changes the semantics of pnRequest=NULL. It is now different from | ||
36 | 47 | requesting up to 1 array element, which may return a valid empty array. | ||
37 | 48 | |||
38 | 49 | ### Writing empty arrays to scalar records | ||
39 | 50 | |||
40 | 51 | Witing an empty array to a scalar field now sets the target record to | ||
41 | 52 | INVALID/LINK alarm but does not modify the value. Before, the value used | ||
42 | 53 | to be set to 0 (without any alarm). | ||
43 | 54 | A target field needs to have the SPC_DBADDR tag to be recognized as an array | ||
44 | 55 | field and the record support must define a put_array_info method. | ||
45 | 20 | 56 | ||
46 | 21 | ## EPICS Release 7.0.4 | 57 | ## EPICS Release 7.0.4 |
47 | 22 | 58 | ||
48 | diff --git a/modules/ca/src/client/db_access.h b/modules/ca/src/client/db_access.h | |||
49 | index 810f82f..ad10a88 100644 | |||
50 | --- a/modules/ca/src/client/db_access.h | |||
51 | +++ b/modules/ca/src/client/db_access.h | |||
52 | @@ -516,7 +516,7 @@ struct dbr_ctrl_double{ | |||
53 | 516 | }; | 516 | }; |
54 | 517 | 517 | ||
55 | 518 | #define dbr_size_n(TYPE,COUNT)\ | 518 | #define dbr_size_n(TYPE,COUNT)\ |
57 | 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])) |
58 | 520 | 520 | ||
59 | 521 | /* size for each type - array indexed by the DBR_ type code */ | 521 | /* size for each type - array indexed by the DBR_ type code */ |
60 | 522 | LIBCA_API extern const unsigned short dbr_size[]; | 522 | LIBCA_API extern const unsigned short dbr_size[]; |
61 | diff --git a/modules/ca/src/client/nciu.cpp b/modules/ca/src/client/nciu.cpp | |||
62 | index 0a7e708..ebf30fe 100644 | |||
63 | --- a/modules/ca/src/client/nciu.cpp | |||
64 | +++ b/modules/ca/src/client/nciu.cpp | |||
65 | @@ -328,7 +328,7 @@ void nciu::write ( | |||
66 | 328 | if ( ! this->accessRightState.writePermit() ) { | 328 | if ( ! this->accessRightState.writePermit() ) { |
67 | 329 | throw cacChannel::noWriteAccess(); | 329 | throw cacChannel::noWriteAccess(); |
68 | 330 | } | 330 | } |
70 | 331 | if ( countIn > this->count || countIn == 0 ) { | 331 | if ( countIn > this->count) { |
71 | 332 | throw cacChannel::outOfBounds(); | 332 | throw cacChannel::outOfBounds(); |
72 | 333 | } | 333 | } |
73 | 334 | if ( type == DBR_STRING ) { | 334 | if ( type == DBR_STRING ) { |
74 | @@ -349,7 +349,7 @@ cacChannel::ioStatus nciu::write ( | |||
75 | 349 | if ( ! this->accessRightState.writePermit() ) { | 349 | if ( ! this->accessRightState.writePermit() ) { |
76 | 350 | throw cacChannel::noWriteAccess(); | 350 | throw cacChannel::noWriteAccess(); |
77 | 351 | } | 351 | } |
79 | 352 | if ( countIn > this->count || countIn == 0 ) { | 352 | if ( countIn > this->count) { |
80 | 353 | throw cacChannel::outOfBounds(); | 353 | throw cacChannel::outOfBounds(); |
81 | 354 | } | 354 | } |
82 | 355 | if ( type == DBR_STRING ) { | 355 | if ( type == DBR_STRING ) { |
83 | diff --git a/modules/database/src/ioc/db/dbAccess.c b/modules/database/src/ioc/db/dbAccess.c | |||
84 | index 9cda401..28273f6 100644 | |||
85 | --- a/modules/database/src/ioc/db/dbAccess.c | |||
86 | +++ b/modules/database/src/ioc/db/dbAccess.c | |||
87 | @@ -945,6 +945,11 @@ long dbGet(DBADDR *paddr, short dbrType, | |||
88 | 945 | if (offset == 0 && (!nRequest || no_elements == 1)) { | 945 | if (offset == 0 && (!nRequest || no_elements == 1)) { |
89 | 946 | if (nRequest) | 946 | if (nRequest) |
90 | 947 | *nRequest = 1; | 947 | *nRequest = 1; |
91 | 948 | else if (no_elements < 1) { | ||
92 | 949 | status = S_db_onlyOne; | ||
93 | 950 | goto done; | ||
94 | 951 | } | ||
95 | 952 | |||
96 | 948 | if (!pfl || pfl->type == dbfl_type_rec) { | 953 | if (!pfl || pfl->type == dbfl_type_rec) { |
97 | 949 | status = dbFastGetConvertRoutine[field_type][dbrType] | 954 | status = dbFastGetConvertRoutine[field_type][dbrType] |
98 | 950 | (paddr->pfield, pbuf, paddr); | 955 | (paddr->pfield, pbuf, paddr); |
99 | @@ -1329,25 +1334,21 @@ long dbPut(DBADDR *paddr, short dbrType, | |||
100 | 1329 | status = prset->get_array_info(paddr, &dummy, &offset); | 1334 | status = prset->get_array_info(paddr, &dummy, &offset); |
101 | 1330 | /* paddr->pfield may be modified */ | 1335 | /* paddr->pfield may be modified */ |
102 | 1331 | if (status) goto done; | 1336 | if (status) goto done; |
103 | 1332 | } else | ||
104 | 1333 | offset = 0; | ||
105 | 1334 | |||
106 | 1335 | if (no_elements <= 1) { | ||
107 | 1336 | status = dbFastPutConvertRoutine[dbrType][field_type](pbuffer, | ||
108 | 1337 | paddr->pfield, paddr); | ||
109 | 1338 | nRequest = 1; | ||
110 | 1339 | } else { | ||
111 | 1340 | if (no_elements < nRequest) | 1337 | if (no_elements < nRequest) |
112 | 1341 | nRequest = no_elements; | 1338 | nRequest = no_elements; |
113 | 1342 | status = dbPutConvertRoutine[dbrType][field_type](paddr, pbuffer, | 1339 | status = dbPutConvertRoutine[dbrType][field_type](paddr, pbuffer, |
114 | 1343 | nRequest, no_elements, offset); | 1340 | nRequest, no_elements, offset); |
122 | 1344 | } | 1341 | /* update array info */ |
123 | 1345 | 1342 | if (!status && prset->put_array_info) | |
124 | 1346 | /* update array info */ | 1343 | status = prset->put_array_info(paddr, nRequest); |
125 | 1347 | if (!status && | 1344 | } else { |
126 | 1348 | paddr->pfldDes->special == SPC_DBADDR && | 1345 | if (nRequest < 1) { |
127 | 1349 | prset && prset->put_array_info) { | 1346 | recGblSetSevr(precord, LINK_ALARM, INVALID_ALARM); |
128 | 1350 | status = prset->put_array_info(paddr, nRequest); | 1347 | } else { |
129 | 1348 | status = dbFastPutConvertRoutine[dbrType][field_type](pbuffer, | ||
130 | 1349 | paddr->pfield, paddr); | ||
131 | 1350 | nRequest = 1; | ||
132 | 1351 | } | ||
133 | 1351 | } | 1352 | } |
134 | 1352 | 1353 | ||
135 | 1353 | /* Always do special processing if needed */ | 1354 | /* Always do special processing if needed */ |
136 | diff --git a/modules/database/src/ioc/db/dbCa.c b/modules/database/src/ioc/db/dbCa.c | |||
137 | index 4ae39bb..28e0df6 100644 | |||
138 | --- a/modules/database/src/ioc/db/dbCa.c | |||
139 | +++ b/modules/database/src/ioc/db/dbCa.c | |||
140 | @@ -410,9 +410,15 @@ long dbCaGetLink(struct link *plink, short dbrType, void *pdest, | |||
141 | 410 | goto done; | 410 | goto done; |
142 | 411 | } | 411 | } |
143 | 412 | newType = dbDBRoldToDBFnew[pca->dbrType]; | 412 | newType = dbDBRoldToDBFnew[pca->dbrType]; |
145 | 413 | if (!nelements || *nelements == 1) { | 413 | if (!nelements) { |
146 | 414 | long (*fConvert)(const void *from, void *to, struct dbAddr *paddr); | 414 | long (*fConvert)(const void *from, void *to, struct dbAddr *paddr); |
147 | 415 | 415 | ||
148 | 416 | if (pca->usedelements < 1) { | ||
149 | 417 | pca->sevr = INVALID_ALARM; | ||
150 | 418 | pca->stat = LINK_ALARM; | ||
151 | 419 | status = -1; | ||
152 | 420 | goto done; | ||
153 | 421 | } | ||
154 | 416 | fConvert = dbFastGetConvertRoutine[newType][dbrType]; | 422 | fConvert = dbFastGetConvertRoutine[newType][dbrType]; |
155 | 417 | assert(pca->pgetNative); | 423 | assert(pca->pgetNative); |
156 | 418 | status = fConvert(pca->pgetNative, pdest, 0); | 424 | status = fConvert(pca->pgetNative, pdest, 0); |
157 | diff --git a/modules/database/src/ioc/db/dbTest.c b/modules/database/src/ioc/db/dbTest.c | |||
158 | index 1193954..9368bfc 100644 | |||
159 | --- a/modules/database/src/ioc/db/dbTest.c | |||
160 | +++ b/modules/database/src/ioc/db/dbTest.c | |||
161 | @@ -40,6 +40,7 @@ | |||
162 | 40 | #include "recGbl.h" | 40 | #include "recGbl.h" |
163 | 41 | #include "recSup.h" | 41 | #include "recSup.h" |
164 | 42 | #include "special.h" | 42 | #include "special.h" |
165 | 43 | #include "dbConvertJSON.h" | ||
166 | 43 | 44 | ||
167 | 44 | #define MAXLINE 80 | 45 | #define MAXLINE 80 |
168 | 45 | #define MAXMESS 128 | 46 | #define MAXMESS 128 |
169 | @@ -363,8 +364,9 @@ long dbpf(const char *pname,const char *pvalue) | |||
170 | 363 | { | 364 | { |
171 | 364 | DBADDR addr; | 365 | DBADDR addr; |
172 | 365 | long status; | 366 | long status; |
175 | 366 | short dbrType; | 367 | short dbrType = DBR_STRING; |
176 | 367 | size_t n = 1; | 368 | long n = 1; |
177 | 369 | char *array = NULL; | ||
178 | 368 | 370 | ||
179 | 369 | if (!pname || !*pname || !pvalue) { | 371 | if (!pname || !*pname || !pvalue) { |
180 | 370 | printf("Usage: dbpf \"pv name\", \"value\"\n"); | 372 | printf("Usage: dbpf \"pv name\", \"value\"\n"); |
181 | @@ -379,16 +381,25 @@ long dbpf(const char *pname,const char *pvalue) | |||
182 | 379 | return -1; | 381 | return -1; |
183 | 380 | } | 382 | } |
184 | 381 | 383 | ||
187 | 382 | if (addr.no_elements > 1 && | 384 | if (addr.no_elements > 1) { |
186 | 383 | (addr.dbr_field_type == DBR_CHAR || addr.dbr_field_type == DBR_UCHAR)) { | ||
188 | 384 | dbrType = addr.dbr_field_type; | 385 | dbrType = addr.dbr_field_type; |
193 | 385 | n = strlen(pvalue) + 1; | 386 | if (addr.dbr_field_type == DBR_CHAR || addr.dbr_field_type == DBR_UCHAR) { |
194 | 386 | } | 387 | n = (long)strlen(pvalue) + 1; |
195 | 387 | else { | 388 | } else { |
196 | 388 | dbrType = DBR_STRING; | 389 | n = addr.no_elements; |
197 | 390 | array = calloc(n, dbValueSize(dbrType)); | ||
198 | 391 | if (!array) { | ||
199 | 392 | printf("Out of memory\n"); | ||
200 | 393 | return -1; | ||
201 | 394 | } | ||
202 | 395 | status = dbPutConvertJSON(pvalue, dbrType, array, &n); | ||
203 | 396 | if (status) | ||
204 | 397 | return status; | ||
205 | 398 | pvalue = array; | ||
206 | 399 | } | ||
207 | 389 | } | 400 | } |
210 | 390 | 401 | status = dbPutField(&addr, dbrType, pvalue, n); | |
211 | 391 | status = dbPutField(&addr, dbrType, pvalue, (long) n); | 402 | free(array); |
212 | 392 | dbgf(pname); | 403 | dbgf(pname); |
213 | 393 | return status; | 404 | return status; |
214 | 394 | } | 405 | } |
215 | diff --git a/modules/database/src/std/dev/devAaiSoft.c b/modules/database/src/std/dev/devAaiSoft.c | |||
216 | index 1f57656..0fbb114 100644 | |||
217 | --- a/modules/database/src/std/dev/devAaiSoft.c | |||
218 | +++ b/modules/database/src/std/dev/devAaiSoft.c | |||
219 | @@ -60,9 +60,10 @@ static long init_record(dbCommon *pcommon) | |||
220 | 60 | } | 60 | } |
221 | 61 | 61 | ||
222 | 62 | status = dbLoadLinkArray(plink, prec->ftvl, prec->bptr, &nRequest); | 62 | status = dbLoadLinkArray(plink, prec->ftvl, prec->bptr, &nRequest); |
224 | 63 | if (!status && nRequest > 0) { | 63 | if (!status) { |
225 | 64 | prec->nord = nRequest; | 64 | prec->nord = nRequest; |
226 | 65 | prec->udf = FALSE; | 65 | prec->udf = FALSE; |
227 | 66 | return status; | ||
228 | 66 | } | 67 | } |
229 | 67 | } | 68 | } |
230 | 68 | return 0; | 69 | return 0; |
231 | @@ -74,7 +75,7 @@ static long readLocked(struct link *pinp, void *dummy) | |||
232 | 74 | long nRequest = prec->nelm; | 75 | long nRequest = prec->nelm; |
233 | 75 | long status = dbGetLink(pinp, prec->ftvl, prec->bptr, 0, &nRequest); | 76 | long status = dbGetLink(pinp, prec->ftvl, prec->bptr, 0, &nRequest); |
234 | 76 | 77 | ||
236 | 77 | if (!status && nRequest > 0) { | 78 | if (!status) { |
237 | 78 | prec->nord = nRequest; | 79 | prec->nord = nRequest; |
238 | 79 | prec->udf = FALSE; | 80 | prec->udf = FALSE; |
239 | 80 | 81 | ||
240 | @@ -89,8 +90,12 @@ static long read_aai(aaiRecord *prec) | |||
241 | 89 | { | 90 | { |
242 | 90 | epicsUInt32 nord = prec->nord; | 91 | epicsUInt32 nord = prec->nord; |
243 | 91 | struct link *pinp = prec->simm == menuYesNoYES ? &prec->siol : &prec->inp; | 92 | struct link *pinp = prec->simm == menuYesNoYES ? &prec->siol : &prec->inp; |
245 | 92 | long status = dbLinkDoLocked(pinp, readLocked, NULL); | 93 | long status; |
246 | 93 | 94 | ||
247 | 95 | if (dbLinkIsConstant(pinp)) | ||
248 | 96 | return 0; | ||
249 | 97 | |||
250 | 98 | status = dbLinkDoLocked(pinp, readLocked, NULL); | ||
251 | 94 | if (status == S_db_noLSET) | 99 | if (status == S_db_noLSET) |
252 | 95 | status = readLocked(pinp, NULL); | 100 | status = readLocked(pinp, NULL); |
253 | 96 | 101 | ||
254 | diff --git a/modules/database/src/std/dev/devAiSoft.c b/modules/database/src/std/dev/devAiSoft.c | |||
255 | index e41d9fe..a9b32a3 100644 | |||
256 | --- a/modules/database/src/std/dev/devAiSoft.c | |||
257 | +++ b/modules/database/src/std/dev/devAiSoft.c | |||
258 | @@ -85,9 +85,10 @@ static long read_ai(aiRecord *prec) | |||
259 | 85 | 85 | ||
260 | 86 | prec->udf = FALSE; | 86 | prec->udf = FALSE; |
261 | 87 | prec->dpvt = &devAiSoft; /* Any non-zero value */ | 87 | prec->dpvt = &devAiSoft; /* Any non-zero value */ |
262 | 88 | return 2; | ||
263 | 88 | } | 89 | } |
264 | 89 | else | 90 | else |
265 | 90 | prec->dpvt = NULL; | 91 | prec->dpvt = NULL; |
266 | 91 | 92 | ||
268 | 92 | return 2; | 93 | return status; |
269 | 93 | } | 94 | } |
270 | diff --git a/modules/database/src/std/dev/devSASoft.c b/modules/database/src/std/dev/devSASoft.c | |||
271 | index be32af4..8db192f 100644 | |||
272 | --- a/modules/database/src/std/dev/devSASoft.c | |||
273 | +++ b/modules/database/src/std/dev/devSASoft.c | |||
274 | @@ -65,7 +65,7 @@ static long init_record(dbCommon *pcommon) | |||
275 | 65 | 65 | ||
276 | 66 | status = dbLoadLinkArray(&prec->inp, prec->ftvl, prec->bptr, &nRequest); | 66 | status = dbLoadLinkArray(&prec->inp, prec->ftvl, prec->bptr, &nRequest); |
277 | 67 | 67 | ||
279 | 68 | if (!status && nRequest > 0) | 68 | if (!status) |
280 | 69 | subset(prec, nRequest); | 69 | subset(prec, nRequest); |
281 | 70 | 70 | ||
282 | 71 | return status; | 71 | return status; |
283 | @@ -115,7 +115,7 @@ static long read_sa(subArrayRecord *prec) | |||
284 | 115 | status = readLocked(&prec->inp, &rt); | 115 | status = readLocked(&prec->inp, &rt); |
285 | 116 | } | 116 | } |
286 | 117 | 117 | ||
288 | 118 | if (!status && rt.nRequest > 0) { | 118 | if (!status) { |
289 | 119 | subset(prec, rt.nRequest); | 119 | subset(prec, rt.nRequest); |
290 | 120 | 120 | ||
291 | 121 | if (nord != prec->nord) | 121 | if (nord != prec->nord) |
292 | diff --git a/modules/database/src/std/dev/devWfSoft.c b/modules/database/src/std/dev/devWfSoft.c | |||
293 | index 0a089b8..29a617b 100644 | |||
294 | --- a/modules/database/src/std/dev/devWfSoft.c | |||
295 | +++ b/modules/database/src/std/dev/devWfSoft.c | |||
296 | @@ -41,7 +41,7 @@ static long init_record(dbCommon *pcommon) | |||
297 | 41 | long nelm = prec->nelm; | 41 | long nelm = prec->nelm; |
298 | 42 | long status = dbLoadLinkArray(&prec->inp, prec->ftvl, prec->bptr, &nelm); | 42 | long status = dbLoadLinkArray(&prec->inp, prec->ftvl, prec->bptr, &nelm); |
299 | 43 | 43 | ||
301 | 44 | if (!status && nelm > 0) { | 44 | if (!status) { |
302 | 45 | prec->nord = nelm; | 45 | prec->nord = nelm; |
303 | 46 | prec->udf = FALSE; | 46 | prec->udf = FALSE; |
304 | 47 | } | 47 | } |
305 | @@ -77,11 +77,14 @@ static long read_wf(waveformRecord *prec) | |||
306 | 77 | rt.ptime = (dbLinkIsConstant(&prec->tsel) && | 77 | rt.ptime = (dbLinkIsConstant(&prec->tsel) && |
307 | 78 | prec->tse == epicsTimeEventDeviceTime) ? &prec->time : NULL; | 78 | prec->tse == epicsTimeEventDeviceTime) ? &prec->time : NULL; |
308 | 79 | 79 | ||
309 | 80 | if (dbLinkIsConstant(&prec->inp)) | ||
310 | 81 | return 0; | ||
311 | 82 | |||
312 | 80 | status = dbLinkDoLocked(&prec->inp, readLocked, &rt); | 83 | status = dbLinkDoLocked(&prec->inp, readLocked, &rt); |
313 | 81 | if (status == S_db_noLSET) | 84 | if (status == S_db_noLSET) |
314 | 82 | status = readLocked(&prec->inp, &rt); | 85 | status = readLocked(&prec->inp, &rt); |
315 | 83 | 86 | ||
317 | 84 | if (!status && rt.nRequest > 0) { | 87 | if (!status) { |
318 | 85 | prec->nord = rt.nRequest; | 88 | prec->nord = rt.nRequest; |
319 | 86 | prec->udf = FALSE; | 89 | prec->udf = FALSE; |
320 | 87 | if (nord != prec->nord) | 90 | if (nord != prec->nord) |
321 | diff --git a/modules/database/src/std/rec/aSubRecord.c b/modules/database/src/std/rec/aSubRecord.c | |||
322 | index 666558b..1bcbb63 100644 | |||
323 | --- a/modules/database/src/std/rec/aSubRecord.c | |||
324 | +++ b/modules/database/src/std/rec/aSubRecord.c | |||
325 | @@ -277,10 +277,9 @@ static long fetch_values(aSubRecord *prec) | |||
326 | 277 | long nRequest = (&prec->noa)[i]; | 277 | long nRequest = (&prec->noa)[i]; |
327 | 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, |
328 | 279 | &nRequest); | 279 | &nRequest); |
329 | 280 | if (nRequest > 0) | ||
330 | 281 | (&prec->nea)[i] = nRequest; | ||
331 | 282 | if (status) | 280 | if (status) |
332 | 283 | return status; | 281 | return status; |
333 | 282 | (&prec->nea)[i] = nRequest; | ||
334 | 284 | } | 283 | } |
335 | 285 | return 0; | 284 | return 0; |
336 | 286 | } | 285 | } |
337 | diff --git a/modules/database/test/std/rec/regressTest.c b/modules/database/test/std/rec/regressTest.c | |||
338 | index 6614639..fd4f0a8 100644 | |||
339 | --- a/modules/database/test/std/rec/regressTest.c | |||
340 | +++ b/modules/database/test/std/rec/regressTest.c | |||
341 | @@ -132,7 +132,7 @@ void testCADisconn(void) | |||
342 | 132 | 132 | ||
343 | 133 | startRegressTestIoc("badCaLink.db"); | 133 | startRegressTestIoc("badCaLink.db"); |
344 | 134 | 134 | ||
346 | 135 | testdbPutFieldOk("ai:disconn.PROC", DBF_LONG, 1); | 135 | testdbPutFieldFail(-1, "ai:disconn.PROC", DBF_LONG, 1); |
347 | 136 | testdbGetFieldEqual("ai:disconn.SEVR", DBF_LONG, INVALID_ALARM); | 136 | testdbGetFieldEqual("ai:disconn.SEVR", DBF_LONG, INVALID_ALARM); |
348 | 137 | testdbGetFieldEqual("ai:disconn.STAT", DBF_LONG, LINK_ALARM); | 137 | testdbGetFieldEqual("ai:disconn.STAT", DBF_LONG, LINK_ALARM); |
349 | 138 | } | 138 | } |
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.