Merge ~dirk.zimoch/epics-base:named-events-backward-compatibility into epics-base:3.15

Proposed by Dirk Zimoch
Status: Merged
Approved by: Andrew Johnson
Approved revision: 2b4a9632b7e21a60b80e1454e44db41d8128ad06
Merged at revision: a2ae07dfcdaf172dbef1db692714dfbfb0e3d5ec
Proposed branch: ~dirk.zimoch/epics-base:named-events-backward-compatibility
Merge into: epics-base:3.15
Diff against target: 400 lines (+225/-40)
7 files modified
documentation/RELEASE_NOTES.html (+8/-0)
src/ioc/db/dbScan.c (+47/-39)
src/ioc/db/dbScan.h (+1/-1)
src/std/rec/test/Makefile (+9/-0)
src/std/rec/test/epicsRunRecordTests.c (+2/-0)
src/std/rec/test/scanEventTest.c (+142/-0)
src/std/rec/test/scanEventTest.db (+16/-0)
Reviewer Review Type Date Requested Status
Ralph Lange Approve
Andrew Johnson Approve
mdavidsaver Needs Fixing
Review via email: mp+337850@code.launchpad.net

This proposal supersedes a proposal from 2018-02-13.

Description of the change

Make named soft events backward compatible to calculated numeric events (e.g by calc records).
Every event name that represents a number and that rounds down to an integer 1 ... 255 is a numeric events. If it rounds to 0 or is not finite (nan, +/-inf) it is no event.

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

Hi Dirk,

You can commit to a branch after you have submitted a Merge Request for it and the new commits will automatically appear in the Merge Request (check this one out, it has your b2d6b67 commit in it). Submitting a *new* MR implies there's something major that is different than before, maybe you rebased the branch. Please don't do resubmits for minor additional changes like this, they are unnecessary and send confusing signals to the reviewers.

I will take a look at this when I get a chance.

Thanks,

- Andrew

Revision history for this message
mdavidsaver (mdavidsaver) wrote : Posted in a previous version of this proposal

I haven't had a chance to do any run tests, but from reading the code it looks reasonable.

The only (minor) issue I see is that the test code for eventRecord needs to be moved under src/std/rec/test/

Revision history for this message
Dirk Zimoch (dirk.zimoch) wrote : Posted in a previous version of this proposal

Oh, I didn‘t know that.
Dirk

> Am 16.02.2018 um 17:17 schrieb Andrew Johnson <email address hidden>:
>
> Hi Dirk,
>
> You can commit to a branch after you have submitted a Merge Request for it and the new commits will automatically appear in the Merge Request (check this one out, it has your b2d6b67 commit in it). Submitting a *new* MR implies there's something major that is different than before, maybe you rebased the branch. Please don't do resubmits for minor additional changes like this, they are unnecessary and send confusing signals to the reviewers.
>
> I will take a look at this when I get a chance.
>
> Thanks,
>
> - Andrew
> --
> https://code.launchpad.net/~dirk.zimoch/epics-base/+git/epics-base/+merge/337644
> You are the owner of ~dirk.zimoch/epics-base:named-events-backward-compatibility.

Revision history for this message
Dirk Zimoch (dirk.zimoch) wrote : Posted in a previous version of this proposal

I had put it in db/test because my intent was to test the db event code,
not to test the records. The records are just the tool to test that the
db code works as intended. But if there is a problem having records in
db/test I see no problem moving it.

Dirk

