Merged Ralph's fix-nan-inf-updates branch.

Added a few cosmetic changes en route.
This commit is contained in:
Andrew Johnson
2014-08-05 15:51:52 -05:00
19 changed files with 615 additions and 235 deletions

View File

@@ -75,6 +75,8 @@ std_DEPEND_DIRS = ioc libCom/RTEMS
DIRS += std/filters/test
std/filters/test_DEPEND_DIRS = std
DIRS += std/rec/test
std/rec/test_DEPEND_DIRS = std
include $(TOP)/configure/RULES_DIRS

View File

@@ -274,7 +274,34 @@ void recGblTSELwasModified(struct link *plink)
ppv_link->pvlMask |= pvlOptTSELisTime;
}
}
void recGblCheckDeadband(epicsFloat64 *poldval, const epicsFloat64 newval,
const epicsFloat64 deadband, unsigned *monitor_mask, const unsigned add_mask)
{
double delta = 0;
if (finite(newval) && finite(*poldval)) {
/* both are finite -> compare delta with deadband */
delta = *poldval - newval;
if (delta < 0.0) delta = -delta;
}
else if (!isnan(newval) != !isnan(*poldval) ||
!isinf(newval) != !isinf(*poldval)) {
/* one is NaN or +-inf, the other not -> send update */
delta = epicsINF;
}
else if (isinf(newval) && newval != *poldval) {
/* one is +inf, the other -inf -> send update */
delta = epicsINF;
}
if (delta > deadband) {
/* add bits to monitor mask */
*monitor_mask |= add_mask;
/* update last value monitored */
*poldval = newval;
}
}
static void getMaxRangeValues(short field_type, double *pupper_limit,
double *plower_limit)
{

View File

@@ -62,6 +62,8 @@ epicsShareFunc int recGblSetSevr(void *precord, epicsEnum16 new_stat,
epicsShareFunc void recGblFwdLink(void *precord);
epicsShareFunc void recGblGetTimeStamp(void *precord);
epicsShareFunc void recGblTSELwasModified(struct link *plink);
epicsShareFunc void recGblCheckDeadband(epicsFloat64 *poldval, const epicsFloat64 newval,
const epicsFloat64 deadband, unsigned *monitor_mask, const unsigned add_mask);
#ifdef __cplusplus
}

View File

@@ -81,6 +81,12 @@ TESTS += arrShorthandTest
TESTPROD_HOST += benchdbConvert
benchdbConvert_SRCS += benchdbConvert.c
TESTPROD_HOST += recGblCheckDeadbandTest
recGblCheckDeadbandTest_SRCS += recGblCheckDeadbandTest.c
recGblCheckDeadbandTest_SRCS += dbTestIoc_registerRecordDeviceDriver.cpp
testHarness_SRCS += recGblCheckDeadbandTest.c
TESTS += recGblCheckDeadbandTest
# The testHarness runs all the test programs in a known working order.
testHarness_SRCS += epicsRunDbTests.c

View File

@@ -25,6 +25,7 @@ int dbPutLinkTest(void);
int testDbChannel(void);
int chfPluginTest(void);
int arrShorthandTest(void);
int recGblCheckDeadbandTest(void);
void epicsRunDbTests(void)
{
@@ -38,6 +39,7 @@ void epicsRunDbTests(void)
runTest(dbPutLinkTest);
runTest(testDbChannel);
runTest(arrShorthandTest);
runTest(recGblCheckDeadbandTest);
runTest(chfPluginTest);
dbmfFreeChunks();

View File

@@ -0,0 +1,119 @@
/*************************************************************************\
* Copyright (c) 2014 ITER Organization.
* EPICS BASE is distributed subject to a Software License Agreement found
* in file LICENSE that is included with this distribution.
\*************************************************************************/
/*
* Author: Ralph Lange <Ralph.Lange@gmx.de>
*/
#include <string.h>
#include "recGbl.h"
#include "dbBase.h"
#include "epicsMath.h"
#include "epicsUnitTest.h"
#include "testMain.h"
/* Test parameters */
#define NO_OF_DEADBANDS 3
#define NO_OF_PATTERNS 19
void dbTestIoc_registerRecordDeviceDriver(struct dbBase *);
/* Indices for deadband value, test number, val in sequence */
static int idbnd, itest;
/* Different deadbands to test with */
static double t_Deadband[NO_OF_DEADBANDS] = { -1, 0, 1.5 };
/* Value sequences for each of the 16 tests */
static double t_SetValues[NO_OF_PATTERNS][2];
/* Expected updates (1=yes) for each sequence of each test of each deadband */
static int t_ExpectedUpdates[NO_OF_DEADBANDS][NO_OF_PATTERNS] = {
{ /* deadband = -1 */
1, 1, 1, 1,
1, 1, 1,
1, 1, 1, 1,
1, 1, 1, 1,
1, 1, 1, 1,
},
{ /* deadband = 0 */
1, 1, 0, 0,
1, 1, 1,
1, 0, 1, 1,
1, 1, 0, 1,
1, 1, 1, 0,
},
{ /* deadband = 1.5 */
0, 1, 0, 0,
1, 1, 1,
1, 0, 1, 1,
1, 1, 0, 1,
1, 1, 1, 0,
},
};
MAIN(recGblCheckDeadbandTest)
{
unsigned mask;
double oldval, newval;
/* Test patterns:
* 0: step less than deadband (of 1.5)
* 1: step larger than deadband (of 1.5)
* 2: no change
* 3: -0.0 -> +0.0
* ... all possible combinations of steps
* between: finite / NaN / -inf / +inf
*/
t_SetValues[ 0][0] = 1.0; t_SetValues[ 0][1] = 2.0;
t_SetValues[ 1][0] = 0.0; t_SetValues[ 1][1] = 2.0;
t_SetValues[ 2][0] = 0.0; t_SetValues[ 2][1] = 0.0;
t_SetValues[ 3][0] = -0.0; t_SetValues[ 3][1] = 0.0;
t_SetValues[ 4][0] = 1.0; t_SetValues[ 4][1] = epicsNAN;
t_SetValues[ 5][0] = 1.0; t_SetValues[ 5][1] = epicsINF;
t_SetValues[ 6][0] = 1.0; t_SetValues[ 6][1] = -epicsINF;
t_SetValues[ 7][0] = epicsNAN; t_SetValues[ 7][1] = 1.0;
t_SetValues[ 8][0] = epicsNAN; t_SetValues[ 8][1] = epicsNAN;
t_SetValues[ 9][0] = epicsNAN; t_SetValues[ 9][1] = epicsINF;
t_SetValues[10][0] = epicsNAN; t_SetValues[10][1] = -epicsINF;
t_SetValues[11][0] = epicsINF; t_SetValues[11][1] = 1.0;
t_SetValues[12][0] = epicsINF; t_SetValues[12][1] = epicsNAN;
t_SetValues[13][0] = epicsINF; t_SetValues[13][1] = epicsINF;
t_SetValues[14][0] = epicsINF; t_SetValues[14][1] = -epicsINF;
t_SetValues[15][0] = -epicsINF; t_SetValues[15][1] = 1.0;
t_SetValues[16][0] = -epicsINF; t_SetValues[16][1] = epicsNAN;
t_SetValues[17][0] = -epicsINF; t_SetValues[17][1] = epicsINF;
t_SetValues[18][0] = -epicsINF; t_SetValues[18][1] = -epicsINF;
testPlan(114);
/* Loop over all tested deadband values */
for (idbnd = 0; idbnd < NO_OF_DEADBANDS; idbnd++) {
/* Loop over all test patterns */
for (itest = 0; itest < NO_OF_PATTERNS; itest++) {
oldval = t_SetValues[itest][0];
newval = t_SetValues[itest][1];
mask = 0;
recGblCheckDeadband(&oldval, newval, t_Deadband[idbnd], &mask, 1);
/* Check expected vs. actual test result */
testOk(t_ExpectedUpdates[idbnd][itest] == mask,
"deadband=%2.1f: check for oldvalue=%f newvalue=%f (expected %d, got %d)",
t_Deadband[idbnd], t_SetValues[itest][0], t_SetValues[itest][1],
t_ExpectedUpdates[idbnd][itest], mask);
if (mask) {
testOk((oldval == newval) || (isnan(oldval) && isnan(newval)), "mask set, oldval equals newval");
} else {
testOk((oldval == t_SetValues[itest][0]) || (isnan(oldval) && isnan(t_SetValues[itest][0])),
"mask not set, oldval unchanged");
}
}
}
return testDone();
}

View File

@@ -16,6 +16,7 @@
#include <freeList.h>
#include <dbConvertFast.h>
#include <chfPlugin.h>
#include <recGbl.h>
#include <db_field_log.h>
#include <epicsExport.h>
@@ -56,17 +57,11 @@ static int parse_ok(void *pvt)
return 0;
}
static void shiftval (myStruct *my, double val) {
my->last = val;
if (my->mode == 1)
my->hyst = val * my->cval/100.;
}
static db_field_log* filter(void* pvt, dbChannel *chan, db_field_log *pfl) {
myStruct *my = (myStruct*) pvt;
long status;
double val, delta;
short drop = 0;
double val;
unsigned send = 1;
/*
* Only scalar values supported - strings, arrays, and conversion errors
@@ -81,19 +76,14 @@ static db_field_log* filter(void* pvt, dbChannel *chan, db_field_log *pfl) {
status = dbFastGetConvertRoutine[pfl->field_type][DBR_DOUBLE]
(localAddr.pfield, (void*) &val, &localAddr);
if (!status) {
if (isnan(my->last)) {
shiftval(my, val);
} else {
delta = fabs(my->last - val);
if (delta <= my->hyst) {
drop = 1;
} else {
shiftval(my, val);
}
send = 0;
recGblCheckDeadband(&my->last, val, my->hyst, &send, 1);
if (send && my->mode == 1) {
my->hyst = val * my->cval/100.;
}
}
}
if (drop) {
if (!send) {
db_delete_field_log(pfl);
return NULL;
} else return pfl;

View File

@@ -456,29 +456,13 @@ static void convert(aiRecord *prec)
static void monitor(aiRecord *prec)
{
unsigned short monitor_mask;
double delta;
unsigned monitor_mask = recGblResetAlarms(prec);
monitor_mask = recGblResetAlarms(prec);
/* check for value change */
delta = prec->mlst - prec->val;
if(delta<0.0) delta = -delta;
if (!(delta <= prec->mdel)) { /* Handles MDEL == NAN */
/* post events for value change */
monitor_mask |= DBE_VALUE;
/* update last value monitored */
prec->mlst = prec->val;
}
/* check for value change */
recGblCheckDeadband(&prec->mlst, prec->val, prec->mdel, &monitor_mask, DBE_VALUE);
/* check for archive change */
delta = prec->alst - prec->val;
if(delta<0.0) delta = -delta;
if (!(delta <= prec->adel)) { /* Handles ADEL == NAN */
/* post events on value field for archive change */
monitor_mask |= DBE_LOG;
/* update last archive value monitored */
prec->alst = prec->val;
}
/* check for archive change */
recGblCheckDeadband(&prec->alst, prec->val, prec->adel, &monitor_mask, DBE_ARCHIVE);
/* send out monitors connected to the value field */
if (monitor_mask){

View File

@@ -507,34 +507,19 @@ static void convert(aoRecord *prec, double value)
static void monitor(aoRecord *prec)
{
unsigned short monitor_mask;
double delta;
unsigned monitor_mask = recGblResetAlarms(prec);
monitor_mask = recGblResetAlarms(prec);
/* check for value change */
delta = prec->mlst - prec->val;
if(delta<0.0) delta = -delta;
if (!(delta <= prec->mdel)) { /* Handles MDEL == NAN */
/* post events for value change */
monitor_mask |= DBE_VALUE;
/* update last value monitored */
prec->mlst = prec->val;
}
/* check for archive change */
delta = prec->alst - prec->val;
if(delta<0.0) delta = -delta;
if (!(delta <= prec->adel)) { /* Handles ADEL == NAN */
/* post events on value field for archive change */
monitor_mask |= DBE_LOG;
/* update last archive value monitored */
prec->alst = prec->val;
}
/* check for value change */
recGblCheckDeadband(&prec->mlst, prec->val, prec->mdel, &monitor_mask, DBE_VALUE);
/* check for archive change */
recGblCheckDeadband(&prec->alst, prec->val, prec->adel, &monitor_mask, DBE_ARCHIVE);
/* send out monitors connected to the value field */
if (monitor_mask){
db_post_events(prec,&prec->val,monitor_mask);
}
/* send out monitors connected to the value field */
if (monitor_mask){
db_post_events(prec,&prec->val,monitor_mask);
}
if(prec->omod) monitor_mask |= (DBE_VALUE|DBE_LOG);
if(monitor_mask) {
prec->omod = FALSE;

View File

@@ -395,34 +395,23 @@ static void checkAlarms(calcRecord *prec, epicsTimeStamp *timeLast)
static void monitor(calcRecord *prec)
{
unsigned short monitor_mask;
double delta, *pnew, *pprev;
unsigned monitor_mask;
double *pnew, *pprev;
int i;
monitor_mask = recGblResetAlarms(prec);
/* check for value change */
delta = prec->mlst - prec->val;
if (delta < 0.0) delta = -delta;
if (!(delta <= prec->mdel)) { /* Handles MDEL == NAN */
/* post events for value change */
monitor_mask |= DBE_VALUE;
/* update last value monitored */
prec->mlst = prec->val;
}
recGblCheckDeadband(&prec->mlst, prec->val, prec->mdel, &monitor_mask, DBE_VALUE);
/* check for archive change */
delta = prec->alst - prec->val;
if (delta < 0.0) delta = -delta;
if (!(delta <= prec->adel)) { /* Handles ADEL == NAN */
/* post events on value field for archive change */
monitor_mask |= DBE_LOG;
/* update last archive value monitored */
prec->alst = prec->val;
}
recGblCheckDeadband(&prec->alst, prec->val, prec->adel, &monitor_mask, DBE_ARCHIVE);
/* send out monitors connected to the value field */
if (monitor_mask){
db_post_events(prec, &prec->val, monitor_mask);
}
/* check all input fields for changes*/
pnew = &prec->a;
pprev = &prec->la;

View File

@@ -622,50 +622,38 @@ static void execOutput(calcoutRecord *prec)
static void monitor(calcoutRecord *prec)
{
unsigned monitor_mask;
double delta;
double *pnew;
double *pprev;
int i;
unsigned monitor_mask;
double *pnew;
double *pprev;
int i;
monitor_mask = recGblResetAlarms(prec);
/* check for value change */
delta = prec->mlst - prec->val;
if (delta<0.0) delta = -delta;
if (!(delta <= prec->mdel)) { /* Handles MDEL == NAN */
/* post events for value change */
monitor_mask |= DBE_VALUE;
/* update last value monitored */
prec->mlst = prec->val;
}
/* check for archive change */
delta = prec->alst - prec->val;
if (delta<0.0) delta = -delta;
if (!(delta <= prec->adel)) { /* Handles ADEL == NAN */
/* post events on value field for archive change */
monitor_mask |= DBE_LOG;
/* update last archive value monitored */
prec->alst = prec->val;
}
monitor_mask = recGblResetAlarms(prec);
/* send out monitors connected to the value field */
if (monitor_mask){
db_post_events(prec, &prec->val, monitor_mask);
/* check for value change */
recGblCheckDeadband(&prec->mlst, prec->val, prec->mdel, &monitor_mask, DBE_VALUE);
/* check for archive change */
recGblCheckDeadband(&prec->alst, prec->val, prec->adel, &monitor_mask, DBE_ARCHIVE);
/* send out monitors connected to the value field */
if (monitor_mask){
db_post_events(prec, &prec->val, monitor_mask);
}
/* check all input fields for changes*/
for (i = 0, pnew = &prec->a, pprev = &prec->la; i<CALCPERFORM_NARGS;
i++, pnew++, pprev++) {
if ((*pnew != *pprev) || (monitor_mask&DBE_ALARM)) {
db_post_events(prec, pnew, monitor_mask|DBE_VALUE|DBE_LOG);
*pprev = *pnew;
}
/* check all input fields for changes*/
for (i = 0, pnew = &prec->a, pprev = &prec->la; i<CALCPERFORM_NARGS;
i++, pnew++, pprev++) {
if ((*pnew != *pprev) || (monitor_mask&DBE_ALARM)) {
db_post_events(prec, pnew, monitor_mask|DBE_VALUE|DBE_LOG);
*pprev = *pnew;
}
}
/* Check OVAL field */
if (prec->povl != prec->oval) {
db_post_events(prec, &prec->oval, monitor_mask|DBE_VALUE|DBE_LOG);
prec->povl = prec->oval;
}
return;
}
/* Check OVAL field */
if (prec->povl != prec->oval) {
db_post_events(prec, &prec->oval, monitor_mask|DBE_VALUE|DBE_LOG);
prec->povl = prec->oval;
}
return;
}
static int fetch_values(calcoutRecord *prec)

View File

@@ -259,37 +259,22 @@ static void checkAlarms(dfanoutRecord *prec)
static void monitor(dfanoutRecord *prec)
{
unsigned short monitor_mask;
unsigned monitor_mask = recGblResetAlarms(prec);
double delta;
/* check for value change */
recGblCheckDeadband(&prec->mlst, prec->val, prec->mdel, &monitor_mask, DBE_VALUE);
monitor_mask = recGblResetAlarms(prec);
/* check for value change */
delta = prec->mlst - prec->val;
if(delta<0) delta = -delta;
if (!(delta <= prec->mdel)) { /* Handles MDEL == NAN */
/* post events for value change */
monitor_mask |= DBE_VALUE;
/* update last value monitored */
prec->mlst = prec->val;
}
/* check for archive change */
delta = prec->alst - prec->val;
if(delta<0) delta = -delta;
if (!(delta <= prec->adel)) { /* Handles ADEL == NAN */
/* post events on value field for archive change */
monitor_mask |= DBE_LOG;
/* update last archive value monitored */
prec->alst = prec->val;
}
/* check for archive change */
recGblCheckDeadband(&prec->alst, prec->val, prec->adel, &monitor_mask, DBE_ARCHIVE);
/* send out monitors connected to the value field */
if (monitor_mask){
db_post_events(prec,&prec->val,monitor_mask);
}
return;
/* send out monitors connected to the value field */
if (monitor_mask){
db_post_events(prec,&prec->val,monitor_mask);
}
return;
}
static void push_values(dfanoutRecord *prec)
{
struct link *plink; /* structure of the link field */

View File

@@ -307,31 +307,18 @@ static void checkAlarms(selRecord *prec)
static void monitor(selRecord *prec)
{
unsigned short monitor_mask;
double delta;
unsigned monitor_mask;
double *pnew;
double *pprev;
int i;
monitor_mask = recGblResetAlarms(prec);
/* check for value change */
delta = prec->mlst - prec->val;
if(delta<0.0) delta = -delta;
if (!(delta <= prec->mdel)) { /* Handles MDEL == NAN */
/* post events for value change */
monitor_mask |= DBE_VALUE;
/* update last value monitored */
prec->mlst = prec->val;
}
recGblCheckDeadband(&prec->mlst, prec->val, prec->mdel, &monitor_mask, DBE_VALUE);
/* check for archive change */
delta = prec->alst - prec->val;
if(delta<0.0) delta = -delta;
if (!(delta <= prec->adel)) { /* Handles ADEL == NAN */
/* post events on value field for archive change */
monitor_mask |= DBE_LOG;
/* update last archive value monitored */
prec->alst = prec->val;
}
recGblCheckDeadband(&prec->alst, prec->val, prec->adel, &monitor_mask, DBE_ARCHIVE);
/* send out monitors connected to the value field */
if (monitor_mask)
@@ -367,62 +354,57 @@ static void do_sel(selRecord *prec)
pvalue = &prec->a;
switch (prec->selm){
case (selSELM_Specified):
if (prec->seln >= SEL_MAX) {
recGblSetSevr(prec,SOFT_ALARM,INVALID_ALARM);
return;
}
val = *(pvalue+prec->seln);
break;
if (prec->seln >= SEL_MAX) {
recGblSetSevr(prec,SOFT_ALARM,INVALID_ALARM);
return;
}
val = *(pvalue+prec->seln);
break;
case (selSELM_High_Signal):
val = -epicsINF;
for (i = 0; i < SEL_MAX; i++,pvalue++){
if (!isnan(*pvalue) && val < *pvalue) {
val = *pvalue;
prec->seln = i;
}
}
break;
val = -epicsINF;
for (i = 0; i < SEL_MAX; i++,pvalue++){
if (!isnan(*pvalue) && val < *pvalue) {
val = *pvalue;
prec->seln = i;
}
}
break;
case (selSELM_Low_Signal):
val = epicsINF;
for (i = 0; i < SEL_MAX; i++,pvalue++){
if (!isnan(*pvalue) && val > *pvalue) {
val = *pvalue;
prec->seln = i;
}
}
break;
val = epicsINF;
for (i = 0; i < SEL_MAX; i++,pvalue++){
if (!isnan(*pvalue) && val > *pvalue) {
val = *pvalue;
prec->seln = i;
}
}
break;
case (selSELM_Median_Signal):
count = 0;
order[0] = epicsNAN;
for (i = 0; i < SEL_MAX; i++,pvalue++){
if (!isnan(*pvalue)){
/* Insertion sort */
j = count;
while ((j > 0) && (order[j-1] > *pvalue)){
order[j] = order[j-1];
j--;
}
order[j] = *pvalue;
count++;
}
}
prec->seln = count;
val = order[count / 2];
break;
count = 0;
order[0] = epicsNAN;
for (i = 0; i < SEL_MAX; i++,pvalue++){
if (!isnan(*pvalue)){
/* Insertion sort */
j = count;
while ((j > 0) && (order[j-1] > *pvalue)){
order[j] = order[j-1];
j--;
}
order[j] = *pvalue;
count++;
}
}
prec->seln = count;
val = order[count / 2];
break;
default:
recGblSetSevr(prec,CALC_ALARM,INVALID_ALARM);
return;
}
if (!isinf(val)){
prec->val=val;
prec->udf=isnan(prec->val);
} else {
recGblSetSevr(prec,UDF_ALARM,MAJOR_ALARM);
/* If UDF is TRUE this alarm will be overwritten by checkAlarms*/
recGblSetSevr(prec,CALC_ALARM,INVALID_ALARM);
return;
}
prec->val = val;
prec->udf = isnan(prec->val);
return;
}
/*
* FETCH_VALUES
*

View File

@@ -373,35 +373,24 @@ static void checkAlarms(subRecord *prec)
static void monitor(subRecord *prec)
{
unsigned monitor_mask;
double delta;
double *pnew;
double *pold;
int i;
/* get alarm mask */
monitor_mask = recGblResetAlarms(prec);
/* check for value change */
delta = prec->val - prec->mlst;
if (delta < 0.0) delta = -delta;
if (!(delta <= prec->mdel)) { /* Handles MDEL == NAN */
/* post events for value change */
monitor_mask |= DBE_VALUE;
/* update last value monitored */
prec->mlst = prec->val;
}
recGblCheckDeadband(&prec->mlst, prec->val, prec->mdel, &monitor_mask, DBE_VALUE);
/* check for archive change */
delta = prec->val - prec->alst;
if (delta < 0.0) delta = -delta;
if (!(delta <= prec->adel)) { /* Handles ADEL == NAN */
/* post events on value field for archive change */
monitor_mask |= DBE_LOG;
/* update last archive value monitored */
prec->alst = prec->val;
}
recGblCheckDeadband(&prec->alst, prec->val, prec->adel, &monitor_mask, DBE_ARCHIVE);
/* send out monitors connected to the value field */
if (monitor_mask) {
db_post_events(prec, &prec->val, monitor_mask);
}
/* check all input fields for changes */
for (i = 0, pnew = &prec->a, pold = &prec->la;
i < INP_ARG_MAX; i++, pnew++, pold++) {

39
src/std/rec/test/Makefile Normal file
View File

@@ -0,0 +1,39 @@
#*************************************************************************
# Copyright (c) 2012 UChicago Argonne LLC, as Operator of Argonne
# National Laboratory.
# Copyright (c) 2002 The Regents of the University of California, as
# Operator of Los Alamos National Laboratory.
# EPICS BASE is distributed subject to a Software License Agreement found
# in the file LICENSE that is included with this distribution.
#*************************************************************************
TOP=../../../..
include $(TOP)/configure/CONFIG
PROD_LIBS = dbRecStd dbCore ca Com
TARGETS += $(COMMON_DIR)/analogMonitorTest.dbd
analogMonitorTest_DBD += base.dbd
TESTPROD_HOST += analogMonitorTest
analogMonitorTest_SRCS += analogMonitorTest.c
analogMonitorTest_SRCS += analogMonitorTest_registerRecordDeviceDriver.cpp
testHarness_SRCS += analogMonitorTest.c
testHarness_SRCS += analogMonitorTest_registerRecordDeviceDriver.cpp
TESTFILES += $(COMMON_DIR)/analogMonitorTest.dbd ../analogMonitorTest.db
TESTS += analogMonitorTest
# epicsRunTests runs all the test programs in a known working order.
testHarness_SRCS += epicsRunTests.c
recordTestHarness_SRCS += $(testHarness_SRCS)
recordTestHarness_SRCS_RTEMS += rtemsTestHarness.c
PROD_vxWorks = recordTestHarness
PROD_RTEMS = recordTestHarness
TESTSPEC_vxWorks = recordTestHarness.munch; epicsRunTests
TESTSPEC_RTEMS = recordTestHarness.boot; epicsRunTests
TESTSCRIPTS_HOST += $(TESTS:%=%.t)
include $(TOP)/configure/RULES

View File

@@ -0,0 +1,240 @@
/*************************************************************************\
* Copyright (c) 2014 ITER Organization.
* EPICS BASE is distributed subject to a Software License Agreement found
* in file LICENSE that is included with this distribution.
\*************************************************************************/
/*
* Author: Ralph Lange <Ralph.Lange@gmx.de>
*/
#include <string.h>
#include "registryFunction.h"
#include "osiFileName.h"
#include "epicsThread.h"
#include "epicsMath.h"
#include "epicsUnitTest.h"
#include "dbAccessDefs.h"
#include "dbStaticLib.h"
#include "dbEvent.h"
#include "caeventmask.h"
#include "db_field_log.h"
#include "chfPlugin.h"
#include "iocInit.h"
#include "testMain.h"
#include "epicsExport.h"
/* Test parameters */
#define NO_OF_RECORD_TYPES 7
#define NO_OF_DEADBANDS 3
#define NO_OF_PATTERNS 16
#define NO_OF_VALUES_PER_SEQUENCE 2
void analogMonitorTest_registerRecordDeviceDriver(struct dbBase *);
/* Indices for record type, deadband type, deadband value, test number, val in sequence */
static int irec, ityp, idbnd, itest, iseq;
/* Records to test with */
static const char t_Record[NO_OF_RECORD_TYPES][10] = {
{"ai"}, {"ao"}, {"calc"}, {"calcout"}, {"dfanout"}, {"sel"}, {"sub"},
};
/* Deadband types to test */
static const char t_DbndType[2][6] = { {".MDEL"}, {".ADEL"} };
/* Different deadbands to test with */
static double t_Deadband[NO_OF_DEADBANDS] = { -1, 0, 1.5 };
/* Value sequences for each of the 16 tests */
static double t_SetValues[NO_OF_PATTERNS][NO_OF_VALUES_PER_SEQUENCE];
/* Expected updates (1=yes) for each sequence of each test of each deadband */
static int t_ExpectedUpdates[NO_OF_DEADBANDS][NO_OF_PATTERNS][NO_OF_VALUES_PER_SEQUENCE] = {
{ /* deadband = -1 */
{1, 1}, {1, 1}, {1, 1}, {1, 1},
{1, 1}, {1, 1}, {1, 1}, {1, 1},
{1, 1}, {1, 1}, {1, 1}, {1, 1},
{1, 1}, {1, 1}, {1, 1}, {1, 1},
},
{ /* deadband = 0 */
{1, 1}, {0, 1}, {0, 0}, {0, 0},
{1, 1}, {1, 0}, {1, 1}, {1, 1},
{1, 1}, {1, 1}, {1, 0}, {1, 1},
{1, 1}, {1, 1}, {1, 1}, {1, 0},
},
{ /* deadband = 1.5 */
{0, 1}, {0, 1}, {0, 0}, {0, 0},
{1, 1}, {1, 0}, {1, 1}, {1, 1},
{1, 1}, {1, 1}, {1, 0}, {1, 1},
{1, 1}, {1, 1}, {1, 1}, {1, 0},
},
};
static int t_ReceivedUpdates[NO_OF_PATTERNS][NO_OF_VALUES_PER_SEQUENCE];
/* Dummy subroutine needed for sub record */
static long myTestSub(void *p) {
return 0;
}
/* Minimal pre-chain plugin to divert all monitors back into the test (before they hit the queue) */
static db_field_log* filter(void* pvt, dbChannel *chan, db_field_log *pfl) {
double val = *((double*)chan->addr.pfield);
/* iseq == -1 is the value reset before the test pattern -> do not count */
if (iseq >= 0) {
t_ReceivedUpdates[itest][iseq] = 1;
testOk((val == t_SetValues[itest][iseq]) || (isnan(val) && isnan(t_SetValues[itest][iseq])),
"update %d pattern %2d with %s = %2.1f (expected %f, got %f)",
iseq, itest, (ityp==0?"MDEL":"ADEL"), t_Deadband[idbnd], t_SetValues[itest][iseq], val);
}
db_delete_field_log(pfl);
return NULL;
}
static void channelRegisterPre(dbChannel *chan, void *pvt,
chPostEventFunc **cb_out, void **arg_out, db_field_log *probe)
{
*cb_out = filter;
}
static chfPluginIf pif = {
NULL, /* allocPvt, */
NULL, /* freePvt, */
NULL, /* parse_error, */
NULL, /* parse_ok, */
NULL, /* channel_open, */
channelRegisterPre,
NULL, /* channelRegisterPost, */
NULL, /* channel_report, */
NULL /* channel_close */
};
MAIN(analogMonitorTest)
{
dbChannel *pch;
const chFilterPlugin *plug;
const char test[] = "test";
dbEventCtx evtctx;
dbEventSubscription subscr;
unsigned mask;
struct dbAddr vaddr, daddr;
double val;
char chan[50]; /* Channel name */
char cval[50]; /* Name for test values */
char cdel[50]; /* Name for deadband values */
/* Test patterns:
* 0: step less than deadband (of 1.5)
* 1: step larger than deadband (of 1.5)
* 2: no change
* 3: -0.0 -> +0.0
* ... all possible combinations of steps
* between: finite / NaN / -inf / +inf
*/
t_SetValues[ 0][0] = 1.0; t_SetValues[ 0][1] = 2.0;
t_SetValues[ 1][0] = 0.0; t_SetValues[ 1][1] = 2.0;
t_SetValues[ 2][0] = 0.0; t_SetValues[ 2][1] = 0.0;
t_SetValues[ 3][0] = -0.0; t_SetValues[ 3][1] = 0.0;
t_SetValues[ 4][0] = epicsNAN; t_SetValues[ 4][1] = 1.0;
t_SetValues[ 5][0] = epicsNAN; t_SetValues[ 5][1] = epicsNAN;
t_SetValues[ 6][0] = epicsNAN; t_SetValues[ 6][1] = epicsINF;
t_SetValues[ 7][0] = epicsNAN; t_SetValues[ 7][1] = -epicsINF;
t_SetValues[ 8][0] = epicsINF; t_SetValues[ 8][1] = 1.0;
t_SetValues[ 9][0] = epicsINF; t_SetValues[ 9][1] = epicsNAN;
t_SetValues[10][0] = epicsINF; t_SetValues[10][1] = epicsINF;
t_SetValues[11][0] = epicsINF; t_SetValues[11][1] = -epicsINF;
t_SetValues[12][0] = -epicsINF; t_SetValues[12][1] = 1.0;
t_SetValues[13][0] = -epicsINF; t_SetValues[13][1] = epicsNAN;
t_SetValues[14][0] = -epicsINF; t_SetValues[14][1] = epicsINF;
t_SetValues[15][0] = -epicsINF; t_SetValues[15][1] = -epicsINF;
registryFunctionAdd("myTestSub", (REGISTRYFUNCTION) myTestSub);
testPlan(1793);
if (dbReadDatabase(&pdbbase, "analogMonitorTest.dbd",
"." OSI_PATH_LIST_SEPARATOR ".." OSI_PATH_LIST_SEPARATOR
"../O.Common" OSI_PATH_LIST_SEPARATOR "O.Common", NULL))
testAbort("Error reading database description 'analogMonitorTest.dbd'");
analogMonitorTest_registerRecordDeviceDriver(pdbbase);
if (dbReadDatabase(&pdbbase, "analogMonitorTest.db",
"." OSI_PATH_LIST_SEPARATOR "..", NULL))
testAbort("Error reading test database 'analogMonitorTest.db'");
/* Start the core IOC (no CA) */
iocBuildIsolated();
evtctx = db_init_events();
chfPluginRegister(test, &pif, NULL);
plug = dbFindFilter(test, strlen(test));
testOk(!!plug, "interceptor plugin registered");
/* Loop over all analog record types (one instance each) */
for (irec = 0; irec < NO_OF_RECORD_TYPES; irec++) {
strcpy(cval, t_Record[irec]);
strcpy(chan, cval);
strcat(chan, ".VAL{\"test\":{}}");
if ((strcmp(t_Record[irec], "sel") == 0)
|| (strcmp(t_Record[irec], "calc") == 0)
|| (strcmp(t_Record[irec], "calcout") == 0)) {
strcat(cval, ".A");
} else {
strcat(cval, ".VAL");
}
testDiag("--------------------------------------------------------");
testDiag("Testing the %s record", t_Record[irec]);
testDiag("--------------------------------------------------------");
pch = dbChannelCreate(chan);
testOk(!!pch, "dbChannel with test plugin created");
testOk(!dbChannelOpen(pch), "dbChannel opened");
dbNameToAddr(cval, &vaddr);
/* Loop over both tested deadband types */
for (ityp = 0; ityp < 2; ityp++) {
strcpy(cdel, t_Record[irec]);
strcat(cdel, t_DbndType[ityp]);
dbNameToAddr(cdel, &daddr);
mask = (ityp==0?DBE_VALUE:DBE_ARCHIVE);
subscr = db_add_event(evtctx, pch, NULL, NULL, mask);
db_event_enable(subscr);
/* Loop over all tested deadband values */
for (idbnd = 0; idbnd < NO_OF_DEADBANDS; idbnd++) {
testDiag("Test %s%s = %g", t_Record[irec], t_DbndType[ityp], t_Deadband[idbnd]);
dbPutField(&daddr, DBR_DOUBLE, (void*) &t_Deadband[idbnd], 1);
memset(t_ReceivedUpdates, 0, sizeof(t_ReceivedUpdates));
/* Loop over all test patterns */
for (itest = 0; itest < NO_OF_PATTERNS; itest++) {
iseq = -1;
val = 0.0;
dbPutField(&vaddr, DBR_DOUBLE, (void*) &val, 1);
/* Loop over the test sequence */
for (iseq = 0; iseq < NO_OF_VALUES_PER_SEQUENCE; iseq++) {
dbPutField(&vaddr, DBR_DOUBLE, (void*) &t_SetValues[itest][iseq], 1);
}
/* Check expected vs. actual monitors */
testOk( (t_ExpectedUpdates[idbnd][itest][0] == t_ReceivedUpdates[itest][0]) &&
(t_ExpectedUpdates[idbnd][itest][1] == t_ReceivedUpdates[itest][1]),
"pattern %2d with %s = %2.1f (expected %d-%d, got %d-%d)",
itest, (ityp==0?"MDEL":"ADEL"), t_Deadband[idbnd],
t_ExpectedUpdates[idbnd][itest][0], t_ExpectedUpdates[idbnd][itest][1],
t_ReceivedUpdates[itest][0], t_ReceivedUpdates[itest][1]);
}
}
db_cancel_event(subscr);
}
}
iocShutdown();
return testDone();
}

View File

@@ -0,0 +1,13 @@
record(ai, "ai") {}
record(ao, "ao") {}
record(calc, "calc") {
field(CALC, "A")
}
record(calcout, "calcout") {
field(CALC, "A")
}
record(dfanout, "dfanout") {}
record(sel, "sel") {}
record(sub, "sub") {
field(SNAM, "myTestSub")
}

View File

@@ -0,0 +1,24 @@
/*************************************************************************\
* Copyright (c) 2012 UChicago Argonne LLC, as Operator of Argonne
* National Laboratory.
* EPICS BASE is distributed subject to a Software License Agreement found
* in file LICENSE that is included with this distribution.
\*************************************************************************/
/*
* Run tests as a batch.
*/
#include "epicsUnitTest.h"
#include "epicsExit.h"
int analogMonitorTest(void);
void epicsRunTests(void)
{
testHarness();
runTest(analogMonitorTest);
epicsExit(0); /* Trigger test harness */
}

View File

@@ -0,0 +1,14 @@
/*************************************************************************\
* Copyright (c) 2012 UChicago Argonne LLC, as Operator of Argonne
* National Laboratory.
* EPICS BASE is distributed subject to a Software License Agreement found
* in file LICENSE that is included with this distribution.
\*************************************************************************/
extern void epicsRunTests(void);
int main(int argc, char **argv)
{
epicsRunTests(); /* calls epicsExit(0) */
return 0;
}