Merge remote-tracking branch 'int64b/master'
* int64b/master: (25 commits) std/rec: streamline variable names in int64in/int64out std/rec/test: recMiscTest check in64in/out ioc/db: dbUnitTest helps support int64 std/rec: ioc64in/outRecord typed rset minor std/rec: fix copyright in int64in sources std: remove SCM keywords from int64in and int64out sources std/rec: fix promptgroups in dbd.pod files std/rec: add and improve pod for int64in and int64out std/rec: add pod documentation content for int64out std/rec: add pod documentation for int64out record dbStatic: dbPutStringNum() overflow handling dbStatic: more informative error for bad field value dbStatic: dbPutStringNum(, "") not an error Release Notes cvtFastPerform: Rename reserved member names, fix for vxWorks Simplify epicsConvertDoubleToFloat() More cvtFastPerform changes Collate and report by precision Restructure cvtFastPerform, fix VxWorks build ...
This commit is contained in:
@@ -28,6 +28,8 @@ dbRecStd_SRCS += devBoDbState.c
|
||||
dbRecStd_SRCS += devCalcoutSoft.c
|
||||
dbRecStd_SRCS += devEventSoft.c
|
||||
dbRecStd_SRCS += devHistogramSoft.c
|
||||
dbRecStd_SRCS += devI64inSoft.c
|
||||
dbRecStd_SRCS += devI64outSoft.c
|
||||
dbRecStd_SRCS += devLiSoft.c
|
||||
dbRecStd_SRCS += devLoSoft.c
|
||||
dbRecStd_SRCS += devLsiSoft.c
|
||||
@@ -49,6 +51,7 @@ dbRecStd_SRCS += devGeneralTime.c
|
||||
|
||||
dbRecStd_SRCS += devAiSoftCallback.c
|
||||
dbRecStd_SRCS += devBiSoftCallback.c
|
||||
dbRecStd_SRCS += devI64inSoftCallback.c
|
||||
dbRecStd_SRCS += devLiSoftCallback.c
|
||||
dbRecStd_SRCS += devMbbiDirectSoftCallback.c
|
||||
dbRecStd_SRCS += devMbbiSoftCallback.c
|
||||
@@ -57,6 +60,7 @@ dbRecStd_SRCS += devSiSoftCallback.c
|
||||
dbRecStd_SRCS += devAoSoftCallback.c
|
||||
dbRecStd_SRCS += devBoSoftCallback.c
|
||||
dbRecStd_SRCS += devCalcoutSoftCallback.c
|
||||
dbRecStd_SRCS += devI64outSoftCallback.c
|
||||
dbRecStd_SRCS += devLoSoftCallback.c
|
||||
dbRecStd_SRCS += devLsoSoftCallback.c
|
||||
dbRecStd_SRCS += devMbboSoftCallback.c
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
/*************************************************************************\
|
||||
* Copyright (c) 2016 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 file LICENSE that is included with this distribution.
|
||||
\*************************************************************************/
|
||||
|
||||
/*
|
||||
* Original Author: Janet Anderson
|
||||
* Date: 09-23-91
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "alarm.h"
|
||||
#include "dbDefs.h"
|
||||
#include "dbAccess.h"
|
||||
#include "recGbl.h"
|
||||
#include "devSup.h"
|
||||
#include "int64inRecord.h"
|
||||
#include "epicsExport.h"
|
||||
|
||||
/* Create the dset for devI64inSoft */
|
||||
static long init_record(int64inRecord *prec);
|
||||
static long read_int64in(int64inRecord *prec);
|
||||
|
||||
struct {
|
||||
long number;
|
||||
DEVSUPFUN report;
|
||||
DEVSUPFUN init;
|
||||
DEVSUPFUN init_record;
|
||||
DEVSUPFUN get_ioint_info;
|
||||
DEVSUPFUN read_int64in;
|
||||
} devI64inSoft = {
|
||||
5,
|
||||
NULL,
|
||||
NULL,
|
||||
init_record,
|
||||
NULL,
|
||||
read_int64in
|
||||
};
|
||||
epicsExportAddress(dset, devI64inSoft);
|
||||
|
||||
static long init_record(int64inRecord *prec)
|
||||
{
|
||||
/* INP must be CONSTANT, PV_LINK, DB_LINK or CA_LINK*/
|
||||
switch (prec->inp.type) {
|
||||
case CONSTANT:
|
||||
if (recGblInitConstantLink(&prec->inp, DBF_INT64, &prec->val))
|
||||
prec->udf = FALSE;
|
||||
break;
|
||||
case PV_LINK:
|
||||
case DB_LINK:
|
||||
case CA_LINK:
|
||||
break;
|
||||
default:
|
||||
recGblRecordError(S_db_badField, (void *)prec,
|
||||
"devI64inSoft (init_record) Illegal INP field");
|
||||
return S_db_badField;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long read_int64in(int64inRecord *prec)
|
||||
{
|
||||
long status;
|
||||
|
||||
status = dbGetLink(&prec->inp, DBR_INT64, &prec->val, 0, 0);
|
||||
if (!status &&
|
||||
prec->tsel.type == CONSTANT &&
|
||||
prec->tse == epicsTimeEventDeviceTime)
|
||||
dbGetTimeStamp(&prec->inp, &prec->time);
|
||||
return status;
|
||||
}
|
||||
@@ -0,0 +1,222 @@
|
||||
/*************************************************************************\
|
||||
* Copyright (c) 2016 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 file LICENSE that is included with this distribution.
|
||||
\*************************************************************************/
|
||||
/* devI64inSoftCallback.c */
|
||||
/*
|
||||
* Authors: Marty Kraimer & Andrew Johnson
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "alarm.h"
|
||||
#include "callback.h"
|
||||
#include "cantProceed.h"
|
||||
#include "dbCommon.h"
|
||||
#include "dbDefs.h"
|
||||
#include "dbAccess.h"
|
||||
#include "dbChannel.h"
|
||||
#include "dbNotify.h"
|
||||
#include "epicsAssert.h"
|
||||
#include "recGbl.h"
|
||||
#include "recSup.h"
|
||||
#include "devSup.h"
|
||||
#include "link.h"
|
||||
#include "int64inRecord.h"
|
||||
#include "epicsExport.h"
|
||||
|
||||
|
||||
#define GET_OPTIONS (DBR_STATUS | DBR_TIME)
|
||||
|
||||
typedef struct devPvt {
|
||||
processNotify pn;
|
||||
CALLBACK callback;
|
||||
long options;
|
||||
int status;
|
||||
struct {
|
||||
DBRstatus
|
||||
DBRtime
|
||||
epicsInt32 value;
|
||||
} buffer;
|
||||
} devPvt;
|
||||
|
||||
|
||||
static void getCallback(processNotify *ppn, notifyGetType type)
|
||||
{
|
||||
int64inRecord *prec = (int64inRecord *)ppn->usrPvt;
|
||||
devPvt *pdevPvt = (devPvt *)prec->dpvt;
|
||||
long no_elements = 1;
|
||||
|
||||
if (ppn->status == notifyCanceled) {
|
||||
printf("devI64inSoftCallback::getCallback notifyCanceled\n");
|
||||
return;
|
||||
}
|
||||
|
||||
assert(type == getFieldType);
|
||||
pdevPvt->status = dbChannelGetField(ppn->chan, DBR_INT64,
|
||||
&pdevPvt->buffer, &pdevPvt->options, &no_elements, 0);
|
||||
}
|
||||
|
||||
static void doneCallback(processNotify *ppn)
|
||||
{
|
||||
int64inRecord *prec = (int64inRecord *)ppn->usrPvt;
|
||||
devPvt *pdevPvt = (devPvt *)prec->dpvt;
|
||||
|
||||
callbackRequestProcessCallback(&pdevPvt->callback, prec->prio, prec);
|
||||
}
|
||||
|
||||
static long add_record(dbCommon *pcommon)
|
||||
{
|
||||
int64inRecord *prec = (int64inRecord *)pcommon;
|
||||
DBLINK *plink = &prec->inp;
|
||||
dbChannel *chan;
|
||||
devPvt *pdevPvt;
|
||||
processNotify *ppn;
|
||||
|
||||
if (plink->type == CONSTANT) return 0;
|
||||
|
||||
if (plink->type != PV_LINK) {
|
||||
long status = S_db_badField;
|
||||
|
||||
recGblRecordError(status, (void *)prec,
|
||||
"devI64inSoftCallback (add_record) Illegal INP field");
|
||||
return status;
|
||||
}
|
||||
|
||||
chan = dbChannelCreate(plink->value.pv_link.pvname);
|
||||
if (!chan) {
|
||||
long status = S_db_notFound;
|
||||
|
||||
recGblRecordError(status, (void *)prec,
|
||||
"devI64inSoftCallback (init_record) linked record not found");
|
||||
return status;
|
||||
}
|
||||
|
||||
pdevPvt = calloc(1, sizeof(*pdevPvt));
|
||||
if (!pdevPvt) {
|
||||
long status = S_db_noMemory;
|
||||
|
||||
recGblRecordError(status, (void *)prec,
|
||||
"devI64inSoftCallback (add_record) out of memory, calloc() failed");
|
||||
return status;
|
||||
}
|
||||
ppn = &pdevPvt->pn;
|
||||
|
||||
plink->type = PN_LINK;
|
||||
plink->value.pv_link.pvlMask &= pvlOptMsMode; /* Severity flags only */
|
||||
|
||||
ppn->usrPvt = prec;
|
||||
ppn->chan = chan;
|
||||
ppn->getCallback = getCallback;
|
||||
ppn->doneCallback = doneCallback;
|
||||
ppn->requestType = processGetRequest;
|
||||
|
||||
pdevPvt->options = GET_OPTIONS;
|
||||
|
||||
prec->dpvt = pdevPvt;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long del_record(dbCommon *pcommon) {
|
||||
int64inRecord *prec = (int64inRecord *)pcommon;
|
||||
DBLINK *plink = &prec->inp;
|
||||
devPvt *pdevPvt = (devPvt *)prec->dpvt;
|
||||
|
||||
if (plink->type == CONSTANT) return 0;
|
||||
assert(plink->type == PN_LINK);
|
||||
|
||||
dbNotifyCancel(&pdevPvt->pn);
|
||||
dbChannelDelete(pdevPvt->pn.chan);
|
||||
free(pdevPvt);
|
||||
|
||||
plink->type = PV_LINK;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct dsxt dsxtSoftCallback = {
|
||||
add_record, del_record
|
||||
};
|
||||
|
||||
static long init(int pass)
|
||||
{
|
||||
if (pass == 0) devExtend(&dsxtSoftCallback);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long init_record(int64inRecord *prec)
|
||||
{
|
||||
/* INP must be CONSTANT or PN_LINK */
|
||||
switch (prec->inp.type) {
|
||||
case CONSTANT:
|
||||
if (recGblInitConstantLink(&prec->inp, DBR_INT64, &prec->val))
|
||||
prec->udf = FALSE;
|
||||
break;
|
||||
case PN_LINK:
|
||||
/* Handled by add_record */
|
||||
break;
|
||||
default:
|
||||
recGblRecordError(S_db_badField, (void *)prec,
|
||||
"devI64inSoftCallback (init_record) Illegal INP field");
|
||||
prec->pact = TRUE;
|
||||
return S_db_badField;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long read_int64in(int64inRecord *prec)
|
||||
{
|
||||
devPvt *pdevPvt = (devPvt *)prec->dpvt;
|
||||
|
||||
if (!prec->dpvt)
|
||||
return 0;
|
||||
|
||||
if (!prec->pact) {
|
||||
dbProcessNotify(&pdevPvt->pn);
|
||||
prec->pact = TRUE;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (pdevPvt->status) {
|
||||
recGblSetSevr(prec, READ_ALARM, INVALID_ALARM);
|
||||
return pdevPvt->status;
|
||||
}
|
||||
|
||||
prec->val = pdevPvt->buffer.value;
|
||||
prec->udf = FALSE;
|
||||
|
||||
switch (prec->inp.value.pv_link.pvlMask & pvlOptMsMode) {
|
||||
case pvlOptNMS:
|
||||
break;
|
||||
case pvlOptMSI:
|
||||
if (pdevPvt->buffer.severity < INVALID_ALARM)
|
||||
break;
|
||||
/* else fall through */
|
||||
case pvlOptMS:
|
||||
recGblSetSevr(prec, LINK_ALARM, pdevPvt->buffer.severity);
|
||||
break;
|
||||
case pvlOptMSS:
|
||||
recGblSetSevr(prec, pdevPvt->buffer.status,
|
||||
pdevPvt->buffer.severity);
|
||||
break;
|
||||
}
|
||||
|
||||
if (prec->tsel.type == CONSTANT &&
|
||||
prec->tse == epicsTimeEventDeviceTime)
|
||||
prec->time = pdevPvt->buffer.time;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Create the dset for devI64inSoftCallback */
|
||||
struct {
|
||||
dset common;
|
||||
DEVSUPFUN read_int64in;
|
||||
} devI64inSoftCallback = {
|
||||
{5, NULL, init, init_record, NULL},
|
||||
read_int64in
|
||||
};
|
||||
epicsExportAddress(dset, devI64inSoftCallback);
|
||||
@@ -0,0 +1,57 @@
|
||||
/*************************************************************************\
|
||||
* Copyright (c) 2016 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 file LICENSE that is included with this distribution.
|
||||
\*************************************************************************/
|
||||
|
||||
/*
|
||||
* Original Author: Janet Anderson
|
||||
* Date: 09-23-91
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "alarm.h"
|
||||
#include "dbDefs.h"
|
||||
#include "dbAccess.h"
|
||||
#include "recGbl.h"
|
||||
#include "recSup.h"
|
||||
#include "devSup.h"
|
||||
#include "int64outRecord.h"
|
||||
#include "epicsExport.h"
|
||||
|
||||
/* Create the dset for devI64outSoft */
|
||||
static long init_record(int64outRecord *prec);
|
||||
static long write_int64out(int64outRecord *prec);
|
||||
struct {
|
||||
long number;
|
||||
DEVSUPFUN report;
|
||||
DEVSUPFUN init;
|
||||
DEVSUPFUN init_record;
|
||||
DEVSUPFUN get_ioint_info;
|
||||
DEVSUPFUN write_int64out;
|
||||
} devI64outSoft = {
|
||||
5,
|
||||
NULL,
|
||||
NULL,
|
||||
init_record,
|
||||
NULL,
|
||||
write_int64out
|
||||
};
|
||||
epicsExportAddress(dset, devI64outSoft);
|
||||
|
||||
static long init_record(int64outRecord *prec)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long write_int64out(int64outRecord *prec)
|
||||
{
|
||||
dbPutLink(&prec->out, DBR_INT64, &prec->val,1);
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
/*************************************************************************\
|
||||
* Copyright (c) 2016 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 file LICENSE that is included with this distribution.
|
||||
\*************************************************************************/
|
||||
|
||||
/*
|
||||
* Original Author: Marty Kraimer
|
||||
* Date: 04NOV2003
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "alarm.h"
|
||||
#include "dbDefs.h"
|
||||
#include "dbAccess.h"
|
||||
#include "recGbl.h"
|
||||
#include "recSup.h"
|
||||
#include "devSup.h"
|
||||
#include "int64outRecord.h"
|
||||
#include "epicsExport.h"
|
||||
|
||||
/* Create the dset for devI64outSoftCallback */
|
||||
static long write_int64out(int64outRecord *prec);
|
||||
struct {
|
||||
long number;
|
||||
DEVSUPFUN report;
|
||||
DEVSUPFUN init;
|
||||
DEVSUPFUN init_record;
|
||||
DEVSUPFUN get_ioint_info;
|
||||
DEVSUPFUN write_int64out;
|
||||
} devI64outSoftCallback = {
|
||||
5,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
write_int64out
|
||||
};
|
||||
epicsExportAddress(dset, devI64outSoftCallback);
|
||||
|
||||
static long write_int64out(int64outRecord *prec)
|
||||
{
|
||||
struct link *plink = &prec->out;
|
||||
long status;
|
||||
|
||||
if (prec->pact)
|
||||
return 0;
|
||||
|
||||
if (plink->type != CA_LINK) {
|
||||
status = dbPutLink(plink, DBR_INT64, &prec->val, 1);
|
||||
return status;
|
||||
}
|
||||
|
||||
status = dbCaPutLinkCallback(plink, DBR_INT64, &prec->val, 1,
|
||||
dbCaCallbackProcess, plink);
|
||||
if (status) {
|
||||
recGblSetSevr(prec, LINK_ALARM, INVALID_ALARM);
|
||||
return status;
|
||||
}
|
||||
|
||||
prec->pact = TRUE;
|
||||
return 0;
|
||||
}
|
||||
@@ -9,6 +9,8 @@ device(bo,CONSTANT,devBoSoft,"Soft Channel")
|
||||
device(calcout,CONSTANT,devCalcoutSoft,"Soft Channel")
|
||||
device(event,CONSTANT,devEventSoft,"Soft Channel")
|
||||
device(histogram,CONSTANT,devHistogramSoft,"Soft Channel")
|
||||
device(int64in,CONSTANT,devI64inSoft,"Soft Channel")
|
||||
device(int64out,CONSTANT,devI64outSoft,"Soft Channel")
|
||||
device(longin,CONSTANT,devLiSoft,"Soft Channel")
|
||||
device(longout,CONSTANT,devLoSoft,"Soft Channel")
|
||||
device(lsi,CONSTANT,devLsiSoft,"Soft Channel")
|
||||
@@ -37,6 +39,8 @@ device(ao,CONSTANT,devAoSoftCallback,"Async Soft Channel")
|
||||
device(bi,CONSTANT,devBiSoftCallback,"Async Soft Channel")
|
||||
device(bo,CONSTANT,devBoSoftCallback,"Async Soft Channel")
|
||||
device(calcout,CONSTANT,devCalcoutSoftCallback,"Async Soft Channel")
|
||||
device(int64in,CONSTANT,devI64inSoftCallback,"Async Soft Channel")
|
||||
device(int64out,CONSTANT,devI64outSoftCallback,"Async Soft Channel")
|
||||
device(longin,CONSTANT,devLiSoftCallback,"Async Soft Channel")
|
||||
device(longout,CONSTANT,devLoSoftCallback,"Async Soft Channel")
|
||||
device(lso,CONSTANT,devLsoSoftCallback,"Async Soft Channel")
|
||||
|
||||
@@ -25,6 +25,8 @@ stdRecords += dfanoutRecord
|
||||
stdRecords += eventRecord
|
||||
stdRecords += fanoutRecord
|
||||
stdRecords += histogramRecord
|
||||
stdRecords += int64inRecord
|
||||
stdRecords += int64outRecord
|
||||
stdRecords += longinRecord
|
||||
stdRecords += longoutRecord
|
||||
stdRecords += lsiRecord
|
||||
|
||||
@@ -97,7 +97,7 @@ recordtype(compress) {
|
||||
}
|
||||
field(BALG,DBF_MENU) {
|
||||
prompt("Buffering Algorithm")
|
||||
promptgroup(GUI_ALARMS)
|
||||
promptgroup("30 - Action")
|
||||
special(SPC_RESET)
|
||||
interest(1)
|
||||
menu(bufferingALG)
|
||||
|
||||
@@ -0,0 +1,416 @@
|
||||
/*************************************************************************\
|
||||
* Copyright (c) 2016 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 file LICENSE that is included with this distribution.
|
||||
\*************************************************************************/
|
||||
|
||||
/* int64inRecord.c - Record Support Routines for int64in records */
|
||||
/*
|
||||
* Original Author: Janet Anderson
|
||||
* Date: 9/23/91
|
||||
*/
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
|
||||
#include "dbDefs.h"
|
||||
#include "epicsPrint.h"
|
||||
#include "alarm.h"
|
||||
#include "dbAccess.h"
|
||||
#include "dbEvent.h"
|
||||
#include "dbFldTypes.h"
|
||||
#include "devSup.h"
|
||||
#include "errMdef.h"
|
||||
#include "recSup.h"
|
||||
#include "recGbl.h"
|
||||
#include "menuYesNo.h"
|
||||
|
||||
#define GEN_SIZE_OFFSET
|
||||
#include "int64inRecord.h"
|
||||
#undef GEN_SIZE_OFFSET
|
||||
#include "epicsExport.h"
|
||||
|
||||
/* Hysterisis for alarm filtering: 1-1/e */
|
||||
#define THRESHOLD 0.6321
|
||||
/* Create RSET - Record Support Entry Table*/
|
||||
#define report NULL
|
||||
#define initialize NULL
|
||||
static long init_record(dbCommon *, int);
|
||||
static long process(dbCommon *);
|
||||
#define special NULL
|
||||
#define get_value NULL
|
||||
#define cvt_dbaddr NULL
|
||||
#define get_array_info NULL
|
||||
#define put_array_info NULL
|
||||
static long get_units(DBADDR *, char *);
|
||||
#define get_precision NULL
|
||||
#define get_enum_str NULL
|
||||
#define get_enum_strs NULL
|
||||
#define put_enum_str NULL
|
||||
static long get_graphic_double(DBADDR *, struct dbr_grDouble *);
|
||||
static long get_control_double(DBADDR *, struct dbr_ctrlDouble *);
|
||||
static long get_alarm_double(DBADDR *, struct dbr_alDouble *);
|
||||
|
||||
rset int64inRSET={
|
||||
RSETNUMBER,
|
||||
report,
|
||||
initialize,
|
||||
init_record,
|
||||
process,
|
||||
special,
|
||||
get_value,
|
||||
cvt_dbaddr,
|
||||
get_array_info,
|
||||
put_array_info,
|
||||
get_units,
|
||||
get_precision,
|
||||
get_enum_str,
|
||||
get_enum_strs,
|
||||
put_enum_str,
|
||||
get_graphic_double,
|
||||
get_control_double,
|
||||
get_alarm_double
|
||||
};
|
||||
epicsExportAddress(rset,int64inRSET);
|
||||
|
||||
|
||||
struct int64indset { /* int64in input dset */
|
||||
long number;
|
||||
DEVSUPFUN dev_report;
|
||||
DEVSUPFUN init;
|
||||
DEVSUPFUN init_record; /*returns: (-1,0)=>(failure,success)*/
|
||||
DEVSUPFUN get_ioint_info;
|
||||
DEVSUPFUN read_int64in; /*returns: (-1,0)=>(failure,success)*/
|
||||
};
|
||||
static void checkAlarms(int64inRecord *prec, epicsTimeStamp *timeLast);
|
||||
static void monitor(int64inRecord *prec);
|
||||
static long readValue(int64inRecord *prec);
|
||||
|
||||
|
||||
static long init_record(dbCommon *pcommon, int pass)
|
||||
{
|
||||
int64inRecord *prec = (int64inRecord*)pcommon;
|
||||
struct int64indset *pdset;
|
||||
long status;
|
||||
|
||||
if (pass==0) return(0);
|
||||
|
||||
/* int64in.siml must be a CONSTANT or a PV_LINK or a DB_LINK */
|
||||
if (prec->siml.type == CONSTANT) {
|
||||
recGblInitConstantLink(&prec->siml,DBF_USHORT,&prec->simm);
|
||||
}
|
||||
|
||||
/* int64in.siol must be a CONSTANT or a PV_LINK or a DB_LINK */
|
||||
if (prec->siol.type == CONSTANT) {
|
||||
recGblInitConstantLink(&prec->siol,DBF_LONG,&prec->sval);
|
||||
}
|
||||
|
||||
if(!(pdset = (struct int64indset *)(prec->dset))) {
|
||||
recGblRecordError(S_dev_noDSET,(void *)prec,"int64in: init_record");
|
||||
return(S_dev_noDSET);
|
||||
}
|
||||
/* must have read_int64in function defined */
|
||||
if( (pdset->number < 5) || (pdset->read_int64in == NULL) ) {
|
||||
recGblRecordError(S_dev_missingSup,(void *)prec,"int64in: init_record");
|
||||
return(S_dev_missingSup);
|
||||
}
|
||||
if( pdset->init_record ) {
|
||||
if((status=(*pdset->init_record)(prec))) return(status);
|
||||
}
|
||||
prec->mlst = prec->val;
|
||||
prec->alst = prec->val;
|
||||
prec->lalm = prec->val;
|
||||
return(0);
|
||||
}
|
||||
|
||||
static long process(dbCommon *pcommon)
|
||||
{
|
||||
int64inRecord *prec = (int64inRecord*)pcommon;
|
||||
struct int64indset *pdset = (struct int64indset *)(prec->dset);
|
||||
long status;
|
||||
unsigned char pact=prec->pact;
|
||||
epicsTimeStamp timeLast;
|
||||
|
||||
if( (pdset==NULL) || (pdset->read_int64in==NULL) ) {
|
||||
prec->pact=TRUE;
|
||||
recGblRecordError(S_dev_missingSup,(void *)prec,"read_int64in");
|
||||
return(S_dev_missingSup);
|
||||
}
|
||||
timeLast = prec->time;
|
||||
|
||||
status=readValue(prec); /* read the new value */
|
||||
/* check if device support set pact */
|
||||
if ( !pact && prec->pact ) return(0);
|
||||
prec->pact = TRUE;
|
||||
|
||||
recGblGetTimeStamp(prec);
|
||||
if (status==0) prec->udf = FALSE;
|
||||
|
||||
/* check for alarms */
|
||||
checkAlarms(prec, &timeLast);
|
||||
/* check event list */
|
||||
monitor(prec);
|
||||
/* process the forward scan link record */
|
||||
recGblFwdLink(prec);
|
||||
|
||||
prec->pact=FALSE;
|
||||
return(status);
|
||||
}
|
||||
|
||||
#define indexof(field) int64inRecord##field
|
||||
|
||||
static long get_units(DBADDR *paddr,char *units)
|
||||
{
|
||||
int64inRecord *prec=(int64inRecord *)paddr->precord;
|
||||
|
||||
if(paddr->pfldDes->field_type == DBF_LONG) {
|
||||
strncpy(units,prec->egu,DB_UNITS_SIZE);
|
||||
}
|
||||
return(0);
|
||||
}
|
||||
|
||||
|
||||
static long get_graphic_double(DBADDR *paddr, struct dbr_grDouble *pgd)
|
||||
{
|
||||
int64inRecord *prec=(int64inRecord *)paddr->precord;
|
||||
|
||||
switch (dbGetFieldIndex(paddr)) {
|
||||
case indexof(VAL):
|
||||
case indexof(HIHI):
|
||||
case indexof(HIGH):
|
||||
case indexof(LOW):
|
||||
case indexof(LOLO):
|
||||
case indexof(LALM):
|
||||
case indexof(ALST):
|
||||
case indexof(MLST):
|
||||
case indexof(SVAL):
|
||||
pgd->upper_disp_limit = prec->hopr;
|
||||
pgd->lower_disp_limit = prec->lopr;
|
||||
break;
|
||||
default:
|
||||
recGblGetGraphicDouble(paddr,pgd);
|
||||
}
|
||||
return(0);
|
||||
}
|
||||
|
||||
static long get_control_double(DBADDR *paddr, struct dbr_ctrlDouble *pcd)
|
||||
{
|
||||
int64inRecord *prec=(int64inRecord *)paddr->precord;
|
||||
|
||||
switch (dbGetFieldIndex(paddr)) {
|
||||
case indexof(VAL):
|
||||
case indexof(HIHI):
|
||||
case indexof(HIGH):
|
||||
case indexof(LOW):
|
||||
case indexof(LOLO):
|
||||
case indexof(LALM):
|
||||
case indexof(ALST):
|
||||
case indexof(MLST):
|
||||
case indexof(SVAL):
|
||||
pcd->upper_ctrl_limit = prec->hopr;
|
||||
pcd->lower_ctrl_limit = prec->lopr;
|
||||
break;
|
||||
default:
|
||||
recGblGetControlDouble(paddr,pcd);
|
||||
}
|
||||
return(0);
|
||||
}
|
||||
|
||||
static long get_alarm_double(DBADDR *paddr, struct dbr_alDouble *pad)
|
||||
{
|
||||
int64inRecord *prec=(int64inRecord *)paddr->precord;
|
||||
|
||||
if(dbGetFieldIndex(paddr) == indexof(VAL)){
|
||||
pad->upper_alarm_limit = prec->hihi;
|
||||
pad->upper_warning_limit = prec->high;
|
||||
pad->lower_warning_limit = prec->low;
|
||||
pad->lower_alarm_limit = prec->lolo;
|
||||
} else recGblGetAlarmDouble(paddr,pad);
|
||||
return(0);
|
||||
}
|
||||
|
||||
static void checkAlarms(int64inRecord *prec, epicsTimeStamp *timeLast)
|
||||
{
|
||||
enum {
|
||||
range_Lolo = 1,
|
||||
range_Low,
|
||||
range_Normal,
|
||||
range_High,
|
||||
range_Hihi
|
||||
} alarmRange;
|
||||
static const epicsEnum16 range_stat[] = {
|
||||
SOFT_ALARM, LOLO_ALARM, LOW_ALARM,
|
||||
NO_ALARM, HIGH_ALARM, HIHI_ALARM
|
||||
};
|
||||
|
||||
double aftc, afvl;
|
||||
epicsInt64 val, hyst, lalm;
|
||||
epicsInt64 alev;
|
||||
epicsEnum16 asev;
|
||||
|
||||
if (prec->udf) {
|
||||
recGblSetSevr(prec, UDF_ALARM, prec->udfs);
|
||||
prec->afvl = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
val = prec->val;
|
||||
hyst = prec->hyst;
|
||||
lalm = prec->lalm;
|
||||
|
||||
/* check VAL against alarm limits */
|
||||
if ((asev = prec->hhsv) &&
|
||||
(val >= (alev = prec->hihi) ||
|
||||
((lalm == alev) && (val >= alev - hyst))))
|
||||
alarmRange = range_Hihi;
|
||||
else
|
||||
if ((asev = prec->llsv) &&
|
||||
(val <= (alev = prec->lolo) ||
|
||||
((lalm == alev) && (val <= alev + hyst))))
|
||||
alarmRange = range_Lolo;
|
||||
else
|
||||
if ((asev = prec->hsv) &&
|
||||
(val >= (alev = prec->high) ||
|
||||
((lalm == alev) && (val >= alev - hyst))))
|
||||
alarmRange = range_High;
|
||||
else
|
||||
if ((asev = prec->lsv) &&
|
||||
(val <= (alev = prec->low) ||
|
||||
((lalm == alev) && (val <= alev + hyst))))
|
||||
alarmRange = range_Low;
|
||||
else {
|
||||
alev = val;
|
||||
asev = NO_ALARM;
|
||||
alarmRange = range_Normal;
|
||||
}
|
||||
|
||||
aftc = prec->aftc;
|
||||
afvl = 0;
|
||||
|
||||
if (aftc > 0) {
|
||||
/* Apply level filtering */
|
||||
afvl = prec->afvl;
|
||||
if (afvl == 0) {
|
||||
afvl = (double)alarmRange;
|
||||
} else {
|
||||
double t = epicsTimeDiffInSeconds(&prec->time, timeLast);
|
||||
double alpha = aftc / (t + aftc);
|
||||
|
||||
/* The sign of afvl indicates whether the result should be
|
||||
* rounded up or down. This gives the filter hysteresis.
|
||||
* If afvl > 0 the floor() function rounds to a lower alarm
|
||||
* level, otherwise to a higher.
|
||||
*/
|
||||
afvl = alpha * afvl +
|
||||
((afvl > 0) ? (1 - alpha) : (alpha - 1)) * alarmRange;
|
||||
if (afvl - floor(afvl) > THRESHOLD)
|
||||
afvl = -afvl; /* reverse rounding */
|
||||
|
||||
alarmRange = abs((int)floor(afvl));
|
||||
switch (alarmRange) {
|
||||
case range_Hihi:
|
||||
asev = prec->hhsv;
|
||||
alev = prec->hihi;
|
||||
break;
|
||||
case range_High:
|
||||
asev = prec->hsv;
|
||||
alev = prec->high;
|
||||
break;
|
||||
case range_Normal:
|
||||
asev = NO_ALARM;
|
||||
break;
|
||||
case range_Low:
|
||||
asev = prec->lsv;
|
||||
alev = prec->low;
|
||||
break;
|
||||
case range_Lolo:
|
||||
asev = prec->llsv;
|
||||
alev = prec->lolo;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
prec->afvl = afvl;
|
||||
|
||||
if (asev) {
|
||||
/* Report alarm condition, store LALM for future HYST calculations */
|
||||
if (recGblSetSevr(prec, range_stat[alarmRange], asev))
|
||||
prec->lalm = alev;
|
||||
} else {
|
||||
/* No alarm condition, reset LALM */
|
||||
prec->lalm = val;
|
||||
}
|
||||
}
|
||||
|
||||
/* DELTA calculates the absolute difference between its arguments
|
||||
* expressed as an unsigned 32-bit integer */
|
||||
#define DELTA(last, val) \
|
||||
((epicsUInt32) ((last) > (val) ? (last) - (val) : (val) - (last)))
|
||||
|
||||
static void monitor(int64inRecord *prec)
|
||||
{
|
||||
unsigned short monitor_mask = recGblResetAlarms(prec);
|
||||
|
||||
if (prec->mdel < 0 ||
|
||||
DELTA(prec->mlst, prec->val) > (epicsUInt32) prec->mdel) {
|
||||
/* post events for value change */
|
||||
monitor_mask |= DBE_VALUE;
|
||||
/* update last value monitored */
|
||||
prec->mlst = prec->val;
|
||||
}
|
||||
|
||||
if (prec->adel < 0 ||
|
||||
DELTA(prec->alst, prec->val) > (epicsUInt32) prec->adel) {
|
||||
/* post events for archive value change */
|
||||
monitor_mask |= DBE_LOG;
|
||||
/* update last archive value monitored */
|
||||
prec->alst = prec->val;
|
||||
}
|
||||
|
||||
/* send out monitors connected to the value field */
|
||||
if (monitor_mask)
|
||||
db_post_events(prec, &prec->val, monitor_mask);
|
||||
}
|
||||
|
||||
static long readValue(int64inRecord *prec)
|
||||
{
|
||||
long status;
|
||||
struct int64indset *pdset = (struct int64indset *) (prec->dset);
|
||||
|
||||
if (prec->pact == TRUE){
|
||||
status=(*pdset->read_int64in)(prec);
|
||||
return(status);
|
||||
}
|
||||
|
||||
status=dbGetLink(&(prec->siml),DBR_USHORT, &(prec->simm),0,0);
|
||||
if (status)
|
||||
return(status);
|
||||
|
||||
if (prec->simm == menuYesNoNO){
|
||||
status=(*pdset->read_int64in)(prec);
|
||||
return(status);
|
||||
}
|
||||
if (prec->simm == menuYesNoYES){
|
||||
status=dbGetLink(&(prec->siol),DBR_LONG,
|
||||
&(prec->sval),0,0);
|
||||
|
||||
if (status==0) {
|
||||
prec->val=prec->sval;
|
||||
prec->udf=FALSE;
|
||||
}
|
||||
} else {
|
||||
status=-1;
|
||||
recGblSetSevr(prec,SOFT_ALARM,INVALID_ALARM);
|
||||
return(status);
|
||||
}
|
||||
recGblSetSevr(prec,SIMM_ALARM,prec->sims);
|
||||
|
||||
return(status);
|
||||
}
|
||||
@@ -0,0 +1,528 @@
|
||||
#*************************************************************************
|
||||
# Copyright (c) 2016 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 file LICENSE that is included with this distribution.
|
||||
#*************************************************************************
|
||||
|
||||
=title 64bit Integer Input Record (int64in)
|
||||
|
||||
This record type is normally used to obtain an integer value of up to 64 bits
|
||||
from a hardware input.
|
||||
The record supports alarm limits, alarm filtering, graphics and control
|
||||
limits.
|
||||
|
||||
=head2 Parameter Fields
|
||||
|
||||
The record-specific fields are described below.
|
||||
|
||||
=recordtype int64in
|
||||
|
||||
=cut
|
||||
|
||||
recordtype(int64in) {
|
||||
|
||||
=head3 Input Specification
|
||||
|
||||
These fields control where the record will read data from when it is processed:
|
||||
|
||||
=fields DTYP, INP
|
||||
|
||||
The DTYP field selects which device support layer should be responsible for
|
||||
providing input data to the record.
|
||||
The int64in device support layers provided by EPICS Base are documented in the
|
||||
L<Device Support> section.
|
||||
External support modules may provide additional device support for this record
|
||||
type.
|
||||
If not set explicitly, the DTYP value defaults to the first device support that
|
||||
is loaded for the record type, which will usually be the C<Soft Channel> support
|
||||
that comes with Base.
|
||||
|
||||
The INP link field contains a database or channel access link or provides
|
||||
hardware address information that the device support uses to determine where the
|
||||
input data should come from.
|
||||
The format for the INP field value depends on the device support layer that is
|
||||
selected by the DTYP field.
|
||||
See L<Address Specification|...> for a description of the various hardware
|
||||
address formats supported.
|
||||
|
||||
=head3 Operator Display Parameters
|
||||
|
||||
These parameters are used to present meaningful data to the operator.
|
||||
They do not affect the functioning of the record.
|
||||
|
||||
=over
|
||||
|
||||
=item *
|
||||
DESC is a string that is usually used to briefly describe the record.
|
||||
|
||||
=item *
|
||||
EGU is a string of up to 16 characters naming the engineering units
|
||||
that the VAL field represents.
|
||||
|
||||
=item *
|
||||
The HOPR and LOPR fields set the upper and lower display limits for the VAL,
|
||||
HIHI, HIGH, LOW, and LOLO fields.
|
||||
|
||||
=back
|
||||
|
||||
=fields DESC, EGU, HOPR, LOPR
|
||||
|
||||
=head3 Alarm Limits
|
||||
|
||||
The user configures limit alarms by putting numerical values into the HIHI,
|
||||
HIGH, LOW and LOLO fields, and by setting the associated alarm severity in the
|
||||
corresponding HHSV, HSV, LSV and LLSV menu fields.
|
||||
|
||||
The HYST field controls hysteresis to prevent alarm chattering from an input
|
||||
signal that is close to one of the limits and suffers from significant readout
|
||||
noise.
|
||||
|
||||
The AFTC field sets the time constant on a low-pass filter that delays the
|
||||
reporting of limit alarms until the signal has been within the alarm range for
|
||||
that number of seconds (the default AFTC value of zero retains the previous
|
||||
behavior).
|
||||
|
||||
The LALM field is used by the record at run-time to implement the alarm limit
|
||||
functionality.
|
||||
|
||||
=fields HIHI, HIGH, LOW, LOLO, HHSV, HSV, LSV, LLSV, HYST, AFTC, LALM
|
||||
|
||||
=head3 Monitor Parameters
|
||||
|
||||
These parameters are used to determine when to send monitors placed on the VAL
|
||||
field.
|
||||
The monitors are sent when the current value exceeds the last transmitted value
|
||||
by the appropriate deadband.
|
||||
If these fields are set to zero, a monitor will be triggered every time the
|
||||
value changes; if set to -1, a monitor will be sent every time the record is
|
||||
processed.
|
||||
|
||||
The ADEL field sets the deadband for archive monitors (C<DBE_LOG> events), while
|
||||
the MDEL field controls value monitors (C<DBE_VALUE> events).
|
||||
|
||||
The remaining fields are used by the record at run-time to implement the record
|
||||
monitoring deadband functionality.
|
||||
|
||||
=fields ADEL, MDEL, ALST, MLST
|
||||
|
||||
=cut
|
||||
|
||||
include "dbCommon.dbd"
|
||||
field(VAL,DBF_INT64) {
|
||||
prompt("Current value")
|
||||
promptgroup("40 - Input")
|
||||
asl(ASL0)
|
||||
pp(TRUE)
|
||||
}
|
||||
field(INP,DBF_INLINK) {
|
||||
prompt("Input Specification")
|
||||
promptgroup("40 - Input")
|
||||
interest(1)
|
||||
}
|
||||
field(EGU,DBF_STRING) {
|
||||
prompt("Units name")
|
||||
promptgroup("80 - Display")
|
||||
interest(1)
|
||||
size(16)
|
||||
prop(YES)
|
||||
}
|
||||
field(HOPR,DBF_INT64) {
|
||||
prompt("High Operating Range")
|
||||
promptgroup("80 - Display")
|
||||
interest(1)
|
||||
prop(YES)
|
||||
}
|
||||
field(LOPR,DBF_INT64) {
|
||||
prompt("Low Operating Range")
|
||||
promptgroup("80 - Display")
|
||||
interest(1)
|
||||
prop(YES)
|
||||
}
|
||||
field(HIHI,DBF_INT64) {
|
||||
prompt("Hihi Alarm Limit")
|
||||
promptgroup("70 - Alarm")
|
||||
pp(TRUE)
|
||||
interest(1)
|
||||
prop(YES)
|
||||
}
|
||||
field(LOLO,DBF_INT64) {
|
||||
prompt("Lolo Alarm Limit")
|
||||
promptgroup("70 - Alarm")
|
||||
pp(TRUE)
|
||||
interest(1)
|
||||
prop(YES)
|
||||
}
|
||||
field(HIGH,DBF_INT64) {
|
||||
prompt("High Alarm Limit")
|
||||
promptgroup("70 - Alarm")
|
||||
pp(TRUE)
|
||||
interest(1)
|
||||
prop(YES)
|
||||
}
|
||||
field(LOW,DBF_INT64) {
|
||||
prompt("Low Alarm Limit")
|
||||
promptgroup("70 - Alarm")
|
||||
pp(TRUE)
|
||||
interest(1)
|
||||
prop(YES)
|
||||
}
|
||||
field(HHSV,DBF_MENU) {
|
||||
prompt("Hihi Severity")
|
||||
promptgroup("70 - Alarm")
|
||||
pp(TRUE)
|
||||
interest(1)
|
||||
menu(menuAlarmSevr)
|
||||
}
|
||||
field(LLSV,DBF_MENU) {
|
||||
prompt("Lolo Severity")
|
||||
promptgroup("70 - Alarm")
|
||||
pp(TRUE)
|
||||
interest(1)
|
||||
menu(menuAlarmSevr)
|
||||
}
|
||||
field(HSV,DBF_MENU) {
|
||||
prompt("High Severity")
|
||||
promptgroup("70 - Alarm")
|
||||
pp(TRUE)
|
||||
interest(1)
|
||||
menu(menuAlarmSevr)
|
||||
}
|
||||
field(LSV,DBF_MENU) {
|
||||
prompt("Low Severity")
|
||||
promptgroup("70 - Alarm")
|
||||
pp(TRUE)
|
||||
interest(1)
|
||||
menu(menuAlarmSevr)
|
||||
}
|
||||
field(HYST,DBF_INT64) {
|
||||
prompt("Alarm Deadband")
|
||||
promptgroup("70 - Alarm")
|
||||
interest(1)
|
||||
}
|
||||
field(AFTC, DBF_DOUBLE) {
|
||||
prompt("Alarm Filter Time Constant")
|
||||
promptgroup("70 - Alarm")
|
||||
interest(1)
|
||||
}
|
||||
field(AFVL, DBF_DOUBLE) {
|
||||
prompt("Alarm Filter Value")
|
||||
special(SPC_NOMOD)
|
||||
interest(3)
|
||||
}
|
||||
field(ADEL,DBF_INT64) {
|
||||
prompt("Archive Deadband")
|
||||
promptgroup("80 - Display")
|
||||
interest(1)
|
||||
}
|
||||
field(MDEL,DBF_INT64) {
|
||||
prompt("Monitor Deadband")
|
||||
promptgroup("80 - Display")
|
||||
interest(1)
|
||||
}
|
||||
field(LALM,DBF_INT64) {
|
||||
prompt("Last Value Alarmed")
|
||||
special(SPC_NOMOD)
|
||||
interest(3)
|
||||
}
|
||||
field(ALST,DBF_INT64) {
|
||||
prompt("Last Value Archived")
|
||||
special(SPC_NOMOD)
|
||||
interest(3)
|
||||
}
|
||||
field(MLST,DBF_INT64) {
|
||||
prompt("Last Val Monitored")
|
||||
special(SPC_NOMOD)
|
||||
interest(3)
|
||||
}
|
||||
|
||||
=head3 Simulation Mode
|
||||
|
||||
The record provides several fields to support simulation of absent hardware.
|
||||
If the SIML field is set it is used to read a value into the SIMM field, which
|
||||
controls whether simulation is used or not:
|
||||
|
||||
=over
|
||||
|
||||
=item *
|
||||
SIMM must be zero (C<NO>) for the record to request a value from the device
|
||||
support.
|
||||
|
||||
=item *
|
||||
If SIMM is C<YES> and the SIOL link field is set, a simulated value in
|
||||
engineering units is read using the link into the SVAL field, from where it will
|
||||
subsequently be copied into the VAL field.
|
||||
|
||||
=back
|
||||
|
||||
The SIMS field can be set to give the record an alarm severity while it is in
|
||||
simulation mode.
|
||||
|
||||
=fields SIML, SIMM, SIOL, SVAL, SIMS
|
||||
|
||||
=cut
|
||||
|
||||
field(SIOL,DBF_INLINK) {
|
||||
prompt("Sim Input Specifctn")
|
||||
promptgroup("90 - Simulate")
|
||||
interest(1)
|
||||
}
|
||||
field(SVAL,DBF_INT64) {
|
||||
prompt("Simulation Value")
|
||||
}
|
||||
field(SIML,DBF_INLINK) {
|
||||
prompt("Sim Mode Location")
|
||||
promptgroup("90 - Simulate")
|
||||
interest(1)
|
||||
}
|
||||
field(SIMM,DBF_MENU) {
|
||||
prompt("Simulation Mode")
|
||||
interest(1)
|
||||
menu(menuYesNo)
|
||||
}
|
||||
field(SIMS,DBF_MENU) {
|
||||
prompt("Sim mode Alarm Svrty")
|
||||
promptgroup("90 - Simulate")
|
||||
interest(2)
|
||||
menu(menuAlarmSevr)
|
||||
}
|
||||
}
|
||||
|
||||
=head2 Record Support
|
||||
|
||||
=head3 Record Support Routines
|
||||
|
||||
The following are the record support routines that would be of interest
|
||||
to an application developer.
|
||||
Other routines are the C<get_units>, C<get_graphic_double>,
|
||||
C<get_alarm_double> and C<get_control_double> routines, which are used to
|
||||
collect properties from the record for the complex DBR data structures.
|
||||
|
||||
=head4 init_record
|
||||
|
||||
This routine first initializes the simulation mode mechanism by setting SIMM
|
||||
if SIML is a constant, and setting SVAL if SIOL is a constant.
|
||||
|
||||
It then checks if the device support and the device support's
|
||||
C<read_int64in> routine are defined.
|
||||
If either one does not exist, an error message is issued
|
||||
and processing is terminated.
|
||||
|
||||
If device support includes C<init_record>, it is called.
|
||||
|
||||
Finally, the deadband mechanisms for monitors and level alarms are
|
||||
initialized.
|
||||
|
||||
=head4 process
|
||||
|
||||
See next section.
|
||||
|
||||
=head3 Record Processing
|
||||
|
||||
Routine C<process> implements the following algorithm:
|
||||
|
||||
=over
|
||||
|
||||
=item 1.
|
||||
|
||||
Check to see that the appropriate device support module and its
|
||||
C<read_int64in> routine are defined.
|
||||
If either one does not exist, an error message is issued and processing is
|
||||
terminated with the PACT field set to TRUE, effectively blocking the record
|
||||
to avoid error storms.
|
||||
|
||||
=item 2.
|
||||
|
||||
Determine the value:
|
||||
|
||||
If PACT is TRUE, call the device support C<read_int64in> routine and return.
|
||||
|
||||
If PACT is FALSE, read the value, honoring simulation mode:
|
||||
|
||||
=over
|
||||
|
||||
=item * Get SIMM by reading the SIML link.
|
||||
|
||||
=item * If SIMM is C<NO>,
|
||||
call the device support C<read_int64in> routine and return.
|
||||
|
||||
=item * If SIMM is C<YES>,
|
||||
read the simulated value into SVAL using the SIOL link,
|
||||
then copy the value into VAL and set UDF to 0 on success.
|
||||
|
||||
=item * Raise an alarm for other values of SIMM.
|
||||
|
||||
=item * Set the record to the severity configured in SIMS.
|
||||
|
||||
=back
|
||||
|
||||
=item 3.
|
||||
|
||||
If PACT has been changed to TRUE, the device support signals asynchronous
|
||||
processing: its C<read_int64in> output routine has started, but not
|
||||
completed reading the new value.
|
||||
In this case, the processing routine merely returns, leaving PACT TRUE.
|
||||
|
||||
=item 4.
|
||||
|
||||
Set PACT to TRUE. Get the processing time stamp. Set UDF to 0 if reading
|
||||
the value was successful.
|
||||
|
||||
=item 5.
|
||||
|
||||
Check UDF and level alarms: This routine checks to see if the record is
|
||||
undefined (UDF is TRUE) or if the new VAL causes the alarm status
|
||||
and severity to change. In the latter case, NSEV, NSTA and LALM are set.
|
||||
It also honors the alarm hysteresis factor (HYST): the value must change
|
||||
by at least HYST between level alarm status and severity changes.
|
||||
If AFTC is set, alarm level filtering is applied.
|
||||
|
||||
=item 6.
|
||||
|
||||
Check to see if monitors should be invoked:
|
||||
|
||||
=over
|
||||
|
||||
=item * Alarm monitors are posted if the alarm status or severity have
|
||||
changed.
|
||||
|
||||
=item * Archive and value change monitors are posted if ADEL and MDEL
|
||||
conditions (see L<Monitor Parameters>) are met.
|
||||
|
||||
=back
|
||||
|
||||
=item 7.
|
||||
|
||||
Scan (process) forward link if necessary, set PACT to FALSE, and return.
|
||||
|
||||
=back
|
||||
|
||||
=head2 Device Support
|
||||
|
||||
=head3 Device Support Interface
|
||||
|
||||
The record requires device support to provide an entry table (dset) which
|
||||
defines the following members:
|
||||
|
||||
typedef struct {
|
||||
long number;
|
||||
long (*report)(int level);
|
||||
long (*init)(int after);
|
||||
long (*init_record)(int64inRecord *prec);
|
||||
long (*get_ioint_info)(int cmd, int64inRecord *prec, IOSCANPVT *piosl);
|
||||
long (*read_int64in)(int64inRecord *prec);
|
||||
} int64indset;
|
||||
|
||||
The module must set C<number> to at least 5, and provide a pointer to its
|
||||
C<read_int64in()> routine; the other function pointers may be C<NULL> if their
|
||||
associated functionality is not required for this support layer.
|
||||
Most device supports also provide an C<init_record()> routine to configure the
|
||||
record instance and connect it to the hardware or driver support layer.
|
||||
|
||||
The individual routines are described below.
|
||||
|
||||
=head3 Device Support Routines
|
||||
|
||||
=head4 long report(int level)
|
||||
|
||||
This optional routine is called by the IOC command C<dbior> and is passed the
|
||||
report level that was requested by the user.
|
||||
It should print a report on the state of the device support to stdout.
|
||||
The C<level> parameter may be used to output increasingly more detailed
|
||||
information at higher levels, or to select different types of information with
|
||||
different levels.
|
||||
Level zero should print no more than a small summary.
|
||||
|
||||
=head4 long init(int after)
|
||||
|
||||
This optional routine is called twice at IOC initialization time.
|
||||
The first call happens before any of the C<init_record()> calls are made, with
|
||||
the integer parameter C<after> set to 0.
|
||||
The second call happens after all of the C<init_record()> calls have been made,
|
||||
with C<after> set to 1.
|
||||
|
||||
=head4 long init_record(int64inRecord *prec)
|
||||
|
||||
This optional routine is called by the record initialization code for each
|
||||
int64in record instance that has its DTYP field set to use this device support.
|
||||
It is normally used to check that the INP address is the expected type and that
|
||||
it points to a valid device; to allocate any record-specific buffer space and
|
||||
other memory; and to connect any communication channels needed for the
|
||||
C<read_int64in()> routine to work properly.
|
||||
|
||||
=head4 long get_ioint_info(int cmd, int64inRecord *prec, IOSCANPVT *piosl)
|
||||
|
||||
This optional routine is called whenever the record's SCAN field is being
|
||||
changed to or from the value C<I/O Intr> to find out which I/O Interrupt Scan
|
||||
list the record should be added to or deleted from.
|
||||
If this routine is not provided, it will not be possible to set the SCAN field
|
||||
to the value C<I/O Intr> at all.
|
||||
|
||||
The C<cmd> parameter is zero when the record is being added to the scan list,
|
||||
and one when it is being removed from the list.
|
||||
The routine must determine which interrupt source the record should be connected
|
||||
to, which it indicates by the scan list that it points the location at C<*piosl>
|
||||
to before returning.
|
||||
It can prevent the SCAN field from being changed at all by returning a non-zero
|
||||
value to its caller.
|
||||
|
||||
In most cases the device support will create the I/O Interrupt Scan lists that
|
||||
it returns for itself, by calling C<void scanIoInit(IOSCANPVT *piosl)> once for
|
||||
each separate interrupt source.
|
||||
That routine allocates memory and inializes the list, then passes back a pointer
|
||||
to the new list in the location at C<*piosl>.
|
||||
|
||||
When the device support receives notification that the interrupt has occurred,
|
||||
it announces that to the IOC by calling C<void scanIoRequest(IOSCANPVT iosl)>
|
||||
which will arrange for the appropriate records to be processed in a suitable
|
||||
thread.
|
||||
The C<scanIoRequest()> routine is safe to call from an interrupt service routine
|
||||
on embedded architectures (vxWorks and RTEMS).
|
||||
|
||||
=head4 long read_int64in(int64inRecord *prec)
|
||||
|
||||
This essential routine is called when the record wants a new value from the
|
||||
addressed device.
|
||||
It is responsible for performing (or at least initiating) a read operation, and
|
||||
(eventually) returning its value to the record.
|
||||
|
||||
If the device may take more than a few microseconds to return the new value,
|
||||
this routine must never block (busy-wait), but use the asynchronous
|
||||
processing mechanism.
|
||||
In that case it signals the asynchronous operation by setting the record's
|
||||
PACT field to TRUE before it returns, having arranged for the record's
|
||||
C<process()> routine to be called later once the read operation is finished.
|
||||
When that happens, the C<read_int64in()> routine will be called again with
|
||||
PACT still set to TRUE; it should then set it to FALSE to indicate the read
|
||||
has completed, and return.
|
||||
|
||||
A return value of zero indicates success, any other value indicates that an
|
||||
error occurred.
|
||||
|
||||
=head3 Extended Device Support
|
||||
|
||||
...
|
||||
|
||||
=cut
|
||||
|
||||
=head2 Device Support For Soft Records
|
||||
|
||||
Two soft device support modules, Soft Channel and Soft Callback Channel, are
|
||||
provided for input records not related to actual hardware devices. The
|
||||
INP link type must be either a CONSTANT, DB_LINK, or CA_LINK.
|
||||
|
||||
=head3 Soft Channel
|
||||
|
||||
This module reads the value using the record's INP link.
|
||||
|
||||
C<read_int64in> calls C<dbGetLink> to read the value.
|
||||
|
||||
=head3 Soft Callback Channel
|
||||
|
||||
This module is like the previous except that it reads the value
|
||||
using asynchronous processing that will not complete until an asynchronous
|
||||
processing of the INP target record has completed.
|
||||
|
||||
=cut
|
||||
@@ -0,0 +1,394 @@
|
||||
/*************************************************************************\
|
||||
* Copyright (c) 2016 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 file LICENSE that is included with this distribution.
|
||||
\*************************************************************************/
|
||||
|
||||
/*
|
||||
* Original Author: Janet Anderson
|
||||
* Date: 9/23/91
|
||||
*/
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "dbDefs.h"
|
||||
#include "epicsPrint.h"
|
||||
#include "alarm.h"
|
||||
#include "dbAccess.h"
|
||||
#include "dbEvent.h"
|
||||
#include "dbFldTypes.h"
|
||||
#include "devSup.h"
|
||||
#include "errMdef.h"
|
||||
#include "recSup.h"
|
||||
#include "recGbl.h"
|
||||
#include "menuYesNo.h"
|
||||
#include "menuIvoa.h"
|
||||
#include "menuOmsl.h"
|
||||
|
||||
#define GEN_SIZE_OFFSET
|
||||
#include "int64outRecord.h"
|
||||
#undef GEN_SIZE_OFFSET
|
||||
#include "epicsExport.h"
|
||||
|
||||
/* Create RSET - Record Support Entry Table*/
|
||||
#define report NULL
|
||||
#define initialize NULL
|
||||
static long init_record(dbCommon *, int);
|
||||
static long process(dbCommon *);
|
||||
#define special NULL
|
||||
#define get_value NULL
|
||||
#define cvt_dbaddr NULL
|
||||
#define get_array_info NULL
|
||||
#define put_array_info NULL
|
||||
static long get_units(DBADDR *, char *);
|
||||
#define get_precision NULL
|
||||
#define get_enum_str NULL
|
||||
#define get_enum_strs NULL
|
||||
#define put_enum_str NULL
|
||||
static long get_graphic_double(DBADDR *, struct dbr_grDouble *);
|
||||
static long get_control_double(DBADDR *, struct dbr_ctrlDouble *);
|
||||
static long get_alarm_double(DBADDR *, struct dbr_alDouble *);
|
||||
|
||||
rset int64outRSET={
|
||||
RSETNUMBER,
|
||||
report,
|
||||
initialize,
|
||||
init_record,
|
||||
process,
|
||||
special,
|
||||
get_value,
|
||||
cvt_dbaddr,
|
||||
get_array_info,
|
||||
put_array_info,
|
||||
get_units,
|
||||
get_precision,
|
||||
get_enum_str,
|
||||
get_enum_strs,
|
||||
put_enum_str,
|
||||
get_graphic_double,
|
||||
get_control_double,
|
||||
get_alarm_double
|
||||
};
|
||||
epicsExportAddress(rset,int64outRSET);
|
||||
|
||||
|
||||
struct int64outdset { /* int64out input dset */
|
||||
long number;
|
||||
DEVSUPFUN dev_report;
|
||||
DEVSUPFUN init;
|
||||
DEVSUPFUN init_record; /*returns: (-1,0)=>(failure,success)*/
|
||||
DEVSUPFUN get_ioint_info;
|
||||
DEVSUPFUN write_int64out;/*(-1,0)=>(failure,success*/
|
||||
};
|
||||
static void checkAlarms(int64outRecord *prec);
|
||||
static void monitor(int64outRecord *prec);
|
||||
static long writeValue(int64outRecord *prec);
|
||||
static void convert(int64outRecord *prec, epicsInt64 value);
|
||||
|
||||
|
||||
static long init_record(dbCommon *pcommon, int pass)
|
||||
{
|
||||
int64outRecord *prec = (int64outRecord*)pcommon;
|
||||
struct int64outdset *pdset;
|
||||
long status=0;
|
||||
|
||||
if (pass==0) return(0);
|
||||
if (prec->siml.type == CONSTANT) {
|
||||
recGblInitConstantLink(&prec->siml,DBF_USHORT,&prec->simm);
|
||||
}
|
||||
if(!(pdset = (struct int64outdset *)(prec->dset))) {
|
||||
recGblRecordError(S_dev_noDSET,(void *)prec,"int64out: init_record");
|
||||
return(S_dev_noDSET);
|
||||
}
|
||||
/* must have write_int64out functions defined */
|
||||
if( (pdset->number < 5) || (pdset->write_int64out == NULL) ) {
|
||||
recGblRecordError(S_dev_missingSup,(void *)prec,"int64out: init_record");
|
||||
return(S_dev_missingSup);
|
||||
}
|
||||
if (prec->dol.type == CONSTANT) {
|
||||
if(recGblInitConstantLink(&prec->dol,DBF_INT64,&prec->val))
|
||||
prec->udf=FALSE;
|
||||
}
|
||||
if( pdset->init_record ) {
|
||||
if((status=(*pdset->init_record)(prec))) return(status);
|
||||
}
|
||||
prec->mlst = prec->val;
|
||||
prec->alst = prec->val;
|
||||
prec->lalm = prec->val;
|
||||
return(0);
|
||||
}
|
||||
|
||||
static long process(dbCommon *pcommon)
|
||||
{
|
||||
int64outRecord *prec = (int64outRecord*)pcommon;
|
||||
struct int64outdset *pdset = (struct int64outdset *)(prec->dset);
|
||||
long status=0;
|
||||
epicsInt64 value;
|
||||
unsigned char pact=prec->pact;
|
||||
|
||||
if( (pdset==NULL) || (pdset->write_int64out==NULL) ) {
|
||||
prec->pact=TRUE;
|
||||
recGblRecordError(S_dev_missingSup,(void *)prec,"write_int64out");
|
||||
return(S_dev_missingSup);
|
||||
}
|
||||
if (!prec->pact) {
|
||||
if((prec->dol.type != CONSTANT)
|
||||
&& (prec->omsl == menuOmslclosed_loop)) {
|
||||
status = dbGetLink(&(prec->dol),DBR_INT64,
|
||||
&value,0,0);
|
||||
if (prec->dol.type!=CONSTANT && RTN_SUCCESS(status))
|
||||
prec->udf=FALSE;
|
||||
}
|
||||
else {
|
||||
value = prec->val;
|
||||
}
|
||||
if (!status) convert(prec,value);
|
||||
}
|
||||
|
||||
/* check for alarms */
|
||||
checkAlarms(prec);
|
||||
|
||||
if (prec->nsev < INVALID_ALARM )
|
||||
status=writeValue(prec); /* write the new value */
|
||||
else {
|
||||
switch (prec->ivoa) {
|
||||
case (menuIvoaContinue_normally) :
|
||||
status=writeValue(prec); /* write the new value */
|
||||
break;
|
||||
case (menuIvoaDon_t_drive_outputs) :
|
||||
break;
|
||||
case (menuIvoaSet_output_to_IVOV) :
|
||||
if(prec->pact == FALSE){
|
||||
prec->val=prec->ivov;
|
||||
}
|
||||
status=writeValue(prec); /* write the new value */
|
||||
break;
|
||||
default :
|
||||
status=-1;
|
||||
recGblRecordError(S_db_badField,(void *)prec,
|
||||
"int64out:process Illegal IVOA field");
|
||||
}
|
||||
}
|
||||
|
||||
/* check if device support set pact */
|
||||
if ( !pact && prec->pact ) return(0);
|
||||
prec->pact = TRUE;
|
||||
|
||||
recGblGetTimeStamp(prec);
|
||||
|
||||
/* check event list */
|
||||
monitor(prec);
|
||||
|
||||
/* process the forward scan link record */
|
||||
recGblFwdLink(prec);
|
||||
|
||||
prec->pact=FALSE;
|
||||
return(status);
|
||||
}
|
||||
|
||||
#define indexof(field) int64outRecord##field
|
||||
|
||||
static long get_units(DBADDR *paddr,char *units)
|
||||
{
|
||||
int64outRecord *prec=(int64outRecord *)paddr->precord;
|
||||
|
||||
if(paddr->pfldDes->field_type == DBF_INT64) {
|
||||
strncpy(units,prec->egu,DB_UNITS_SIZE);
|
||||
}
|
||||
return(0);
|
||||
}
|
||||
|
||||
static long get_graphic_double(DBADDR *paddr,struct dbr_grDouble *pgd)
|
||||
{
|
||||
int64outRecord *prec=(int64outRecord *)paddr->precord;
|
||||
|
||||
switch (dbGetFieldIndex(paddr)) {
|
||||
case indexof(VAL):
|
||||
case indexof(HIHI):
|
||||
case indexof(HIGH):
|
||||
case indexof(LOW):
|
||||
case indexof(LOLO):
|
||||
case indexof(LALM):
|
||||
case indexof(ALST):
|
||||
case indexof(MLST):
|
||||
pgd->upper_disp_limit = prec->hopr;
|
||||
pgd->lower_disp_limit = prec->lopr;
|
||||
break;
|
||||
default:
|
||||
recGblGetGraphicDouble(paddr,pgd);
|
||||
}
|
||||
return(0);
|
||||
}
|
||||
|
||||
static long get_control_double(DBADDR *paddr,struct dbr_ctrlDouble *pcd)
|
||||
{
|
||||
int64outRecord *prec=(int64outRecord *)paddr->precord;
|
||||
|
||||
switch (dbGetFieldIndex(paddr)) {
|
||||
case indexof(VAL):
|
||||
case indexof(HIHI):
|
||||
case indexof(HIGH):
|
||||
case indexof(LOW):
|
||||
case indexof(LOLO):
|
||||
case indexof(LALM):
|
||||
case indexof(ALST):
|
||||
case indexof(MLST):
|
||||
/* do not change pre drvh/drvl behavior */
|
||||
if(prec->drvh > prec->drvl) {
|
||||
pcd->upper_ctrl_limit = prec->drvh;
|
||||
pcd->lower_ctrl_limit = prec->drvl;
|
||||
} else {
|
||||
pcd->upper_ctrl_limit = prec->hopr;
|
||||
pcd->lower_ctrl_limit = prec->lopr;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
recGblGetControlDouble(paddr,pcd);
|
||||
}
|
||||
return(0);
|
||||
}
|
||||
|
||||
static long get_alarm_double(DBADDR *paddr,struct dbr_alDouble *pad)
|
||||
{
|
||||
int64outRecord *prec=(int64outRecord *)paddr->precord;
|
||||
|
||||
if(dbGetFieldIndex(paddr) == indexof(VAL)) {
|
||||
pad->upper_alarm_limit = prec->hihi;
|
||||
pad->upper_warning_limit = prec->high;
|
||||
pad->lower_warning_limit = prec->low;
|
||||
pad->lower_alarm_limit = prec->lolo;
|
||||
} else recGblGetAlarmDouble(paddr,pad);
|
||||
return(0);
|
||||
}
|
||||
|
||||
static void checkAlarms(int64outRecord *prec)
|
||||
{
|
||||
epicsInt64 val, hyst, lalm;
|
||||
epicsInt64 alev;
|
||||
epicsEnum16 asev;
|
||||
|
||||
if (prec->udf) {
|
||||
recGblSetSevr(prec, UDF_ALARM, prec->udfs);
|
||||
return;
|
||||
}
|
||||
|
||||
val = prec->val;
|
||||
hyst = prec->hyst;
|
||||
lalm = prec->lalm;
|
||||
|
||||
/* alarm condition hihi */
|
||||
asev = prec->hhsv;
|
||||
alev = prec->hihi;
|
||||
if (asev && (val >= alev || ((lalm == alev) && (val >= alev - hyst)))) {
|
||||
if (recGblSetSevr(prec, HIHI_ALARM, asev))
|
||||
prec->lalm = alev;
|
||||
return;
|
||||
}
|
||||
|
||||
/* alarm condition lolo */
|
||||
asev = prec->llsv;
|
||||
alev = prec->lolo;
|
||||
if (asev && (val <= alev || ((lalm == alev) && (val <= alev + hyst)))) {
|
||||
if (recGblSetSevr(prec, LOLO_ALARM, asev))
|
||||
prec->lalm = alev;
|
||||
return;
|
||||
}
|
||||
|
||||
/* alarm condition high */
|
||||
asev = prec->hsv;
|
||||
alev = prec->high;
|
||||
if (asev && (val >= alev || ((lalm == alev) && (val >= alev - hyst)))) {
|
||||
if (recGblSetSevr(prec, HIGH_ALARM, asev))
|
||||
prec->lalm = alev;
|
||||
return;
|
||||
}
|
||||
|
||||
/* alarm condition low */
|
||||
asev = prec->lsv;
|
||||
alev = prec->low;
|
||||
if (asev && (val <= alev || ((lalm == alev) && (val <= alev + hyst)))) {
|
||||
if (recGblSetSevr(prec, LOW_ALARM, asev))
|
||||
prec->lalm = alev;
|
||||
return;
|
||||
}
|
||||
|
||||
/* we get here only if val is out of alarm by at least hyst */
|
||||
prec->lalm = val;
|
||||
return;
|
||||
}
|
||||
|
||||
/* DELTA calculates the absolute difference between its arguments
|
||||
* expressed as an unsigned 64-bit integer */
|
||||
#define DELTA(last, val) \
|
||||
((epicsUInt64) ((last) > (val) ? (last) - (val) : (val) - (last)))
|
||||
|
||||
static void monitor(int64outRecord *prec)
|
||||
{
|
||||
unsigned short monitor_mask = recGblResetAlarms(prec);
|
||||
|
||||
if (prec->mdel < 0 ||
|
||||
DELTA(prec->mlst, prec->val) > (epicsUInt64) prec->mdel) {
|
||||
/* post events for value change */
|
||||
monitor_mask |= DBE_VALUE;
|
||||
/* update last value monitored */
|
||||
prec->mlst = prec->val;
|
||||
}
|
||||
|
||||
if (prec->adel < 0 ||
|
||||
DELTA(prec->alst, prec->val) > (epicsUInt64) prec->adel) {
|
||||
/* post events for archive value change */
|
||||
monitor_mask |= DBE_LOG;
|
||||
/* update last archive value monitored */
|
||||
prec->alst = prec->val;
|
||||
}
|
||||
|
||||
/* send out monitors connected to the value field */
|
||||
if (monitor_mask)
|
||||
db_post_events(prec, &prec->val, monitor_mask);
|
||||
}
|
||||
|
||||
static long writeValue(int64outRecord *prec)
|
||||
{
|
||||
long status;
|
||||
struct int64outdset *pdset = (struct int64outdset *) (prec->dset);
|
||||
|
||||
if (prec->pact == TRUE){
|
||||
status=(*pdset->write_int64out)(prec);
|
||||
return(status);
|
||||
}
|
||||
|
||||
status=dbGetLink(&(prec->siml),DBR_USHORT,&(prec->simm),0,0);
|
||||
if (!RTN_SUCCESS(status))
|
||||
return(status);
|
||||
|
||||
if (prec->simm == menuYesNoNO){
|
||||
status=(*pdset->write_int64out)(prec);
|
||||
return(status);
|
||||
}
|
||||
if (prec->simm == menuYesNoYES){
|
||||
status=dbPutLink(&prec->siol,DBR_INT64,&prec->val,1);
|
||||
} else {
|
||||
status=-1;
|
||||
recGblSetSevr(prec,SOFT_ALARM,INVALID_ALARM);
|
||||
return(status);
|
||||
}
|
||||
recGblSetSevr(prec,SIMM_ALARM,prec->sims);
|
||||
|
||||
return(status);
|
||||
}
|
||||
|
||||
static void convert(int64outRecord *prec, epicsInt64 value)
|
||||
{
|
||||
/* check drive limits */
|
||||
if(prec->drvh > prec->drvl) {
|
||||
if (value > prec->drvh) value = prec->drvh;
|
||||
else if (value < prec->drvl) value = prec->drvl;
|
||||
}
|
||||
prec->val = value;
|
||||
}
|
||||
@@ -0,0 +1,596 @@
|
||||
#*************************************************************************
|
||||
# Copyright (c) 2016 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 file LICENSE that is included with this distribution.
|
||||
#*************************************************************************
|
||||
|
||||
=title 64bit Integer Output Record (int64out)
|
||||
|
||||
This record type is normally used to send an integer value of up to 64 bits
|
||||
to an output device.
|
||||
The record supports alarm, drive, graphics and control limits.
|
||||
|
||||
=head2 Parameter Fields
|
||||
|
||||
The record-specific fields are described below.
|
||||
|
||||
=recordtype int64out
|
||||
|
||||
=cut
|
||||
|
||||
recordtype(int64out) {
|
||||
|
||||
=head3 Output Value Determination
|
||||
|
||||
These fields control how the record determines the value to be output when it
|
||||
gets processed:
|
||||
|
||||
=fields OMSL, DOL, DRVH, DRVL, VAL
|
||||
|
||||
The following steps are performed in order during record processing.
|
||||
|
||||
=head4 Fetch Value
|
||||
|
||||
The OMSL menu field is used to determine whether the DOL link field
|
||||
should be used during processing or not:
|
||||
|
||||
=over
|
||||
|
||||
=item *
|
||||
If OMSL is C<supervisory> the DOL link field is not used.
|
||||
The new output value is taken from the VAL field, which may have been set from
|
||||
elsewhere.
|
||||
|
||||
=item *
|
||||
If OMSL is C<closed_loop> the DOL link field is used to obtain a value.
|
||||
|
||||
=back
|
||||
|
||||
=head4 Drive Limits
|
||||
|
||||
The output value is clipped to the range DRVL to DRVH inclusive, provided
|
||||
that DRVH > DRVL.
|
||||
The result is copied into the VAL field.
|
||||
|
||||
=head3 Output Specification
|
||||
|
||||
These fields control where the record will read data from when it is processed:
|
||||
|
||||
=fields DTYP, OUT
|
||||
|
||||
The DTYP field selects which device support layer should be responsible for
|
||||
writing output data.
|
||||
The int64out device support layers provided by EPICS Base are documented in the
|
||||
L<Device Support> section.
|
||||
External support modules may provide additional device support for this record
|
||||
type.
|
||||
If not set explicitly, the DTYP value defaults to the first device support that
|
||||
is loaded for the record type, which will usually be the C<Soft Channel> support
|
||||
that comes with Base.
|
||||
|
||||
The OUT link field contains a database or channel access link or provides
|
||||
hardware address information that the device support uses to determine where the
|
||||
output data should be sent to.
|
||||
The format for the OUT field value depends on the device support layer that is
|
||||
selected by the DTYP field.
|
||||
See L<Address Specification|...> for a description of the various hardware
|
||||
address formats supported.
|
||||
|
||||
=head3 Operator Display Parameters
|
||||
|
||||
These parameters are used to present meaningful data to the operator.
|
||||
They do not affect the functioning of the record.
|
||||
|
||||
=over
|
||||
|
||||
=item *
|
||||
DESC is a string that is usually used to briefly describe the record.
|
||||
|
||||
=item *
|
||||
EGU is a string of up to 16 characters naming the engineering units
|
||||
that the VAL field represents.
|
||||
|
||||
=item *
|
||||
The HOPR and LOPR fields set the upper and lower display limits for the VAL,
|
||||
HIHI, HIGH, LOW, and LOLO fields.
|
||||
|
||||
=back
|
||||
|
||||
=fields DESC, EGU, HOPR, LOPR
|
||||
|
||||
=head3 Alarm Limits
|
||||
|
||||
The user configures limit alarms by putting numerical values into the HIHI,
|
||||
HIGH, LOW and LOLO fields, and by setting the associated alarm severities
|
||||
in the corresponding HHSV, HSV, LSV and LLSV menu fields.
|
||||
|
||||
The HYST field controls hysteresis to prevent alarm chattering from an input
|
||||
signal that is close to one of the limits and suffers from significant readout
|
||||
noise.
|
||||
|
||||
The LALM field is used by the record at run-time to implement the alarm limit
|
||||
hysteresis functionality.
|
||||
|
||||
=fields HIHI, HIGH, LOW, LOLO, HHSV, HSV, LSV, LLSV, HYST, LALM
|
||||
|
||||
=head3 Monitor Parameters
|
||||
|
||||
These parameters are used to determine when to send monitors placed on the VAL
|
||||
field.
|
||||
The monitors are sent when the current value exceeds the last transmitted value
|
||||
by the appropriate deadband.
|
||||
If these fields are set to zero, a monitor will be triggered every time the
|
||||
value changes; if set to -1, a monitor will be sent every time the record is
|
||||
processed.
|
||||
|
||||
The ADEL field sets the deadband for archive monitors (C<DBE_LOG> events), while
|
||||
the MDEL field controls value monitors (C<DBE_VALUE> events).
|
||||
|
||||
The remaining fields are used by the record at run-time to implement the record
|
||||
monitoring deadband functionality.
|
||||
|
||||
=fields ADEL, MDEL, ALST, MLST
|
||||
|
||||
=cut
|
||||
|
||||
include "dbCommon.dbd"
|
||||
field(VAL,DBF_INT64) {
|
||||
prompt("Desired Output")
|
||||
promptgroup("50 - Output")
|
||||
asl(ASL0)
|
||||
pp(TRUE)
|
||||
}
|
||||
field(OUT,DBF_OUTLINK) {
|
||||
prompt("Output Specification")
|
||||
promptgroup("50 - Output")
|
||||
interest(1)
|
||||
}
|
||||
field(DOL,DBF_INLINK) {
|
||||
prompt("Desired Output Loc")
|
||||
promptgroup("40 - Input")
|
||||
interest(1)
|
||||
}
|
||||
field(OMSL,DBF_MENU) {
|
||||
prompt("Output Mode Select")
|
||||
promptgroup("50 - Output")
|
||||
interest(1)
|
||||
menu(menuOmsl)
|
||||
}
|
||||
field(EGU,DBF_STRING) {
|
||||
prompt("Units name")
|
||||
promptgroup("80 - Display")
|
||||
interest(1)
|
||||
size(16)
|
||||
prop(YES)
|
||||
}
|
||||
field(DRVH,DBF_INT64) {
|
||||
prompt("Drive High Limit")
|
||||
promptgroup("30 - Action")
|
||||
pp(TRUE)
|
||||
interest(1)
|
||||
prop(YES)
|
||||
}
|
||||
field(DRVL,DBF_INT64) {
|
||||
prompt("Drive Low Limit")
|
||||
promptgroup("30 - Action")
|
||||
pp(TRUE)
|
||||
interest(1)
|
||||
prop(YES)
|
||||
}
|
||||
field(HOPR,DBF_INT64) {
|
||||
prompt("High Operating Range")
|
||||
promptgroup("80 - Display")
|
||||
interest(1)
|
||||
prop(YES)
|
||||
}
|
||||
field(LOPR,DBF_INT64) {
|
||||
prompt("Low Operating Range")
|
||||
promptgroup("80 - Display")
|
||||
interest(1)
|
||||
prop(YES)
|
||||
}
|
||||
field(HIHI,DBF_INT64) {
|
||||
prompt("Hihi Alarm Limit")
|
||||
promptgroup("70 - Alarm")
|
||||
pp(TRUE)
|
||||
interest(1)
|
||||
prop(YES)
|
||||
}
|
||||
field(LOLO,DBF_INT64) {
|
||||
prompt("Lolo Alarm Limit")
|
||||
promptgroup("70 - Alarm")
|
||||
pp(TRUE)
|
||||
interest(1)
|
||||
prop(YES)
|
||||
}
|
||||
field(HIGH,DBF_INT64) {
|
||||
prompt("High Alarm Limit")
|
||||
promptgroup("70 - Alarm")
|
||||
pp(TRUE)
|
||||
interest(1)
|
||||
prop(YES)
|
||||
}
|
||||
field(LOW,DBF_INT64) {
|
||||
prompt("Low Alarm Limit")
|
||||
promptgroup("70 - Alarm")
|
||||
pp(TRUE)
|
||||
interest(1)
|
||||
prop(YES)
|
||||
}
|
||||
field(HHSV,DBF_MENU) {
|
||||
prompt("Hihi Severity")
|
||||
promptgroup("70 - Alarm")
|
||||
pp(TRUE)
|
||||
interest(1)
|
||||
menu(menuAlarmSevr)
|
||||
}
|
||||
field(LLSV,DBF_MENU) {
|
||||
prompt("Lolo Severity")
|
||||
promptgroup("70 - Alarm")
|
||||
pp(TRUE)
|
||||
interest(1)
|
||||
menu(menuAlarmSevr)
|
||||
}
|
||||
field(HSV,DBF_MENU) {
|
||||
prompt("High Severity")
|
||||
promptgroup("70 - Alarm")
|
||||
pp(TRUE)
|
||||
interest(1)
|
||||
menu(menuAlarmSevr)
|
||||
}
|
||||
field(LSV,DBF_MENU) {
|
||||
prompt("Low Severity")
|
||||
promptgroup("70 - Alarm")
|
||||
pp(TRUE)
|
||||
interest(1)
|
||||
menu(menuAlarmSevr)
|
||||
}
|
||||
field(HYST,DBF_INT64) {
|
||||
prompt("Alarm Deadband")
|
||||
promptgroup("70 - Alarm")
|
||||
interest(1)
|
||||
}
|
||||
field(ADEL,DBF_INT64) {
|
||||
prompt("Archive Deadband")
|
||||
promptgroup("80 - Display")
|
||||
interest(1)
|
||||
}
|
||||
field(MDEL,DBF_INT64) {
|
||||
prompt("Monitor Deadband")
|
||||
promptgroup("80 - Display")
|
||||
interest(1)
|
||||
}
|
||||
field(LALM,DBF_INT64) {
|
||||
prompt("Last Value Alarmed")
|
||||
special(SPC_NOMOD)
|
||||
interest(3)
|
||||
}
|
||||
field(ALST,DBF_INT64) {
|
||||
prompt("Last Value Archived")
|
||||
special(SPC_NOMOD)
|
||||
interest(3)
|
||||
}
|
||||
field(MLST,DBF_INT64) {
|
||||
prompt("Last Val Monitored")
|
||||
special(SPC_NOMOD)
|
||||
interest(3)
|
||||
}
|
||||
|
||||
=head3 Simulation Mode
|
||||
|
||||
The record provides several fields to support simulation of absent hardware.
|
||||
If the SIML field is set it is used to read a value into the SIMM field,
|
||||
which controls whether simulation is used or not:
|
||||
|
||||
=over
|
||||
|
||||
=item *
|
||||
SIMM must be zero (C<NO>) for the record to write a value to the device
|
||||
support.
|
||||
|
||||
=item *
|
||||
If SIMM is C<YES> and the SIOL link field is set, the value in engineering
|
||||
units is written using the link.
|
||||
|
||||
=back
|
||||
|
||||
The SIMS field can be set to give the record an alarm severity while it is in
|
||||
simulation mode.
|
||||
|
||||
=fields SIML, SIMM, SIOL, SIMS
|
||||
|
||||
=cut
|
||||
|
||||
field(SIOL,DBF_OUTLINK) {
|
||||
prompt("Sim Output Specifctn")
|
||||
promptgroup("90 - Simulate")
|
||||
interest(1)
|
||||
}
|
||||
field(SIML,DBF_INLINK) {
|
||||
prompt("Sim Mode Location")
|
||||
promptgroup("90 - Simulate")
|
||||
interest(1)
|
||||
}
|
||||
field(SIMM,DBF_MENU) {
|
||||
prompt("Simulation Mode")
|
||||
interest(1)
|
||||
menu(menuYesNo)
|
||||
}
|
||||
field(SIMS,DBF_MENU) {
|
||||
prompt("Sim mode Alarm Svrty")
|
||||
promptgroup("90 - Simulate")
|
||||
interest(2)
|
||||
menu(menuAlarmSevr)
|
||||
}
|
||||
|
||||
=head3 Invalid Alarm Output Action
|
||||
|
||||
Whenever an output record is put into INVALID alarm severity, IVOA specifies
|
||||
the action to take.
|
||||
|
||||
=over
|
||||
|
||||
=item C<Continue normally> (default)
|
||||
|
||||
Write the value. Same as if severity is lower than INVALID.
|
||||
|
||||
=item C<Don't drive outputs>
|
||||
|
||||
Do not write value.
|
||||
|
||||
=item C<Set output to IVOV>
|
||||
|
||||
Set VAL to IVOV, then write the value.
|
||||
|
||||
=back
|
||||
|
||||
=fields IVOA, IVOV
|
||||
|
||||
=cut
|
||||
|
||||
field(IVOA,DBF_MENU) {
|
||||
prompt("INVALID output action")
|
||||
promptgroup("50 - Output")
|
||||
interest(2)
|
||||
menu(menuIvoa)
|
||||
}
|
||||
field(IVOV,DBF_INT64) {
|
||||
prompt("INVALID output value")
|
||||
promptgroup("50 - Output")
|
||||
interest(2)
|
||||
}
|
||||
}
|
||||
|
||||
=head2 Record Support
|
||||
|
||||
=head3 Record Support Routines
|
||||
|
||||
The following are the record support routines that would be of interest
|
||||
to an application developer.
|
||||
Other routines are the C<get_units>, C<get_graphic_double>,
|
||||
C<get_alarm_double> and C<get_control_double> routines, which are used to
|
||||
collect properties from the record for the complex DBR data structures.
|
||||
|
||||
=head4 init_record
|
||||
|
||||
This routine first initializes the simulation mode mechanism by setting SIMM
|
||||
if SIML is a constant.
|
||||
|
||||
It then checks if the device support and the device support's
|
||||
C<write_int64out> routine are defined.
|
||||
If either one does not exist, an error message is issued
|
||||
and processing is terminated.
|
||||
|
||||
If DOL is a constant, then VAL is initialized with its value and UDF is
|
||||
set to FALSE.
|
||||
|
||||
If device support includes C<init_record>, it is called.
|
||||
|
||||
Finally, the deadband mechanisms for monitors and level alarms are
|
||||
initialized.
|
||||
|
||||
=head4 process
|
||||
|
||||
See next section.
|
||||
|
||||
=head3 Record Processing
|
||||
|
||||
Routine C<process> implements the following algorithm:
|
||||
|
||||
=over
|
||||
|
||||
=item 1.
|
||||
|
||||
Check to see that the appropriate device support module and its
|
||||
C<write_int64out> routine are defined.
|
||||
If either one does not exist, an error message is issued and processing is
|
||||
terminated with the PACT field set to TRUE, effectively blocking the record
|
||||
to avoid error storms.
|
||||
|
||||
=item 2.
|
||||
|
||||
Check PACT. If PACT is FALSE, do the following:
|
||||
|
||||
=over
|
||||
|
||||
=item * Determine value, honoring closed loop mode:
|
||||
if DOL is not a CONSTANT and OMSL is CLOSED_LOOP then get value from DOL
|
||||
setting UDF to FALSE in case of success, else use the VAL field.
|
||||
|
||||
=item * Call C<convert>:
|
||||
if drive limits are defined then force value to be within those limits.
|
||||
|
||||
=back
|
||||
|
||||
=item 3.
|
||||
|
||||
Check UDF and level alarms: This routine checks to see if the record is
|
||||
undefined (UDF is TRUE) or if the new VAL causes the alarm status
|
||||
and severity to change. In the latter case, NSEV, NSTA and LALM are set.
|
||||
It also honors the alarm hysteresis factor (HYST): the value must change
|
||||
by at least HYST between level alarm status and severity changes.
|
||||
|
||||
=item 4.
|
||||
|
||||
Check severity and write the new value. See L<Invalid Alarm Output Action>
|
||||
for details on how invalid alarms affect output records.
|
||||
|
||||
=item 5.
|
||||
|
||||
If PACT has been changed to TRUE, the device support signals asynchronous
|
||||
processing: its C<write_int64out> output routine has started, but not
|
||||
completed writing the new value.
|
||||
In this case, the processing routine merely returns, leaving PACT TRUE.
|
||||
|
||||
=item 6.
|
||||
|
||||
Check to see if monitors should be invoked:
|
||||
|
||||
=over
|
||||
|
||||
=item * Alarm monitors are posted if the alarm status or severity have
|
||||
changed.
|
||||
|
||||
=item * Archive and value change monitors are posted if ADEL and MDEL
|
||||
conditions (see L<Monitor Parameters>) are met.
|
||||
|
||||
=item * NSEV and NSTA are reset to 0.
|
||||
|
||||
=back
|
||||
|
||||
=item 7.
|
||||
|
||||
Scan (process) forward link if necessary, set PACT to FALSE, and return.
|
||||
|
||||
=back
|
||||
|
||||
=head2 Device Support
|
||||
|
||||
=head3 Device Support Interface
|
||||
|
||||
The record requires device support to provide an entry table (dset) which
|
||||
defines the following members:
|
||||
|
||||
typedef struct {
|
||||
long number;
|
||||
long (*report)(int level);
|
||||
long (*init)(int after);
|
||||
long (*init_record)(int64outRecord *prec);
|
||||
long (*get_ioint_info)(int cmd, int64outRecord *prec, IOSCANPVT *piosl);
|
||||
long (*write_int64out)(int64outRecord *prec);
|
||||
} int64outdset;
|
||||
|
||||
The module must set C<number> to at least 5, and provide a pointer to its
|
||||
C<write_int64out()> routine; the other function pointers may be C<NULL> if their
|
||||
associated functionality is not required for this support layer.
|
||||
Most device supports also provide an C<init_record()> routine to configure the
|
||||
record instance and connect it to the hardware or driver support layer.
|
||||
|
||||
The individual routines are described below.
|
||||
|
||||
=head3 Device Support Routines
|
||||
|
||||
=head4 long report(int level)
|
||||
|
||||
This optional routine is called by the IOC command C<dbior> and is passed the
|
||||
report level that was requested by the user.
|
||||
It should print a report on the state of the device support to stdout.
|
||||
The C<level> parameter may be used to output increasingly more detailed
|
||||
information at higher levels, or to select different types of information with
|
||||
different levels.
|
||||
Level zero should print no more than a small summary.
|
||||
|
||||
=head4 long init(int after)
|
||||
|
||||
This optional routine is called twice at IOC initialization time.
|
||||
The first call happens before any of the C<init_record()> calls are made, with
|
||||
the integer parameter C<after> set to 0.
|
||||
The second call happens after all of the C<init_record()> calls have been made,
|
||||
with C<after> set to 1.
|
||||
|
||||
=head4 long init_record(int64outRecord *prec)
|
||||
|
||||
This optional routine is called by the record initialization code for each
|
||||
int64out record instance that has its DTYP field set to use this device support.
|
||||
It is normally used to check that the OUT address is the expected type and that
|
||||
it points to a valid device, to allocate any record-specific buffer space and
|
||||
other memory, and to connect any communication channels needed for the
|
||||
C<write_int64out()> routine to work properly.
|
||||
|
||||
=head4 long get_ioint_info(int cmd, int64outRecord *prec, IOSCANPVT *piosl)
|
||||
|
||||
This optional routine is called whenever the record's SCAN field is being
|
||||
changed to or from the value C<I/O Intr> to find out which I/O Interrupt Scan
|
||||
list the record should be added to or deleted from.
|
||||
If this routine is not provided, it will not be possible to set the SCAN field
|
||||
to the value C<I/O Intr> at all.
|
||||
|
||||
The C<cmd> parameter is zero when the record is being added to the scan list,
|
||||
and one when it is being removed from the list.
|
||||
The routine must determine which interrupt source the record should be connected
|
||||
to, which it indicates by the scan list that it points the location at C<*piosl>
|
||||
to before returning.
|
||||
It can prevent the SCAN field from being changed at all by returning a non-zero
|
||||
value to its caller.
|
||||
|
||||
In most cases the device support will create the I/O Interrupt Scan lists that
|
||||
it returns for itself, by calling C<void scanIoInit(IOSCANPVT *piosl)> once for
|
||||
each separate interrupt source.
|
||||
That routine allocates memory and inializes the list, then passes back a pointer
|
||||
to the new list in the location at C<*piosl>.
|
||||
|
||||
When the device support receives notification that the interrupt has occurred,
|
||||
it announces that to the IOC by calling C<void scanIoRequest(IOSCANPVT iosl)>
|
||||
which will arrange for the appropriate records to be processed in a suitable
|
||||
thread.
|
||||
The C<scanIoRequest()> routine is safe to call from an interrupt service routine
|
||||
on embedded architectures (vxWorks and RTEMS).
|
||||
|
||||
=head4 long write_int64out(int64outRecord *prec)
|
||||
|
||||
This essential routine is called when the record wants to write a new value
|
||||
to the addressed device.
|
||||
It is responsible for performing (or at least initiating) a write operation,
|
||||
using the value from the record's VAL field.
|
||||
|
||||
If the device may take more than a few microseconds to accept the new value,
|
||||
this routine must never block (busy-wait), but use the asynchronous
|
||||
processing mechanism.
|
||||
In that case it signals the asynchronous operation by setting the record's
|
||||
PACT field to TRUE before it returns, having arranged for the record's
|
||||
C<process()> routine to be called later once the write operation is over.
|
||||
When that happens, the C<write_int64out()> routine will be called again with
|
||||
PACT still set to TRUE; it should then set it to FALSE to indicate the write
|
||||
has completed, and return.
|
||||
|
||||
A return value of zero indicates success, any other value indicates that an
|
||||
error occurred.
|
||||
|
||||
=head3 Extended Device Support
|
||||
|
||||
...
|
||||
|
||||
=cut
|
||||
|
||||
=head2 Device Support For Soft Records
|
||||
|
||||
Two soft device support modules, Soft Channel and Soft Callback Channel, are
|
||||
provided for output records not related to actual hardware devices. The
|
||||
OUT link type must be either a CONSTANT, DB_LINK, or CA_LINK.
|
||||
|
||||
=head3 Soft Channel
|
||||
|
||||
This module writes the current value using the record's VAL field.
|
||||
|
||||
C<write_int64out> calls C<dbPutLink> to write the current value.
|
||||
|
||||
=head3 Soft Callback Channel
|
||||
|
||||
This module is like the previous except that it writes the current value
|
||||
using asynchronous processing that will not complete until an asynchronous
|
||||
processing of the target record has completed.
|
||||
|
||||
=cut
|
||||
@@ -32,6 +32,13 @@ testHarness_SRCS += arrayOpTest.c
|
||||
TESTFILES += ../arrayOpTest.db
|
||||
TESTS += arrayOpTest
|
||||
|
||||
TESTPROD_HOST += recMiscTest
|
||||
recMiscTest_SRCS += recMiscTest.c
|
||||
recMiscTest_SRCS += recTestIoc_registerRecordDeviceDriver.cpp
|
||||
testHarness_SRCS += recMiscTest.c
|
||||
TESTFILES += ../recMiscTest.db
|
||||
TESTS += recMiscTest
|
||||
|
||||
TESTPROD_HOST += linkRetargetLinkTest
|
||||
linkRetargetLinkTest_SRCS += linkRetargetLinkTest.c
|
||||
linkRetargetLinkTest_SRCS += recTestIoc_registerRecordDeviceDriver.cpp
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
|
||||
int analogMonitorTest(void);
|
||||
int compressTest(void);
|
||||
int recMiscTest(void);
|
||||
int arrayOpTest(void);
|
||||
int asTest(void);
|
||||
int linkRetargetLinkTest(void);
|
||||
@@ -27,6 +28,8 @@ void epicsRunRecordTests(void)
|
||||
|
||||
runTest(compressTest);
|
||||
|
||||
runTest(recMiscTest);
|
||||
|
||||
runTest(arrayOpTest);
|
||||
|
||||
runTest(asTest);
|
||||
|
||||
@@ -0,0 +1,88 @@
|
||||
/*************************************************************************\
|
||||
* Copyright (c) 2017 Michael Davidsaver
|
||||
* EPICS BASE is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
\*************************************************************************/
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "dbAccess.h"
|
||||
#include "errlog.h"
|
||||
#include "dbStaticLib.h"
|
||||
#include "dbUnitTest.h"
|
||||
#include "testMain.h"
|
||||
|
||||
static
|
||||
void testint64BeforeInit(void)
|
||||
{
|
||||
const char *S;
|
||||
DBENTRY dbent;
|
||||
|
||||
/* check dbGet/PutString */
|
||||
|
||||
testDiag("In %s", EPICS_FUNCTION);
|
||||
|
||||
dbInitEntryFromRecord(testdbRecordPtr("out64"), &dbent);
|
||||
if(dbFindField(&dbent, "VAL"))
|
||||
testAbort("Failed to find out64.VAL");
|
||||
|
||||
S = dbGetString(&dbent);
|
||||
testOk(S && strcmp(S, "0")==0, "initial value \"%s\"", S);
|
||||
|
||||
testOk1(dbPutString(&dbent, "0x12345678abcdef00")==0);
|
||||
|
||||
S = dbGetString(&dbent);
|
||||
testOk(S && strcmp(S, "1311768467750121216")==0, "1311768467750121216 \"%s\"", S);
|
||||
|
||||
dbFinishEntry(&dbent);
|
||||
}
|
||||
|
||||
static
|
||||
void testint64AfterInit(void)
|
||||
{
|
||||
testDiag("In %s", EPICS_FUNCTION);
|
||||
|
||||
/* check dbGet/PutField and DB links */
|
||||
|
||||
testdbGetFieldEqual("in64", DBF_UINT64, 0llu);
|
||||
testdbGetFieldEqual("out64", DBF_UINT64, 0x12345678abcdef00llu);
|
||||
|
||||
testdbPutFieldOk("out64.PROC", DBF_LONG, 1);
|
||||
|
||||
testdbGetFieldEqual("in64", DBF_UINT64, 0x12345678abcdef00llu);
|
||||
|
||||
testdbPutFieldOk("out64.VAL", DBF_UINT64, 0x22345678abcdef00llu);
|
||||
|
||||
testdbPutFieldOk("in64.PROC", DBF_LONG, 1);
|
||||
|
||||
testdbGetFieldEqual("in64", DBF_UINT64, 0x22345678abcdef00llu);
|
||||
}
|
||||
|
||||
void recTestIoc_registerRecordDeviceDriver(struct dbBase *);
|
||||
|
||||
MAIN(recMiscTest)
|
||||
{
|
||||
testPlan(0);
|
||||
|
||||
testdbPrepare();
|
||||
|
||||
testdbReadDatabase("recTestIoc.dbd", NULL, NULL);
|
||||
|
||||
recTestIoc_registerRecordDeviceDriver(pdbbase);
|
||||
|
||||
testdbReadDatabase("recMiscTest.db", NULL, NULL);
|
||||
|
||||
testint64BeforeInit();
|
||||
|
||||
eltc(0);
|
||||
testIocInitOk();
|
||||
eltc(1);
|
||||
|
||||
testint64AfterInit();
|
||||
|
||||
testIocShutdownOk();
|
||||
|
||||
testdbCleanup();
|
||||
|
||||
return testDone();
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
|
||||
# check int64in/out
|
||||
|
||||
record(int64in, "in64") {
|
||||
field(INP , "out64 NPP")
|
||||
}
|
||||
|
||||
record(int64out, "out64") {
|
||||
field(OUT , "in64 NPP")
|
||||
}
|
||||
Reference in New Issue
Block a user