On 16.02.2018 17:38, mdavidsaver wrote:
> I haven't had a chance to do any run tests, but from reading the code it looks reasonable.
>
> The only (minor) issue I see is that the test code for eventRecord needs to be moved under src/std/rec/test/
>
> Diff comments:
>
>> diff --git a/src/ioc/db/test/Makefile b/src/ioc/db/test/Makefile
>> index 215545f..77e485a 100644
>> --- a/src/ioc/db/test/Makefile
>> +++ b/src/ioc/db/test/Makefile
>> @@ -138,6 +138,21 @@ testHarness_SRCS += dbPutGetTest.db
>> TESTFILES += ../dbPutGetTest.db
>> TESTS += testPutGetTest
>>
>> +TARGETS += $(COMMON_DIR)/scanEventTest.dbd
>> +DBDDEPENDS_FILES += scanEventTest.dbd$(DEP)
>> +scanEventTest_DBD += menuGlobal.dbd
>> +scanEventTest_DBD += menuConvert.dbd
>> +scanEventTest_DBD += menuScan.dbd
>> +scanEventTest_DBD += eventRecord.dbd
>> +scanEventTest_DBD += devEventSoft.dbd
>> +scanEventTest_DBD += calcRecord.dbd
>> +scanEventTest_DBD += dfanoutRecord.dbd
>> +TESTPROD_HOST += scanEventTest
>> +scanEventTest_SRCS += scanEventTest.c
>> +scanEventTest_SRCS += scanEventTest_registerRecordDeviceDriver.cpp
>> +scanEventTest_LIBS = dbRecStd
>> +TESTS += scanEventTest
>
> Tests using libdbRecStd and base.dbd need to be under src/std/rec/test/ (cf. compressTest.c). So the test code needs to move.
>
>> +
>> # This runs all the test programs in a known working order:
>> testHarness_SRCS += epicsRunDbTests.c
>>
>
>

Revision history for this message
mdavidsaver (mdavidsaver) wrote :

> if there is a problem having records in db/test

cf src/Makefile. ioc/db/test can be build before libdbRecStd (containing record support) is linked, which will fail. Try "make distclean" then "make -C src ioc/db/test". Testing with parallel make will also catch issues like this.

> DIRS += ioc
> ioc_DEPEND_DIRS = libCom ca/client
>
> DIRS += ioc/db/test
> ioc/db/test_DEPEND_DIRS = ioc libCom/RTEMS
>
> DIRS += std
> std_DEPEND_DIRS = ioc libCom/RTEMS
>
> DIRS += std/rec/test
> std/rec/test_DEPEND_DIRS = std

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

I see...

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

> Try "make distclean" then "make -C src ioc/db/test".

Do you mean "make -C src/ioc/db/test" ?

$ make -C src std/rec/test/
make: Entering directory `[my top dir]/epics-base-3.15/src'
make: Nothing to be done for `std/rec/test/'.
make: Leaving directory `[my top dir]/epics-base-3.15/src

$ make -C src/ioc/db/test/
make: Entering directory `[my top dir]/epics-base-3.15/src/ioc/db/test'
perl -CSD [my top dir]/epics-base-3.15/bin/[my host arch]/makeMakefile.pl O.[my host arch] ../../../../..
Can't open perl script "[my top dir]/epics-base-3.15/bin/[my host arch]/makeMakefile.pl": No such file or directory

So it seems this does not work anyway.. ???

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

I have moved the test.

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

The new Release Notes entry should go at the top of the file (where it says "Insert new items immediately below here ..."). I don't think it needs to be quite so explicit about the new algorithm, a brief description in words would suffice (I won't insist you change the text if you don't want to, but see the question below). It should mention that the post_event() routine has been undeprecated again since there may be quite a lot of older software that still calls this entry-point so we don't intend to remove it.

Looking at your event number compatibility logic, if I've understood the code properly it seems that I can legally create an event named "-1" or "256", although neither could be triggered by post_event(), but the names "Inf" and "Nan" are seen as illegal and will be rejected. That doesn't seem terribly logical to me, why couldn't they be allowed as purely string names?

I haven't tried pulling the code yet, I'm assuming it builds and works as promised (I will check that on Linux at least when merging).

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

> Am 02.03.2018 um 21:31 schrieb Andrew Johnson <email address hidden>:
>
> The new Release Notes entry should go at the top of the file (where it says "Insert new items immediately below here ..."). I don't think it needs to be quite so explicit about the new algorithm, a brief description in words would suffice (I won't insist you change the text if you don't want to, but see the question below). It should mention that the post_event() routine has been undeprecated again since there may be quite a lot of older software that still calls this entry-point so we don't intend to remove it.

I‘ll change it. (On Monday)

