Merge Dirk Zimoch's named-events-backward-compatibility branch into 3.15
This commit is contained in:
@@ -16,6 +16,14 @@
|
||||
|
||||
<!-- Insert new items immediately below here ... -->
|
||||
|
||||
<h3>Fix problem with numeric soft events</h3>
|
||||
<p>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 <tt>post_event()</tt> API is not marked deprecated any more.
|
||||
|
||||
<p>Also <code>scanpel</code> has been modified to accept a glob pattern for
|
||||
event name filtering and to show events with no connected records as well.</p>
|
||||
|
||||
<h3>Add osiSockOptMcastLoop_t and osiSockTest</h3>
|
||||
|
||||
<p>Added a new OS-independent typedef for multicast socket options, and a test
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 */
|
||||
}
|
||||
|
||||
142
src/std/rec/test/scanEventTest.c
Normal file
142
src/std/rec/test/scanEventTest.c
Normal file
@@ -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 <dirk.zimoch@psi.ch>
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#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();
|
||||
}
|
||||
16
src/std/rec/test/scanEventTest.db
Normal file
16
src/std/rec/test/scanEventTest.db
Normal file
@@ -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") {
|
||||
}
|
||||
Reference in New Issue
Block a user