Merge ~bfrk/epics-base:address-modifiers into ~epics-core/epics-base/+git/epics-base:7.0

Proposed by Ben Franksen
Status: Superseded
Proposed branch: ~bfrk/epics-base:address-modifiers
Merge into: ~epics-core/epics-base/+git/epics-base:7.0
Prerequisite: ~bfrk/epics-base:address-modifiers-prerequisites
Diff against target: 1769 lines (+857/-306)
21 files modified
documentation/RELEASE_NOTES.md (+0/-9)
modules/database/src/ioc/db/Makefile (+3/-0)
modules/database/src/ioc/db/arrayRangeModifier.c (+167/-0)
modules/database/src/ioc/db/arrayRangeModifier.h (+69/-0)
modules/database/src/ioc/db/dbAccess.c (+39/-57)
modules/database/src/ioc/db/dbAccessDefs.h (+10/-0)
modules/database/src/ioc/db/dbAddrModifier.h (+48/-0)
modules/database/src/ioc/db/dbChannel.c (+25/-119)
modules/database/src/ioc/db/dbChannel.h (+3/-23)
modules/database/src/ioc/db/dbDbLink.c (+3/-26)
modules/database/src/ioc/db/dbEvent.h (+0/-1)
modules/database/src/ioc/db/dbUnitTest.c (+2/-2)
modules/database/src/ioc/db/db_field_log.h (+0/-3)
modules/database/src/ioc/dbStatic/link.h (+1/-7)
modules/database/src/std/filters/arr.c (+1/-24)
modules/database/test/std/rec/Makefile (+11/-0)
modules/database/test/std/rec/addrModifierTest.c (+247/-0)
modules/database/test/std/rec/addrModifierTest.db (+24/-0)
modules/database/test/std/rec/arroutRecord.c (+169/-0)
modules/database/test/std/rec/arroutRecord.dbd (+35/-0)
modules/database/test/std/rec/linkFilterTest.c (+0/-35)
Reviewer Review Type Date Requested Status
EPICS Core Developers Pending
Review via email: mp+381628@code.launchpad.net

This proposal has been superseded by a proposal from 2021-04-01.

Description of the change

As discussed on core-talk, this enables interpretation of "array address modifiers" a la "PV[s:i:e]" when using dbChannelPut or dbChannelPutField. This means the feature works for all PV links (I changed DB links to call dbChannelPut) as well as for caput.

The work is based on Dirk's dbChannelForDBLink branch (merged into 7.0) and some of my previous work (see branch address-modifiers-prerequisites).

The current implementation is /very/ liberal in that it never fails if the address modifier is out-of-bounds wrt the (target) record field. In this case the put operation is truncated to the overlap between the modifier and the target array, which may be empty (i.e. no change in the target). Similarly, if the source array has too few elements or excess elements, relative to the modifier, the request gets truncated to the smaller of these numbers.

Another design decision was that a put operation with a modifier never changes the current number of elements of the target. So you cannot currently "extend" an array using a modifier by writing beyond its current size.

All of these particular behaviors are, of course, open for debate. I can easily change any aspect as the implementation is quite simple.

To post a comment you must log in.
Revision history for this message
Andrew Johnson (anj) wrote :

Hi Ben, this looks very interesting; could you take it a little further? See my diff comments, in particular the one in dbAddrModifier.h where I speculate about enabling future modifiers. I don't have any arguments with your design decisions as described, and the code generally looks nice.

Revision history for this message
Ben Franksen (bfrk) wrote :
Download full text (3.8 KiB)

Hi Andrew, thanks for your comments.

>> +long dbPutModifier(DBADDR *paddr, short dbrType,
>> + const void *pbuffer, long nRequest, dbAddrModifier *pmod)
>> {
>> dbCommon *precord = paddr->precord;
>> short field_type = paddr->field_type;
>> + short field_size = paddr->field_size;
>> long no_elements = paddr->no_elements;
>> long special = paddr->special;
>> void *pfieldsave = paddr->pfield;
>> rset *prset = dbGetRset(paddr);
>> long status = 0;
>> + long available_no_elements = no_elements;
>
> s/available_no_elements/capacity/ maybe? It's a lot shorter...

Ah, but it's used with the opposite meaning: while initialized with the
capacity, it gets overwritten with the /current/ number of elements by
get_array_info which was previously ignored (passing &dummy).

> Please add the license header to all new source files; the Copyright lines aren't essential, but the 2 lines starting "EPICS BASE is distributed..." should be included.

Will do.

> I could see us wanting to add more kinds of modifiers in the future, although I'm not looking to make them pluggable at run-time. (NB: I'm speculating here).

Should that allow to combine multiple address modifiers, like for
filters? This would be very hard to get right. For the moment I will
assume that only one address modifier can be given per channel.

(Run-time pluggability indeed seems excessive, given that we have to
restart IOCs anyway whenever the database gets re-configured i.e.
records added or removed).

> Could you extract the array-specific operations and data and make dbAddrModifier polymorphic, with the implementation collected into a separate source file maybe, so other modifiers could be added too?

Separating out the code I added to dbPut was already on my TODO list.
Especially after adding ad-hoc index lists as another form of modifier
as you had suggested previously, it became clear that this doesn't
scale. So I will start with this move and then see if/how I can make
address modifiers polymorphic.

> I still want to be able to create a put filter that could accept a JSON string as the value, but this implementation currently hard-codes the dbAddrModifier to only support array filters. My filter doesn't have to use JSON in the PV name modifier, but it does need to do different things when the value comes in and I don't want to have to add that processing code to the dbAccess.c file. Maybe you could even extract the code for the '$' modifier into a separate parse-time routine too?
>
> I'm trying to increase modularity here.

I understand your point. Be warned though that this will increase the
overall complexity, not sure yet how much. Also, and unfortunately,
polymorphism in C is always kind of hazardous and an invitation for
stupid mistakes that should be type errors but can't because the type
checker is too dumb (see the error I recently made with &prec->val
versus prec->val).

>> +/** @brief The address modifier that modifies nothing. */
>> +#define defaultAddrModifier (struct dbAddrModifier){0,1,-1}
>
> Not sure that all our supported C compilers can accept this syntax, but this macro isn't actually being used anywhere in you...

Read more...

~bfrk/epics-base:address-modifiers updated
8f4ed7d... by Ben Franksen

factor out dbHandleModifier from dbPutModifier

This is the first step in changing address modifiers to be polymorphic.

a30969c... by Ben Franksen

address modifier API is now polymorphic

There is still only one implementation, the array range modifier, but
alternative ones could be added now. The concrete behavior of the array
range modifier is unchanged and merely hidden in a module. However parsing
is still part of the dbChannel module. Disentangling this part from
dbChannel will require a bit more restructuring.

Since the generic dbAddrModifier now has a small constant size (two
pointers), it makes more sense to embed it fully inside dbChannel.

Revision history for this message
Ben Franksen (bfrk) wrote :

@Andrew I have addressed most of the issues you raised and made the address modifier API polymorphic.

The parsing is not yet encapsulated. There is a trade-off here: we use the same syntax as a short-cut for filters. Not sure what to do here; perhaps create a separate module just for this parser. I'll have to experiment to see if that results in a better structure.

~bfrk/epics-base:address-modifiers updated
6aaf531... by Ben Franksen

fix API docs in arrayRangeModifier.h

aaf6163... by Ben Franksen

move array range parsing to arrayRangeModifier module

8ca85b3... by Ben Franksen

fix (justified) warnings in dbUnitTest.c

Unmerged commits

8ca85b3... by Ben Franksen

fix (justified) warnings in dbUnitTest.c

aaf6163... by Ben Franksen

move array range parsing to arrayRangeModifier module

6aaf531... by Ben Franksen

fix API docs in arrayRangeModifier.h

a30969c... by Ben Franksen

address modifier API is now polymorphic

There is still only one implementation, the array range modifier, but
alternative ones could be added now. The concrete behavior of the array
range modifier is unchanged and merely hidden in a module. However parsing
is still part of the dbChannel module. Disentangling this part from
dbChannel will require a bit more restructuring.

Since the generic dbAddrModifier now has a small constant size (two
pointers), it makes more sense to embed it fully inside dbChannel.

8f4ed7d... by Ben Franksen

factor out dbHandleModifier from dbPutModifier

This is the first step in changing address modifiers to be polymorphic.

f0dbd54... by Ben Franksen

add tests for address modifiers in output links

2ca474f... by Ben Franksen

add support for array address modifiers on put