>
> Looking at your event number compatibility logic, if I've understood the code properly it seems that I can legally create an event named "-1" or "256", although neither could be triggered by post_event(), but the names "Inf" and "Nan" are seen as illegal and will be rejected. That doesn't seem terribly logical to me, why couldn't they be allowed as purely string names?

Ralph suggested that.

>
> I haven't tried pulling the code yet, I'm assuming it builds and works as promised (I will check that on Linux at least when merging).
> --
> https://code.launchpad.net/~dirk.zimoch/epics-base/+git/epics-base/+merge/337850
> You are the owner of ~dirk.zimoch/epics-base:named-events-backward-compatibility.

Revision history for this message
Ralph Lange (ralph-lange) wrote :

Clarification.

> > Am 02.03.2018 um 21:31 schrieb Andrew Johnson <email address hidden>:
> >
> > Looking at your event number compatibility logic, if I've understood the
> code properly it seems that I can legally create an event named "-1" or "256",
> although neither could be triggered by post_event(), but the names "Inf" and
> "Nan" are seen as illegal and will be rejected. That doesn't seem terribly
> logical to me, why couldn't they be allowed as purely string names?
>
> Ralph suggested that.

Sorry - I must have been not very clear...

The issue - as I understood it - was implementing a mapping from double numbers (use case was a calc record) to db event names.

Dirk mentioned which double numbers he would see as illegal and not map to a valid db event name, and I suggested (or wanted to) that double numbers that are not finite (negative and positive infinity and the not-a-number value) should also not map to a db event name.
Given that the original numeric db event codes were [1-255], I don't see where infinite values would map to.

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

> Am 03.03.2018 um 17:21 schrieb Ralph Lange <email address hidden>:
>
> Clarification.
>
>>> Am 02.03.2018 um 21:31 schrieb Andrew Johnson <email address hidden>:
>>>
>>> Looking at your event number compatibility logic, if I've understood the
>> code properly it seems that I can legally create an event named "-1" or "256",
>> although neither could be triggered by post_event(), but the names "Inf" and
>> "Nan" are seen as illegal and will be rejected. That doesn't seem terribly
>> logical to me, why couldn't they be allowed as purely string names?
>>
>> Ralph suggested that.
>
> Sorry - I must have been not very clear...
>
> The issue - as I understood it - was implementing a mapping from double numbers (use case was a calc record) to db event names.
>
> Dirk mentioned which double numbers he would see as illegal and not map to a valid db event name, and I suggested (or wanted to) that double numbers that are not finite (negative and positive infinity and the not-a-number value) should also not map to a db event name.
> Given that the original numeric db event codes were [1-255], I don't see where infinite values would map to.
>

Hmmm... As this fix is about backward compatibility with numeric events 0-255, non-finite values do not really need special treatment. If an event „Inf“ is posted but no record waits for it, nothing happens. But if there is a record waiting for „Inf“ events, we have an even cooler feature. The original purpose of the fix was to trigger event 2 with 2.000000, nothing more. Maybe I simply remove the non-finite handling. But then with the same reasoning 0 needs no special treatment as „no event“. Simply have no records on event „0“. — Or have some if you want. Why not? But maybe do not trigger them with post_event()?

What about spaces? It probably causes more confusion than benefit to distinguish event „ “ from „ “. At the moment leading and trailing blanks are removed. So both are equal to the empty string, which is „no event“ for good reason.

What about other numbers as suggested recently? I am not really fond of the idea to extend the post_event() functionality.

BTW: The whole problem started with the double to string conversion using fixed 6 decimals instead of .PREC. But to change that may cause even more incompatibilities.
How backward compatible do we want/need to be?

> --
> https://code.launchpad.net/~dirk.zimoch/epics-base/+git/epics-base/+merge/337850
> You are the owner of ~dirk.zimoch/epics-base:named-events-backward-compatibility.

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

On 03.03.2018 17:21, Ralph Lange wrote:
> Given that the original numeric db event codes were [1-255], I don't see where infinite values would map to.

Infinite values would not map to numeric events and thus not be
available to post_event() anyway. But as named events they would be
available as to postEvent().

I re-enable them now.

