diff --git a/documentation/RELEASE_NOTES.html b/documentation/RELEASE_NOTES.html index 33f12c441..d19664ea1 100644 --- a/documentation/RELEASE_NOTES.html +++ b/documentation/RELEASE_NOTES.html @@ -16,6 +16,14 @@ +

Fix problem with numeric soft events

+

Changing from numeric to named soft events introduced an incompatibility +when a numeric event 1-255 is converted from a DOUBLE, e.g. from a calc record. +The post_event() API is not marked deprecated any more. + +

Also scanpel has been modified to accept a glob pattern for +event name filtering and to show events with no connected records as well.

+

Add osiSockOptMcastLoop_t and osiSockTest

Added a new OS-independent typedef for multicast socket options, and a test diff --git a/src/ioc/db/dbScan.c b/src/ioc/db/dbScan.c index e0c48d4c3..e9dc178a2 100644 --- a/src/ioc/db/dbScan.c +++ b/src/ioc/db/dbScan.c @@ -111,8 +111,8 @@ static char *priorityName[NUM_CALLBACK_PRIORITIES] = { typedef struct event_list { CALLBACK callback[NUM_CALLBACK_PRIORITIES]; scan_list scan_list[NUM_CALLBACK_PRIORITIES]; - struct event_list *next; - char event_name[MAX_STRING_SIZE]; + struct event_list *next; + char eventname[1]; /* actually arbitrary size */ } event_list; static event_list * volatile pevent_list[256]; static epicsMutexId event_lock; @@ -249,11 +249,6 @@ void scanAdd(struct dbCommon *precord) event_list *pel; eventname = precord->evnt; - if (strlen(eventname) >= MAX_STRING_SIZE) { - recGblRecordError(S_db_badField, (void *)precord, - "scanAdd: too long EVNT value"); - return; - } prio = precord->prio; if (prio < 0 || prio >= NUM_CALLBACK_PRIORITIES) { recGblRecordError(-1, (void *)precord, @@ -317,24 +312,17 @@ void scanDelete(struct dbCommon *precord) recGblRecordError(-1, (void *)precord, "scanDelete detected illegal SCAN value"); } else if (scan == menuScanEvent) { - char* eventname; int prio; event_list *pel; scan_list *psl = 0; - eventname = precord->evnt; prio = precord->prio; if (prio < 0 || prio >= NUM_CALLBACK_PRIORITIES) { recGblRecordError(-1, (void *)precord, "scanDelete detected illegal PRIO field"); return; } - do /* multithreading: make sure pel is consistent */ - pel = pevent_list[0]; - while (pel != pevent_list[0]); - for (; pel; pel=pel->next) { - if (strcmp(pel->event_name, eventname) == 0) break; - } + pel = eventNameToHandle(precord->evnt); if (pel && (psl = &pel->scan_list[prio])) deleteFromList(precord, psl); } else if (scan == menuScanI_O_Intr) { @@ -422,14 +410,12 @@ int scanpel(const char* eventname) /* print event list */ int prio; event_list *pel; - do /* multithreading: make sure pel is consistent */ - pel = pevent_list[0]; - while (pel != pevent_list[0]); - for (; pel; pel = pel->next) { - if (!eventname || strcmp(pel->event_name, eventname) == 0) { + for (pel = pevent_list[0]; pel; pel = pel->next) { + if (!eventname || epicsStrGlobMatch(pel->eventname, eventname)) { + printf("Event \"%s\"\n", pel->eventname); for (prio = 0; prio < NUM_CALLBACK_PRIORITIES; prio++) { if (ellCount(&pel->scan_list[prio].list) == 0) continue; - sprintf(message, "Event \"%s\" Priority %s", pel->event_name, priorityName[prio]); + sprintf(message, " Priority %s", priorityName[prio]); printList(&pel->scan_list[prio], message); } } @@ -480,18 +466,51 @@ event_list *eventNameToHandle(const char *eventname) int prio; event_list *pel; static epicsThreadOnceId onceId = EPICS_THREAD_ONCE_INIT; + double eventnumber = 0; + size_t namelength; - if (!eventname || eventname[0] == 0) - return NULL; + if (!eventname) return NULL; + while (isspace((unsigned char)eventname[0])) eventname++; + if (!eventname[0]) return NULL; + namelength = strlen(eventname); + while (isspace((unsigned char)eventname[namelength-1])) namelength--; + + /* Backward compatibility with numeric events: + Treat any string that represents a double with an + integer part between 0 and 255 the same as the integer + because it is most probably a conversion from double + like from a calc record. + */ + if (epicsParseDouble(eventname, &eventnumber, NULL) == 0) + { + if (eventnumber >= 0 && eventnumber < 256) + { + if (eventnumber < 1) + return NULL; /* 0 is no event */ + if ((pel = pevent_list[(int)eventnumber]) != NULL) + return pel; + } + else + eventnumber = 0; /* not a numeric event between 1 and 255 */ + } epicsThreadOnce(&onceId, eventOnce, NULL); epicsMutexMustLock(event_lock); for (pel = pevent_list[0]; pel; pel=pel->next) { - if (strcmp(pel->event_name, eventname) == 0) break; + if (strncmp(pel->eventname, eventname, namelength) == 0 + && pel->eventname[namelength] == 0) + break; } if (pel == NULL) { - pel = dbCalloc(1, sizeof(event_list)); - strcpy(pel->event_name, eventname); + pel = dbCalloc(1, sizeof(event_list) + namelength); + if (eventnumber > 0) + { + /* backward compatibility: make all numeric events look like integers */ + sprintf(pel->eventname, "%i", (int)eventnumber); + pevent_list[(int)eventnumber] = pel; + } + else + strncpy(pel->eventname, eventname, namelength); for (prio = 0; prio < NUM_CALLBACK_PRIORITIES; prio++) { callbackSetUser(&pel->scan_list[prio], &pel->callback[prio]); callbackSetPriority(prio, &pel->callback[prio]); @@ -501,12 +520,6 @@ event_list *eventNameToHandle(const char *eventname) } pel->next=pevent_list[0]; pevent_list[0]=pel; - { /* backward compatibility */ - char* p; - long e = strtol(eventname, &p, 0); - if (*p == 0 && e > 0 && e <= 255) - pevent_list[e] = pel; - } } epicsMutexUnlock(event_lock); return pel; @@ -527,13 +540,8 @@ void postEvent(event_list *pel) /* backward compatibility */ void post_event(int event) { - event_list* pel; - if (event <= 0 || event > 255) return; - do { /* multithreading: make sure pel is consistent */ - pel = pevent_list[event]; - } while (pel != pevent_list[event]); - postEvent(pel); + postEvent(pevent_list[event]); } static void ioscanOnce(void *arg) diff --git a/src/ioc/db/dbScan.h b/src/ioc/db/dbScan.h index 28d2e133d..d3102712d 100644 --- a/src/ioc/db/dbScan.h +++ b/src/ioc/db/dbScan.h @@ -50,7 +50,7 @@ epicsShareFunc void scanCleanup(void); epicsShareFunc EVENTPVT eventNameToHandle(const char* event); epicsShareFunc void postEvent(EVENTPVT epvt); -epicsShareFunc void post_event(int event) EPICS_DEPRECATED; +epicsShareFunc void post_event(int event); epicsShareFunc void scanAdd(struct dbCommon *); epicsShareFunc void scanDelete(struct dbCommon *); epicsShareFunc double scanPeriod(int scan); diff --git a/src/std/rec/test/Makefile b/src/std/rec/test/Makefile index 5322c8e92..70b6e173e 100644 --- a/src/std/rec/test/Makefile +++ b/src/std/rec/test/Makefile @@ -37,6 +37,15 @@ testHarness_SRCS += analogMonitorTest_registerRecordDeviceDriver.cpp TESTFILES += $(COMMON_DIR)/analogMonitorTest.dbd ../analogMonitorTest.db TESTS += analogMonitorTest +TARGETS += $(COMMON_DIR)/scanEventTest.dbd +DBDDEPENDS_FILES += scanEventTest.dbd$(DEP) +scanEventTest_DBD += base.dbd +TESTPROD_HOST += scanEventTest +scanEventTest_SRCS += scanEventTest.c +scanEventTest_SRCS += scanEventTest_registerRecordDeviceDriver.cpp +TESTFILES += $(COMMON_DIR)/scanEventTest.dbd ../scanEventTest.db +TESTS += scanEventTest + TARGETS += $(COMMON_DIR)/regressTest.dbd DBDDEPENDS_FILES += regressTest.dbd$(DEP) regressTest_DBD += base.dbd diff --git a/src/std/rec/test/epicsRunRecordTests.c b/src/std/rec/test/epicsRunRecordTests.c index 092c0a6ce..1a7133c19 100644 --- a/src/std/rec/test/epicsRunRecordTests.c +++ b/src/std/rec/test/epicsRunRecordTests.c @@ -14,6 +14,7 @@ int analogMonitorTest(void); int arrayOpTest(void); +int scanEventTest(void); void epicsRunRecordTests(void) { @@ -22,6 +23,7 @@ void epicsRunRecordTests(void) runTest(analogMonitorTest); runTest(arrayOpTest); + runTest(scanEventTest); epicsExit(0); /* Trigger test harness */ } diff --git a/src/std/rec/test/scanEventTest.c b/src/std/rec/test/scanEventTest.c new file mode 100644 index 000000000..77acbbeca --- /dev/null +++ b/src/std/rec/test/scanEventTest.c @@ -0,0 +1,142 @@ +/*************************************************************************\ +* Copyright (c) 2018 Paul Scherrer Institut +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. + \*************************************************************************/ + +/* + * Author: Dirk Zimoch + */ + +#include +#include "dbStaticLib.h" +#include "dbAccessDefs.h" +#include "dbUnitTest.h" +#include "testMain.h" +#include "osiFileName.h" +#include "epicsThread.h" +#include "dbScan.h" + +void scanEventTest_registerRecordDeviceDriver(struct dbBase *); + +/* test name to event number: + num = 0 is no event, + num > 0 is numeric event + num < 0 is string event (use same unique number for aliases) +*/ +const struct {char* name; int num;} events[] = { +/* No events */ + {NULL, 0}, + {"", 0}, + {" ", 0}, + {"0", 0}, + {"0.000000", 0}, + {"-0.00000", 0}, + {"0.9", 0}, +/* Numeric events */ + {"2", 2}, + {"2.000000", 2}, + {"2.5", 2}, + {" 2.5 ", 2}, + {"+0x02", 2}, + {"3", 3}, +/* Named events */ + {"info 1", -1}, + {" info 1 ", -1}, + {"-0.9", -2}, + {"-2", -3}, + {"-2.000000", -4}, + {"-2.5", -5}, + {" -2.5 ", -5}, + {"nan", -6}, + {"NaN", -7}, + {"-NAN", -8}, + {"-inf", -9}, + {"inf", -10}, +}; + +MAIN(scanEventTest) +{ + int i, e; + int aliases[512] ; + int expected_count[512]; + #define INDX(i) 256-events[i].num + #define MAXEV 5 + + testPlan(NELEMENTS(events)*2+(MAXEV+1)*5); + + memset(aliases, 0, sizeof(aliases)); + memset(expected_count, 0, sizeof(expected_count)); + + if (dbReadDatabase(&pdbbase, "scanEventTest.dbd", + "." OSI_PATH_LIST_SEPARATOR ".." OSI_PATH_LIST_SEPARATOR + "../O.Common" OSI_PATH_LIST_SEPARATOR "O.Common", NULL)) + testAbort("Database description 'scanEventTest.dbd' not found"); + + scanEventTest_registerRecordDeviceDriver(pdbbase); + for (i = 0; i < NELEMENTS(events); i++) { + char substitutions[256]; + sprintf(substitutions, "N=%d,EVENT=%s", i, events[i].name); + if (dbReadDatabase(&pdbbase, "scanEventTest.db", + "." OSI_PATH_LIST_SEPARATOR "..", substitutions)) + testAbort("Error reading test database 'scanEventTest.db'"); + } + testIocInitOk(); + testDiag("Test if eventNameToHandle() strips spaces and handles numeric events"); + for (i = 0; i < NELEMENTS(events); i++) { + EVENTPVT pev = eventNameToHandle(events[i].name); + /* test that some names are not events (num=0) */ + if (events[i].num == 0) + testOk(pev == NULL, "\"%s\" -> no event", events[i].name); + else + { + expected_count[INDX(i)]++; /* +1 for postEvent */ + if (events[i].num > 0) + { + testOk(pev != NULL, "\"%s\" -> numeric event %d", events[i].name, events[i].num); + expected_count[INDX(i)]++; /* +1 for post_event */ + } + else + { + /* test that some strings resolve the same event (num!=0) */ + if (!aliases[INDX(i)]) + { + aliases[INDX(i)] = i; + testOk(pev != NULL, "\"%s\" -> new named event", events[i].name); + } + else + { + testOk(pev == eventNameToHandle(events[aliases[INDX(i)]].name), + "\"%s\" alias for \"%s\"", events[i].name, events[aliases[INDX(i)]].name); + } + } + } + post_event(events[i].num); /* triggers numeric events only */ + postEvent(pev); + } + + testDiag("Check calculated numeric events (backward compatibility)"); + for (e = 0; e <= MAXEV; e++) { + testdbPutFieldOk("eventnum", DBR_LONG, e); + testdbGetFieldEqual("e1", DBR_LONG, e); + testdbGetFieldEqual("e2", DBR_LONG, e); + testdbPutFieldOk("e3", DBR_LONG, e); + testdbPutFieldOk("e3.PROC", DBR_LONG, 1); + for (i = 0; i < NELEMENTS(events); i++) + if (e > 0 && e < 256 && events[i].num == e) { /* numeric events */ + expected_count[INDX(i)]+=3; /* +1 for eventnum->e1, +1 for e2<-eventnum, +1 for e3 */ + break; + } + } + /* Allow records to finish processing */ + epicsThreadSleep(0.1); + testDiag("Check if events have been processed the expected number of times"); + for (i = 0; i < NELEMENTS(events); i++) { + char pvname[100]; + sprintf(pvname, "c%d", i); + testDiag("Event \"%s\" expected %d times", events[i].name, expected_count[INDX(i)]); + testdbGetFieldEqual(pvname, DBR_LONG, expected_count[INDX(i)]); + } + + return testDone(); +} diff --git a/src/std/rec/test/scanEventTest.db b/src/std/rec/test/scanEventTest.db new file mode 100644 index 000000000..9118823d8 --- /dev/null +++ b/src/std/rec/test/scanEventTest.db @@ -0,0 +1,16 @@ +record(calc, "c$(N)") { + field(SCAN, "Event") + field(EVNT, "$(EVENT)") + field(CALC, "VAL+1") +} +record(dfanout, "eventnum") { + field(OUTA, "e1 PP") + field(FLNK, "e2") +} +record(event, "e1") { +} +record(event, "e2") { + field(INP, "eventnum") +} +record(event, "e3") { +}