This is independent from server-side filters. It only works with the bracket
notation "PV[s:i:e]", and not with the JSON filter syntax. However, we
re-use the function wrapArrayIndices from the array filters, moving it to
the ioc/db layer.

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 6129a4c..49cdd74 100644
--- a/documentation/RELEASE_NOTES.md
+++ b/documentation/RELEASE_NOTES.md
@@ -553,15 +553,6 @@ As long as all support modules and IOCs are rebuilt from source after updating
553them to use this release of EPICS Base, these changes should not have any553them to use this release of EPICS Base, these changes should not have any
554affect.554affect.
555555
556### Filters in database links
557
558Input links can now use filters, most importantly array element and sub array
559access, even if they are not channel access links.
560
561### ai Soft Channel support
562
563The Soft Channel device support for ai records now returns failure when
564fetching the INP link fails.
565556
566### logClient reliability557### logClient reliability
567558
diff --git a/modules/database/src/ioc/db/Makefile b/modules/database/src/ioc/db/Makefile
index ebb78e2..e590cc6 100644
--- a/modules/database/src/ioc/db/Makefile
+++ b/modules/database/src/ioc/db/Makefile
@@ -11,10 +11,12 @@
1111
12SRC_DIRS += $(IOCDIR)/db12SRC_DIRS += $(IOCDIR)/db
1313
14INC += arrayRangeModifier.h
14INC += callback.h15INC += callback.h
15INC += dbAccess.h16INC += dbAccess.h
16INC += dbAccessDefs.h17INC += dbAccessDefs.h
17INC += dbAddr.h18INC += dbAddr.h
19INC += dbAddrModifier.h
18INC += dbBkpt.h20INC += dbBkpt.h
19INC += dbCa.h21INC += dbCa.h
20INC += dbChannel.h22INC += dbChannel.h
@@ -66,6 +68,7 @@ HTMLS += dbCommonRecord.html
66HTMLS += dbCommonInput.html68HTMLS += dbCommonInput.html
67HTMLS += dbCommonOutput.html69HTMLS += dbCommonOutput.html
6870
71dbCore_SRCS += arrayRangeModifier.c
69dbCore_SRCS += dbLock.c72dbCore_SRCS += dbLock.c
70dbCore_SRCS += dbAccess.c73dbCore_SRCS += dbAccess.c
71dbCore_SRCS += dbBkpt.c74dbCore_SRCS += dbBkpt.c
diff --git a/modules/database/src/ioc/db/arrayRangeModifier.c b/modules/database/src/ioc/db/arrayRangeModifier.c
72new file mode 10064475new file mode 100644
index 0000000..1feb7bc
--- /dev/null
+++ b/modules/database/src/ioc/db/arrayRangeModifier.c
@@ -0,0 +1,167 @@
1/*************************************************************************\
2* EPICS BASE is distributed subject to a Software License Agreement found
3* in file LICENSE that is included with this distribution.
4\*************************************************************************/
5
6#include <assert.h>
7#include <stdlib.h>
8#include <stdio.h>
9
10#include "arrayRangeModifier.h"
11#include "dbAccessDefs.h"
12#include "dbConvertFast.h"
13#include "dbConvert.h"
14
15typedef struct {
16 long start;
17 long incr;
18 long end;
19} arrayRangeModifier;
20
21long wrapArrayIndices(long *start, const long increment,
22 long *end, const long no_elements)
23{
24 if (*start < 0) *start = no_elements + *start;
25 if (*start < 0) *start = 0;
26 if (*start > no_elements) *start = no_elements;
27
28 if (*end < 0) *end = no_elements + *end;
29 if (*end < 0) *end = 0;
30 if (*end >= no_elements) *end = no_elements - 1;
31
32 if (*end - *start >= 0)
33 return 1 + (*end - *start) / increment;
34 else
35 return 0;
36}
37
38static long handleArrayRangeModifier(DBADDR *paddr, short dbrType, const void
39 *pbuffer, long nRequest, void *pvt, long offset, long available)
40{
41 arrayRangeModifier *pmod = (arrayRangeModifier *)pvt;
42 long status = 0;
43 long start = pmod->start;
44 long end = pmod->end;
45 /* Note that this limits the return value to be <= available */
46 long n = wrapArrayIndices(&start, pmod->incr, &end, available);
47
48 assert(pmod->incr > 0);
49 if (pmod->incr > 1) {
50 long i, j;
51 long (*putCvt) (const void *from, void *to, const dbAddr * paddr) =
52 dbFastPutConvertRoutine[dbrType][paddr->field_type];
53 short dbr_size = dbValueSize(dbrType);
54 if (nRequest > n)
55 nRequest = n;
56 for (i = 0, j = (offset + start) % paddr->no_elements; i < nRequest;
57 i++, j = (j + pmod->incr) % paddr->no_elements) {
58 status = putCvt(pbuffer + (i * dbr_size),
59 paddr->pfield + (j * paddr->field_size), paddr);
60 }
61 } else {
62 offset = (offset + start) % paddr->no_elements;
63 if (nRequest > n)
64 nRequest = n;
65 if (paddr->no_elements <= 1) {
66 status = dbFastPutConvertRoutine[dbrType][paddr->field_type] (pbuffer,
67 paddr->pfield, paddr);
68 } else {
69 if (paddr->no_elements < nRequest)
70 nRequest = paddr->no_elements;
71 status = dbPutConvertRoutine[dbrType][paddr->field_type] (paddr,
72 pbuffer, nRequest, paddr->no_elements, offset);
73 }
74 }
75 return status;
76}
77
78long createArrayRangeModifier(dbAddrModifier *pmod, long start, long incr, long end)
79{
80 arrayRangeModifier *pvt =
81 (arrayRangeModifier *) malloc(sizeof(arrayRangeModifier));
82 if (incr <= 0) {
83 return S_db_errArg;
84 }
85 if (!pvt) {
86 return S_db_noMemory;
87 }
88 pvt->start = start;
89 pvt->incr = incr;
90 pvt->end = end;
91 pmod->pvt = pvt;
92 pmod->handle = handleArrayRangeModifier;
93 return 0;
94}
95
96void deleteArrayRangeModifier(dbAddrModifier *pmod)
97{
98 if (pmod->pvt)
99 free(pmod->pvt);
100}
101
102long parseArrayRange(dbAddrModifier *pmod, const char **pstring)
103{
104 long start = 0;
105 long end = -1;
106 long incr = 1;
107 long tmp;
108 const char *pcurrent = *pstring;
109 char *pnext;
110 ptrdiff_t advance;
111 long status = 0;
112
113 if (*pcurrent != '[') {
114 return -1;
115 }
116 pcurrent++;
117 /* If no number is present, strtol() returns 0 and sets pnext=pcurrent,
118 else pnext points to the first char after the number */
119 tmp = strtol(pcurrent, &pnext, 0);
120 advance = pnext - pcurrent;
121 if (advance) start = tmp;
122 pcurrent = pnext;
123 if (*pcurrent == ']' && advance) {
124 end = start;
125 goto done;
126 }
127 if (*pcurrent != ':') {
128 return -1;
129 }
130 pcurrent++;
131 tmp = strtol(pcurrent, &pnext, 0);
132 advance = pnext - pcurrent;
133 pcurrent = pnext;
134 if (*pcurrent == ']') {
135 if (advance) end = tmp;
136 goto done;
137 }
138 if (advance) incr = tmp;
139 if (*pcurrent != ':') {
140 return -1;
141 }
142 pcurrent++;
143 tmp = strtol(pcurrent, &pnext, 0);
144 advance = pnext - pcurrent;
145 if (advance) end = tmp;
146 pcurrent = pnext;
147 if (*pcurrent != ']') {
148 return -1;
149 }
150
151done:
152 status = createArrayRangeModifier(pmod, start, incr, end);
153 if (status) {
154 return status;
155 }
156 pcurrent++;
157 *pstring = pcurrent;
158 return 0;
159}
160
161void getArrayRange(dbAddrModifier *pmod, long *pstart, long *pincr, long *pend)
162{
163 arrayRangeModifier *pvt = (arrayRangeModifier *)pmod->pvt;
164 *pstart = pvt->start;
165 *pincr = pvt->incr;
166 *pend = pvt->end;
167}
diff --git a/modules/database/src/ioc/db/arrayRangeModifier.h b/modules/database/src/ioc/db/arrayRangeModifier.h
0new file mode 100644168new file mode 100644
index 0000000..8e0cd2f
--- /dev/null
+++ b/modules/database/src/ioc/db/arrayRangeModifier.h
@@ -0,0 +1,69 @@
1/*************************************************************************\
2* EPICS BASE is distributed subject to a Software License Agreement found
3* in file LICENSE that is included with this distribution.
4\*************************************************************************/
5#ifndef ARRAYRANGEMODIFIER_H
6#define ARRAYRANGEMODIFIER_H
7
8#include "dbAddrModifier.h"
9#include "shareLib.h"
10
11/** @brief The array range address modifier. */
12
13/** @brief Given a number of elements, convert negative start and end indices
14 * to non-negative ones by counting from the end of the array.
15 *
16 * @param start Pointer to possibly negative start index
17 * @param increment Increment
18 * @param end Pointer to possibly negative end index
19 * @param no_elements Number of array elements
20 * @return Final number of elements
21 */
22epicsShareFunc
23long wrapArrayIndices(long *start, long increment, long *end, long no_elements);
24
25/** @brief Create an array range modifier from start index, increment, and end index
26 *
27 * @param pmod Pointer to address modifier (user allocated)
28 * @param start Start index (possibly negative)
29 * @param incr Increment (must be positive)
30 * @param end End index (possibly negative)
31 * @return Status
32 */
33epicsShareFunc
34long createArrayRangeModifier(dbAddrModifier *pmod, long start, long incr, long end);
35
36/** @brief Alternative creation function that parses the data from a string.
37 *
38 * If successful, advances 'pstring' to point after the recognized address modifier.
39 *
40 * @param pmod Pointer to uninitialized address modifier (user allocated)
41 * @param pstring Pointer to string to parse (in/out)
42 * @return 0 (success) or -1 (failure)
43 */
44epicsShareFunc
45long parseArrayRange(dbAddrModifier *pmod, const char **pstring);
46
47/** @brief Extract the private data
48 *
49 * The 'pmod' argument must point to a valid array range address modifier.
50 * The other aruments must point to appropriately sized storage.
51 *
52 * @param pmod Pointer to uninitialized address modifier (user allocated)
53 * @param pstart Start index (out)
54 * @param pincr Increment (out)
55 * @param pend End index (out)
56 */
57epicsShareFunc
58void getArrayRange(dbAddrModifier *pmod, long *pstart, long *pincr, long *pend);
59
60/** @brief Free private memory associated with an array range modifier
61 *
62 * Note this does not free 'pmod' which is always user allocated.
63 *
64 * @param pmod Pointer to address modifier (user allocated)
65 */
66epicsShareFunc
67void deleteArrayRangeModifier(dbAddrModifier *pmod);
68
69#endif /* ARRAYRANGEMODIFIER_H */
diff --git a/modules/database/src/ioc/db/dbAccess.c b/modules/database/src/ioc/db/dbAccess.c
index 539e35f..52169c0 100644
--- a/modules/database/src/ioc/db/dbAccess.c
+++ b/modules/database/src/ioc/db/dbAccess.c
@@ -40,6 +40,7 @@
40#include "callback.h"40#include "callback.h"
41#include "dbAccessDefs.h"41#include "dbAccessDefs.h"
42#include "dbAddr.h"42#include "dbAddr.h"
43#include "dbAddrModifier.h"
43#include "dbBase.h"44#include "dbBase.h"
44#include "dbBkpt.h"45#include "dbBkpt.h"
45#include "dbCommonPvt.h"46#include "dbCommonPvt.h"
@@ -375,7 +376,6 @@ static void getOptions(DBADDR *paddr, char **poriginal, long *options,
375 if((field_type==DBF_FLOAT || field_type==DBF_DOUBLE)376 if((field_type==DBF_FLOAT || field_type==DBF_DOUBLE)
376 && prset && prset->get_precision ){377 && prset && prset->get_precision ){
377 (*prset->get_precision)(paddr,(long *)pbuffer);378 (*prset->get_precision)(paddr,(long *)pbuffer);
378<<<<<<< modules/database/src/ioc/db/dbAccess.c
379 } else {379 } else {
380 *options ^= DBR_PRECISION; /*Turn off DBR_PRECISION*/380 *options ^= DBR_PRECISION; /*Turn off DBR_PRECISION*/
381 }381 }
@@ -383,15 +383,6 @@ static void getOptions(DBADDR *paddr, char **poriginal, long *options,
383 }383 }
384 if( (*options) & DBR_TIME ) {384 if( (*options) & DBR_TIME ) {
385 epicsUInt32 *ptime = (epicsUInt32 *)pbuffer;385 epicsUInt32 *ptime = (epicsUInt32 *)pbuffer;
386=======
387 } else {
388 *options ^= DBR_PRECISION; /*Turn off DBR_PRECISION*/
389 }
390 pbuffer += dbr_precision_size;
391 }
392 if( (*options) & DBR_TIME ) {
393 epicsUInt32 *ptime = (epicsUInt32 *)pbuffer;
394>>>>>>> modules/database/src/ioc/db/dbAccess.c
395386
396 if (!pfl) {387 if (!pfl) {
397 *ptime++ = pcommon->time.secPastEpoch;388 *ptime++ = pcommon->time.secPastEpoch;
@@ -906,20 +897,13 @@ long dbGet(DBADDR *paddr, short dbrType,
906 db_field_log *pfl = (db_field_log *)pflin;897 db_field_log *pfl = (db_field_log *)pflin;
907 short field_type;898 short field_type;
908 long capacity, no_elements, offset;899 long capacity, no_elements, offset;
909 int is_scalar_request = !nRequest;
910 long n_scalar_request = 1;
911 rset *prset;900 rset *prset;
912 long status = 0;901 long status = 0;
913902
914 if (options && *options)903 if (options && *options)
915 getOptions(paddr, &pbuf, options, pflin);904 getOptions(paddr, &pbuf, options, pflin);
916905 if (nRequest && *nRequest == 0)
917 if (is_scalar_request)
918 nRequest = &n_scalar_request;
919 else if (*nRequest == 0)
920 /* trivially succeed for zero length array request */
921 return 0;906 return 0;
922 assert(nRequest && *nRequest > 0);
923907
924 if (!pfl) {908 if (!pfl) {
925 field_type = paddr->field_type;909 field_type = paddr->field_type;
@@ -961,7 +945,6 @@ long dbGet(DBADDR *paddr, short dbrType,
961 goto done;945 goto done;
962 }946 }
963947
964<<<<<<< modules/database/src/ioc/db/dbAccess.c
965 if (offset == 0 && (!nRequest || no_elements == 1)) {948 if (offset == 0 && (!nRequest || no_elements == 1)) {
966 if (nRequest)949 if (nRequest)
967 *nRequest = 1;950 *nRequest = 1;
@@ -971,17 +954,6 @@ long dbGet(DBADDR *paddr, short dbrType,
971 }954 }
972955
973 if (!dbfl_has_copy(pfl)) {956 if (!dbfl_has_copy(pfl)) {
974=======
975 /* A scalar request that cannot be met is an error. */
976 if (is_scalar_request && no_elements < 1) {
977 status = S_db_badField;
978 goto done;
979 }
980
981 if (offset == 0 && (is_scalar_request || no_elements == 1)) {
982 *nRequest = 1;
983 if (!pfl) {
984>>>>>>> modules/database/src/ioc/db/dbAccess.c
985 status = dbFastGetConvertRoutine[field_type][dbrType]957 status = dbFastGetConvertRoutine[field_type][dbrType]
986 (paddr->pfield, pbuf, paddr);958 (paddr->pfield, pbuf, paddr);
987 } else {959 } else {
@@ -1004,7 +976,6 @@ long dbGet(DBADDR *paddr, short dbrType,
1004 long n;976 long n;
1005 GETCONVERTFUNC convert;977 GETCONVERTFUNC convert;
1006978
1007<<<<<<< modules/database/src/ioc/db/dbAccess.c
1008 if (nRequest) {979 if (nRequest) {
1009 if (no_elements < *nRequest)980 if (no_elements < *nRequest)
1010 *nRequest = no_elements;981 *nRequest = no_elements;
@@ -1014,11 +985,6 @@ long dbGet(DBADDR *paddr, short dbrType,
1014 } else {985 } else {
1015 n = 1;986 n = 1;
1016 }987 }
1017=======
1018 if (no_elements < *nRequest)
1019 *nRequest = no_elements;
1020 n = *nRequest;
1021>>>>>>> modules/database/src/ioc/db/dbAccess.c
1022 convert = dbGetConvertRoutine[field_type][dbrType];988 convert = dbGetConvertRoutine[field_type][dbrType];
1023 if (!convert) {989 if (!convert) {
1024 char message[80];990 char message[80];
@@ -1032,11 +998,7 @@ long dbGet(DBADDR *paddr, short dbrType,
1032 /* convert data into the caller's buffer */998 /* convert data into the caller's buffer */
1033 if (n <= 0) {999 if (n <= 0) {
1034 ; /*do nothing */1000 ; /*do nothing */
1035<<<<<<< modules/database/src/ioc/db/dbAccess.c
1036 } else if (!dbfl_has_copy(pfl)) {1001 } else if (!dbfl_has_copy(pfl)) {
1037=======
1038 } else if (!pfl) {
1039>>>>>>> modules/database/src/ioc/db/dbAccess.c
1040 status = convert(paddr, pbuf, n, capacity, offset);1002 status = convert(paddr, pbuf, n, capacity, offset);
1041 } else {1003 } else {
1042 DBADDR localAddr = *paddr; /* Structure copy */1004 DBADDR localAddr = *paddr; /* Structure copy */
@@ -1054,10 +1016,10 @@ long dbGet(DBADDR *paddr, short dbrType,
1054 status = convert(&localAddr, pbuf, n, capacity, offset);1016 status = convert(&localAddr, pbuf, n, capacity, offset);
1055 }1017 }
10561018
1057 if(!status && dbrType==DBF_CHAR && !is_scalar_request &&1019 if(!status && dbrType==DBF_CHAR && nRequest &&
1058 paddr->pfldDes && paddr->pfldDes->field_type==DBF_STRING)1020 paddr->pfldDes && paddr->pfldDes->field_type==DBF_STRING)
1059 {1021 {
1060 /* long string: ensure termination and truncate to actual length */1022 /* long string ensure nil and truncate to actual length */
1061 long nReq = *nRequest;1023 long nReq = *nRequest;
1062 pbuf[nReq-1] = '\0';1024 pbuf[nReq-1] = '\0';
1063 *nRequest = strlen(pbuf)+1;1025 *nRequest = strlen(pbuf)+1;
@@ -1264,6 +1226,12 @@ cleanup:
1264long dbPutField(DBADDR *paddr, short dbrType,1226long dbPutField(DBADDR *paddr, short dbrType,
1265 const void *pbuffer, long nRequest)1227 const void *pbuffer, long nRequest)
1266{1228{
1229 return dbPutFieldModifier(paddr, dbrType, pbuffer, nRequest, NULL);
1230}
1231
1232long dbPutFieldModifier(DBADDR *paddr, short dbrType,
1233 const void *pbuffer, long nRequest, dbAddrModifier *pmod)
1234{
1267 long status = 0;1235 long status = 0;
1268 long special = paddr->special;1236 long special = paddr->special;
1269 dbFldDes *pfldDes = paddr->pfldDes;1237 dbFldDes *pfldDes = paddr->pfldDes;
@@ -1281,7 +1249,7 @@ long dbPutField(DBADDR *paddr, short dbrType,
1281 return dbPutFieldLink(paddr, dbrType, pbuffer, nRequest);1249 return dbPutFieldLink(paddr, dbrType, pbuffer, nRequest);
12821250
1283 dbScanLock(precord);1251 dbScanLock(precord);
1284 status = dbPut(paddr, dbrType, pbuffer, nRequest);1252 status = dbPutModifier(paddr, dbrType, pbuffer, nRequest, pmod);
1285 if (status == 0) {1253 if (status == 0) {
1286 if (paddr->pfield == &precord->proc ||1254 if (paddr->pfield == &precord->proc ||
1287 (pfldDes->process_passive &&1255 (pfldDes->process_passive &&
@@ -1335,8 +1303,13 @@ static long putAcks(DBADDR *paddr, const void *pbuffer, long nRequest,
1335 return 0;1303 return 0;
1336}1304}
13371305
1338long dbPut(DBADDR *paddr, short dbrType,1306long dbPut(DBADDR *paddr, short dbrType, const void *pbuffer, long nRequest)
1339 const void *pbuffer, long nRequest)1307{
1308 return dbPutModifier(paddr, dbrType, pbuffer, nRequest, NULL);
1309}
1310
1311long dbPutModifier(DBADDR *paddr, short dbrType,
1312 const void *pbuffer, long nRequest, dbAddrModifier *pmod)
1340{1313{
1341 dbCommon *precord = paddr->precord;1314 dbCommon *precord = paddr->precord;
1342 short field_type = paddr->field_type;1315 short field_type = paddr->field_type;
@@ -1345,6 +1318,7 @@ long dbPut(DBADDR *paddr, short dbrType,
1345 void *pfieldsave = paddr->pfield;1318 void *pfieldsave = paddr->pfield;
1346 rset *prset = dbGetRset(paddr);1319 rset *prset = dbGetRset(paddr);
1347 long status = 0;1320 long status = 0;
1321 long available_no_elements = no_elements;
1348 long offset;1322 long offset;
1349 dbFldDes *pfldDes;1323 dbFldDes *pfldDes;
1350 int isValueField;1324 int isValueField;
@@ -1371,25 +1345,33 @@ long dbPut(DBADDR *paddr, short dbrType,
13711345
1372 if (paddr->pfldDes->special == SPC_DBADDR &&1346 if (paddr->pfldDes->special == SPC_DBADDR &&
1373 prset && prset->get_array_info) {1347 prset && prset->get_array_info) {
1374 long dummy;
13751348
1376 status = prset->get_array_info(paddr, &dummy, &offset);1349 status = prset->get_array_info(paddr, &available_no_elements, &offset);
1377 /* paddr->pfield may be modified */1350 /* paddr->pfield may be modified */
1378 if (status) goto done;1351 if (status) goto done;
1379 if (no_elements < nRequest)1352 } else
1380 nRequest = no_elements;1353 offset = 0;
1381 status = dbPutConvertRoutine[dbrType][field_type](paddr, pbuffer,1354
1382 nRequest, no_elements, offset);1355 if (pmod && pmod->handle && pmod->pvt) {
1383 /* update array info */1356 status = pmod->handle(paddr, dbrType, pbuffer, nRequest,
1384 if (!status && prset->put_array_info)1357 pmod->pvt, offset, available_no_elements);
1385 status = prset->put_array_info(paddr, nRequest);
1386 } else {1358 } else {
1387 if (nRequest < 1) {1359 if (no_elements <= 1) {
1388 recGblSetSevr(precord, LINK_ALARM, INVALID_ALARM);
1389 } else {
1390 status = dbFastPutConvertRoutine[dbrType][field_type](pbuffer,1360 status = dbFastPutConvertRoutine[dbrType][field_type](pbuffer,
1391 paddr->pfield, paddr);1361 paddr->pfield, paddr);
1392 nRequest = 1;1362 nRequest = 1;
1363 } else {
1364 if (no_elements < nRequest)
1365 nRequest = no_elements;
1366 status = dbPutConvertRoutine[dbrType][field_type](paddr, pbuffer,
1367 nRequest, no_elements, offset);
1368 }
1369
1370 /* update array info unless writing failed */
1371 if (!status &&
1372 paddr->pfldDes->special == SPC_DBADDR &&
1373 prset && prset->put_array_info) {
1374 status = prset->put_array_info(paddr, nRequest);
1393 }1375 }
1394 }1376 }
13951377
diff --git a/modules/database/src/ioc/db/dbAccessDefs.h b/modules/database/src/ioc/db/dbAccessDefs.h
index f2cf091..45e9f6e 100644
--- a/modules/database/src/ioc/db/dbAccessDefs.h
+++ b/modules/database/src/ioc/db/dbAccessDefs.h
@@ -27,7 +27,9 @@
2727
28#include "dbBase.h"28#include "dbBase.h"
29#include "dbAddr.h"29#include "dbAddr.h"
30#include "dbAddrModifier.h"
30#include "recSup.h"31#include "recSup.h"
32#include "db_field_log.h"
3133
32#ifdef __cplusplus34#ifdef __cplusplus
33extern "C" {35extern "C" {
@@ -247,11 +249,19 @@ epicsShareFunc long dbGetField(
247epicsShareFunc long dbGet(249epicsShareFunc long dbGet(
248 struct dbAddr *,short dbrType,void *pbuffer,long *options,250 struct dbAddr *,short dbrType,void *pbuffer,long *options,
249 long *nRequest,void *pfl);251 long *nRequest,void *pfl);
252
250epicsShareFunc long dbPutField(253epicsShareFunc long dbPutField(
251 struct dbAddr *,short dbrType,const void *pbuffer,long nRequest);254 struct dbAddr *,short dbrType,const void *pbuffer,long nRequest);
252epicsShareFunc long dbPut(255epicsShareFunc long dbPut(
253 struct dbAddr *,short dbrType,const void *pbuffer,long nRequest);256 struct dbAddr *,short dbrType,const void *pbuffer,long nRequest);
254257
258epicsShareFunc long dbPutFieldModifier(
259 struct dbAddr *,short dbrType,const void *pbuffer,long nRequest,
260 dbAddrModifier *pmod);
261epicsShareFunc long dbPutModifier(
262 struct dbAddr *,short dbrType,const void *pbuffer,long nRequest,
263 dbAddrModifier *pmod);
264
255typedef void(*SPC_ASCALLBACK)(struct dbCommon *);265typedef void(*SPC_ASCALLBACK)(struct dbCommon *);
256/*dbSpcAsRegisterCallback called by access security */266/*dbSpcAsRegisterCallback called by access security */
257epicsShareFunc void dbSpcAsRegisterCallback(SPC_ASCALLBACK func);267epicsShareFunc void dbSpcAsRegisterCallback(SPC_ASCALLBACK func);
diff --git a/modules/database/src/ioc/db/dbAddrModifier.h b/modules/database/src/ioc/db/dbAddrModifier.h
258new file mode 100644268new file mode 100644
index 0000000..dcc3c86
--- /dev/null
+++ b/modules/database/src/ioc/db/dbAddrModifier.h
@@ -0,0 +1,48 @@
1/*************************************************************************\
2* EPICS BASE is distributed subject to a Software License Agreement found
3* in file LICENSE that is included with this distribution.
4\*************************************************************************/
5#ifndef DBADDRMODIFIER_H
6#define DBADDRMODIFIER_H
7
8#include "dbAddr.h"
9
10/** @brief Generic API for address modifiers. */
11
12/** @brief Type of functions that handle an address modifier.
13 *
14 * This function should write the value in 'pbuffer' to the target 'pAddr',
15 * according to the number of requested elements 'nRequest', the requested type
16 * 'dbrType', and the address modifier private data 'pvt'. It may also take
17 * into account the actual number of elements 'available' in the target, and
18 * the 'offset', both of which usually result from a previous call to
19 * 'get_array_info'.
20 * The targeted record must be locked. Furthermore, the lock must not be given
21 * up between the call to 'get_array_info' and call to this function, otherwise
22 * 'offset' and 'available' may be out of sync.
23 *
24 * @param paddr Target (field) address
25 * @param dbrType Requested (element) type
26 * @param pbuffer Data requested to be written
27 * @param nRequest Number of elements in pbuffer
28 * @param pvt Private modifier data
29 * @param offset Current offset in the target field
30 * @param available Current number of elements in the target field
31 * @return Status
32 */
33typedef long dbHandleAddrModifier(DBADDR *paddr, short dbrType,
34 const void *pbuffer, long nRequest, void *pvt, long offset,
35 long available);
36
37/** @brief Structure of an address modifier.
38 *
39 * This will normally be allocated by user code. An address modifier
40 * implementation should supply a function to create the private data 'pvt'
41 * and initialize 'handle' with a function to handle the modifier.
42 */
43typedef struct {
44 void *pvt; /** @brief Private modifier data */
45 dbHandleAddrModifier *handle; /** @brief Function to handle the modifier */
46} dbAddrModifier;
47
48#endif /* DBADDRMODIFIER_H */
diff --git a/modules/database/src/ioc/db/dbChannel.c b/modules/database/src/ioc/db/dbChannel.c
index 33c44f8..a9eb4e2 100644
--- a/modules/database/src/ioc/db/dbChannel.c
+++ b/modules/database/src/ioc/db/dbChannel.c
@@ -30,11 +30,11 @@
30#include "yajl_parse.h"30#include "yajl_parse.h"
3131
32#define epicsExportSharedSymbols32#define epicsExportSharedSymbols
33#include "arrayRangeModifier.h"
33#include "dbAccessDefs.h"34#include "dbAccessDefs.h"
34#include "dbBase.h"35#include "dbBase.h"
35#include "dbChannel.h"36#include "dbChannel.h"
36#include "dbCommon.h"37#include "dbCommon.h"
37#include "dbConvertFast.h"
38#include "dbEvent.h"38#include "dbEvent.h"
39#include "dbLock.h"39#include "dbLock.h"
40#include "dbStaticLib.h"40#include "dbStaticLib.h"
@@ -350,75 +350,27 @@ if (Func) { \
350 if (result != parse_continue) goto failure; \350 if (result != parse_continue) goto failure; \
351}351}
352352
353static long parseArrayRange(dbChannel* chan, const char *pname, const char **ppnext) {353static long createArrayRangeFilter(dbChannel* chan) {
354 epicsInt32 start = 0;354 long start, incr, end;
355 epicsInt32 end = -1;
356 epicsInt32 incr = 1;
357 epicsInt32 l;
358 char *pnext;
359 ptrdiff_t exist;
360 chFilter *filter;355 chFilter *filter;
361 const chFilterPlugin *plug;356 const chFilterPlugin *plug;
362 parse_result result;357 parse_result result;
363 long status = 0;
364
365 /* If no number is present, strtol() returns 0 and sets pnext=pname,
366 else pnext points to the first char after the number */
367 pname++;
368 l = strtol(pname, &pnext, 0);
369 exist = pnext - pname;
370 if (exist) start = l;
371 pname = pnext;
372 if (*pname == ']' && exist) {
373 end = start;
374 goto insertplug;
375 }
376 if (*pname != ':') {
377 status = S_dbLib_fieldNotFound;
378 goto finish;
379 }
380 pname++;
381 l = strtol(pname, &pnext, 0);
382 exist = pnext - pname;
383 pname = pnext;
384 if (*pname == ']') {
385 if (exist) end = l;
386 goto insertplug;
387 }
388 if (exist) incr = l;
389 if (*pname != ':') {
390 status = S_dbLib_fieldNotFound;
391 goto finish;
392 }
393 pname++;
394 l = strtol(pname, &pnext, 0);
395 exist = pnext - pname;
396 if (exist) end = l;
397 pname = pnext;
398 if (*pname != ']') {
399 status = S_dbLib_fieldNotFound;
400 goto finish;
401 }
402
403 insertplug:
404 pname++;
405 *ppnext = pname;
406358
407 plug = dbFindFilter("arr", 3);359 plug = dbFindFilter("arr", 3);
408 if (!plug) {360 if (!plug) {
409 status = S_dbLib_fieldNotFound;361 /* "arr" plugin not found, this is not an error! */
410 goto finish;362 return 0;
411 }363 }
412364
413 filter = freeListCalloc(chFilterFreeList);365 filter = freeListCalloc(chFilterFreeList);
414 if (!filter) {366 if (!filter) {
415 status = S_db_noMemory;367 return S_db_noMemory;
416 goto finish;
417 }368 }
418 filter->chan = chan;369 filter->chan = chan;
419 filter->plug = plug;370 filter->plug = plug;
420 filter->puser = NULL;371 filter->puser = NULL;
421372
373 getArrayRange(&chan->addrModifier, &start, &incr, &end);
422 TRY(filter->plug->fif->parse_start, (filter));374 TRY(filter->plug->fif->parse_start, (filter));
423 TRY(filter->plug->fif->parse_start_map, (filter));375 TRY(filter->plug->fif->parse_start_map, (filter));
424 if (start != 0) {376 if (start != 0) {
@@ -439,12 +391,9 @@ static long parseArrayRange(dbChannel* chan, const char *pname, const char **ppn
439 ellAdd(&chan->filters, &filter->list_node);391 ellAdd(&chan->filters, &filter->list_node);
440 return 0;392 return 0;
441393
442 failure:394failure:
443 freeListFree(chFilterFreeList, filter);395 freeListFree(chFilterFreeList, filter);
444 status = S_dbLib_fieldNotFound;396 return S_dbLib_fieldNotFound;
445
446 finish:
447 return status;
448}397}
449398
450dbChannel * dbChannelCreate(const char *name)399dbChannel * dbChannelCreate(const char *name)
@@ -482,6 +431,8 @@ dbChannel * dbChannelCreate(const char *name)
482 goto finish;431 goto finish;
483432
484 /* Handle field modifiers */433 /* Handle field modifiers */
434 chan->addrModifier.pvt = NULL;
435 chan->addrModifier.handle = NULL;
485 if (*pname) {436 if (*pname) {
486 short dbfType = paddr->field_type;437 short dbfType = paddr->field_type;
487438
@@ -506,12 +457,15 @@ dbChannel * dbChannelCreate(const char *name)
506 pname++;457 pname++;
507 }458 }
508459
509 if (*pname == '[') {460 /* Try to recognize an array range expression */
510 status = parseArrayRange(chan, pname, &pname);461 if (parseArrayRange(&chan->addrModifier, &pname)==0) {
462 status = createArrayRangeFilter(chan);
511 if (status) goto finish;463 if (status) goto finish;
512 }464 }
513465
514 /* JSON may follow */466 /* JSON may follow */
467 /* Note that chf_parse issues error messages if it fails,
468 which is why we have to check for the opening brace here. */
515 if (*pname == '{') {469 if (*pname == '{') {
516 status = chf_parse(chan, &pname);470 status = chf_parse(chan, &pname);
517 if (status) goto finish;471 if (status) goto finish;
@@ -631,65 +585,14 @@ long dbChannelOpen(dbChannel *chan)
631}585}
632586
633/* Only use dbChannelGet() if the record is already locked. */587/* Only use dbChannelGet() if the record is already locked. */
634long dbChannelGet(dbChannel *chan, short dbrType, void *pbuffer,588long dbChannelGet(dbChannel *chan, short type, void *pbuffer,
635 long *options, long *nRequest, db_field_log *pfl)589 long *options, long *nRequest, void *pfl)
636{590{
637 dbAddr addr = chan->addr; /* structure copy */591 return dbGet(&chan->addr, type, pbuffer, options, nRequest, pfl);
638 int pfl_has_copy = pfl && (pfl->type != dbfl_type_ref || pfl->u.r.dtor);
639
640 if (pfl) {
641 addr.field_size = pfl->field_size;
642 addr.field_type = pfl->field_type;
643 addr.no_elements = pfl->no_elements;
644 switch ((enum dbfl_type)pfl->type) {
645 case dbfl_type_val: addr.pfield = &pfl->u.v.field; break;
646 case dbfl_type_ref: addr.pfield = pfl->u.r.field; break;
647 }
648 }
649
650 /*
651 * Try to optimize scalar requests by caching (just) the conversion
652 * routine. This is possible only if we have no options, the field and the
653 * request have exactly one element, and we have no special processing to
654 * do. Note that if the db_field_log has already copied the data, dbGet
655 * does not call get_array_info.
656 */
657 if ((options && *options)
658 || addr.no_elements > 1
659 || (nRequest && *nRequest > 1)
660 || (!pfl_has_copy && addr.special == SPC_DBADDR)
661 || addr.special == SPC_ATTRIBUTE)
662 {
663 chan->getCvt = NULL;
664 return dbGet(&addr, dbrType, pbuffer, options, nRequest, pfl);
665 }
666
667 /* If this is a scalar request, fail if it cannot be met */
668 if (!nRequest && addr.no_elements < 1)
669 return S_db_badField;
670
671 /*
672 * Optimization for scalar requests with no options,
673 * no special processing, and where the channel has only one element.
674 */
675 if (chan->getCvt && chan->lastGetdbrType == dbrType) {
676 return chan->getCvt(addr.pfield, pbuffer, &addr);
677 } else {
678 unsigned short dbfType = addr.field_type;
679
680 if (dbrType < 0 || dbrType > DBR_ENUM || dbfType > DBF_DEVICE)
681 return S_db_badDbrtype;
682
683 chan->getCvt = dbFastGetConvertRoutine[dbfType][dbrType];
684 chan->lastGetdbrType = dbrType;
685 if (nRequest)
686 *nRequest = addr.no_elements;
687 return chan->getCvt(addr.pfield, pbuffer, &addr);
688 }
689}592}
690593
691long dbChannelGetField(dbChannel *chan, short dbrType, void *pbuffer,594long dbChannelGetField(dbChannel *chan, short dbrType, void *pbuffer,
692 long *options, long *nRequest, db_field_log *pfl)595 long *options, long *nRequest, void *pfl)
693{596{
694 dbCommon *precord = chan->addr.precord;597 dbCommon *precord = chan->addr.precord;
695 long status = 0;598 long status = 0;
@@ -707,13 +610,15 @@ long dbChannelGetField(dbChannel *chan, short dbrType, void *pbuffer,
707long dbChannelPut(dbChannel *chan, short type, const void *pbuffer,610long dbChannelPut(dbChannel *chan, short type, const void *pbuffer,
708 long nRequest)611 long nRequest)
709{612{
710 return dbPut(&chan->addr, type, pbuffer, nRequest);613 return dbPutModifier(&chan->addr, type, pbuffer, nRequest,
614 &chan->addrModifier);
711}615}
712616
713long dbChannelPutField(dbChannel *chan, short type, const void *pbuffer,617long dbChannelPutField(dbChannel *chan, short type, const void *pbuffer,
714 long nRequest)618 long nRequest)
715{619{
716 return dbPutField(&chan->addr, type, pbuffer, nRequest);620 return dbPutFieldModifier(&chan->addr, type, pbuffer, nRequest,
621 &chan->addrModifier);
717}622}
718623
719void dbChannelShow(dbChannel *chan, int level, const unsigned short indent)624void dbChannelShow(dbChannel *chan, int level, const unsigned short indent)
@@ -767,6 +672,7 @@ void dbChannelDelete(dbChannel *chan)
767 freeListFree(chFilterFreeList, filter);672 freeListFree(chFilterFreeList, filter);
768 }673 }
769 free((char *) chan->name);674 free((char *) chan->name);
675 deleteArrayRangeModifier(&chan->addrModifier);
770 freeListFree(dbChannelFreeList, chan);676 freeListFree(dbChannelFreeList, chan);
771}677}
772678
diff --git a/modules/database/src/ioc/db/dbChannel.h b/modules/database/src/ioc/db/dbChannel.h
index 1825cb3..60fbb9d 100644
--- a/modules/database/src/ioc/db/dbChannel.h
+++ b/modules/database/src/ioc/db/dbChannel.h
@@ -23,6 +23,7 @@
23#include "epicsTypes.h"23#include "epicsTypes.h"
24#include "errMdef.h"24#include "errMdef.h"
25#include "db_field_log.h"25#include "db_field_log.h"
26#include "dbAddrModifier.h"
26#include "dbEvent.h"27#include "dbEvent.h"
27#include "dbCoreAPI.h"28#include "dbCoreAPI.h"
2829
@@ -50,21 +51,18 @@ typedef struct evSubscrip {
5051
51typedef struct chFilter chFilter;52typedef struct chFilter chFilter;
5253
53typedef long fastConvert(void *from, void *to, const dbAddr *paddr);
54
55/* A dbChannel points to a record field, and can have multiple filters */54/* A dbChannel points to a record field, and can have multiple filters */
56typedef struct dbChannel {55typedef struct dbChannel {
57 const char *name;56 const char *name;
58 dbAddr addr; /* address structure for record/field */57 dbAddr addr; /* address structure for record/field */
58 dbAddrModifier addrModifier;
59 /* optional: which indices are targeted */
59 long final_no_elements; /* final number of elements (arrays) */60 long final_no_elements; /* final number of elements (arrays) */
60 short final_field_size; /* final size of element */61 short final_field_size; /* final size of element */
61 short final_type; /* final type of database field */62 short final_type; /* final type of database field */
62 ELLLIST filters; /* list of filters as created from JSON */63 ELLLIST filters; /* list of filters as created from JSON */
63 ELLLIST pre_chain; /* list of filters to be called pre-event-queue */64 ELLLIST pre_chain; /* list of filters to be called pre-event-queue */
64 ELLLIST post_chain; /* list of filters to be called post-event-queue */65 ELLLIST post_chain; /* list of filters to be called post-event-queue */
65 /* Support for optimizing scalar requests */
66 fastConvert *getCvt; /* fast get conversion function */
67 short lastGetdbrType; /* last dbChannelGet dbrType */
68} dbChannel;66} dbChannel;
6967
70/* Prototype for the channel event function that is called in filter stacks68/* Prototype for the channel event function that is called in filter stacks
@@ -212,19 +210,11 @@ DBCORE_API extern unsigned short dbDBRnewToDBRold[];
212#define dbChannelField(pChan) ((pChan)->addr.pfield)210#define dbChannelField(pChan) ((pChan)->addr.pfield)
213211
214212
215<<<<<<< modules/database/src/ioc/db/dbChannel.h
216DBCORE_API long dbChannelGet(dbChannel *chan, short type,213DBCORE_API long dbChannelGet(dbChannel *chan, short type,
217 void *pbuffer, long *options, long *nRequest, void *pfl);214 void *pbuffer, long *options, long *nRequest, void *pfl);
218DBCORE_API long dbChannelGetField(dbChannel *chan, short type,215DBCORE_API long dbChannelGetField(dbChannel *chan, short type,
219 void *pbuffer, long *options, long *nRequest, void *pfl);216 void *pbuffer, long *options, long *nRequest, void *pfl);
220DBCORE_API long dbChannelPut(dbChannel *chan, short type,217DBCORE_API long dbChannelPut(dbChannel *chan, short type,
221=======
222epicsShareFunc long dbChannelGet(dbChannel *chan, short type,
223 void *pbuffer, long *options, long *nRequest, db_field_log *pfl);
224epicsShareFunc long dbChannelGetField(dbChannel *chan, short type,
225 void *pbuffer, long *options, long *nRequest, db_field_log *pfl);
226epicsShareFunc long dbChannelPut(dbChannel *chan, short type,
227>>>>>>> modules/database/src/ioc/db/dbChannel.h
228 const void *pbuffer, long nRequest);218 const void *pbuffer, long nRequest);
229DBCORE_API long dbChannelPutField(dbChannel *chan, short type,219DBCORE_API long dbChannelPutField(dbChannel *chan, short type,
230 const void *pbuffer, long nRequest);220 const void *pbuffer, long nRequest);
@@ -232,7 +222,6 @@ DBCORE_API void dbChannelShow(dbChannel *chan, int level,
232 const unsigned short indent);222 const unsigned short indent);
233DBCORE_API void dbChannelFilterShow(dbChannel *chan, int level,223DBCORE_API void dbChannelFilterShow(dbChannel *chan, int level,
234 const unsigned short indent);224 const unsigned short indent);
235<<<<<<< modules/database/src/ioc/db/dbChannel.h
236DBCORE_API void dbChannelDelete(dbChannel *chan);225DBCORE_API void dbChannelDelete(dbChannel *chan);
237226
238DBCORE_API void dbRegisterFilter(const char *key, const chFilterIf *fif, void *puser);227DBCORE_API void dbRegisterFilter(const char *key, const chFilterIf *fif, void *puser);
@@ -240,15 +229,6 @@ DBCORE_API db_field_log* dbChannelRunPreChain(dbChannel *chan, db_field_log *pLo
240DBCORE_API db_field_log* dbChannelRunPostChain(dbChannel *chan, db_field_log *pLogIn);229DBCORE_API db_field_log* dbChannelRunPostChain(dbChannel *chan, db_field_log *pLogIn);
241DBCORE_API const chFilterPlugin * dbFindFilter(const char *key, size_t len);230DBCORE_API const chFilterPlugin * dbFindFilter(const char *key, size_t len);
242DBCORE_API void dbChannelGetArrayInfo(dbChannel *chan,231DBCORE_API void dbChannelGetArrayInfo(dbChannel *chan,
243=======
244epicsShareFunc void dbChannelDelete(dbChannel *chan);
245
246epicsShareFunc void dbRegisterFilter(const char *key, const chFilterIf *fif, void *puser);
247epicsShareFunc db_field_log* dbChannelRunPreChain(dbChannel *chan, db_field_log *pLogIn);
248epicsShareFunc db_field_log* dbChannelRunPostChain(dbChannel *chan, db_field_log *pLogIn);
249epicsShareFunc const chFilterPlugin * dbFindFilter(const char *key, size_t len);
250epicsShareFunc void dbChannelGetArrayInfo(dbChannel *chan,
251>>>>>>> modules/database/src/ioc/db/dbChannel.h
252 void **pfield, long *no_elements, long *offset);232 void **pfield, long *no_elements, long *offset);
253233
254#ifdef __cplusplus234#ifdef __cplusplus
diff --git a/modules/database/src/ioc/db/dbDbLink.c b/modules/database/src/ioc/db/dbDbLink.c
index 0c9aa8f..fe7c372 100644
--- a/modules/database/src/ioc/db/dbDbLink.c
+++ b/modules/database/src/ioc/db/dbDbLink.c
@@ -58,6 +58,7 @@
58#include "dbBase.h"58#include "dbBase.h"
59#include "dbBkpt.h"59#include "dbBkpt.h"
60#include "dbCommonPvt.h"60#include "dbCommonPvt.h"
61#include "dbConvertFast.h"
61#include "dbConvert.h"62#include "dbConvert.h"
62#include "db_field_log.h"63#include "db_field_log.h"
63#include "db_access_routines.h"64#include "db_access_routines.h"
@@ -134,11 +135,9 @@ static void dbDbRemoveLink(struct dbLocker *locker, struct link *plink)
134 /* locker is NULL when an isolated IOC is closing its links */135 /* locker is NULL when an isolated IOC is closing its links */
135 if (locker) {136 if (locker) {
136 plink->value.pv_link.pvt = 0;137 plink->value.pv_link.pvt = 0;
138 plink->value.pv_link.getCvt = 0;
137 plink->value.pv_link.pvlMask = 0;139 plink->value.pv_link.pvlMask = 0;
138<<<<<<< modules/database/src/ioc/db/dbDbLink.c
139 plink->value.pv_link.lastGetdbrType = 0;140 plink->value.pv_link.lastGetdbrType = 0;
140=======
141>>>>>>> modules/database/src/ioc/db/dbDbLink.c
142 ellDelete(&precord->bklnk, &plink->value.pv_link.backlinknode);141 ellDelete(&precord->bklnk, &plink->value.pv_link.backlinknode);
143 dbLockSetSplit(locker, plink->precord, precord);142 dbLockSetSplit(locker, plink->precord, precord);
144 }143 }
@@ -168,10 +167,7 @@ static long dbDbGetValue(struct link *plink, short dbrType, void *pbuffer,
168{167{
169 struct pv_link *ppv_link = &plink->value.pv_link;168 struct pv_link *ppv_link = &plink->value.pv_link;
170 dbChannel *chan = linkChannel(plink);169 dbChannel *chan = linkChannel(plink);
171<<<<<<< modules/database/src/ioc/db/dbDbLink.c
172 DBADDR *paddr = &chan->addr;170 DBADDR *paddr = &chan->addr;
173=======
174>>>>>>> modules/database/src/ioc/db/dbDbLink.c
175 dbCommon *precord = plink->precord;171 dbCommon *precord = plink->precord;
176 db_field_log *pfl = NULL;172 db_field_log *pfl = NULL;
177 long status;173 long status;
@@ -183,7 +179,6 @@ static long dbDbGetValue(struct link *plink, short dbrType, void *pbuffer,
183 return status;179 return status;
184 }180 }
185181
186<<<<<<< modules/database/src/ioc/db/dbDbLink.c
187 if (ppv_link->getCvt && ppv_link->lastGetdbrType == dbrType)182 if (ppv_link->getCvt && ppv_link->lastGetdbrType == dbrType)
188 {183 {
189 /* shortcut: scalar with known conversion, no filter */184 /* shortcut: scalar with known conversion, no filter */
@@ -229,23 +224,6 @@ static long dbDbGetValue(struct link *plink, short dbrType, void *pbuffer,
229 }224 }
230225
231 if (!status && precord != dbChannelRecord(chan))226 if (!status && precord != dbChannelRecord(chan))
232=======
233 /* If filters are involved in a read, create field log and run filters */
234 if (ellCount(&chan->filters)) {
235 pfl = db_create_read_log(chan);
236 if (!pfl)
237 return S_db_noMemory;
238 pfl = dbChannelRunPreChain(chan, pfl);
239 pfl = dbChannelRunPostChain(chan, pfl);
240 }
241 status = dbChannelGet(chan, dbrType, pbuffer, NULL, pnRequest, pfl);
242 if (pfl)
243 db_delete_field_log(pfl);
244 if (status)
245 return status;
246
247 if (precord != dbChannelRecord(chan))
248>>>>>>> modules/database/src/ioc/db/dbDbLink.c
249 recGblInheritSevr(plink->value.pv_link.pvlMask & pvlOptMsMode,227 recGblInheritSevr(plink->value.pv_link.pvlMask & pvlOptMsMode,
250 plink->precord,228 plink->precord,
251 dbChannelRecord(chan)->stat, dbChannelRecord(chan)->sevr);229 dbChannelRecord(chan)->stat, dbChannelRecord(chan)->sevr);
@@ -386,9 +364,8 @@ static long dbDbPutValue(struct link *plink, short dbrType,
386 struct pv_link *ppv_link = &plink->value.pv_link;364 struct pv_link *ppv_link = &plink->value.pv_link;
387 dbChannel *chan = linkChannel(plink);365 dbChannel *chan = linkChannel(plink);
388 struct dbCommon *psrce = plink->precord;366 struct dbCommon *psrce = plink->precord;
389 DBADDR *paddr = &chan->addr;
390 dbCommon *pdest = dbChannelRecord(chan);367 dbCommon *pdest = dbChannelRecord(chan);
391 long status = dbPut(paddr, dbrType, pbuffer, nRequest);368 long status = dbChannelPut(chan, dbrType, pbuffer, nRequest);
392369
393 recGblInheritSevr(ppv_link->pvlMask & pvlOptMsMode, pdest, psrce->nsta,370 recGblInheritSevr(ppv_link->pvlMask & pvlOptMsMode, pdest, psrce->nsta,
394 psrce->nsev);371 psrce->nsev);
diff --git a/modules/database/src/ioc/db/dbEvent.h b/modules/database/src/ioc/db/dbEvent.h
index 0cefcab..2c6feca 100644
--- a/modules/database/src/ioc/db/dbEvent.h
+++ b/modules/database/src/ioc/db/dbEvent.h
@@ -51,7 +51,6 @@ epicsShareFunc int db_post_events (
51typedef void * dbEventCtx;51typedef void * dbEventCtx;
5252
53typedef void EXTRALABORFUNC (void *extralabor_arg);53typedef void EXTRALABORFUNC (void *extralabor_arg);
54void db_init_event_freelists (void);
55epicsShareFunc dbEventCtx db_init_events (void);54epicsShareFunc dbEventCtx db_init_events (void);
56epicsShareFunc int db_start_events (55epicsShareFunc int db_start_events (
57 dbEventCtx ctx, const char *taskname, void (*init_func)(void *),56 dbEventCtx ctx, const char *taskname, void (*init_func)(void *),
diff --git a/modules/database/src/ioc/db/dbUnitTest.c b/modules/database/src/ioc/db/dbUnitTest.c
index 0b40d92..1dd4909 100644
--- a/modules/database/src/ioc/db/dbUnitTest.c
+++ b/modules/database/src/ioc/db/dbUnitTest.c
@@ -267,7 +267,7 @@ done:
267void testdbPutArrFieldOk(const char* pv, short dbrType, unsigned long count, const void *pbuf)267void testdbPutArrFieldOk(const char* pv, short dbrType, unsigned long count, const void *pbuf)
268{268{
269 dbChannel *chan = dbChannelCreate(pv);269 dbChannel *chan = dbChannelCreate(pv);
270 long status;270 long status = 0;
271271
272 if(!chan || (status=dbChannelOpen(chan))) {272 if(!chan || (status=dbChannelOpen(chan))) {
273 testFail("Channel error (%p, %ld) : %s", chan, status, pv);273 testFail("Channel error (%p, %ld) : %s", chan, status, pv);
@@ -290,7 +290,7 @@ void testdbGetArrFieldEqual(const char* pv, short dbfType, long nRequest, unsign
290 const long vSize = dbValueSize(dbfType);290 const long vSize = dbValueSize(dbfType);
291 const long nStore = vSize * nRequest;291 const long nStore = vSize * nRequest;
292 long status = S_dbLib_recNotFound;292 long status = S_dbLib_recNotFound;
293 char *gbuf, *gstore;293 char *gbuf, *gstore = NULL;
294 const char *pbuf = pbufraw;294 const char *pbuf = pbufraw;
295295
296 if(!chan || (status=dbChannelOpen(chan))) {296 if(!chan || (status=dbChannelOpen(chan))) {
diff --git a/modules/database/src/ioc/db/db_field_log.h b/modules/database/src/ioc/db/db_field_log.h
index 5059fb2..e517d52 100644
--- a/modules/database/src/ioc/db/db_field_log.h
+++ b/modules/database/src/ioc/db/db_field_log.h
@@ -133,12 +133,9 @@ typedef struct db_field_log {
133 */133 */
134#define dbfl_has_copy(p)\134#define dbfl_has_copy(p)\
135 ((p) && ((p)->type==dbfl_type_val || (p)->u.r.dtor || (p)->no_elements==0))135 ((p) && ((p)->type==dbfl_type_val || (p)->u.r.dtor || (p)->no_elements==0))
136<<<<<<< modules/database/src/ioc/db/db_field_log.h
137136
138#define dbfl_pfield(p)\137#define dbfl_pfield(p)\
139 ((p)->type==dbfl_type_val ? &p->u.v.field : p->u.r.field)138 ((p)->type==dbfl_type_val ? &p->u.v.field : p->u.r.field)
140=======
141>>>>>>> modules/database/src/ioc/db/db_field_log.h
142139
143#ifdef __cplusplus140#ifdef __cplusplus
144}141}
diff --git a/modules/database/src/ioc/dbStatic/link.h b/modules/database/src/ioc/dbStatic/link.h
index b98181f..c3c0045 100644
--- a/modules/database/src/ioc/dbStatic/link.h
+++ b/modules/database/src/ioc/dbStatic/link.h
@@ -78,21 +78,15 @@ struct macro_link {
78};78};
7979
80struct dbCommon;80struct dbCommon;
81typedef long (*LINKCVT)();
8182
82struct pv_link {83struct pv_link {
83<<<<<<< modules/database/src/ioc/dbStatic/link.h
84 ELLNODE backlinknode;84 ELLNODE backlinknode;
85 char *pvname; /* pvname link points to */85 char *pvname; /* pvname link points to */
86 void *pvt; /* CA or DB private */86 void *pvt; /* CA or DB private */
87 LINKCVT getCvt; /* input conversion function */87 LINKCVT getCvt; /* input conversion function */
88 short pvlMask; /* Options mask */88 short pvlMask; /* Options mask */
89 short lastGetdbrType; /* last dbrType for DB or CA get */89 short lastGetdbrType; /* last dbrType for DB or CA get */
90=======
91 ELLNODE backlinknode;
92 char *pvname; /* pvname link points to */
93 void *pvt; /* CA or DB private */
94 short pvlMask; /* Options mask */
95>>>>>>> modules/database/src/ioc/dbStatic/link.h
96};90};
9791
98struct jlink;92struct jlink;
diff --git a/modules/database/src/std/filters/arr.c b/modules/database/src/std/filters/arr.c
index b35bdf5..ed42c67 100644
--- a/modules/database/src/std/filters/arr.c
+++ b/modules/database/src/std/filters/arr.c
@@ -13,6 +13,7 @@
1313
14#include <stdio.h>14#include <stdio.h>
1515
16#include "arrayRangeModifier.h"
16#include "chfPlugin.h"17#include "chfPlugin.h"
17#include "dbAccessDefs.h"18#include "dbAccessDefs.h"
18#include "dbExtractArray.h"19#include "dbExtractArray.h"
@@ -74,23 +75,6 @@ static void freeArray(db_field_log *pfl)
74 }75 }
75}76}
7677
77static long wrapArrayIndices(long *start, const long increment, long *end,
78 const long no_elements)
79{
80 if (*start < 0) *start = no_elements + *start;
81 if (*start < 0) *start = 0;
82 if (*start > no_elements) *start = no_elements;
83
84 if (*end < 0) *end = no_elements + *end;
85 if (*end < 0) *end = 0;
86 if (*end >= no_elements) *end = no_elements - 1;
87
88 if (*end - *start >= 0)
89 return 1 + (*end - *start) / increment;
90 else
91 return 0;
92}
93
94static db_field_log* filter(void* pvt, dbChannel *chan, db_field_log *pfl)78static db_field_log* filter(void* pvt, dbChannel *chan, db_field_log *pfl)
95{79{
96 myStruct *my = (myStruct*) pvt;80 myStruct *my = (myStruct*) pvt;
@@ -128,14 +112,7 @@ static db_field_log* filter(void* pvt, dbChannel *chan, db_field_log *pfl)
128 pfl->u.r.dtor = freeArray;112 pfl->u.r.dtor = freeArray;
129 pfl->u.r.pvt = my->arrayFreeList;113 pfl->u.r.pvt = my->arrayFreeList;
130 }114 }
131<<<<<<< modules/database/src/std/filters/arr.c
132 /* adjust no_elements (even if zero elements remain) */115 /* adjust no_elements (even if zero elements remain) */
133=======
134 /*
135 * Adjust offset and no_elements to refer to the new pTarget.
136 * (If zero elements remain, they need not refer to anything.)
137 */
138>>>>>>> modules/database/src/std/filters/arr.c
139 pfl->no_elements = nTarget;116 pfl->no_elements = nTarget;
140 if (must_lock)117 if (must_lock)
141 dbScanUnlock(dbChannelRecord(chan));118 dbScanUnlock(dbChannelRecord(chan));
diff --git a/modules/database/test/std/rec/Makefile b/modules/database/test/std/rec/Makefile
index e8c5464..71884b4 100644
--- a/modules/database/test/std/rec/Makefile
+++ b/modules/database/test/std/rec/Makefile
@@ -15,6 +15,7 @@ USR_CPPFLAGS += -DUSE_TYPED_DSET
1515
16TESTLIBRARY = dbRecStdTest16TESTLIBRARY = dbRecStdTest
1717
18dbRecStdTest_SRCS += arroutRecord.c
18dbRecStdTest_SRCS += asTestLib.c19dbRecStdTest_SRCS += asTestLib.c
19dbRecStdTest_LIBS += dbRecStd dbCore ca Com20dbRecStdTest_LIBS += dbRecStd dbCore ca Com
2021
@@ -23,6 +24,7 @@ PROD_LIBS = dbRecStdTest dbRecStd dbCore ca Com
23TARGETS += $(COMMON_DIR)/recTestIoc.dbd24TARGETS += $(COMMON_DIR)/recTestIoc.dbd
24DBDDEPENDS_FILES += recTestIoc.dbd$(DEP)25DBDDEPENDS_FILES += recTestIoc.dbd$(DEP)
25recTestIoc_DBD = base.dbd26recTestIoc_DBD = base.dbd
27recTestIoc_DBD += arroutRecord.dbd
26TESTFILES += $(COMMON_DIR)/recTestIoc.dbd28TESTFILES += $(COMMON_DIR)/recTestIoc.dbd
2729
28testHarness_SRCS += recTestIoc_registerRecordDeviceDriver.cpp30testHarness_SRCS += recTestIoc_registerRecordDeviceDriver.cpp
@@ -166,6 +168,13 @@ testHarness_SRCS += linkFilterTest.c
166TESTFILES += ../linkFilterTest.db168TESTFILES += ../linkFilterTest.db
167TESTS += linkFilterTest169TESTS += linkFilterTest
168170
171TESTPROD_HOST += addrModifierTest
172addrModifierTest_SRCS += addrModifierTest.c
173addrModifierTest_SRCS += recTestIoc_registerRecordDeviceDriver.cpp
174testHarness_SRCS += addrModifierTest.c
175TESTFILES += ../addrModifierTest.db
176TESTS += addrModifierTest
177
169# dbHeader* is only a compile test178# dbHeader* is only a compile test
170# no need to actually run179# no need to actually run
171TESTPROD += dbHeaderTest180TESTPROD += dbHeaderTest
@@ -199,5 +208,7 @@ endif
199208
200include $(TOP)/configure/RULES209include $(TOP)/configure/RULES
201210
211arroutRecord$(DEP): $(COMMON_DIR)/arroutRecord.h
212
202rtemsTestData.c : $(TESTFILES) $(TOOLS)/epicsMakeMemFs.pl213rtemsTestData.c : $(TESTFILES) $(TOOLS)/epicsMakeMemFs.pl
203 $(PERL) $(TOOLS)/epicsMakeMemFs.pl $@ epicsRtemsFSImage $(TESTFILES)214 $(PERL) $(TOOLS)/epicsMakeMemFs.pl $@ epicsRtemsFSImage $(TESTFILES)
diff --git a/modules/database/test/std/rec/addrModifierTest.c b/modules/database/test/std/rec/addrModifierTest.c
204new file mode 100644215new file mode 100644
index 0000000..d555b91
--- /dev/null
+++ b/modules/database/test/std/rec/addrModifierTest.c
@@ -0,0 +1,247 @@
1/*************************************************************************\
2* Copyright (c) 2020 Dirk Zimoch
3* EPICS BASE is distributed subject to a Software License Agreement found
4* in file LICENSE that is included with this distribution.
5\*************************************************************************/
6
7#include <string.h>
8
9#include "dbAccess.h"
10#include "devSup.h"
11#include "alarm.h"
12#include "dbUnitTest.h"
13#include "errlog.h"
14#include "epicsThread.h"
15
16#include "longinRecord.h"
17
18#include "testMain.h"
19
20void recTestIoc_registerRecordDeviceDriver(struct dbBase *);
21
22static void startTestIoc(const char *dbfile)
23{
24 testdbPrepare();
25 testdbReadDatabase("recTestIoc.dbd", NULL, NULL);
26 recTestIoc_registerRecordDeviceDriver(pdbbase);
27 testdbReadDatabase(dbfile, NULL, NULL);
28
29 eltc(0);
30 testIocInitOk();
31 eltc(1);
32}
33
34struct pv {
35 const char *name;
36 long count;
37 double values[4];
38};
39
40static void expectProcSuccess(struct pv *pv)
41{
42 char fieldname[20];
43 testdbPutFieldOk("reset.PROC", DBF_LONG, 1);
44 testDiag("expecting success from %s", pv->name);
45 sprintf(fieldname, "%s.PROC", pv->name);
46 testdbPutFieldOk(fieldname, DBF_LONG, 1);
47 sprintf(fieldname, "%s.SEVR", pv->name);
48 testdbGetFieldEqual(fieldname, DBF_LONG, NO_ALARM);
49 sprintf(fieldname, "%s.STAT", pv->name);
50 testdbGetFieldEqual(fieldname, DBF_LONG, NO_ALARM);
51}
52
53#if 0
54static void expectProcFailure(struct pv *pv)
55{
56 char fieldname[20];
57 testdbPutFieldOk("reset.PROC", DBF_LONG, 1);
58 testDiag("expecting failure S_db_badField %#x from %s", S_db_badField, pv->name);
59 sprintf(fieldname, "%s.PROC", pv->name);
60 testdbPutFieldFail(S_db_badField, fieldname, DBF_LONG, 1);
61 sprintf(fieldname, "%s.SEVR", pv->name);
62 testdbGetFieldEqual(fieldname, DBF_LONG, INVALID_ALARM);
63 sprintf(fieldname, "%s.STAT", pv->name);
64 testdbGetFieldEqual(fieldname, DBF_LONG, LINK_ALARM);
65}
66#endif
67
68static double initial[] = {0,1,2,3,4,5,6,7,8,9};
69static double buf[10];
70
71static void changeRange(struct pv *pv, long start, long incr, long end)
72{
73 char linkstring[60];
74 char pvstring[10];
75
76 if (incr)
77 sprintf(linkstring, "tgt.[%ld:%ld:%ld]", start, incr, end);
78 else if (end)
79 sprintf(linkstring, "tgt.[%ld:%ld]", start, end);
80 else
81 sprintf(linkstring, "tgt.[%ld]", start);
82 testDiag("modifying %s.OUT link: %s", pv->name, linkstring);
83 sprintf(pvstring, "%s.OUT", pv->name);
84 testdbPutFieldOk(pvstring, DBF_STRING, linkstring);
85}
86
87static void expectRange(double *values, long start, long incr, long end)
88{
89 int i,j;
90 if (!incr)
91 incr = 1;
92 for (i=0; i<10; i++)
93 buf[i] = initial[i];
94 for (i=0, j=start; j<=end; i++, j+=incr)
95 buf[j] = values[i];
96 testdbGetFieldEqual("tgt.NORD", DBF_LONG, 8);
97 testdbGetFieldEqual("tgt.SEVR", DBF_LONG, NO_ALARM);
98 testdbGetFieldEqual("tgt.STAT", DBF_LONG, NO_ALARM);
99 testdbGetArrFieldEqual("tgt.VAL", DBF_DOUBLE, 10, 8, buf);
100}
101
102#if 0
103static void expectEmptyArray(void)
104{
105 /* empty arrays are now allowed at the moment */
106 testDiag("expecting empty array");
107 testdbGetFieldEqual("tgt.NORD", DBF_LONG, 0);
108}
109#endif
110
111struct pv ao_pv = {"ao",1,{20,0,0,0}};
112struct pv src_pv = {"src",4,{30,40,50,60}};
113struct pv *ao = &ao_pv;
114struct pv *src = &src_pv;
115
116static double pini_values[] = {20,30,40,50};
117
118#define expectEmptyRange() expectRange(0,0,0,-1)
119
120MAIN(addrModifierTest)
121{
122 testPlan(205);
123 startTestIoc("addrModifierTest.db");
124
125 testdbGetFieldEqual("src.NORD", DBF_LONG, 4);
126 testDiag("PINI");
127 expectRange(pini_values,2,1,5);
128
129 testDiag("after processing");
130 testdbPutFieldOk("ao.PROC", DBF_LONG, 1);
131 testdbPutFieldOk("src.PROC", DBF_LONG, 1);
132 expectRange(pini_values,2,1,5);
133
134 testDiag("after processing target record");
135 testdbPutFieldOk("tgt.PROC", DBF_LONG, 1);
136 expectRange(pini_values,2,1,5);
137
138 testDiag("modify range");
139
140 changeRange(ao,3,0,0);
141 expectProcSuccess(ao);
142 expectRange(ao->values,3,1,3);
143
144 changeRange(src,4,0,7);
145 expectProcSuccess(src);
146 expectRange(src->values,4,1,7);
147
148 testDiag("put more than available");
149
150 changeRange(ao,3,0,6);
151 expectProcSuccess(ao);
152 expectRange(ao->values,3,1,3); /* clipped range */
153
154 changeRange(src,3,0,9);
155 expectProcSuccess(src);
156 expectRange(src->values,3,1,6); /* clipped range */
157
158 testDiag("backward range");
159
160 changeRange(ao,5,0,3);
161 expectProcSuccess(ao);
162 expectEmptyRange();
163
164 changeRange(src,5,0,3);
165 expectProcSuccess(src);
166 expectEmptyRange();
167
168 testDiag("step 2");
169
170 changeRange(ao,1,2,6);
171 expectProcSuccess(ao);
172 expectRange(ao->values,1,1,1); /* clipped range */
173
174 changeRange(src,1,2,6);
175 expectProcSuccess(src);
176 expectRange(src->values,1,2,5);
177
178 testDiag("range start beyond tgt.NORD");
179
180 changeRange(ao,8,0,0);
181 expectProcSuccess(ao);
182 expectEmptyRange();
183
184 changeRange(src,8,0,9);
185 expectProcSuccess(src);
186 expectEmptyRange();
187
188 testDiag("range end beyond tgt.NORD");
189
190 changeRange(ao,3,0,9);
191 expectProcSuccess(ao);
192 expectRange(ao->values,3,1,3); /* clipped range */
193
194 changeRange(src,3,0,9);
195 expectProcSuccess(src);
196 expectRange(src->values,3,1,6); /* clipped range */
197
198 testDiag("range start beyond tgt.NELM");
199
200 changeRange(ao,11,0,12);
201 expectProcSuccess(ao);
202 expectEmptyRange();
203
204 changeRange(src,11,0,12);
205 expectProcSuccess(src);
206 expectEmptyRange();
207
208 testDiag("range end beyond tgt.NELM");
209
210 changeRange(src,4,0,12);
211 expectProcSuccess(src);
212 expectRange(src->values,4,1,7); /* clipped range */
213
214 testDiag("single value beyond tgt.NORD");
215
216 changeRange(ao,8,0,0);
217 expectProcSuccess(ao);
218 expectEmptyRange();
219
220 changeRange(src,8,0,0);
221 expectProcSuccess(src);
222 expectEmptyRange();
223
224 testDiag("single value");
225
226 changeRange(ao,5,0,0);
227 expectProcSuccess(ao);
228 expectRange(ao->values,5,1,5);
229
230 changeRange(src,5,0,0);
231 expectProcSuccess(src);
232 expectRange(src->values,5,1,5);
233
234 testDiag("single values beyond tgt.NELM");
235
236 changeRange(ao,12,0,0);
237 expectProcSuccess(ao);
238 expectEmptyRange();
239
240 changeRange(src,12,0,0);
241 expectProcSuccess(src);
242 expectEmptyRange();
243
244 testIocShutdownOk();
245 testdbCleanup();
246 return testDone();
247}
diff --git a/modules/database/test/std/rec/addrModifierTest.db b/modules/database/test/std/rec/addrModifierTest.db
0new file mode 100644248new file mode 100644
index 0000000..ed28a80
--- /dev/null
+++ b/modules/database/test/std/rec/addrModifierTest.db
@@ -0,0 +1,24 @@
1record(arrout, "tgt") {
2 field(NELM, "10")
3 field(FTVL, "SHORT")
4 field(DOL, [0, 1, 2, 3, 4, 5, 6, 7])
5 field(PINI, "YES")
6}
7record(ao, "ao") {
8 field(OUT, "tgt.[2]")
9 field(DOL, 20)
10 field(PINI, "YES")
11}
12record(arrout, "src") {
13 field(NELM, "5")
14 field(FTVL, "DOUBLE")
15 field(DOL, [30.0, 40.0, 50.0, 60.0])
16 field(OUT, "tgt.[3:5]")
17 field(PINI, "YES")
18}
19record(arrout, "reset") {
20 field(NELM, "10")
21 field(FTVL, "SHORT")
22 field(DOL, [0, 1, 2, 3, 4, 5, 6, 7])
23 field(OUT, "tgt PP")
24}
diff --git a/modules/database/test/std/rec/arroutRecord.c b/modules/database/test/std/rec/arroutRecord.c
0new file mode 10064425new file mode 100644
index 0000000..61c7680
--- /dev/null
+++ b/modules/database/test/std/rec/arroutRecord.c
@@ -0,0 +1,169 @@
1/*************************************************************************\
2* Copyright (c) 2010 Brookhaven National Laboratory.
3* Copyright (c) 2010 Helmholtz-Zentrum Berlin
4* fuer Materialien und Energie GmbH.
5* Copyright (c) 2008 UChicago Argonne LLC, as Operator of Argonne
6* National Laboratory.
7* Copyright (c) 2002 The Regents of the University of California, as
8* Operator of Los Alamos National Laboratory.
9* EPICS BASE is distributed subject to a Software License Agreement found
10* in file LICENSE that is included with this distribution.
11\*************************************************************************/
12
13/* arroutRecord.c - minimal array output record for test purposes: no processing */
14
15/* adapted from: arrRecord.c
16 *
17 * Author: Ralph Lange <Ralph.Lange@bessy.de>
18 *
19 * vaguely implemented like parts of recWaveform.c by Bob Dalesio
20 *
21 */
22
23#include <stdio.h>
24
25#include "alarm.h"
26#include "epicsPrint.h"
27#include "dbAccess.h"
28#include "dbEvent.h"
29#include "dbFldTypes.h"
30#include "recSup.h"
31#include "recGbl.h"
32#include "cantProceed.h"
33#define GEN_SIZE_OFFSET
34#include "arroutRecord.h"
35#undef GEN_SIZE_OFFSET
36#include "epicsExport.h"
37
38/* Create RSET - Record Support Entry Table*/
39#define report NULL
40#define initialize NULL
41static long init_record(struct dbCommon *, int);
42static long process(struct dbCommon *);
43#define special NULL
44#define get_value NULL
45static long cvt_dbaddr(DBADDR *);
46static long get_array_info(DBADDR *, long *, long *);
47static long put_array_info(DBADDR *, long);
48#define get_units NULL
49#define get_precision NULL
50#define get_enum_str NULL
51#define get_enum_strs NULL
52#define put_enum_str NULL
53#define get_graphic_double NULL
54#define get_control_double NULL
55#define get_alarm_double NULL
56
57rset arroutRSET = {
58 RSETNUMBER,
59 report,
60 initialize,
61 init_record,
62 process,
63 special,
64 get_value,
65 cvt_dbaddr,
66 get_array_info,
67 put_array_info,
68 get_units,
69 get_precision,
70 get_enum_str,
71 get_enum_strs,
72 put_enum_str,
73 get_graphic_double,
74 get_control_double,
75 get_alarm_double
76};
77epicsExportAddress(rset, arroutRSET);
78
79static long init_record(struct dbCommon *pcommon, int pass)
80{
81 struct arroutRecord *prec = (struct arroutRecord *)pcommon;
82
83 if (pass == 0) {
84 if (prec->nelm <= 0)
85 prec->nelm = 1;
86 if (prec->ftvl > DBF_ENUM)
87 prec->ftvl = DBF_UCHAR;
88 prec->val = callocMustSucceed(prec->nelm, dbValueSize(prec->ftvl),
89 "arr calloc failed");
90 return 0;
91 } else {
92 /* copied from devWfSoft.c */
93 long nelm = prec->nelm;
94 long status = dbLoadLinkArray(&prec->dol, prec->ftvl, prec->val, &nelm);
95
96 if (!status && nelm > 0) {
97 prec->nord = nelm;
98 prec->udf = FALSE;
99 }
100 else
101 prec->nord = 0;
102 return status;
103 }
104}
105
106static long process(struct dbCommon *pcommon)
107{
108 struct arroutRecord *prec = (struct arroutRecord *)pcommon;
109 long status = 0;
110
111 prec->pact = TRUE;
112 /* read DOL */
113 if (!dbLinkIsConstant(&prec->dol)) {
114 long nReq = prec->nelm;
115
116 status = dbGetLink(&prec->dol, prec->ftvl, prec->val, 0, &nReq);
117 if (status) {
118 recGblSetSevr(prec, LINK_ALARM, INVALID_ALARM);
119 } else {
120 prec->nord = nReq;
121 }
122 }
123 /* soft "device support": write OUT */
124 status = dbPutLink(&prec->out, prec->ftvl, prec->val, prec->nord);
125 if (status) {
126 recGblSetSevr(prec, LINK_ALARM, INVALID_ALARM);
127 }
128 recGblGetTimeStamp(prec);
129 recGblResetAlarms(prec);
130 recGblFwdLink(prec);
131 prec->pact = FALSE;
132 return status;
133}
134
135static long cvt_dbaddr(DBADDR *paddr)
136{
137 arroutRecord *prec = (arroutRecord *) paddr->precord;
138 int fieldIndex = dbGetFieldIndex(paddr);
139
140 if (fieldIndex == arroutRecordVAL) {
141 paddr->pfield = prec->val;
142 paddr->no_elements = prec->nelm;
143 paddr->field_type = prec->ftvl;
144 paddr->field_size = dbValueSize(prec->ftvl);
145 paddr->dbr_field_type = prec->ftvl;
146 }
147 return 0;
148}
149
150static long get_array_info(DBADDR *paddr, long *no_elements, long *offset)
151{
152 arroutRecord *prec = (arroutRecord *) paddr->precord;
153
154 *no_elements = prec->nord;
155 *offset = prec->off;
156
157 return 0;
158}
159
160static long put_array_info(DBADDR *paddr, long nNew)
161{
162 arroutRecord *prec = (arroutRecord *) paddr->precord;
163
164 prec->nord = nNew;
165 if (prec->nord > prec->nelm)
166 prec->nord = prec->nelm;
167
168 return 0;
169}
diff --git a/modules/database/test/std/rec/arroutRecord.dbd b/modules/database/test/std/rec/arroutRecord.dbd
0new file mode 100644170new file mode 100644
index 0000000..2c99e64
--- /dev/null
+++ b/modules/database/test/std/rec/arroutRecord.dbd
@@ -0,0 +1,35 @@
1include "menuGlobal.dbd"
2include "menuConvert.dbd"
3include "menuScan.dbd"
4recordtype(arrout) {
5 include "dbCommon.dbd"
6 field(VAL, DBF_NOACCESS) {
7 prompt("Value")
8 special(SPC_DBADDR)
9 pp(TRUE)
10 extra("void *val")
11 }
12 field(OUT, DBF_OUTLINK) {
13 prompt("Output Link")
14 }
15 field(NELM, DBF_ULONG) {
16 prompt("Number of Elements")
17 special(SPC_NOMOD)
18 initial("1")
19 }
20 field(FTVL, DBF_MENU) {
21 prompt("Field Type of Value")
22 special(SPC_NOMOD)
23 menu(menuFtype)
24 }
25 field(NORD, DBF_ULONG) {
26 prompt("Number elements read")
27 special(SPC_NOMOD)
28 }
29 field(OFF, DBF_ULONG) {
30 prompt("Offset into array")
31 }
32 field(DOL, DBF_INLINK) {
33 prompt("Desired Output Link")
34 }
35}
diff --git a/modules/database/test/std/rec/linkFilterTest.c b/modules/database/test/std/rec/linkFilterTest.c
index 75c3421..1996c8c 100644
--- a/modules/database/test/std/rec/linkFilterTest.c
+++ b/modules/database/test/std/rec/linkFilterTest.c
@@ -48,11 +48,7 @@ static void expectProcFailure(const char *rec)
48 char fieldname[20];48 char fieldname[20];
49 testDiag("expecting failure S_db_badField %#x from %s", S_db_badField, rec);49 testDiag("expecting failure S_db_badField %#x from %s", S_db_badField, rec);
50 sprintf(fieldname, "%s.PROC", rec);50 sprintf(fieldname, "%s.PROC", rec);
51<<<<<<< modules/database/test/std/rec/linkFilterTest.c
52 testdbPutFieldFail(S_db_onlyOne, fieldname, DBF_LONG, 1);51 testdbPutFieldFail(S_db_onlyOne, fieldname, DBF_LONG, 1);
53=======
54 testdbPutFieldFail(S_db_badField, fieldname, DBF_LONG, 1);
55>>>>>>> modules/database/test/std/rec/linkFilterTest.c
56 sprintf(fieldname, "%s.SEVR", rec);52 sprintf(fieldname, "%s.SEVR", rec);
57 testdbGetFieldEqual(fieldname, DBF_LONG, INVALID_ALARM);53 testdbGetFieldEqual(fieldname, DBF_LONG, INVALID_ALARM);
58 sprintf(fieldname, "%s.STAT", rec);54 sprintf(fieldname, "%s.STAT", rec);
@@ -73,11 +69,7 @@ static void changeRange(long start, long stop, long step)
73 testdbPutFieldOk("wf.INP", DBF_STRING, linkstring);69 testdbPutFieldOk("wf.INP", DBF_STRING, linkstring);
74}70}
7571
76<<<<<<< modules/database/test/std/rec/linkFilterTest.c
77static const double buf[] = {1, 2, 3, 4, 5, 6, 7, 8, 0, 0, 2, 4, 6};72static const double buf[] = {1, 2, 3, 4, 5, 6, 7, 8, 0, 0, 2, 4, 6};
78=======
79static double buf[] = {1, 2, 3, 4, 5, 6, 7, 8, 0, 0, 2, 4, 6};
80>>>>>>> modules/database/test/std/rec/linkFilterTest.c
8173
82static void expectRange(long start, long end)74static void expectRange(long start, long end)
83{75{
@@ -87,7 +79,6 @@ static void expectRange(long start, long end)
87 testdbGetArrFieldEqual("wf.VAL", DBF_DOUBLE, n+2, n, buf+start);79 testdbGetArrFieldEqual("wf.VAL", DBF_DOUBLE, n+2, n, buf+start);
88}80}
8981
90<<<<<<< modules/database/test/std/rec/linkFilterTest.c
91static void expectEmptyArray(void)82static void expectEmptyArray(void)
92{83{
93 testDiag("expecting empty array");84 testDiag("expecting empty array");
@@ -97,20 +88,6 @@ static void expectEmptyArray(void)
97MAIN(linkFilterTest)88MAIN(linkFilterTest)
98{89{
99 testPlan(102);90 testPlan(102);
100=======
101#if 0
102static void expectEmptyArray(void)
103{
104 /* empty arrays are now allowed at the moment */
105 testDiag("expecting empty array");
106 testdbGetFieldEqual("wf.NORD", DBF_LONG, 0);
107}
108#endif
109
110MAIN(linkFilterTest)
111{
112 testPlan(98);
113>>>>>>> modules/database/test/std/rec/linkFilterTest.c
114 startTestIoc("linkFilterTest.db");91 startTestIoc("linkFilterTest.db");
11592
116 testDiag("PINI");93 testDiag("PINI");
@@ -126,10 +103,7 @@ MAIN(linkFilterTest)
126 changeRange(5,3,0);103 changeRange(5,3,0);
127 expectProcFailure("ai");104 expectProcFailure("ai");
128 expectProcSuccess("wf");105 expectProcSuccess("wf");
129<<<<<<< modules/database/test/std/rec/linkFilterTest.c
130 expectEmptyArray();106 expectEmptyArray();
131=======
132>>>>>>> modules/database/test/std/rec/linkFilterTest.c
133107
134 testDiag("step 2");108 testDiag("step 2");
135 changeRange(1,6,2);109 changeRange(1,6,2);
@@ -141,10 +115,7 @@ MAIN(linkFilterTest)
141 changeRange(8,9,0);115 changeRange(8,9,0);
142 expectProcFailure("ai");116 expectProcFailure("ai");
143 expectProcSuccess("wf");117 expectProcSuccess("wf");
144<<<<<<< modules/database/test/std/rec/linkFilterTest.c
145 expectEmptyArray();118 expectEmptyArray();
146=======
147>>>>>>> modules/database/test/std/rec/linkFilterTest.c
148119
149 testDiag("range end beyond src.NORD");120 testDiag("range end beyond src.NORD");
150 changeRange(3,9,0);121 changeRange(3,9,0);
@@ -167,10 +138,7 @@ MAIN(linkFilterTest)
167 changeRange(8,0,0);138 changeRange(8,0,0);
168 expectProcFailure("ai");139 expectProcFailure("ai");
169 expectProcSuccess("wf");140 expectProcSuccess("wf");
170<<<<<<< modules/database/test/std/rec/linkFilterTest.c
171 expectEmptyArray();141 expectEmptyArray();
172=======
173>>>>>>> modules/database/test/std/rec/linkFilterTest.c
174142
175 testDiag("single value");143 testDiag("single value");
176 changeRange(5,0,0);144 changeRange(5,0,0);
@@ -182,10 +150,7 @@ MAIN(linkFilterTest)
182 changeRange(12,0,0);150 changeRange(12,0,0);
183 expectProcFailure("ai");151 expectProcFailure("ai");
184 expectProcSuccess("wf");152 expectProcSuccess("wf");
185<<<<<<< modules/database/test/std/rec/linkFilterTest.c
186 expectEmptyArray();153 expectEmptyArray();
187=======
188>>>>>>> modules/database/test/std/rec/linkFilterTest.c
189154
190 testIocShutdownOk();155 testIocShutdownOk();
191 testdbCleanup();156 testdbCleanup();

Subscribers

People subscribed via source and target branches