Before, 0 was no event, because it was the default value for EVNT
fields. Now the default value is the empty string. Thus I will make "0"
a valid event.

But let's agree on some questions first:
* Should "0" be a different event from "0.000000" like now "256" is
different from "256.000000"?
* Or should any (short, long, signed, unsigned ?) number map to an
integer, but only 1...255 would be available to post_event().
* Or should even more numbers be available to post_event()?
* Is is correct to round down any such number (like 2.999999) to integer
(that would be the full backward compatibility), or should only actual
integer numbers like "2.000000" be treated this way (what I actually
expect calculated event numbers to look like)?

Dirk

Revision history for this message
Ralph Lange (ralph-lange) wrote :

Uh-oh.

As for my original concern, I misunderstood how this was going to be implemented.
I'm fine with mapping to appropriate strings that would be ignored (no records waiting) by default and could be used in an exception handling fashion if desired.

As for the numeric conversions, I would try to be reasonable and minimally invasive, e.g.
If and only if number is in the interval [1...256[ remove the fractional part.
Convert in the %f-variant of the %g style: don't show insignificant zeroes to the right of the decimal point, and don't include the decimal point on whole numbers.

Removing trailing and leading whitespace is fine.

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

My original fix was supposed to be minimally invasive: an integer 0-255 followed by ".000000" was treated as a numeric event, with 0 being no event. Then change requests came in.

> Convert in the %f-variant of the %g style: don't show insignificant zeroes to the right of the decimal point, and don't include the decimal point on whole numbers.

I tested three cases for the conversion from a calc[out] with VAL=2.0:
1. event.INP -> calcout.VAL (or calc)
This is no problem. calcout.PREC is used to convert the value, so I get "2" with the default PREC=0.
2. event.INP -> calcout.VAL CP
This uses precision=6! I get "2.000000"
I find this a bit strange because caget -d0 gives me "2".
3. calcout.OUT -> event.VAL
This uses precision=6 as well. I get "2.000000".

Maybe the problem should really be fixed in the conversion double->string. But this may break other stuff. I will investigate a bit...

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

Giving the event record a get_precision() function that sets precision=0 helps with case 3. But does that make sense? When converting a double to a string, the conversion should use PREC of the source record, not of the target record.

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

This now looks reasonable to me (I haven't built or tested it yet though).

Ralph have Dirk's latest changes resolved your concerns?

review: Approve
Revision history for this message
Ralph Lange (ralph-lange) wrote :

jajaja

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/documentation/RELEASE_NOTES.html b/documentation/RELEASE_NOTES.html
2index 33f12c4..d19664e 100644
3--- a/documentation/RELEASE_NOTES.html
4+++ b/documentation/RELEASE_NOTES.html
5@@ -16,6 +16,14 @@
6
7 <!-- Insert new items immediately below here ... -->
8
9+<h3>Fix problem with numeric soft events</h3>
10+<p>Changing from numeric to named soft events introduced an incompatibility
11+when a numeric event 1-255 is converted from a DOUBLE, e.g. from a calc record.
12+The <tt>post_event()</tt> API is not marked deprecated any more.
13+
14+<p>Also <code>scanpel</code> has been modified to accept a glob pattern for
15+event name filtering and to show events with no connected records as well.</p>
16+
17 <h3>Add osiSockOptMcastLoop_t and osiSockTest</h3>
18
19 <p>Added a new OS-independent typedef for multicast socket options, and a test
20diff --git a/src/ioc/db/dbScan.c b/src/ioc/db/dbScan.c
21index e0c48d4..e9dc178 100644
22--- a/src/ioc/db/dbScan.c
23+++ b/src/ioc/db/dbScan.c
24@@ -111,8 +111,8 @@ static char *priorityName[NUM_CALLBACK_PRIORITIES] = {
25 typedef struct event_list {
26 CALLBACK callback[NUM_CALLBACK_PRIORITIES];
27 scan_list scan_list[NUM_CALLBACK_PRIORITIES];
28- struct event_list *next;
29- char event_name[MAX_STRING_SIZE];
30+ struct event_list *next;
31+ char eventname[1]; /* actually arbitrary size */
32 } event_list;
33 static event_list * volatile pevent_list[256];
34 static epicsMutexId event_lock;
35@@ -249,11 +249,6 @@ void scanAdd(struct dbCommon *precord)
36 event_list *pel;
37
38 eventname = precord->evnt;
39- if (strlen(eventname) >= MAX_STRING_SIZE) {
40- recGblRecordError(S_db_badField, (void *)precord,
41- "scanAdd: too long EVNT value");
42- return;
43- }
44 prio = precord->prio;
45 if (prio < 0 || prio >= NUM_CALLBACK_PRIORITIES) {
46 recGblRecordError(-1, (void *)precord,
47@@ -317,24 +312,17 @@ void scanDelete(struct dbCommon *precord)
48 recGblRecordError(-1, (void *)precord,
49 "scanDelete detected illegal SCAN value");
50 } else if (scan == menuScanEvent) {
51- char* eventname;
52 int prio;
53 event_list *pel;
54 scan_list *psl = 0;
55
56- eventname = precord->evnt;
57 prio = precord->prio;
58 if (prio < 0 || prio >= NUM_CALLBACK_PRIORITIES) {
59 recGblRecordError(-1, (void *)precord,
60 "scanDelete detected illegal PRIO field");
61 return;
62 }
63- do /* multithreading: make sure pel is consistent */
64- pel = pevent_list[0];
65- while (pel != pevent_list[0]);
66- for (; pel; pel=pel->next) {
67- if (strcmp(pel->event_name, eventname) == 0) break;
68- }
69+ pel = eventNameToHandle(precord->evnt);
70 if (pel && (psl = &pel->scan_list[prio]))
71 deleteFromList(precord, psl);
72 } else if (scan == menuScanI_O_Intr) {
73@@ -422,14 +410,12 @@ int scanpel(const char* eventname) /* print event list */
74 int prio;
75 event_list *pel;
76
77- do /* multithreading: make sure pel is consistent */
78- pel = pevent_list[0];
79- while (pel != pevent_list[0]);
80- for (; pel; pel = pel->next) {
81- if (!eventname || strcmp(pel->event_name, eventname) == 0) {
82+ for (pel = pevent_list[0]; pel; pel = pel->next) {
83+ if (!eventname || epicsStrGlobMatch(pel->eventname, eventname)) {
84+ printf("Event \"%s\"\n", pel->eventname);
85 for (prio = 0; prio < NUM_CALLBACK_PRIORITIES; prio++) {
86 if (ellCount(&pel->scan_list[prio].list) == 0) continue;
87- sprintf(message, "Event \"%s\" Priority %s", pel->event_name, priorityName[prio]);
88+ sprintf(message, " Priority %s", priorityName[prio]);
89 printList(&pel->scan_list[prio], message);
90 }
91 }
92@@ -480,18 +466,51 @@ event_list *eventNameToHandle(const char *eventname)
93 int prio;
94 event_list *pel;
95 static epicsThreadOnceId onceId = EPICS_THREAD_ONCE_INIT;
96-
97- if (!eventname || eventname[0] == 0)
98- return NULL;
99+ double eventnumber = 0;
100+ size_t namelength;
101+
102+ if (!eventname) return NULL;
103+ while (isspace((unsigned char)eventname[0])) eventname++;
104+ if (!eventname[0]) return NULL;
105+ namelength = strlen(eventname);
106+ while (isspace((unsigned char)eventname[namelength-1])) namelength--;
107+
108+ /* Backward compatibility with numeric events:
109+ Treat any string that represents a double with an
110+ integer part between 0 and 255 the same as the integer
111+ because it is most probably a conversion from double
112+ like from a calc record.
113+ */
114+ if (epicsParseDouble(eventname, &eventnumber, NULL) == 0)
115+ {
116+ if (eventnumber >= 0 && eventnumber < 256)
117+ {
118+ if (eventnumber < 1)
119+ return NULL; /* 0 is no event */
120+ if ((pel = pevent_list[(int)eventnumber]) != NULL)
121+ return pel;
122+ }
123+ else
124+ eventnumber = 0; /* not a numeric event between 1 and 255 */
125+ }
126
127 epicsThreadOnce(&onceId, eventOnce, NULL);
128 epicsMutexMustLock(event_lock);
129 for (pel = pevent_list[0]; pel; pel=pel->next) {
130- if (strcmp(pel->event_name, eventname) == 0) break;
131+ if (strncmp(pel->eventname, eventname, namelength) == 0
132+ && pel->eventname[namelength] == 0)
133+ break;
134 }
135 if (pel == NULL) {
136- pel = dbCalloc(1, sizeof(event_list));
137- strcpy(pel->event_name, eventname);
138+ pel = dbCalloc(1, sizeof(event_list) + namelength);
139+ if (eventnumber > 0)
140+ {
141+ /* backward compatibility: make all numeric events look like integers */
142+ sprintf(pel->eventname, "%i", (int)eventnumber);
143+ pevent_list[(int)eventnumber] = pel;
144+ }
145+ else
146+ strncpy(pel->eventname, eventname, namelength);
147 for (prio = 0; prio < NUM_CALLBACK_PRIORITIES; prio++) {
148 callbackSetUser(&pel->scan_list[prio], &pel->callback[prio]);
149 callbackSetPriority(prio, &pel->callback[prio]);
150@@ -501,12 +520,6 @@ event_list *eventNameToHandle(const char *eventname)
151 }
152 pel->next=pevent_list[0];
153 pevent_list[0]=pel;
154- { /* backward compatibility */
155- char* p;
156- long e = strtol(eventname, &p, 0);
157- if (*p == 0 && e > 0 && e <= 255)
158- pevent_list[e] = pel;
159- }
160 }
161 epicsMutexUnlock(event_lock);
162 return pel;
163@@ -527,13 +540,8 @@ void postEvent(event_list *pel)
164 /* backward compatibility */
165 void post_event(int event)
166 {
167- event_list* pel;
168-
169 if (event <= 0 || event > 255) return;
170- do { /* multithreading: make sure pel is consistent */
171- pel = pevent_list[event];
172- } while (pel != pevent_list[event]);
173- postEvent(pel);
174+ postEvent(pevent_list[event]);
175 }
176
177
178 static void ioscanOnce(void *arg)
179diff --git a/src/ioc/db/dbScan.h b/src/ioc/db/dbScan.h
180index 28d2e13..d310271 100644
181--- a/src/ioc/db/dbScan.h
182+++ b/src/ioc/db/dbScan.h
183@@ -50,7 +50,7 @@ epicsShareFunc void scanCleanup(void);
184
185 epicsShareFunc EVENTPVT eventNameToHandle(const char* event);
186 epicsShareFunc void postEvent(EVENTPVT epvt);
187-epicsShareFunc void post_event(int event) EPICS_DEPRECATED;
188+epicsShareFunc void post_event(int event);
189 epicsShareFunc void scanAdd(struct dbCommon *);
190 epicsShareFunc void scanDelete(struct dbCommon *);
191 epicsShareFunc double scanPeriod(int scan);
192diff --git a/src/std/rec/test/Makefile b/src/std/rec/test/Makefile
193index 5322c8e..70b6e17 100644
194--- a/src/std/rec/test/Makefile
195+++ b/src/std/rec/test/Makefile
196@@ -37,6 +37,15 @@ testHarness_SRCS += analogMonitorTest_registerRecordDeviceDriver.cpp
197 TESTFILES += $(COMMON_DIR)/analogMonitorTest.dbd ../analogMonitorTest.db
198 TESTS += analogMonitorTest
199
200+TARGETS += $(COMMON_DIR)/scanEventTest.dbd
201+DBDDEPENDS_FILES += scanEventTest.dbd$(DEP)
202+scanEventTest_DBD += base.dbd
203+TESTPROD_HOST += scanEventTest
204+scanEventTest_SRCS += scanEventTest.c
205+scanEventTest_SRCS += scanEventTest_registerRecordDeviceDriver.cpp
206+TESTFILES += $(COMMON_DIR)/scanEventTest.dbd ../scanEventTest.db
207+TESTS += scanEventTest
208+
209 TARGETS += $(COMMON_DIR)/regressTest.dbd
210 DBDDEPENDS_FILES += regressTest.dbd$(DEP)
211 regressTest_DBD += base.dbd
212diff --git a/src/std/rec/test/epicsRunRecordTests.c b/src/std/rec/test/epicsRunRecordTests.c
213index 092c0a6..1a7133c 100644
214--- a/src/std/rec/test/epicsRunRecordTests.c
215+++ b/src/std/rec/test/epicsRunRecordTests.c
216@@ -14,6 +14,7 @@
217
218 int analogMonitorTest(void);
219 int arrayOpTest(void);
220+int scanEventTest(void);
221
222 void epicsRunRecordTests(void)
223 {
224@@ -22,6 +23,7 @@ void epicsRunRecordTests(void)
225 runTest(analogMonitorTest);
226
227 runTest(arrayOpTest);
228+ runTest(scanEventTest);
229
230 epicsExit(0); /* Trigger test harness */
231 }
232diff --git a/src/std/rec/test/scanEventTest.c b/src/std/rec/test/scanEventTest.c
233new file mode 100644
234index 0000000..595264a
235--- /dev/null
236+++ b/src/std/rec/test/scanEventTest.c
237@@ -0,0 +1,142 @@
238+/*************************************************************************\
239+* Copyright (c) 2018 Paul Scherrer Institut
240+* EPICS BASE is distributed subject to a Software License Agreement found
241+* in file LICENSE that is included with this distribution.
242+ \*************************************************************************/
243+
244+/*
245+ * Author: Dirk Zimoch <dirk.zimoch@psi.ch>
246+ */
247+
248+#include <string.h>
249+#include "dbStaticLib.h"
250+#include "dbAccessDefs.h"
251+#include "dbUnitTest.h"
252+#include "testMain.h"
253+#include "osiFileName.h"
254+#include "epicsThread.h"
255+#include "dbScan.h"
256+
257+void scanEventTest_registerRecordDeviceDriver(struct dbBase *);
258+
259+/* test name to event number:
260+ num = 0 is no event,
261+ num > 0 is numeric event
262+ num < 0 is string event (use same unique number for aliases)
263+*/
264+const struct {char* name; int num;} events[] = {
265+/* No events */
266+ {NULL, 0},
267+ {"", 0},
268+ {" ", 0},
269+ {"0", 0},
270+ {"0.000000", 0},
271+ {"-0.00000", 0},
272+ {"0.9", 0},
273+/* Numeric events */
274+ {"2", 2},
275+ {"2.000000", 2},
276+ {"2.5", 2},
277+ {" 2.5 ", 2},
278+ {"+0x02", 2},
279+ {"3", 3},
280+/* Named events */
281+ {"info 1", -1},
282+ {" info 1 ", -1},
283+ {"-0.9", -2},
284+ {"-2", -3},
285+ {"-2.000000", -4},
286+ {"-2.5", -5},
287+ {" -2.5 ", -5},
288+ {"nan", -6},
289+ {"NaN", -7},
290+ {"-NAN", -8},
291+ {"-inf", -9},
292+ {"inf", -10},
293+};
294+
295+MAIN(scanEventTest)
296+{
297+ int i, e;
298+ int aliases[512] ;
299+ int expected_count[512];
300+ #define INDX(i) 256-events[i].num
301+ #define MAXEV 5
302+
303+ testPlan(NELEMENTS(events)*2+(MAXEV+1)*5);
304+
305+ memset(aliases, 0, sizeof(aliases));
306+ memset(expected_count, 0, sizeof(expected_count));
307+
308+ if (dbReadDatabase(&pdbbase, "scanEventTest.dbd",
309+ "." OSI_PATH_LIST_SEPARATOR ".." OSI_PATH_LIST_SEPARATOR
310+ "../O.Common" OSI_PATH_LIST_SEPARATOR "O.Common", NULL))
311+ testAbort("Database description 'scanEventTest.dbd' not found");
312+
313+ scanEventTest_registerRecordDeviceDriver(pdbbase);
314+ for (i = 0; i < NELEMENTS(events); i++) {
315+ char substitutions[256];
316+ sprintf(substitutions, "N=%d,EVENT=%s", i, events[i].name);
317+ if (dbReadDatabase(&pdbbase, "scanEventTest.db",
318+ "." OSI_PATH_LIST_SEPARATOR "..", substitutions))
319+ testAbort("Error reading test database 'scanEventTest.db'");
320+ }
321+ testIocInitOk();
322+ testDiag("Test if eventNameToHandle() strips spaces and handles numeric events");
323+ for (i = 0; i < NELEMENTS(events); i++) {
324+ EVENTPVT pev = eventNameToHandle(events[i].name);
325+ /* test that some names are not events (num=0) */
326+ if (events[i].num == 0)
327+ testOk(pev == NULL, "\"%s\" -> no event", events[i].name);
328+ else
329+ {
330+ expected_count[INDX(i)]++; /* +1 for postEvent */
331+ if (events[i].num > 0)
332+ {
333+ testOk(pev != NULL, "\"%s\" -> numeric event %d", events[i].name, events[i].num);
334+ expected_count[INDX(i)]++; /* +1 for post_event */
335+ }
336+ else
337+ {
338+ /* test that some strings resolve the same event (num!=0) */
339+ if (!aliases[INDX(i)])
340+ {
341+ aliases[INDX(i)] = i;
342+ testOk(pev != NULL, "\"%s\" -> new named event", events[i].name);
343+ }
344+ else
345+ {
346+ testOk(pev == eventNameToHandle(events[aliases[INDX(i)]].name),
347+ "\"%s\" alias for \"%s\"", events[i].name, events[aliases[INDX(i)]].name);
348+ }
349+ }
350+ }
351+ post_event(events[i].num); /* triggers numeric events only */
352+ postEvent(pev);
353+ }
354+
355+ testDiag("Check calculated numeric events (backward compatibility)");
356+ for (e = 0; e <= MAXEV; e++) {
357+ testdbPutFieldOk("eventnum", DBR_LONG, e);
358+ testdbGetFieldEqual("e1", DBR_LONG, e);
359+ testdbGetFieldEqual("e2", DBR_LONG, e);
360+ testdbPutFieldOk("e3", DBR_LONG, e);
361+ testdbPutFieldOk("e3.PROC", DBR_LONG, 1);
362+ for (i = 0; i < NELEMENTS(events); i++)
363+ if (e > 0 && e < 256 && events[i].num == e) { /* numeric events */
364+ expected_count[INDX(i)]+=3; /* +1 for eventnum->e1, +1 for e2<-eventnum, +1 for e3 */
365+ break;
366+ }
367+ }
368+ /* Allow records to finish processing */
369+ epicsThreadSleep(0.1);
370+ testDiag("Check if events have been processed the expected number of times");
371+ for (i = 0; i < NELEMENTS(events); i++) {
372+ char pvname[100];
373+ sprintf(pvname, "c%d", i);
374+ testDiag("Event \"%s\" expected %d times", events[i].name, expected_count[INDX(i)]);
375+ testdbGetFieldEqual(pvname, DBR_LONG, expected_count[INDX(i)]);
376+ }
377+
378+ return testDone();
379+}
380diff --git a/src/std/rec/test/scanEventTest.db b/src/std/rec/test/scanEventTest.db
381new file mode 100644
382index 0000000..9118823
383--- /dev/null
384+++ b/src/std/rec/test/scanEventTest.db
385@@ -0,0 +1,16 @@
386+record(calc, "c$(N)") {
387+ field(SCAN, "Event")
388+ field(EVNT, "$(EVENT)")
389+ field(CALC, "VAL+1")
390+}
391+record(dfanout, "eventnum") {
392+ field(OUTA, "e1 PP")
393+ field(FLNK, "e2")
394+}
395+record(event, "e1") {
396+}
397+record(event, "e2") {
398+ field(INP, "eventnum")
399+}
400+record(event, "e3") {
401+}

Subscribers

People subscribed via source and target branches