merged with upstream

This commit is contained in:
2019-02-20 10:51:02 +01:00
244 changed files with 11196 additions and 3586 deletions
-5
View File
@@ -25,11 +25,6 @@ INC += databaseVersionNum.h
PROD_LIBS = Com
EPICS_DATABASE_MAJOR_VERSION = 3
EPICS_DATABASE_MINOR_VERSION = 17
EPICS_DATABASE_MAINTENANCE_VERSION = 0
EPICS_DATABASE_DEVELOPMENT_FLAG = 1
include $(IOCDIR)/as/Makefile
include $(IOCDIR)/bpt/Makefile
include $(IOCDIR)/db/Makefile
+1 -3
View File
@@ -35,7 +35,6 @@ INC += dbTest.h
INC += dbCaTest.h
INC += db_test.h
INC += db_field_log.h
INC += initHooks.h
INC += recGbl.h
INC += dbIocRegister.h
INC += chfPlugin.h
@@ -63,7 +62,7 @@ DBDINC += menuScan
DBDINC += dbCommon
dbMenusPod = $(notdir $(wildcard ../db/menu*.dbd.pod))
HTMLS += $(patsubst %.dbd.pod,%.html,$(menusPod))
HTMLS += $(patsubst %.dbd.pod,%.html,$(dbMenusPod))
dbCore_SRCS += dbLock.c
dbCore_SRCS += dbAccess.c
@@ -87,7 +86,6 @@ dbCore_SRCS += recGbl.c
dbCore_SRCS += callback.c
dbCore_SRCS += dbCa.c
dbCore_SRCS += dbCaTest.c
dbCore_SRCS += initHooks.c
dbCore_SRCS += cvtBpt.c
dbCore_SRCS += dbContext.cpp
dbCore_SRCS += dbChannelIO.cpp
+47
View File
@@ -54,6 +54,7 @@ typedef struct cbQueueSet {
epicsEventId semWakeUp;
epicsRingPointerId queue;
int queueOverflow;
int queueOverflows;
int shutdown;
int threadsConfigured;
int threadsRunning;
@@ -103,6 +104,51 @@ int callbackSetQueueSize(int size)
return 0;
}
int callbackQueueStatus(const int reset, callbackQueueStats *result)
{
int ret;
if (!callbackIsInit) return -1;
if (result) {
int prio;
result->size = callbackQueueSize;
for(prio = 0; prio < NUM_CALLBACK_PRIORITIES; prio++) {
epicsRingPointerId qId = callbackQueue[prio].queue;
result->numUsed[prio] = epicsRingPointerGetUsed(qId);
result->maxUsed[prio] = epicsRingPointerGetHighWaterMark(qId);
result->numOverflow[prio] = epicsAtomicGetIntT(&callbackQueue[prio].queueOverflows);
}
ret = 0;
} else {
ret = -2;
}
if (reset) {
int prio;
for(prio = 0; prio < NUM_CALLBACK_PRIORITIES; prio++) {
epicsRingPointerResetHighWaterMark(callbackQueue[prio].queue);
}
}
return ret;
}
void callbackQueueShow(const int reset)
{
callbackQueueStats stats;
if (callbackQueueStatus(reset, &stats) == -1) {
fprintf(stderr, "Callback system not initialized, yet. Please run "
"iocInit before using this command.\n");
} else {
int prio;
printf("PRIORITY HIGH-WATER MARK ITEMS IN Q Q SIZE %% USED Q OVERFLOWS\n");
for (prio = 0; prio < NUM_CALLBACK_PRIORITIES; prio++) {
double qusage = 100.0 * stats.numUsed[prio] / stats.size;
printf("%8s %15d %10d %6d %6.1f %11d\n",
threadNamePrefix[prio], stats.maxUsed[prio],
stats.numUsed[prio], stats.size, qusage,
stats.numOverflow[prio]);
}
}
}
int callbackParallelThreads(int count, const char *prio)
{
if (callbackIsInit) {
@@ -290,6 +336,7 @@ int callbackRequest(CALLBACK *pcallback)
if (!pushOK) {
epicsInterruptContextMessage(fullMessage[priority]);
mySet->queueOverflow = TRUE;
epicsAtomicIncrIntT(&mySet->queueOverflows);
return S_db_bufFull;
}
epicsEventSignal(mySet->semWakeUp);
+9
View File
@@ -48,6 +48,13 @@ typedef epicsCallback CALLBACK;
typedef void (*CALLBACKFUNC)(struct callbackPvt*);
typedef struct callbackQueueStats {
int size;
int numUsed[NUM_CALLBACK_PRIORITIES];
int maxUsed[NUM_CALLBACK_PRIORITIES];
int numOverflow[NUM_CALLBACK_PRIORITIES];
} callbackQueueStats;
#define callbackSetCallback(PFUN, PCALLBACK) \
( (PCALLBACK)->callback = (PFUN) )
#define callbackSetPriority(PRIORITY, PCALLBACK) \
@@ -73,6 +80,8 @@ epicsShareFunc void callbackCancelDelayed(CALLBACK *pcallback);
epicsShareFunc void callbackRequestProcessCallbackDelayed(
CALLBACK *pCallback, int Priority, void *pRec, double seconds);
epicsShareFunc int callbackSetQueueSize(int size);
epicsShareFunc int callbackQueueStatus(const int reset, callbackQueueStats *result);
epicsShareFunc void callbackQueueShow(const int reset);
epicsShareFunc int callbackParallelThreads(int count, const char *prio);
#ifdef __cplusplus
+57 -52
View File
@@ -34,7 +34,7 @@
#include "errlog.h"
#include "errMdef.h"
#define epicsExportSharedSymbols
#include "epicsExport.h" /* #define epicsExportSharedSymbols */
#include "caeventmask.h"
#include "callback.h"
#include "dbAccessDefs.h"
@@ -65,6 +65,9 @@
epicsShareDef struct dbBase *pdbbase = 0;
epicsShareDef volatile int interruptAccept=FALSE;
epicsShareDef int dbAccessDebugPUTF = 0;
epicsExportAddress(int, dbAccessDebugPUTF);
/* Hook Routines */
epicsShareDef DB_LOAD_RECORDS_HOOK_ROUTINE dbLoadRecordsHook = NULL;
@@ -446,22 +449,6 @@ int dbGetFieldIndex(const struct dbAddr *paddr)
return paddr->pfldDes->indRecordType;
}
/*
* Process a record if its scan field is passive.
* Will notify if processing is complete by callback.
* (only if you are interested in completion)
*/
long dbScanPassive(dbCommon *pfrom, dbCommon *pto)
{
/* if not passive just return success */
if (pto->scan != 0)
return 0;
if (pfrom && pfrom->ppn)
dbNotifyAdd(pfrom,pto);
return dbProcess(pto);
}
/*
* Process the record.
* 1. Check for breakpoints.
@@ -527,7 +514,8 @@ long dbProcess(dbCommon *precord)
unsigned short monitor_mask;
if (*ptrace)
printf("%s: Active %s\n", context, precord->name);
printf("%s: dbProcess of Active '%s' with RPRO=%d\n",
context, precord->name, precord->rpro);
/* raise scan alarm after MAX_LOCK times */
if ((precord->stat == SCAN_ALARM) ||
@@ -556,7 +544,8 @@ long dbProcess(dbCommon *precord)
/* if disabled check disable alarm severity and return success */
if (precord->disa == precord->disv) {
if (*ptrace)
printf("%s: Disabled %s\n", context, precord->name);
printf("%s: dbProcess of Disabled '%s'\n",
context, precord->name);
/*take care of caching and notifyCompletion*/
precord->rpro = FALSE;
@@ -593,7 +582,7 @@ long dbProcess(dbCommon *precord)
}
if (*ptrace)
printf("%s: Process %s\n", context, precord->name);
printf("%s: dbProcess of '%s'\n", context, precord->name);
/* process record */
status = prset->process(precord);
@@ -612,6 +601,31 @@ all_done:
return status;
}
long dbEntryToAddr(const DBENTRY *pdbentry, DBADDR *paddr)
{
dbFldDes *pflddes = pdbentry->pflddes;
short dbfType = pflddes->field_type;
paddr->precord = pdbentry->precnode->precord;
paddr->pfield = pdbentry->pfield;
paddr->pfldDes = pflddes;
paddr->no_elements = 1;
paddr->field_type = dbfType;
paddr->field_size = pflddes->size;
paddr->special = pflddes->special;
paddr->dbr_field_type = mapDBFToDBR[dbfType];
if (paddr->special == SPC_DBADDR) {
const rset *prset = dbGetRset(paddr);
/* Let record type modify paddr */
if (prset && prset->cvt_dbaddr) {
return prset->cvt_dbaddr(paddr);
}
}
return 0;
}
/*
* Fill out a database structure (*paddr) for
* a record given by the name "pname."
@@ -622,9 +636,7 @@ all_done:
long dbNameToAddr(const char *pname, DBADDR *paddr)
{
DBENTRY dbEntry;
dbFldDes *pflddes;
long status = 0;
short dbfType;
if (!pname || !*pname || !pdbbase)
return S_db_notFound;
@@ -639,46 +651,28 @@ long dbNameToAddr(const char *pname, DBADDR *paddr)
status = dbGetAttributePart(&dbEntry, &pname);
if (status) goto finish;
pflddes = dbEntry.pflddes;
dbfType = pflddes->field_type;
paddr->precord = dbEntry.precnode->precord;
paddr->pfield = dbEntry.pfield;
paddr->pfldDes = pflddes;
paddr->no_elements = 1;
paddr->field_type = dbfType;
paddr->field_size = pflddes->size;
paddr->special = pflddes->special;
paddr->dbr_field_type = mapDBFToDBR[dbfType];
if (paddr->special == SPC_DBADDR) {
rset *prset = dbGetRset(paddr);
/* Let record type modify paddr */
if (prset && prset->cvt_dbaddr) {
status = prset->cvt_dbaddr(paddr);
if (status)
goto finish;
dbfType = paddr->field_type;
}
}
status = dbEntryToAddr(&dbEntry, paddr);
if (status) goto finish;
/* Handle field modifiers */
if (*pname++ == '$') {
short dbfType = paddr->field_type;
/* Some field types can be accessed as char arrays */
if (dbfType == DBF_STRING) {
paddr->no_elements = paddr->field_size;
paddr->field_type = DBF_CHAR;
paddr->field_size = 1;
paddr->dbr_field_type = DBR_CHAR;
} else if (dbfType >= DBF_INLINK && dbfType <= DBF_FWDLINK) {
}
else if (dbfType >= DBF_INLINK && dbfType <= DBF_FWDLINK) {
/* Clients see a char array, but keep original dbfType */
paddr->no_elements = PVLINK_STRINGSZ;
paddr->field_size = 1;
paddr->dbr_field_type = DBR_CHAR;
} else {
}
else {
status = S_dbLib_fieldNotFound;
goto finish;
}
}
@@ -713,6 +707,18 @@ void dbInitEntryFromRecord(struct dbCommon *prec, DBENTRY *pdbentry)
pdbentry->precnode = ppvt->recnode;
}
struct link* dbGetDevLink(struct dbCommon* prec)
{
DBLINK *plink = 0;
DBENTRY entry;
dbInitEntryFromRecord(prec, &entry);
if(dbFindField(&entry, "INP")==0 || dbFindField(&entry, "OUT")==0) {
plink = (DBLINK*)entry.pfield;
}
dbFinishEntry(&entry);
return plink;
}
long dbValueSize(short dbr_type)
{
/* sizes for value associated with each DBR request type */
@@ -1049,7 +1055,7 @@ static long dbPutFieldLink(DBADDR *paddr,
return S_db_badDbrtype;
}
status = dbParseLink(pstring, pfldDes->field_type, &link_info, 0);
status = dbParseLink(pstring, pfldDes->field_type, &link_info);
if (status)
return status;
@@ -1222,8 +1228,8 @@ long dbPutField(DBADDR *paddr, short dbrType,
precord->scan == 0 &&
dbrType < DBR_PUT_ACKT)) {
if (precord->pact) {
if (precord->tpro)
printf("%s: Active %s\n",
if (dbAccessDebugPUTF && precord->tpro)
printf("%s: dbPutField to Active '%s', setting RPRO=1\n",
epicsThreadGetNameSelf(), precord->name);
precord->rpro = TRUE;
} else {
@@ -1356,4 +1362,3 @@ done:
paddr->pfield = pfieldsave;
return status;
}
+26 -2
View File
@@ -34,6 +34,7 @@ extern "C" {
epicsShareExtern struct dbBase *pdbbase;
epicsShareExtern volatile int interruptAccept;
epicsShareExtern int dbAccessDebugPUTF;
/* The database field and request types are defined in dbFldTypes.h*/
/* Data Base Request Options */
@@ -203,6 +204,8 @@ struct dbr_alDouble {DBRalDouble};
#define S_db_notInit (M_dbAccess|67) /*Not initialized*/
#define S_db_bufFull (M_dbAccess|68) /*Buffer full*/
struct dbEntry;
epicsShareFunc long dbPutSpecial(struct dbAddr *paddr,int pass);
epicsShareFunc rset * dbGetRset(const struct dbAddr *paddr);
epicsShareFunc long dbPutAttribute(
@@ -212,8 +215,29 @@ epicsShareFunc int dbGetFieldIndex(const struct dbAddr *paddr);
epicsShareFunc long dbScanPassive(
struct dbCommon *pfrom,struct dbCommon *pto);
epicsShareFunc long dbProcess(struct dbCommon *precord);
epicsShareFunc long dbNameToAddr(
const char *pname,struct dbAddr *);
epicsShareFunc long dbNameToAddr(const char *pname, struct dbAddr *paddr);
/** Initialize DBADDR from a dbEntry
* Also handles SPC_DBADDR processing. This is really an internal
* routine for use by dbNameToAddr() and dbChannelCreate().
*/
epicsShareFunc long dbEntryToAddr(const struct dbEntry *pdbentry,
struct dbAddr *paddr);
/** Initialize DBENTRY from a valid dbAddr*
* Constant time equivalent of dbInitEntry() then dbFindRecord(),
* and finally dbFollowAlias().
*/
epicsShareFunc void dbInitEntryFromAddr(struct dbAddr *paddr,
struct dbEntry *pdbentry);
/** Initialize DBENTRY from a valid record (dbCommon*)
* Constant time equivalent of dbInitEntry() then dbFindRecord(),
* and finally dbFollowAlias() when no field is specified.
*/
epicsShareFunc void dbInitEntryFromRecord(struct dbCommon *prec,
struct dbEntry *pdbentry);
epicsShareFunc devSup* dbDTYPtoDevSup(dbRecordType *prdes, int dtyp);
epicsShareFunc devSup* dbDSETtoDevSup(dbRecordType *prdes, struct dset *pdset);
epicsShareFunc long dbGetField(
+12 -3
View File
@@ -703,7 +703,12 @@ static long doLocked(struct link *plink, dbLinkUserCallback rtn, void *priv)
caLink *pca;
long status;
pcaGetCheck
assert(plink);
if (plink->type != CA_LINK) return -1;
pca = (caLink *)plink->value.pv_link.pvt;
assert(pca);
epicsMutexMustLock(pca->lock);
assert(pca->plink);
status = rtn(plink, priv);
epicsMutexUnlock(pca->lock);
return status;
@@ -842,6 +847,7 @@ static void eventCallback(struct event_handler_args arg)
struct dbr_time_double *pdbr_time_double;
dbCaCallback monitor = 0;
void *userPvt = 0;
int doScan = 1;
assert(pca);
epicsMutexMustLock(pca->lock);
@@ -872,10 +878,13 @@ static void eventCallback(struct event_handler_args arg)
memcpy(pca->pgetString, dbr_value_ptr(arg.dbr, arg.type), size);
pca->gotInString = TRUE;
} else switch (arg.type){
case DBR_TIME_ENUM:
/* Disable the record scan if we also have a string monitor */
doScan = !(plink->value.pv_link.pvlMask & pvlOptInpString);
/* fall through */
case DBR_TIME_STRING:
case DBR_TIME_SHORT:
case DBR_TIME_FLOAT:
case DBR_TIME_ENUM:
case DBR_TIME_CHAR:
case DBR_TIME_LONG:
case DBR_TIME_DOUBLE:
@@ -893,7 +902,7 @@ static void eventCallback(struct event_handler_args arg)
pca->sevr = pdbr_time_double->severity;
pca->stat = pdbr_time_double->status;
memcpy(&pca->timeStamp, &pdbr_time_double->stamp, sizeof(epicsTimeStamp));
if (precord) {
if (doScan && precord) {
struct pv_link *ppv_link = &plink->value.pv_link;
if ((ppv_link->pvlMask & pvlOptCP) ||
+10 -27
View File
@@ -20,6 +20,7 @@
#include "cantProceed.h"
#include "epicsAssert.h"
#include "epicsString.h"
#include "epicsStdio.h"
#include "errlog.h"
#include "freeList.h"
#include "gpHash.h"
@@ -472,9 +473,7 @@ dbChannel * dbChannelCreate(const char *name)
dbChannel *chan = NULL;
char *cname;
dbAddr *paddr;
dbFldDes *pflddes;
long status;
short dbfType;
if (!name || !*name || !pdbbase)
return NULL;
@@ -497,32 +496,14 @@ dbChannel * dbChannelCreate(const char *name)
ellInit(&chan->post_chain);
paddr = &chan->addr;
pflddes = dbEntry.pflddes;
dbfType = pflddes->field_type;
paddr->precord = dbEntry.precnode->precord;
paddr->pfield = dbEntry.pfield;
paddr->pfldDes = pflddes;
paddr->no_elements = 1;
paddr->field_type = dbfType;
paddr->field_size = pflddes->size;
paddr->special = pflddes->special;
paddr->dbr_field_type = mapDBFToDBR[dbfType];
if (paddr->special == SPC_DBADDR) {
rset *prset = dbGetRset(paddr);
/* Let record type modify paddr */
if (prset && prset->cvt_dbaddr) {
status = prset->cvt_dbaddr(paddr);
if (status)
goto finish;
dbfType = paddr->field_type;
}
}
status = dbEntryToAddr(&dbEntry, paddr);
if (status)
goto finish;
/* Handle field modifiers */
if (*pname) {
short dbfType = paddr->field_type;
if (*pname == '$') {
/* Some field types can be accessed as char arrays */
if (dbfType == DBF_STRING) {
@@ -530,12 +511,14 @@ dbChannel * dbChannelCreate(const char *name)
paddr->field_type = DBF_CHAR;
paddr->field_size = 1;
paddr->dbr_field_type = DBR_CHAR;
} else if (dbfType >= DBF_INLINK && dbfType <= DBF_FWDLINK) {
}
else if (dbfType >= DBF_INLINK && dbfType <= DBF_FWDLINK) {
/* Clients see a char array, but keep original dbfType */
paddr->no_elements = PVLINK_STRINGSZ;
paddr->field_size = 1;
paddr->dbr_field_type = DBR_CHAR;
} else {
}
else {
status = S_dbLib_fieldNotFound;
goto finish;
}
+104 -20
View File
@@ -12,6 +12,29 @@
* Current Author: Andrew Johnson
*/
/* The PUTF and RPRO fields in dbCommon are flags that indicate when a record
* is being processed as a result of an external put (i.e. some server process
* calling dbPutField()), ensuring that the record and its successors will
* eventually get processed even if they happen to be busy at the time of the
* put. From Base-3.16.2 and 7.0.2 the code ensures that all records downstream
* from the original are processed even if a busy asynchronous device appears
* in the processing chain (this breaks the chain in older versions).
*
* PUTF - This field is set in dbPutField() prior to it calling dbProcess().
* It is normally cleared at the end of processing in recGblFwdLink().
* It may also be cleared in dbProcess() if DISA==DISV (scan disabled),
* or by the processTarget() function below.
*
* If PUTF is TRUE before a call to dbProcess(prec), then after it returns
* either PACT is TRUE, or PUTF will be FALSE.
*
* RPRO - This field is set by dbPutField() or by the processTarget() function
* below when a record to be processed is found to be busy (PACT==1).
* It is normally cleared in recGblFwdLink() when the record is queued
* for re-processing, or in dbProcess() if DISA==DISV (scan disabled).
*/
#include <stddef.h>
#include <stdlib.h>
#include <stdarg.h>
@@ -43,17 +66,22 @@
#include "dbNotify.h"
#include "dbScan.h"
#include "dbStaticLib.h"
#include "dbServer.h"
#include "devSup.h"
#include "link.h"
#include "recGbl.h"
#include "recSup.h"
#include "special.h"
#include "dbDbLink.h"
/***************************** Database Links *****************************/
/* Forward definition */
/* Forward definitions */
static lset dbDb_lset;
static long processTarget(dbCommon *psrc, dbCommon *pdst);
long dbDbInitLink(struct link *plink, short dbfType)
{
DBADDR dbaddr;
@@ -138,11 +166,7 @@ static long dbDbGetValue(struct link *plink, short dbrType, void *pbuffer,
/* scan passive records if link is process passive */
if (ppv_link->pvlMask & pvlOptPP) {
unsigned char pact = precord->pact;
precord->pact = TRUE;
status = dbScanPassive(precord, paddr->precord);
precord->pact = pact;
if (status)
return status;
}
@@ -311,22 +335,10 @@ static long dbDbPutValue(struct link *plink, short dbrType,
return status;
if (paddr->pfield == (void *) &pdest->proc ||
(ppv_link->pvlMask & pvlOptPP && pdest->scan == 0)) {
/* if dbPutField caused asyn record to process */
/* ask for reprocessing*/
if (pdest->putf) {
pdest->rpro = TRUE;
} else { /* process dest record with source's PACT true */
unsigned char pact;
if (psrce && psrce->ppn)
dbNotifyAdd(psrce, pdest);
pact = psrce->pact;
psrce->pact = TRUE;
status = dbProcess(pdest);
psrce->pact = pact;
}
(ppv_link->pvlMask & pvlOptPP && pdest->scan == 0)) {
status = processTarget(psrce, pdest);
}
return status;
}
@@ -356,3 +368,75 @@ static lset dbDb_lset = {
dbDbPutValue, NULL,
dbDbScanFwdLink, doLocked
};
/*
* Process a record if its scan field is passive.
*/
long dbScanPassive(dbCommon *pfrom, dbCommon *pto)
{
/* if not passive we're done */
if (pto->scan != 0)
return 0;
return processTarget(pfrom, pto);
}
static long processTarget(dbCommon *psrc, dbCommon *pdst)
{
char context[40] = "";
int trace = dbAccessDebugPUTF && *dbLockSetAddrTrace(psrc);
long status;
epicsUInt8 pact = psrc->pact;
psrc->pact = TRUE;
if (psrc && psrc->ppn)
dbNotifyAdd(psrc, pdst);
if (trace && dbServerClient(context, sizeof(context))) {
/* No client, use thread name */
strncpy(context, epicsThreadGetNameSelf(), sizeof(context));
context[sizeof(context) - 1] = 0;
}
if (!pdst->pact) {
/* Normal propagation of PUTF from src to dst */
if (trace)
printf("%s: '%s' -> '%s' with PUTF=%u\n",
context, psrc->name, pdst->name, psrc->putf);
if (pdst->putf)
errlogPrintf("Warning: '%s.PUTF' found true with PACT false\n",
pdst->name);
pdst->putf = psrc->putf;
}
else if (psrc->putf) {
/* The dst record is busy (awaiting async reprocessing) and
* we were originally triggered by a call to dbPutField(),
* so we mark the dst record for reprocessing once the async
* completion is over.
*/
if (trace)
printf("%s: '%s' -> Active '%s', setting RPRO=1\n",
context, psrc->name, pdst->name);
pdst->putf = FALSE;
pdst->rpro = TRUE;
}
else {
/* The dst record is busy, but we weren't triggered by a call
* to dbPutField(). Do nothing.
*/
if (trace)
printf("%s: '%s' -> Active '%s', done\n",
context, psrc->name, pdst->name);
}
status = dbProcess(pdst);
psrc->pact = pact;
return status;
}
@@ -296,6 +296,17 @@ static void scanOnceSetQueueSizeCallFunc(const iocshArgBuf *args)
scanOnceSetQueueSize(args[0].ival);
}
/* scanOnceQueueShow */
static const iocshArg scanOnceQueueShowArg0 = { "reset",iocshArgInt};
static const iocshArg * const scanOnceQueueShowArgs[1] =
{&scanOnceQueueShowArg0};
static const iocshFuncDef scanOnceQueueShowFuncDef =
{"scanOnceQueueShow",1,scanOnceQueueShowArgs};
static void scanOnceQueueShowCallFunc(const iocshArgBuf *args)
{
scanOnceQueueShow(args[0].ival);
}
/* scanppl */
static const iocshArg scanpplArg0 = { "rate",iocshArgDouble};
static const iocshArg * const scanpplArgs[1] = {&scanpplArg0};
@@ -335,6 +346,17 @@ static void callbackSetQueueSizeCallFunc(const iocshArgBuf *args)
callbackSetQueueSize(args[0].ival);
}
/* callbackQueueShow */
static const iocshArg callbackQueueShowArg0 = { "reset", iocshArgInt};
static const iocshArg * const callbackQueueShowArgs[1] =
{&callbackQueueShowArg0};
static const iocshFuncDef callbackQueueShowFuncDef =
{"callbackQueueShow",1,callbackQueueShowArgs};
static void callbackQueueShowCallFunc(const iocshArgBuf *args)
{
callbackQueueShow(args[0].ival);
}
/* callbackParallelThreads */
static const iocshArg callbackParallelThreadsArg0 = { "no of threads", iocshArgInt};
static const iocshArg callbackParallelThreadsArg1 = { "priority", iocshArgString};
@@ -441,12 +463,14 @@ void dbIocRegister(void)
iocshRegister(&dbLockShowLockedFuncDef,dbLockShowLockedCallFunc);
iocshRegister(&scanOnceSetQueueSizeFuncDef,scanOnceSetQueueSizeCallFunc);
iocshRegister(&scanOnceQueueShowFuncDef,scanOnceQueueShowCallFunc);
iocshRegister(&scanpplFuncDef,scanpplCallFunc);
iocshRegister(&scanpelFuncDef,scanpelCallFunc);
iocshRegister(&postEventFuncDef,postEventCallFunc);
iocshRegister(&scanpiolFuncDef,scanpiolCallFunc);
iocshRegister(&callbackSetQueueSizeFuncDef,callbackSetQueueSizeCallFunc);
iocshRegister(&callbackQueueShowFuncDef,callbackQueueShowCallFunc);
iocshRegister(&callbackParallelThreadsFuncDef,callbackParallelThreadsCallFunc);
/* Needed before callback system is initialized */
+107 -53
View File
@@ -2,7 +2,7 @@
* Copyright (c) 2016 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.
* in file LICENSE that is included with this distribution.
\*************************************************************************/
/* dbJLink.c */
@@ -25,19 +25,33 @@
#include "dbLock.h"
#include "dbStaticLib.h"
#include "link.h"
#include "epicsExport.h"
#define IFDEBUG(n) if(parser->parse_debug)
epicsShareDef int dbJLinkDebug = 0;
epicsExportAddress(int, dbJLinkDebug);
#define IFDEBUG(n) if (dbJLinkDebug >= (n))
typedef struct parseContext {
jlink *pjlink;
jlink *product;
short dbfType;
short jsonDepth;
unsigned key_is_link:1;
unsigned parse_debug:1;
unsigned lset_debug:1;
} parseContext;
epicsShareDef const char *jlif_result_name[2] = {
"jlif_stop",
"jlif_continue",
};
epicsShareDef const char *jlif_key_result_name[5] = {
"jlif_key_stop",
"jlif_key_continue",
"jlif_key_child_inlink",
"jlif_key_child_outlink",
"jlif_key_child_fwdlink"
};
#define CALL_OR_STOP(routine) !(routine) ? jlif_stop : (routine)
static int dbjl_return(parseContext *parser, jlif_result result) {
@@ -45,8 +59,8 @@ static int dbjl_return(parseContext *parser, jlif_result result) {
IFDEBUG(10) {
printf("dbjl_return(%s@%p, %d)\t", pjlink ? pjlink->pif->name : "", pjlink, result);
printf(" jsonDepth=%d, parseDepth=%d, key_is_link=%d\n",
parser->jsonDepth, pjlink ? pjlink->parseDepth : 0, parser->key_is_link);
printf(" jsonDepth=%d, parseDepth=%d, dbfType=%d\n",
parser->jsonDepth, pjlink ? pjlink->parseDepth : 0, parser->dbfType);
}
if (result == jlif_stop && pjlink) {
@@ -59,6 +73,9 @@ static int dbjl_return(parseContext *parser, jlif_result result) {
pjlink->pif->free_jlink(pjlink);
}
IFDEBUG(10)
printf(" returning %d %s\n", result,
result == jlif_stop ? "*** STOP ***" : "Continue");
return result;
}
@@ -68,8 +85,8 @@ static int dbjl_value(parseContext *parser, jlif_result result) {
IFDEBUG(10) {
printf("dbjl_value(%s@%p, %d)\t", pjlink ? pjlink->pif->name : "", pjlink, result);
printf(" jsonDepth=%d, parseDepth=%d, key_is_link=%d\n",
parser->jsonDepth, pjlink ? pjlink->parseDepth : 0, parser->key_is_link);
printf(" jsonDepth=%d, parseDepth=%d, dbfType=%d\n",
parser->jsonDepth, pjlink ? pjlink->parseDepth : 0, parser->dbfType);
}
if (result == jlif_stop || pjlink->parseDepth > 0)
@@ -81,7 +98,6 @@ static int dbjl_value(parseContext *parser, jlif_result result) {
} else if (parent->pif->end_child) {
parent->pif->end_child(parent, pjlink);
}
pjlink->debug = 0;
parser->pjlink = parent;
@@ -159,29 +175,46 @@ static int dbjl_start_map(void *ctx) {
if (!pjlink) {
IFDEBUG(10) {
printf("dbjl_start_map(NULL)\t");
printf(" jsonDepth=%d, parseDepth=00, key_is_link=%d\n",
parser->jsonDepth, parser->key_is_link);
printf(" jsonDepth=%d, parseDepth=00, dbfType=%d\n",
parser->jsonDepth, parser->dbfType);
}
assert(parser->jsonDepth == 0);
parser->jsonDepth++;
parser->key_is_link = 1;
return jlif_continue; /* Opening '{' */
}
IFDEBUG(10) {
printf("dbjl_start_map(%s@%p)\t", pjlink ? pjlink->pif->name : "", pjlink);
printf(" jsonDepth=%d, parseDepth=%d, key_is_link=%d\n",
parser->jsonDepth, pjlink ? pjlink->parseDepth : 0, parser->key_is_link);
printf(" jsonDepth=%d, parseDepth=%d, dbfType=%d\n",
parser->jsonDepth, pjlink ? pjlink->parseDepth : 0, parser->dbfType);
}
pjlink->parseDepth++;
parser->jsonDepth++;
result = CALL_OR_STOP(pjlink->pif->parse_start_map)(pjlink);
if (result == jlif_key_child_link) {
parser->key_is_link = 1;
switch (result) {
case jlif_key_child_inlink:
parser->dbfType = DBF_INLINK;
result = jlif_continue;
break;
case jlif_key_child_outlink:
parser->dbfType = DBF_OUTLINK;
result = jlif_continue;
break;
case jlif_key_child_fwdlink:
parser->dbfType = DBF_FWDLINK;
result = jlif_continue;
break;
case jlif_key_stop:
case jlif_key_continue:
break;
default:
errlogPrintf("dbJLinkInit: Bad return %d from '%s'::parse_start_map()\n",
result, pjlink->pif->name);
result = jlif_stop;
break;
}
IFDEBUG(10)
@@ -196,8 +229,9 @@ static int dbjl_map_key(void *ctx, const unsigned char *key, size_t len) {
char *link_name;
linkSup *linkSup;
jlif *pjlif;
jlink *child;
if (!parser->key_is_link) {
if (parser->dbfType == 0) {
if (!pjlink) {
errlogPrintf("dbJLinkInit: Illegal second link key '%.*s'\n",
(int) len, key);
@@ -207,8 +241,8 @@ static int dbjl_map_key(void *ctx, const unsigned char *key, size_t len) {
IFDEBUG(10) {
printf("dbjl_map_key(%s@%p, \"%.*s\")\t",
pjlink->pif->name, pjlink, (int) len, key);
printf(" jsonDepth=%d, parseDepth=%d, key_is_link=%d\n",
parser->jsonDepth, pjlink ? pjlink->parseDepth : 0, parser->key_is_link);
printf(" jsonDepth=%d, parseDepth=%d, dbfType=%d\n",
parser->jsonDepth, pjlink ? pjlink->parseDepth : 0, parser->dbfType);
}
assert(pjlink->parseDepth > 0);
@@ -219,8 +253,8 @@ static int dbjl_map_key(void *ctx, const unsigned char *key, size_t len) {
IFDEBUG(10) {
printf("dbjl_map_key(NULL, \"%.*s\")\t", (int) len, key);
printf(" jsonDepth=%d, parseDepth=00, key_is_link=%d\n",
parser->jsonDepth, parser->key_is_link);
printf(" jsonDepth=%d, parseDepth=00, dbfType=%d\n",
parser->jsonDepth, parser->dbfType);
}
link_name = dbmfStrndup((const char *) key, len);
@@ -241,27 +275,35 @@ static int dbjl_map_key(void *ctx, const unsigned char *key, size_t len) {
return dbjl_return(parser, jlif_stop);
}
dbmfFree(link_name);
pjlink = pjlif->alloc_jlink(parser->dbfType);
if (!pjlink) {
errlogPrintf("dbJLinkInit: Out of memory\n");
child = pjlif->alloc_jlink(parser->dbfType);
if (!child) {
errlogPrintf("dbJLinkInit: Link type '%s' allocation failed. \n",
link_name);
dbmfFree(link_name);
return dbjl_return(parser, jlif_stop);
}
pjlink->pif = pjlif;
pjlink->parent = NULL;
pjlink->parseDepth = 0;
pjlink->debug = !!parser->lset_debug;
child->pif = pjlif;
child->parseDepth = 0;
child->debug = 0;
if (parser->pjlink) {
/* We're starting a child link, save its parent */
pjlink->parent = parser->pjlink;
child->parent = pjlink;
if (pjlink->pif->start_child)
pjlink->pif->start_child(pjlink, child);
}
parser->pjlink = pjlink;
parser->key_is_link = 0;
else
child->parent = NULL;
parser->pjlink = child;
parser->dbfType = 0;
dbmfFree(link_name);
IFDEBUG(8)
printf("dbjl_map_key: New %s@%p\n", pjlink ? pjlink->pif->name : "", pjlink);
printf("dbjl_map_key: New %s@%p\n", child ? child->pif->name : "", child);
return jlif_continue;
}
@@ -274,9 +316,9 @@ static int dbjl_end_map(void *ctx) {
IFDEBUG(10) {
printf("dbjl_end_map(%s@%p)\t",
pjlink ? pjlink->pif->name : "NULL", pjlink);
printf(" jsonDepth=%d, parseDepth=%d, key_is_link=%d\n",
printf(" jsonDepth=%d, parseDepth=%d, dbfType=%d\n",
parser->jsonDepth, pjlink ? pjlink->parseDepth : 0,
parser->key_is_link);
parser->dbfType);
}
parser->jsonDepth--;
@@ -298,8 +340,8 @@ static int dbjl_start_array(void *ctx) {
IFDEBUG(10) {
printf("dbjl_start_array(%s@%p)\t", pjlink ? pjlink->pif->name : "", pjlink);
printf(" jsonDepth=%d, parseDepth=%d, key_is_link=%d\n",
parser->jsonDepth, pjlink ? pjlink->parseDepth : 0, parser->key_is_link);
printf(" jsonDepth=%d, parseDepth=%d, dbfType=%d\n",
parser->jsonDepth, pjlink ? pjlink->parseDepth : 0, parser->dbfType);
}
assert(pjlink);
@@ -316,8 +358,8 @@ static int dbjl_end_array(void *ctx) {
IFDEBUG(10) {
printf("dbjl_end_array(%s@%p)\t", pjlink ? pjlink->pif->name : "", pjlink);
printf(" jsonDepth=%d, parseDepth=%d, key_is_link=%d\n",
parser->jsonDepth, pjlink ? pjlink->parseDepth : 0, parser->key_is_link);
printf(" jsonDepth=%d, parseDepth=%d, dbfType=%d\n",
parser->jsonDepth, pjlink ? pjlink->parseDepth : 0, parser->dbfType);
}
assert(pjlink);
@@ -335,7 +377,7 @@ static yajl_callbacks dbjl_callbacks = {
};
long dbJLinkParse(const char *json, size_t jlen, short dbfType,
jlink **ppjlink, unsigned opts)
jlink **ppjlink)
{
parseContext context, *parser = &context;
yajl_alloc_funcs dbjl_allocs;
@@ -347,17 +389,14 @@ long dbJLinkParse(const char *json, size_t jlen, short dbfType,
parser->product = NULL;
parser->dbfType = dbfType;
parser->jsonDepth = 0;
parser->key_is_link = 0;
parser->parse_debug = !!(opts&LINK_DEBUG_JPARSE);
parser->lset_debug = !!(opts&LINK_DEBUG_LSET);
IFDEBUG(10)
printf("dbJLinkInit(\"%.*s\", %d, %p)\n",
(int) jlen, json, dbfType, ppjlink);
IFDEBUG(10)
printf("dbJLinkInit: jsonDepth=%d, key_is_link=%d\n",
parser->jsonDepth, parser->key_is_link);
printf("dbJLinkInit: jsonDepth=%d, dbfType=%d\n",
parser->jsonDepth, parser->dbfType);
yajl_set_default_alloc_funcs(&dbjl_allocs);
yh = yajl_alloc(&dbjl_callbacks, &dbjl_allocs, parser);
@@ -365,8 +404,14 @@ long dbJLinkParse(const char *json, size_t jlen, short dbfType,
return S_db_noMemory;
ys = yajl_parse(yh, (const unsigned char *) json, jlen);
if (ys == yajl_status_ok)
IFDEBUG(10)
printf("dbJLinkInit: yajl_parse() returned %d\n", ys);
if (ys == yajl_status_ok) {
ys = yajl_complete_parse(yh);
IFDEBUG(10)
printf("dbJLinkInit: yajl_complete_parse() returned %d\n", ys);
}
switch (ys) {
unsigned char *err;
@@ -378,6 +423,9 @@ long dbJLinkParse(const char *json, size_t jlen, short dbfType,
break;
case yajl_status_error:
IFDEBUG(10)
printf(" jsonDepth=%d, product=%p, pjlink=%p\n",
parser->jsonDepth, parser->product, parser->pjlink);
err = yajl_get_error(yh, 1, (const unsigned char *) json, jlen);
errlogPrintf("dbJLinkInit: %s\n", err);
yajl_free_error(yh, err);
@@ -389,18 +437,24 @@ long dbJLinkParse(const char *json, size_t jlen, short dbfType,
}
yajl_free(yh);
IFDEBUG(10)
printf("dbJLinkInit: returning status=0x%lx\n\n",
status);
return status;
}
long dbJLinkInit(struct link *plink)
{
jlink *pjlink;
assert(plink);
pjlink = plink->value.json.jlink;
if (pjlink)
plink->lset = pjlink->pif->get_lset(pjlink);
if (plink->type == JSON_LINK) {
jlink *pjlink = plink->value.json.jlink;
if (pjlink)
plink->lset = pjlink->pif->get_lset(pjlink);
}
dbLinkOpen(plink);
return 0;
+20 -9
View File
@@ -2,7 +2,7 @@
* Copyright (c) 2016 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.
* in file LICENSE that is included with this distribution.
\*************************************************************************/
/* dbJLink.h */
@@ -21,12 +21,16 @@ typedef enum {
jlif_continue = 1
} jlif_result;
epicsShareExtern const char *jlif_result_name[2];
typedef enum {
jlif_key_stop = jlif_stop,
jlif_key_continue = jlif_continue,
jlif_key_child_link
jlif_key_child_inlink, jlif_key_child_outlink, jlif_key_child_fwdlink
} jlif_key_result;
epicsShareExtern const char *jlif_key_result_name[5];
struct link;
struct lset;
struct jlif;
@@ -35,7 +39,7 @@ typedef struct jlink {
struct jlif *pif; /* Link methods */
struct jlink *parent; /* NULL for top-level links */
int parseDepth; /* Used by parser, unused afterwards */
unsigned debug:1; /* set by caller of jlif operations to request debug output to console */
unsigned debug:1; /* Set to request debug output to console */
/* Link types extend or embed this structure for private storage */
} jlink;
@@ -72,8 +76,9 @@ typedef struct jlif {
/* Optional, parser saw a string value */
jlif_key_result (*parse_start_map)(jlink *);
/* Optional, parser saw an open-brace '{'. Return jlif_key_child_link
* to expect a child link next (extra key/value pairs may follow).
/* Optional, parser saw an open-brace '{'. Return jlif_key_child_inlink,
* jlif_key_child_outlink, or jlif_key_child_fwdlink to expect a child
* link next (extra key/value pairs may follow)
*/
jlif_result (*parse_map_key)(jlink *, const char *key, size_t len);
@@ -90,7 +95,8 @@ typedef struct jlif {
void (*end_child)(jlink *parent, jlink *child);
/* Optional, called with pointer to the new child link after
* parse_start_map() returned jlif_key_child_link */
* the child link has finished parsing successfully
*/
struct lset* (*get_lset)(const jlink *);
/* Required, return lset for this link instance */
@@ -98,7 +104,7 @@ typedef struct jlif {
void (*report)(const jlink *, int level, int indent);
/* Optional, print status information about this link instance, then
* if (level > 0) print a link identifier (at indent+2) and call
* dbJLinkReport(child, level-1, indent+4)
* dbJLinkReport(child, level-1, indent+4)
* for each child.
*/
@@ -107,13 +113,19 @@ typedef struct jlif {
* Stop immediately and return status if non-zero.
*/
void (*start_child)(jlink *parent, jlink *child);
/* Optional, called with pointer to the new child link after
* parse_start_map() returned a jlif_key_child_link value and
* the child link has been allocated (but not parsed yet)
*/
/* Link types must NOT extend this table with their own routines,
* this space is reserved for extensions to the jlink interface.
*/
} jlif;
epicsShareFunc long dbJLinkParse(const char *json, size_t len, short dbfType,
jlink **ppjlink, unsigned opts);
jlink **ppjlink);
epicsShareFunc long dbJLinkInit(struct link *plink);
epicsShareFunc void dbJLinkFree(jlink *);
@@ -130,4 +142,3 @@ epicsShareFunc long dbJLinkMapAll(char *recname, jlink_map_fn rtn, void *ctx);
#endif
#endif /* INC_dbJLink_H */
+12 -1
View File
@@ -265,8 +265,19 @@ int dbIsLinkConnected(const struct link *plink)
{
lset *plset = plink->lset;
if (!plset || !plset->isConnected)
if (!plset)
return FALSE;
if (!plset->isVolatile)
return TRUE;
if (!plset->isConnected) {
struct dbCommon *precord = plink->precord;
errlogPrintf("dbLink: Link type for '%s.%s' is volatile but has no"
" lset::isConnected() method\n",
precord->name, dbLinkFieldName(plink));
return FALSE;
}
return plset->isConnected(plink);
}
+288 -5
View File
@@ -27,54 +27,337 @@ extern "C" {
struct dbLocker;
/** @file dbLink.h
* @brief Link Support API
*
* Link support run-time API, all link types provide an lset which is used by
* the IOC database to control and operate the link. This file also declares the
* dbLink routines that IOC, record and device code can call to perform link
* operations.
*/
/** @brief callback routine for locked link operations
*
* Called by the lset::doLocked method to permit multiple link operations
* while the link instance is locked.
*
* @param plink the link
* @param priv context for the callback routine
*/
typedef long (*dbLinkUserCallback)(struct link *plink, void *priv);
/** @brief Link Support Entry Table
*
* This structure provides information about and methods for an individual link
* type. A pointer to this structure is included in every link's lset field, and
* is used to perform operations on the link. For JSON links the pointer is
* obtained by calling pjlink->pif->get_lset() at link initialization time,
* immediately before calling dbLinkOpen() to activate the link.
*/
typedef struct lset {
/* Characteristics of the link type */
/** @brief link constancy
*
* 1 means this is a constant link type whose value doesn't change.
* The link's value will be obtained using one of the methods loadScalar,
* loadLS or loadArray.
*/
const unsigned isConstant:1;
/** @brief link volatility
*
* 0 means the link is always connected.
*/
const unsigned isVolatile:1;
/* Activation */
/** @brief activate link
*
* Optional, called whenever a JSON link is initialized or added at runtime.
*
* @param plink the link
*/
void (*openLink)(struct link *plink);
/* Destructor */
/** @brief deactivate link
*
* Optional, called whenever a link address is changed at runtime, or the
* IOC is shutting down.
*
* @param locker
* @param plink the link
*/
void (*removeLink)(struct dbLocker *locker, struct link *plink);
/* Const init, data type hinting */
/* Constant link initialization and data type hinting */
/** @brief load constant scalar from link type
*
* Usually called during IOC initialization, constant link types must copy a
* scalar value of the indicated data type to the buffer provided and return
* 0. A non-constant link type can use this method call as an early hint
* that subsequent calls to dbGetLink() will request scalar data of the
* indicated type, although the type might change.
*
* @param plink the link
* @param dbrType data type code
* @param pbuffer where to put the value
* @returns 0 if a value was loaded, non-zero otherwise
*/
long (*loadScalar)(struct link *plink, short dbrType, void *pbuffer);
/** @brief load constant long string from link type
*
* Usually called during IOC initialization, constant link types must copy a
* nil-terminated string up to size characters long to the buffer provided,
* and write the length of that string to the plen location. A non-constant
* link type can use this as an early hint that subsequent calls to
* dbGetLink() will request long string data, although this might change.
*
* @param plink the link
* @param pbuffer where to put the string
* @param size length of pbuffer in chars
* @param plen set to number of chars written
* @returns status value
*/
long (*loadLS)(struct link *plink, char *pbuffer, epicsUInt32 size,
epicsUInt32 *plen);
/** @brief load constant array from link type
*
* Usually called during IOC initialization, constant link types must copy
* an array value of the indicated data type to the buffer provided, update
* the pnRequest location to indicate how many elements were loaded, and
* return 0. A non-constant link type can use this method call as an early
* hint that subsequent calls to dbGetLink() will request array data of the
* indicated type and max size, although the request might change.
*
* @param plink the link
* @param dbrType data type code
* @param pbuffer where to put the value
* @param pnRequest Max elements on entry, actual on exit
* @returns 0 if elements were loaded, non-zero otherwise
*/
long (*loadArray)(struct link *plink, short dbrType, void *pbuffer,
long *pnRequest);
/* Metadata */
/** @brief return link connection status
*
* Return an indication whether this link is connected or not. This routine
* is polled by the calcout and some external record types. Not required for
* non-volatile link types, which are by definition always connected.
*
* @param plink the link
* @returns 1 if connected, 0 if disconnected
*/
int (*isConnected)(const struct link *plink);
/** @brief get data type of link destination
*
* Called on both input and output links by long string support code to
* decide whether to use DBR_CHAR/DBR_UCHAR or DBR_STRING for a subsequent
* dbPutLink() or dbGetLink() call. Optional, but if not provided long
* strings cannot be transported over this link type, and no warning or
* error will appear to explain why. Not required for constant link types.
*
* @param plink the link
* @returns DBF_* type code, or -1 on error/disconnected link
*/
int (*getDBFtype)(const struct link *plink);
long (*getElements)(const struct link *plink, long *nelements);
/* Get data */
/** @brief get array size of an input link
*
* Called on input links by the compress record type for memory allocation
* purposes, before using the dbGetLink() routine to fetch the actual
* array data.
*
* @param plink the link
* @param pnElements where to put the answer
* @returns status value
*/
long (*getElements)(const struct link *plink, long *pnElements);
/** @brief get value from an input link
*
* Called to fetch data from the link, which must be converted into the
* given data type and placed in the buffer indicated. The actual number of
* elements retrieved should be updated in the pnRequest location. If this
* method returns an error status value, the link's record will be placed
* into an Invalid severity / Link Alarm state by the dbGetLink() routine
* that calls this method.
*
* @param plink the link
* @param dbrType data type code
* @param pbuffer where to put the value
* @param pnRequest max elements on entry, actual on exit
* @returns status value
*/
long (*getValue)(struct link *plink, short dbrType, void *pbuffer,
long *pnRequest);
/** @brief get the control range for an output link
*
* Called to fetch the control range for the link target, as a pair of
* double values for the lowest and highest values that the target will
* accept. This method is not used at all by the IOC or built-in record
* types, although external record types may require it.
*
* @param plink the link
* @param lo lowest accepted value
* @param hi highest accepted value
* @returns status value
*/
long (*getControlLimits)(const struct link *plink, double *lo, double *hi);
/** @brief get the display range from an input link
*
* Called to fetch the display range for an input link target, as a pair of
* double values for the lowest and highest values that the PV expects to
* return. This method is used by several built-in record types to obtain
* the display range for their generic input links.
*
* @param plink the link
* @param lo lowest accepted value
* @param hi highest accepted value
* @returns status value
*/
long (*getGraphicLimits)(const struct link *plink, double *lo, double *hi);
/** @brief get the alarm limits from an input link
*
* Called to fetch the alarm limits for an input link target, as four
* double values for the warning and alarm levels that the PV checks its
* value against. This method is used by several built-in record types to
* obtain the alarm limits for their generic input links.
*
* @param plink the link
* @param lolo low alarm value
* @param lo low warning value
* @param hi high warning value
* @param hihi high alarm value
* @returns status value
*/
long (*getAlarmLimits)(const struct link *plink, double *lolo, double *lo,
double *hi, double *hihi);
/** @brief get the precision from an input link
*
* Called to fetch the precision for an input link target. This method is
* used by several built-in record types to obtain the precision for their
* generic input links.
*
* @param plink the link
* @param precision where to put the answer
* @returns status value
*/
long (*getPrecision)(const struct link *plink, short *precision);
/** @brief get the units string from an input link
*
* Called to fetch the units string for an input link target. This method is
* used by several built-in record types to obtain the units string for
* their generic input links.
*
* @param plink the link
* @param units where to put the answer
* @param unitsSize buffer size for the answer
* @returns status value
*/
long (*getUnits)(const struct link *plink, char *units, int unitsSize);
/** @brief get the alarm condition from an input link
*
* Called to fetch the alarm status and severity for an input link target.
* Either status or severity pointers may be NULL when that value is not
* needed by the calling code. This method is used by several built-in
* record types to obtain the alarm condition for their generic input links.
*
* @param plink the link
* @param status where to put the alarm status (or NULL)
* @param severity where to put the severity (or NULL)
* @returns status value
*/
long (*getAlarm)(const struct link *plink, epicsEnum16 *status,
epicsEnum16 *severity);
/** @brief get the time-stamp from an input link
*
* Called to fetch the time-stamp for an input link target. This method is
* used by many built-in device supports to obtain the precision for their
* generic input links.
*
* @param plink the link
* @param pstamp where to put the answer
* @returns status value
*/
long (*getTimeStamp)(const struct link *plink, epicsTimeStamp *pstamp);
/* Put data */
/** @brief put a value to an output link
*
* Called to send nRequest elements of type dbrType found at pbuffer to an
* output link target.
*
* @param plink the link
* @param dbrType data type code
* @param pbuffer where to put the value
* @param nRequest number of elements to send
* @returns status value
*/
long (*putValue)(struct link *plink, short dbrType,
const void *pbuffer, long nRequest);
/** @brief put a value to an output link with asynchronous completion
*
* Called to send nRequest elements of type dbrType found at pbuffer to an
* output link target. If the return status is zero, the link type will
* later indicate the put has completed by calling dbLinkAsyncComplete()
* from a background thread, which will be used to continue the record
* process operation from where it left off.
*
* @param plink the link
* @param dbrType data type code
* @param pbuffer where to put the value
* @param nRequest number of elements to send
* @returns status value
*/
long (*putAsync)(struct link *plink, short dbrType,
const void *pbuffer, long nRequest);
/* Process */
/** @brief trigger processing of a forward link
*
* Called to trigger processing of the record pointed to by a forward link.
* This routine is optional, but if not provided no warning message will be
* shown when called by dbScanFwdLink(). JSON link types that do not support
* this operation should return NULL from their jlif::alloc_jlink() method
* if it gets called with a dbfType of DBF_FWDLINK.
*
* @param plink the link
*/
void (*scanForward)(struct link *plink);
/* Atomicity */
/** @brief execute a callback routine with link locked
*
* Called on an input link when multiple link attributes need to be fetched
* in an atomic fashion. The link type must call the callback routine and
* prevent any background I/O from updating any cached link data until that
* routine returns. This method is used by most input device support to
* fetch the timestamp along with the value when the record's TSE field is
* set to epicsTimeEventDeviceTime.
*
* @param plink the link
* @param rtn routine to execute
* @returns status value
*/
long (*doLocked)(struct link *plink, dbLinkUserCallback rtn, void *priv);
} lset;
@@ -99,7 +382,7 @@ epicsShareFunc long dbLoadLink(struct link *plink, short dbrType,
epicsShareFunc long dbLoadLinkArray(struct link *, short dbrType, void *pbuffer,
long *pnRequest);
epicsShareFunc long dbGetNelements(const struct link *plink, long *nelements);
epicsShareFunc long dbGetNelements(const struct link *plink, long *pnElements);
epicsShareFunc int dbIsLinkConnected(const struct link *plink); /* 0 or 1 */
epicsShareFunc int dbGetLinkDBFtype(const struct link *plink);
epicsShareFunc long dbTryGetLink(struct link *, short dbrType, void *pbuffer,
+2
View File
@@ -13,6 +13,8 @@
#ifndef INCdbLockh
#define INCdbLockh
#include <stddef.h>
#include "ellLib.h"
#include "shareLib.h"
+37
View File
@@ -24,6 +24,7 @@
#include "cantProceed.h"
#include "dbDefs.h"
#include "ellLib.h"
#include "epicsAtomic.h"
#include "epicsEvent.h"
#include "epicsMutex.h"
#include "epicsPrint.h"
@@ -63,6 +64,7 @@ static volatile enum ctl scanCtl;
static int onceQueueSize = 1000;
static epicsEventId onceSem;
static epicsRingBytesId onceQ;
static int onceQOverruns = 0;
static epicsThreadId onceTaskId;
static void *exitOnce;
@@ -676,6 +678,7 @@ int scanOnceCallback(struct dbCommon *precord, once_complete cb, void *usr)
if (!pushOK) {
if (newOverflow) errlogPrintf("scanOnce: Ring buffer overflow\n");
newOverflow = FALSE;
epicsAtomicIncrIntT(&onceQOverruns);
} else {
newOverflow = TRUE;
}
@@ -722,6 +725,40 @@ int scanOnceSetQueueSize(int size)
return 0;
}
int scanOnceQueueStatus(const int reset, scanOnceQueueStats *result)
{
int ret;
if (!onceQ) return -1;
if (result) {
result->size = epicsRingBytesSize(onceQ) / sizeof(onceEntry);
result->numUsed = epicsRingBytesUsedBytes(onceQ) / sizeof(onceEntry);
result->maxUsed = epicsRingBytesHighWaterMark(onceQ) / sizeof(onceEntry);
result->numOverflow = epicsAtomicGetIntT(&onceQOverruns);
ret = 0;
} else {
ret = -2;
}
if (reset) {
epicsRingBytesResetHighWaterMark(onceQ);
}
return ret;
}
void scanOnceQueueShow(const int reset)
{
scanOnceQueueStats stats;
if (scanOnceQueueStatus(reset, &stats) == -1) {
fprintf(stderr, "scanOnce system not initialized, yet. Please run "
"iocInit before using this command.\n");
} else {
double qusage = 100.0 * stats.numUsed / stats.size;
printf("PRIORITY HIGH-WATER MARK ITEMS IN Q Q SIZE %% USED Q OVERFLOWS\n");
printf("%8s %15d %10d %6d %6.1f %11d\n", "scanOnce", stats.maxUsed,
stats.numUsed, stats.size, qusage,
epicsAtomicGetIntT(&onceQOverruns));
}
}
static void initOnce(void)
{
if ((onceQ = epicsRingBytesLockedCreate(sizeof(onceEntry)*onceQueueSize)) == NULL) {
+11 -3
View File
@@ -19,6 +19,7 @@
#include "menuScan.h"
#include "shareLib.h"
#include "compilerDependencies.h"
#include "devSup.h"
#ifdef __cplusplus
extern "C" {
@@ -33,9 +34,7 @@ extern "C" {
#define MIN_PHASE SHRT_MIN
/*definitions for I/O Interrupt Scanning */
struct ioscan_head;
typedef struct ioscan_head *IOSCANPVT;
/* IOSCANPVT now defined in devSup.h */
typedef struct event_list *EVENTPVT;
struct dbCommon;
@@ -43,6 +42,13 @@ struct dbCommon;
typedef void (*io_scan_complete)(void *usr, IOSCANPVT, int prio);
typedef void (*once_complete)(void *usr, struct dbCommon*);
typedef struct scanOnceQueueStats {
int size;
int numUsed;
int maxUsed;
int numOverflow;
} scanOnceQueueStats;
epicsShareFunc long scanInit(void);
epicsShareFunc void scanRun(void);
epicsShareFunc void scanPause(void);
@@ -58,6 +64,8 @@ epicsShareFunc double scanPeriod(int scan);
epicsShareFunc int scanOnce(struct dbCommon *);
epicsShareFunc int scanOnceCallback(struct dbCommon *, once_complete cb, void *usr);
epicsShareFunc int scanOnceSetQueueSize(int size);
epicsShareFunc int scanOnceQueueStatus(const int reset, scanOnceQueueStats *result);
epicsShareFunc void scanOnceQueueShow(const int reset);
/*print periodic lists*/
epicsShareFunc int scanppl(double rate);
+10 -4
View File
@@ -12,6 +12,7 @@
#include <stddef.h>
#include <string.h>
#include "dbDefs.h"
#include "ellLib.h"
#include "envDefs.h"
#include "epicsStdio.h"
@@ -29,6 +30,7 @@ static char *stateNames[] = {
int dbRegisterServer(dbServer *psrv)
{
const char * ignore = envGetConfigParamPtr(&EPICS_IOC_IGNORE_SERVERS);
ELLNODE *node;
if (!psrv || !psrv->name || state != registering)
return -1;
@@ -55,10 +57,14 @@ int dbRegisterServer(dbServer *psrv)
}
}
if (ellNext(&psrv->node) || ellLast(&serverList) == &psrv->node) {
fprintf(stderr, "dbRegisterServer: '%s' registered twice?\n",
psrv->name);
return -1;
for(node=ellFirst(&serverList); node; node = ellNext(node)) {
dbServer *cur = CONTAINER(node, dbServer, node);
if(cur==psrv) {
return 0; /* already registered identically. */
} else if(strcmp(cur->name, psrv->name)==0) {
fprintf(stderr, "dbRegisterServer: Can't redefine '%s'.\n", cur->name);
return -1;
}
}
ellAdd(&serverList, &psrv->node);
+9
View File
@@ -15,6 +15,10 @@
#include "shareLib.h"
#ifdef __cplusplus
extern "C" {
#endif
/** @file dbState.h
* @brief Generic IOC state facility
*
@@ -89,4 +93,9 @@ epicsShareFunc void dbStateShow(dbStateId id, unsigned int level);
*/
epicsShareFunc void dbStateShowAll(unsigned int level);
#ifdef __cplusplus
}
#endif
#endif // INCdbStateH
-138
View File
@@ -1,138 +0,0 @@
/*************************************************************************\
* Copyright (c) 2009 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.
\*************************************************************************/
/*
* Authors: Benjamin Franksen (BESY) and Marty Kraimer
* Date: 06-01-91
* major Revision: 07JuL97
*/
#include <stdlib.h>
#include <stddef.h>
#include <stdio.h>
#include "dbDefs.h"
#include "ellLib.h"
#include "epicsMutex.h"
#include "epicsThread.h"
#define epicsExportSharedSymbols
#include "initHooks.h"
typedef struct initHookLink {
ELLNODE node;
initHookFunction func;
} initHookLink;
static ELLLIST functionList = ELLLIST_INIT;
static epicsMutexId listLock;
/*
* Lazy initialization functions
*/
static void initHookOnce(void *arg)
{
listLock = epicsMutexMustCreate();
}
static void initHookInit(void)
{
static epicsThreadOnceId onceFlag = EPICS_THREAD_ONCE_INIT;
epicsThreadOnce(&onceFlag, initHookOnce, NULL);
}
/*
* To be called before iocInit reaches state desired.
*/
int initHookRegister(initHookFunction func)
{
initHookLink *newHook;
if (!func) return 0;
initHookInit();
newHook = (initHookLink *)malloc(sizeof(initHookLink));
if (!newHook) {
printf("Cannot malloc a new initHookLink\n");
return -1;
}
newHook->func = func;
epicsMutexMustLock(listLock);
ellAdd(&functionList, &newHook->node);
epicsMutexUnlock(listLock);
return 0;
}
/*
* Called by iocInit at various points during initialization.
* This function must only be called by iocInit and relatives.
*/
void initHookAnnounce(initHookState state)
{
initHookLink *hook;
initHookInit();
epicsMutexMustLock(listLock);
hook = (initHookLink *)ellFirst(&functionList);
epicsMutexUnlock(listLock);
while (hook != NULL) {
hook->func(state);
epicsMutexMustLock(listLock);
hook = (initHookLink *)ellNext(&hook->node);
epicsMutexUnlock(listLock);
}
}
void initHookFree(void)
{
initHookInit();
epicsMutexMustLock(listLock);
ellFree(&functionList);
epicsMutexUnlock(listLock);
}
/*
* Call any time you want to print out a state name.
*/
const char *initHookName(int state)
{
const char *stateName[] = {
"initHookAtIocBuild",
"initHookAtBeginning",
"initHookAfterCallbackInit",
"initHookAfterCaLinkInit",
"initHookAfterInitDrvSup",
"initHookAfterInitRecSup",
"initHookAfterInitDevSup",
"initHookAfterInitDatabase",
"initHookAfterFinishDevSup",
"initHookAfterScanInit",
"initHookAfterInitialProcess",
"initHookAfterCaServerInit",
"initHookAfterIocBuilt",
"initHookAtIocRun",
"initHookAfterDatabaseRunning",
"initHookAfterCaServerRunning",
"initHookAfterIocRunning",
"initHookAtIocPause",
"initHookAfterCaServerPaused",
"initHookAfterDatabasePaused",
"initHookAfterIocPaused",
"initHookAfterInterruptAccept",
"initHookAtEnd"
};
if (state < 0 || state >= NELEMENTS(stateName)) {
return "Not an initHookState";
}
return stateName[state];
}
-68
View File
@@ -1,68 +0,0 @@
/*************************************************************************\
* Copyright (c) 2009 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.
\*************************************************************************/
/*
* Authors: Benjamin Franksen (BESY) and Marty Kraimer
* Date: 06-01-91
* major Revision: 07JuL97
*/
#ifndef INC_initHooks_H
#define INC_initHooks_H
#include "shareLib.h"
#ifdef __cplusplus
extern "C" {
#endif
/* This enum must agree with the array of names defined in initHookName() */
typedef enum {
initHookAtIocBuild = 0, /* Start of iocBuild/iocInit commands */
initHookAtBeginning,
initHookAfterCallbackInit,
initHookAfterCaLinkInit,
initHookAfterInitDrvSup,
initHookAfterInitRecSup,
initHookAfterInitDevSup,
initHookAfterInitDatabase,
initHookAfterFinishDevSup,
initHookAfterScanInit,
initHookAfterInitialProcess,
initHookAfterCaServerInit,
initHookAfterIocBuilt, /* End of iocBuild command */
initHookAtIocRun, /* Start of iocRun command */
initHookAfterDatabaseRunning,
initHookAfterCaServerRunning,
initHookAfterIocRunning, /* End of iocRun/iocInit commands */
initHookAtIocPause, /* Start of iocPause command */
initHookAfterCaServerPaused,
initHookAfterDatabasePaused,
initHookAfterIocPaused, /* End of iocPause command */
/* Deprecated states, provided for backwards compatibility.
* These states are announced at the same point they were before,
* but will not be repeated if the IOC gets paused and restarted.
*/
initHookAfterInterruptAccept, /* After initHookAfterDatabaseRunning */
initHookAtEnd, /* Before initHookAfterIocRunning */
} initHookState;
typedef void (*initHookFunction)(initHookState state);
epicsShareFunc int initHookRegister(initHookFunction func);
epicsShareFunc void initHookAnnounce(initHookState state);
epicsShareFunc const char *initHookName(int state);
epicsShareFunc void initHookFree(void);
#ifdef __cplusplus
}
#endif
#endif /* INC_initHooks_H */
@@ -1,11 +1,48 @@
#*************************************************************************
# Copyright (c) 2008 UChicago Argonne LLC, as Operator of Argonne
# Copyright (c) 2018 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.
#*************************************************************************
=head1 Menu menuScan
This menu is used for the C<SCAN> field of all record types.
The set of periodic scan rates may be modified for an individual IOC by
copying the F<menuScan.dbd> file from Base into the IOC's source
directory and changing it to contain the desired scan rates.
The scan periods are extracted from the choice strings at runtime, which
must be expressed as a number with any of the following units appended:
=over 4
second
seconds
minute
minutes
hour
hours
Hertz
Hz
=back
At IOC start-up a separate scan thread will be created for each period,
with thread priority increasing further down the list, so faster periods
should appear after slower ones.
Scan rates that cannot be achieved will generate a warning message from
the C<iocInit> command.
=menu menuScan
=cut
menu(menuScan) {
choice(menuScanPassive,"Passive")
choice(menuScanEvent,"Event")
+4
View File
@@ -19,6 +19,7 @@
#include "alarm.h"
#include "dbDefs.h"
#include "alarm.h"
#include "epicsMath.h"
#include "epicsPrint.h"
#include "epicsStdlib.h"
@@ -180,6 +181,9 @@ unsigned short recGblResetAlarms(void *precord)
epicsEnum16 val_mask = 0;
epicsEnum16 stat_mask = 0;
if (new_sevr > INVALID_ALARM)
new_sevr = INVALID_ALARM;
pdbc->stat = new_stat;
pdbc->sevr = new_sevr;
pdbc->nsta = 0;
+120 -169
View File
@@ -79,16 +79,6 @@ epicsShareDef maplinkType pamaplinkType[LINK_NTYPES] = {
{"VXI_IO",VXI_IO}
};
static int mapDBFtoDCT[DBF_NOACCESS+1] = {
DCT_STRING,
DCT_INTEGER,DCT_INTEGER,DCT_INTEGER,DCT_INTEGER,DCT_INTEGER,DCT_INTEGER,
DCT_REAL,DCT_REAL,
DCT_INTEGER,
DCT_MENU,
DCT_MENUFORM,
DCT_INLINK,DCT_OUTLINK,DCT_FWDLINK,
DCT_NOACCESS};
/*forward references for private routines*/
static void dbMsgPrint(DBENTRY *pdbentry, const char *fmt, ...)
EPICS_PRINTF_STYLE(2,3);
@@ -166,38 +156,6 @@ void dbFreePath(DBBASE *pdbbase)
}
static void entryErrMessage(DBENTRY *pdbentry,long status,char *mess)
{
char message[200];
char *pmessage=&message[0];
dbRecordNode *precnode = pdbentry->precnode;
dbFldDes *pflddes = pdbentry->pflddes;
char *pname = NULL;
*pmessage=0;
if(pdbentry->precordType) pname = pdbentry->precordType->name;
if(pname) {
strcat(pmessage,"RecordType:");
strcat(pmessage,pname);
}
if(precnode){
if (dbIsAlias(pdbentry))
strcat(pmessage," Record Alias:");
else
strcat(pmessage," Record:");
strcat(pmessage,(char *)precnode->precord);
}
if(pflddes) {
char *pstr=pflddes->name;
strcat(pmessage," Field:");
strcat(pmessage,pstr);
}
strcat(pmessage,"\n");
strcat(pmessage,mess);
errMessage(status,pmessage);
}
static void zeroDbentry(DBENTRY *pdbentry)
{
/*NOTE that pdbbase and message MUST NOT be set to NULL*/
@@ -1398,19 +1356,6 @@ long dbNextField(DBENTRY *pdbentry,int dctonly)
}
}
int dbGetFieldType(DBENTRY *pdbentry)
{
dbFldDes *pflddes = pdbentry->pflddes;
long status;
if(!pflddes){
status = S_dbLib_flddesNotFound;
entryErrMessage(pdbentry,status,"dbGetFieldType");
return(status);
}
return(mapDBFtoDCT[pflddes->field_type]);
}
int dbGetNFields(DBENTRY *pdbentry,int dctonly)
{
dbRecordType *precordType = pdbentry->precordType;
@@ -2241,7 +2186,7 @@ long dbInitRecordLinks(dbRecordType *rtyp, struct dbCommon *prec)
if(!plink->text)
continue;
if(dbParseLink(plink->text, pflddes->field_type, &link_info, 0)!=0) {
if(dbParseLink(plink->text, pflddes->field_type, &link_info)!=0) {
/* This was already parsed once when ->text was set.
* Any syntax error messages were printed at that time.
*/
@@ -2270,7 +2215,7 @@ void dbFreeLinkInfo(dbLinkInfo *pinfo)
pinfo->target = NULL;
}
long dbParseLink(const char *str, short ftype, dbLinkInfo *pinfo, unsigned opts)
long dbParseLink(const char *str, short ftype, dbLinkInfo *pinfo)
{
char *pstr;
size_t len;
@@ -2306,7 +2251,7 @@ long dbParseLink(const char *str, short ftype, dbLinkInfo *pinfo, unsigned opts)
/* Check for braces => JSON */
if (*str == '{' && str[len-1] == '}') {
if (dbJLinkParse(str, len, ftype, &pinfo->jlink, opts))
if (dbJLinkParse(str, len, ftype, &pinfo->jlink))
goto fail;
pinfo->ltype = JSON_LINK;
@@ -2353,9 +2298,13 @@ long dbParseLink(const char *str, short ftype, dbLinkInfo *pinfo, unsigned opts)
else if (strcmp(pinfo->hwid, "VS")==0) pinfo->ltype = VXI_IO;
else goto fail;
if (parm && pinfo->ltype != RF_IO) {
/* move parm string to beginning of buffer */
memmove(pinfo->target, parm, len + 1);
if (pinfo->ltype != RF_IO) {
if (!parm) {
pinfo->target[0] = '\0';
} else {
/* move parm string to beginning of buffer */
memmove(pinfo->target, parm, len + 1);
}
} else if (!parm && pinfo->ltype == RF_IO) {
/* RF_IO, the string isn't needed at all */
free(pinfo->target);
@@ -2641,21 +2590,8 @@ long dbPutString(DBENTRY *pdbentry,const char *pstring)
case DBF_FWDLINK: {
dbLinkInfo link_info;
DBLINK *plink = (DBLINK *)pfield;
DBENTRY infoentry;
unsigned opts = 0;
if(pdbentry->precnode && ellCount(&pdbentry->precnode->infoList)) {
dbCopyEntryContents(pdbentry, &infoentry);
if(dbFindInfo(&infoentry, "base:lsetDebug")==0 && epicsStrCaseCmp(dbGetInfoString(&infoentry), "YES")==0)
opts |= LINK_DEBUG_LSET;
if(dbFindInfo(&infoentry, "base:jlinkDebug")==0 && epicsStrCaseCmp(dbGetInfoString(&infoentry), "YES")==0)
opts |= LINK_DEBUG_JPARSE;
dbFinishEntry(&infoentry);
}
status = dbParseLink(pstring, pflddes->field_type, &link_info, opts);
status = dbParseLink(pstring, pflddes->field_type, &link_info);
if (status) break;
if (plink->type==CONSTANT && plink->value.constantStr==NULL) {
@@ -3225,48 +3161,21 @@ int dbGetNLinks(DBENTRY *pdbentry)
return((int)precordType->no_links);
}
long dbGetLinkField(DBENTRY *pdbentry,int index)
long dbGetLinkField(DBENTRY *pdbentry, int index)
{
dbRecordType *precordType = pdbentry->precordType;
dbFldDes *pflddes;
dbRecordType *precordType = pdbentry->precordType;
dbFldDes *pflddes;
if (!precordType)
return S_dbLib_recordTypeNotFound;
if (index < 0 || index >= precordType->no_links)
return S_dbLib_badLink;
if(!precordType) return(S_dbLib_recordTypeNotFound);
if(index<0 || index>=precordType->no_links) return(S_dbLib_badLink);
pdbentry->indfield = precordType->link_ind[index];
pdbentry->pflddes = pflddes = precordType->papFldDes[pdbentry->indfield];
dbGetFieldAddress(pdbentry);
return(0);
}
int dbGetLinkType(DBENTRY *pdbentry)
{
dbFldDes *pflddes;
DBLINK *plink;
int field_type;
dbGetFieldAddress(pdbentry);
pflddes = pdbentry->pflddes;
if(!pflddes) return(-1);
plink = (DBLINK *)pdbentry->pfield;
if(!plink) return(-1);
field_type = pflddes->field_type;
switch (field_type) {
case DBF_INLINK:
case DBF_OUTLINK:
case DBF_FWDLINK:
switch(plink->type) {
case CONSTANT:
return(DCT_LINK_CONSTANT);
case PV_LINK:
case PN_LINK:
case DB_LINK:
case CA_LINK:
return(DCT_LINK_PV);
default:
return(DCT_LINK_FORM);
}
}
return(-1);
return 0;
}
void dbDumpPath(DBBASE *pdbbase)
@@ -3565,68 +3474,110 @@ void dbDumpBreaktable(DBBASE *pdbbase,const char *name)
return;
}
static char *bus[VXI_IO+1] = {"","","VME","CAMAC","AB",
"GPIB","BITBUS","","","","","","INST","BBGPIB","VXI"};
void dbReportDeviceConfig(dbBase *pdbbase,FILE *report)
static char *bus[LINK_NTYPES] = {
"", /* CONSTANT */
NULL, /* PV_LINK */
"VME",
"CAMAC",
"AB",
"GPIB",
"BITBUS",
NULL, /* MACRO_LINK */
NULL, /* JSON_LINK */
NULL, /* PN_LINK */
NULL, /* DB_LINK */
NULL, /* CA_LINK */
"INST",
"BBGPIB",
"VXI"
};
void dbReportDeviceConfig(dbBase *pdbbase, FILE *report)
{
DBENTRY dbentry;
DBENTRY *pdbentry=&dbentry;
long status;
char linkValue[messagesize];
char dtypValue[50];
char cvtValue[40];
int ilink,nlinks;
struct link *plink;
int linkType;
FILE *stream = (report==0) ? stdout : report;
DBENTRY dbentry, *pdbentry = &dbentry;
long status;
FILE *stream = report ? report : stdout;
if(!pdbbase) {
fprintf(stderr,"pdbbase not specified\n");
return;
if (!pdbbase) {
fprintf(stderr, "dbReportDeviceConfig: pdbbase not specified\n");
return;
}
dbInitEntry(pdbbase,pdbentry);
status = dbFirstRecordType(pdbentry);
while(!status) {
status = dbFirstRecord(pdbentry);
while(!status) {
nlinks = dbGetNLinks(pdbentry);
for(ilink=0; ilink<nlinks; ilink++) {
status = dbGetLinkField(pdbentry,ilink);
if(status || dbGetLinkType(pdbentry)!=DCT_LINK_FORM) continue;
plink = pdbentry->pfield;
linkType = plink->type;
if(bus[linkType][0]==0) continue;
strncpy(linkValue, dbGetString(pdbentry), NELEMENTS(linkValue)-1);
linkValue[NELEMENTS(linkValue)-1] = '\0';
status = dbFindField(pdbentry,"DTYP");
if(status) break;
strcpy(dtypValue,dbGetString(pdbentry));
status = dbFindField(pdbentry,"LINR");
if(status) {
cvtValue[0] = 0;
} else {
if(strcmp(dbGetString(pdbentry),"LINEAR")!=0) {
cvtValue[0] = 0;
} else {
strcpy(cvtValue,"cvt(");
status = dbFindField(pdbentry,"EGUL");
if(!status) strcat(cvtValue,dbGetString(pdbentry));
status = dbFindField(pdbentry,"EGUF");
if(!status) {
strcat(cvtValue,",");
strcat(cvtValue,dbGetString(pdbentry));
}
strcat(cvtValue,")");
}
}
fprintf(stream,"%-8s %-20s %-20s %-20s %-s\n",
bus[linkType],linkValue,dtypValue,
dbGetRecordName(pdbentry),cvtValue);
break;
}
status = dbNextRecord(pdbentry);
}
status = dbNextRecordType(pdbentry);
while (!status) {
const int nlinks = dbGetNLinks(pdbentry);
status = dbFirstRecord(pdbentry);
while (!status) {
int ilink;
for (ilink=0; ilink<nlinks; ilink++) {
char linkValue[messagesize];
char dtypValue[50];
char cvtValue[40];
struct link *plink;
int linkType;
status = dbGetLinkField(pdbentry, ilink);
if (status)
continue;
plink = pdbentry->pfield;
linkType = plink->type;
if (plink->text) { /* Not yet parsed */
dbLinkInfo linfo;
if (dbParseLink(plink->text, pdbentry->pflddes->field_type, &linfo))
continue;
linkType = linfo.ltype;
if (linkType && bus[linkType])
strncpy(linkValue, plink->text, messagesize-1);
dbFreeLinkInfo(&linfo);
}
else {
strncpy(linkValue, dbGetString(pdbentry), messagesize-1);
}
if (!linkType || !bus[linkType])
continue;
linkValue[messagesize-1] = '\0';
status = dbFindField(pdbentry, "DTYP");
if (status)
break; /* Next record type */
strcpy(dtypValue, dbGetString(pdbentry));
status = dbFindField(pdbentry, "LINR");
if (status) {
cvtValue[0] = 0;
}
else {
if (strcmp(dbGetString(pdbentry), "LINEAR") != 0) {
cvtValue[0] = 0;
}
else {
strcpy(cvtValue,"cvt(");
status = dbFindField(pdbentry, "EGUL");
if (!status)
strcat(cvtValue, dbGetString(pdbentry));
status = dbFindField(pdbentry, "EGUF");
if (!status) {
strcat(cvtValue, ",");
strcat(cvtValue, dbGetString(pdbentry));
}
strcat(cvtValue, ")");
}
}
fprintf(stream,"%-8s %-20s %-20s %-20s %-s\n",
bus[linkType], linkValue, dtypValue,
dbGetRecordName(pdbentry), cvtValue);
break;
}
status = dbNextRecord(pdbentry);
}
status = dbNextRecordType(pdbentry);
}
dbFinishEntry(pdbentry);
finishOutstream(stream);
@@ -28,25 +28,9 @@
extern "C" {
#endif
/*Field types as seen by static database access clients*/
#define DCT_STRING 0
#define DCT_INTEGER 1
#define DCT_REAL 2
#define DCT_MENU 3
#define DCT_MENUFORM 4
#define DCT_INLINK 5
#define DCT_OUTLINK 6
#define DCT_FWDLINK 7
#define DCT_NOACCESS 8
/*Link types as seen by static database access clients*/
#define DCT_LINK_CONSTANT 0
#define DCT_LINK_FORM 1
#define DCT_LINK_PV 2
typedef dbBase DBBASE;
typedef struct{
typedef struct dbEntry {
DBBASE *pdbbase;
dbRecordType *precordType;
dbFldDes *pflddes;
@@ -57,9 +41,6 @@ typedef struct{
short indfield;
} DBENTRY;
struct dbAddr;
struct dbCommon;
/* Static database access routines*/
epicsShareFunc DBBASE * dbAllocBase(void);
epicsShareFunc void dbFreeBase(DBBASE *pdbbase);
@@ -68,18 +49,6 @@ epicsShareFunc void dbFreeEntry(DBENTRY *pdbentry);
epicsShareFunc void dbInitEntry(DBBASE *pdbbase,
DBENTRY *pdbentry);
/** Initialize DBENTRY from a valid dbAddr*.
* Constant time equivalent of dbInitEntry() then dbFindRecord(),
* and finally dbFollowAlias()
*/
epicsShareFunc void dbInitEntryFromAddr(struct dbAddr *paddr, DBENTRY *pdbentry);
/** Initialize DBENTRY from a valid record (dbCommon*).
* Constant time equivalent of dbInitEntry() then dbFindRecord(),
* and finally dbFollowAlias() when no field is specified.
*/
epicsShareFunc void dbInitEntryFromRecord(struct dbCommon *prec, DBENTRY *pdbentry);
epicsShareFunc void dbFinishEntry(DBENTRY *pdbentry);
epicsShareFunc DBENTRY * dbCopyEntry(DBENTRY *pdbentry);
epicsShareFunc void dbCopyEntryContents(DBENTRY *pfrom,
@@ -139,7 +108,6 @@ epicsShareFunc long dbGetAttributePart(DBENTRY *pdbentry,
epicsShareFunc long dbFirstField(DBENTRY *pdbentry, int dctonly);
epicsShareFunc long dbNextField(DBENTRY *pdbentry, int dctonly);
epicsShareFunc int dbGetFieldType(DBENTRY *pdbentry);
epicsShareFunc int dbGetNFields(DBENTRY *pdbentry, int dctonly);
epicsShareFunc char * dbGetFieldName(DBENTRY *pdbentry);
epicsShareFunc char * dbGetDefault(DBENTRY *pdbentry);
@@ -231,7 +199,6 @@ epicsShareFunc linkSup * dbFindLinkSup(dbBase *pdbbase,
epicsShareFunc int dbGetNLinks(DBENTRY *pdbentry);
epicsShareFunc long dbGetLinkField(DBENTRY *pdbentry, int index);
epicsShareFunc int dbGetLinkType(DBENTRY *pdbentry);
/* Dump routines */
epicsShareFunc void dbDumpPath(DBBASE *pdbbase);
@@ -5,7 +5,7 @@
* Operator of Los Alamos National Laboratory.
* EPICS BASE Versions 3.13.7
* and higher are distributed subject to a Software License Agreement found
* in file LICENSE that is included with this distribution.
* in file LICENSE that is included with this distribution.
\*************************************************************************/
/* dbStaticPvt.h */
/*
@@ -59,13 +59,10 @@ typedef struct dbLinkInfo {
long dbInitRecordLinks(dbRecordType *rtyp, struct dbCommon *prec);
#define LINK_DEBUG_LSET 1
#define LINK_DEBUG_JPARSE 2
/* Parse link string. no record locks needed.
* on success caller must free pinfo->target
*/
epicsShareFunc long dbParseLink(const char *str, short ftype, dbLinkInfo *pinfo, unsigned opts);
epicsShareFunc long dbParseLink(const char *str, short ftype, dbLinkInfo *pinfo);
/* Check if link type allow the parsed link value pinfo
* to be assigned to the given link.
* Record containing plink must be locked.
+121 -6
View File
@@ -6,7 +6,11 @@
* EPICS BASE is distributed subject to a Software License Agreement found
* in file LICENSE that is included with this distribution.
\*************************************************************************/
/* devSup.h Device Support */
/** @file devSup.h
*
* @brief Device support routines
*/
/*
* Author: Marty Kraimer
* Date: 6-1-90
@@ -21,6 +25,111 @@
/* structures defined elsewhere */
struct dbCommon;
struct devSup;
typedef struct ioscan_head *IOSCANPVT;
struct link; /* aka DBLINK */
/** Type safe version of 'struct dset'
*
* Recommended usage:
*
* In Makefile:
@code
USR_CFLAGS += -DUSE_TYPED_RSET -DUSE_TYPED_DSET
@endcode
*
* In C source file:
@code
#include <devSup.h>
#include <dbScan.h> // For IOCSCANPVT
...
#include <epicsExport.h> // defines epicsExportSharedSymbols
...
static long init_record(dbCommon *prec);
static long get_iointr_info(int detach, dbCommon *prec, IOCSCANPVT* pscan);
static long longin_read(longinRecord *prec);
const struct {
dset common;
long (*read)(longinRecord *prec);
} devLiDevName = {
{
5, // 4 from dset + 1 from longinRecord
NULL,
NULL,
&init_record,
&get_iointr_info
},
&longin_read
};
epicsExportAddress(dset, devLiDevName);
@endcode
*/
typedef struct typed_dset {
/** Number of function pointers which follow.
* The value depends on the recordtype, but must be >=4 */
long number;
/** Called from dbior() */
long (*report)(int lvl);
/** Called twice during iocInit().
* First with @a after = 0 before init_record() or array field allocation.
* Again with @a after = 1 after init_record() has finished.
*/
long (*init)(int after);
/** Called once per record instance */
long (*init_record)(struct dbCommon *prec);
/** Called when SCAN="I/O Intr" on startup, or after SCAN is changed.
*
* Caller must assign the third arguement (IOCSCANPVT*). eg.
@code
struct mpvt {
IOSCANPVT drvlist;
};
...
// init_record() routine calls
scanIoInit(&pvt->drvlist);
...
static long get_ioint_info(int detach, struct dbCommon *prec, IOCSCANPVT* pscan) {
if(prec->dpvt)
*pscan = &((mypvt*)prec->dpvt)->drvlist;
@endcode
*
* When a particular record instance can/will only used a single scan list,
* the @a detach argument can be ignored.
*
* If this is not the case, then the following should be noted.
* + get_ioint_info() is called with @a detach = 0 to fetch the scan list to
* which this record will be added.
* + get_ioint_info() is called later with @a detach = 1 to fetch the scan
* list from which this record should be removed.
* + Calls will be balanced, so a call with @a detach = 0 will be followed
* by one with @a detach = 1.
*
* @note get_ioint_info() will be called during IOC shutdown if the
* dsxt::del_record() extended callback is defined. (from 3.15.0.1)
*/
long (*get_ioint_info)(int detach, struct dbCommon *prec, IOSCANPVT* pscan);
/* Any further functions are specified by the record type. */
} typed_dset;
/** Device support extension table.
*
* Optional routines to allow run-time address modifications to be communicated
* to device support, which must register a struct dsxt by calling devExtend()
* from its init() routine.
*/
typedef struct dsxt {
/** Optional, called to offer device support a new record to control.
*
* Routine may return a non-zero error code to refuse record.
*/
long (*add_record)(struct dbCommon *precord);
/** Optional, called to remove record from device support control.
*
* Routine return a non-zero error code to refuse record removal.
*/
long (*del_record)(struct dbCommon *precord);
/* Only future Base releases may extend this table. */
} dsxt;
#ifdef __cplusplus
extern "C" {
@@ -29,6 +138,8 @@ extern "C" {
typedef long (*DEVSUPFUN)(); /* ptr to device support function*/
#endif
#ifndef USE_TYPED_DSET
typedef struct dset { /* device support entry table */
long number; /*number of support routines*/
DEVSUPFUN report; /*print report*/
@@ -38,11 +149,15 @@ typedef struct dset { /* device support entry table */
/*other functions are record dependent*/
} dset;
typedef struct dsxt { /* device support extension table */
long (*add_record)(struct dbCommon *precord);
long (*del_record)(struct dbCommon *precord);
/* Recordtypes are *not* allowed to extend this table */
} dsxt;
#else
typedef typed_dset dset;
#endif /* USE_TYPED_DSET */
/** Fetch INP or OUT link (or NULL if record type has neither).
*
* Recommended for use in device support init_record()
*/
epicsShareFunc struct link* dbGetDevLink(struct dbCommon* prec);
epicsShareExtern dsxt devSoft_DSXT; /* Allow anything table */
+33 -8
View File
@@ -6,7 +6,10 @@
* EPICS BASE is distributed subject to a Software License Agreement found
* in file LICENSE that is included with this distribution.
\*************************************************************************/
/* drvSup.h Driver Support */
/** @file drvSup.h
*
* @brief Driver support routines.
*/
/*
* Author: Marty Kraimer
@@ -18,16 +21,38 @@
#include "errMdef.h"
typedef long (*DRVSUPFUN) (); /* ptr to driver support function*/
/** Driver entry table */
typedef struct typed_drvet {
/** Number of function pointers which follow. Must be >=2 */
long number;
/** Called from dbior() */
long (*report)(int lvl);
/** Called during iocInit() */
long (*init)(void);
/* Any further functions are driver-specific */
} typed_drvet;
#ifdef USE_TYPED_DRVET
typedef typed_drvet drvet;
#else
/* These interfaces may eventually get deprecated */
typedef long (*DRVSUPFUN) (); /* ptr to driver support function */
typedef struct drvet { /* driver entry table */
long number; /* number of support routines */
DRVSUPFUN report; /* print report */
DRVSUPFUN init; /* init support */
/* Any further functions are driver-specific */
} drvet;
typedef struct drvet { /* driver entry table */
long number; /*number of support routines*/
DRVSUPFUN report; /*print report*/
DRVSUPFUN init; /*init support*/
/*other functions are device dependent*/
}drvet;
#define DRVETNUMBER ( (sizeof(struct drvet) -sizeof(long))/sizeof(DRVSUPFUN) )
#endif /* USE_TYPED_DRVET */
#define S_drv_noDrvSup (M_drvSup| 1) /*SDR_DRVSUP: Driver support missing*/
#define S_drv_noDrvet (M_drvSup| 3) /*Missing driver support entry table*/
File diff suppressed because it is too large Load Diff
+4 -4
View File
@@ -34,10 +34,10 @@ be written to stdout unless the -o option is given.</p>
<dl>
<dt><tt>-V</tt></dt>
<dd>Verbose warnings; if this parameter is specified then any undefined
macro discovered in the template file which does not have an associated
default value is considered an error. An error message is generated, and
when msi terminates it will do so with an exit status of 2.</dd>
<dd>Verbose warnings; if this parameter is specified then any undefined or
recursive macros discovered in the template will be considered an error and
will be marked in the output file. An error message will be shown, and when
msi terminates it will do so with an exit status of 2.</dd>
<dt><tt>-g</tt></dt>
<dd>When this flag is given all macros defined in a substitution file will
+1
View File
@@ -26,3 +26,4 @@ dbCore_SRCS += miscIocRegister.c
dbCore_SRCS += dlload.c
dbCore_SRCS += iocshRegisterCommon.c
miscIocRegister_CFLAGS_iOS = -DSYSTEM_UNAVAILABLE
+7
View File
@@ -12,6 +12,9 @@ variable(asCaDebug,int)
# CA server debug flag (very verbose) range[0,5]
variable(CASDEBUG,int)
# Link parsing debug
variable(dbJLinkDebug,int)
# Static database access variables
variable(dbRecordsOnceOnly,int)
variable(dbRecordsAbcSorted,int)
@@ -19,8 +22,12 @@ variable(dbBptNotMonotonic,int)
variable(dbQuietMacroWarnings,int)
variable(dbConvertStrict,int)
# PUTF/RPRO tracing; set TPRO on records to trace
variable(dbAccessDebugPUTF,int)
# dbLoadTemplate settings
variable(dbTemplateMaxVars,int)
# Default number of parallel callback threads
variable(callbackParallelThreadsDefault,int)
@@ -66,10 +66,12 @@ void miscIocRegister(void)
/* system -- escape to system command interpreter.
*
* Disabled by default, for security reasons. To enable this command, add
* Disabled by default for security reasons, not available on all OSs.
* To enable this command, add
* registrar(iocshSystemCommand)
* to an application dbd file.
* to an application dbd file, or include system.dbd
*/
#ifndef SYSTEM_UNAVAILABLE
static const iocshArg systemArg0 = { "command string",iocshArgString};
static const iocshArg * const systemArgs[] = {&systemArg0};
static const iocshFuncDef systemFuncDef = {"system",1,systemArgs};
@@ -77,12 +79,15 @@ static void systemCallFunc(const iocshArgBuf *args)
{
system(args[0].sval);
}
#endif
static void iocshSystemCommand(void)
{
#ifndef SYSTEM_UNAVAILABLE
if (system(NULL))
iocshRegister(&systemFuncDef, systemCallFunc);
else
#endif
errlogPrintf ("Can't register 'system' command -- no command interpreter available.\n");
}
epicsExportRegistrar(iocshSystemCommand);
+12 -18
View File
@@ -83,20 +83,22 @@ static void req_server (void *pParm)
while (TRUE) {
SOCKET clientSock;
struct sockaddr sockAddr;
osiSockAddr sockAddr;
osiSocklen_t addLen = sizeof(sockAddr);
while (castcp_ctl == ctlPause) {
epicsThreadSleep(0.1);
}
clientSock = epicsSocketAccept ( IOC_sock, &sockAddr, &addLen );
if ( clientSock == INVALID_SOCKET ) {
clientSock = epicsSocketAccept ( IOC_sock, &sockAddr.sa, &addLen );
if ( clientSock == INVALID_SOCKET ||
sockAddr.sa.sa_family != AF_INET ||
addLen < sizeof(sockAddr.ia) ) {
char sockErrBuf[64];
epicsSocketConvertErrnoToString (
sockErrBuf, sizeof ( sockErrBuf ) );
errlogPrintf("CAS: Client accept error: %s\n",
sockErrBuf );
errlogPrintf("CAS: Client accept error: %s (%d)\n",
sockErrBuf, (int)addLen );
epicsThreadSleep(15.0);
continue;
}
@@ -105,7 +107,7 @@ static void req_server (void *pParm)
struct client *pClient;
/* socket passed in is closed if unsuccessful here */
pClient = create_tcp_client ( clientSock );
pClient = create_tcp_client ( clientSock, &sockAddr );
if ( ! pClient ) {
epicsThreadSleep ( 15.0 );
continue;
@@ -324,7 +326,7 @@ void rsrv_build_addr_lists(void)
#ifdef IP_MULTICAST_TTL
{
int ttl;
osiSockOptMcastTTL_t ttl;
long val;
if(envGetLongConfigParam(&EPICS_CA_MCAST_TTL, &val))
val =1;
@@ -1405,12 +1407,11 @@ void casExpandRecvBuffer ( struct client *pClient, ca_uint32_t size )
/*
* create_tcp_client ()
*/
struct client *create_tcp_client ( SOCKET sock )
struct client *create_tcp_client (SOCKET sock , const osiSockAddr *peerAddr)
{
int status;
struct client *client;
int intTrue = TRUE;
osiSocklen_t addrSize;
unsigned priorityOfEvents;
/* socket passed in is destroyed here if unsuccessful */
@@ -1419,6 +1420,8 @@ struct client *create_tcp_client ( SOCKET sock )
return NULL;
}
client->addr = peerAddr->ia;
/*
* see TCP(4P) this seems to make unsolicited single events much
* faster. I take care of queue up as load increases.
@@ -1470,15 +1473,6 @@ struct client *create_tcp_client ( SOCKET sock )
}
#endif
addrSize = sizeof ( client->addr );
status = getpeername ( sock, (struct sockaddr *)&client->addr,
&addrSize );
if ( status < 0 ) {
epicsPrintf ("CAS: peer address fetch failed\n");
destroy_tcp_client (client);
return NULL;
}
client->evuser = (struct event_user *) db_init_events ();
if ( ! client->evuser ) {
errlogPrintf ("CAS: unable to init the event facility\n");
+27 -7
View File
@@ -22,6 +22,7 @@
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <cantProceed.h>
#include "addrList.h"
#include "dbDefs.h"
@@ -45,6 +46,7 @@ void rsrv_online_notify_task(void *pParm)
caHdr msg;
int status;
ca_uint32_t beaconCounter = 0;
int *lastError;
taskwdInsert (epicsThreadGetIdSelf(),NULL,NULL);
@@ -70,29 +72,45 @@ void rsrv_online_notify_task(void *pParm)
msg.m_count = htons (ca_server_port);
msg.m_dataType = htons (CA_MINOR_PROTOCOL_REVISION);
/* beaconAddrList should not change after rsrv_init(), which then starts this thread */
lastError = callocMustSucceed(ellCount(&beaconAddrList), sizeof(*lastError), "rsrv_online_notify_task lastError");
epicsEventSignal(beacon_startStopEvent);
while (TRUE) {
ELLNODE *cur;
unsigned i;
/* send beacon to each interface */
for(cur=ellFirst(&beaconAddrList); cur; cur=ellNext(cur))
for(i=0, cur=ellFirst(&beaconAddrList); cur; i++, cur=ellNext(cur))
{
osiSockAddrNode *pAddr = CONTAINER(cur, osiSockAddrNode, node);
status = sendto (beaconSocket, (char *)&msg, sizeof(msg), 0,
&pAddr->addr.sa, sizeof(pAddr->addr));
if (status < 0) {
char sockErrBuf[64];
char sockDipBuf[22];
int err = SOCKERRNO;
if(err != lastError[i]) {
char sockErrBuf[64];
char sockDipBuf[22];
epicsSocketConvertErrnoToString(sockErrBuf, sizeof(sockErrBuf));
ipAddrToDottedIP(&pAddr->addr.ia, sockDipBuf, sizeof(sockDipBuf));
errlogPrintf ( "CAS: CA beacon send to %s error: %s\n",
sockDipBuf, sockErrBuf);
epicsSocketConvertErrorToString(sockErrBuf, sizeof(sockErrBuf), err);
ipAddrToDottedIP(&pAddr->addr.ia, sockDipBuf, sizeof(sockDipBuf));
errlogPrintf ( "CAS: CA beacon send to %s error: %s\n",
sockDipBuf, sockErrBuf);
lastError[i] = err;
}
}
else {
assert (status == sizeof(msg));
if(lastError[i]) {
char sockDipBuf[22];
ipAddrToDottedIP(&pAddr->addr.ia, sockDipBuf, sizeof(sockDipBuf));
errlogPrintf ( "CAS: CA beacon send to %s ok\n",
sockDipBuf);
}
lastError[i] = 0;
}
}
@@ -111,6 +129,8 @@ void rsrv_online_notify_task(void *pParm)
delay = 0.02; /* Restart beacon timing if paused */
}
}
free(lastError);
}
+1 -1
View File
@@ -230,7 +230,7 @@ void rsrv_online_notify_task (void *);
void cast_server (void *);
struct client *create_client ( SOCKET sock, int proto );
void destroy_client ( struct client * );
struct client *create_tcp_client ( SOCKET sock );
struct client *create_tcp_client ( SOCKET sock, const osiSockAddr* peerAddr );
void destroy_tcp_client ( struct client * );
void casAttachThreadToClient ( struct client * );
int camessage ( struct client *client );
@@ -23,6 +23,7 @@
#include "dbDefs.h"
#include "dbAccess.h"
#include "dbConstLink.h"
#include "dbEvent.h"
#include "recGbl.h"
#include "devSup.h"
#include "cantProceed.h"
@@ -96,11 +97,15 @@ static long readLocked(struct link *pinp, void *dummy)
static long read_aai(aaiRecord *prec)
{
epicsUInt32 nord = prec->nord;
struct link *pinp = prec->simm == menuYesNoYES ? &prec->siol : &prec->inp;
long status = dbLinkDoLocked(pinp, readLocked, NULL);
if (status == S_db_noLSET)
status = readLocked(pinp, NULL);
if (!status && nord != prec->nord)
db_post_events(prec, &prec->nord, DBE_VALUE | DBE_LOG);
return status;
}
+8 -2
View File
@@ -4,7 +4,7 @@
* Copyright (c) 2002 Lawrence Berkeley Laboratory,The Control Systems
* Group, Systems Engineering Department
* EPICS BASE is distributed subject to a Software License Agreement found
* in file LICENSE that is included with this distribution.
* in file LICENSE that is included with this distribution.
\*************************************************************************/
/*
@@ -19,6 +19,7 @@
#include "alarm.h"
#include "dbDefs.h"
#include "dbAccess.h"
#include "dbEvent.h"
#include "recGbl.h"
#include "devSup.h"
#include "subArrayRecord.h"
@@ -101,6 +102,7 @@ static long read_sa(subArrayRecord *prec)
{
long status;
struct sart rt;
epicsUInt32 nord = prec->nord;
rt.nRequest = prec->indx + prec->nelm;
if (rt.nRequest > prec->malm)
@@ -123,8 +125,12 @@ static long read_sa(subArrayRecord *prec)
status = readLocked(&prec->inp, &rt);
}
if (!status && rt.nRequest > 0)
if (!status && rt.nRequest > 0) {
subset(prec, rt.nRequest);
if (nord != prec->nord)
db_post_events(prec, &prec->nord, DBE_VALUE | DBE_LOG);
}
return status;
}
+4 -1
View File
@@ -4,7 +4,7 @@
* 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.
* in file LICENSE that is included with this distribution.
\*************************************************************************/
/*
@@ -81,6 +81,7 @@ static long read_wf(waveformRecord *prec)
{
long status;
struct wfrt rt;
epicsUInt32 nord = prec->nord;
rt.nRequest = prec->nelm;
rt.ptime = (dbLinkIsConstant(&prec->tsel) &&
@@ -93,6 +94,8 @@ static long read_wf(waveformRecord *prec)
if (!status && rt.nRequest > 0) {
prec->nord = rt.nRequest;
prec->udf = FALSE;
if (nord != prec->nord)
db_post_events(prec, &prec->nord, DBE_VALUE | DBE_LOG);
}
return status;
+3 -2
View File
@@ -2,7 +2,7 @@
# Copyright (c) 2016 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.
# in file LICENSE that is included with this distribution.
#*************************************************************************
# This is a Makefile fragment, see src/std/Makefile.
@@ -13,6 +13,7 @@ DBD += links.dbd
dbRecStd_SRCS += lnkConst.c
dbRecStd_SRCS += lnkCalc.c
dbRecStd_SRCS += lnkState.c
dbRecStd_SRCS += lnkDebug.c
HTMLS += links.html
+113 -9
View File
@@ -13,6 +13,12 @@ The following additional link types are available in this release:
=item * L<Calc|/"Calculation Link calc">
=item * L<dbState|/"dbState Link state">
=item * L<Debug|/"Debug Link debug">
=item * L<Trace|/"Trace Link trace">
=back
=head2 Using JSON Links
@@ -31,15 +37,16 @@ database file syntax.
=cut
link(const, lnkConstIf)
=head3 Constant Link C<"const">
Constant links provide one or more values at link initalization time, but do not
return any data when their C<getValue()> routine is called. Most record types
support the use of constant links by calling C<recGblInitConstantLink()> at
record initialization, which results in the constant value being loaded into the
target field at that time.
Constant links are input links that provide literal values at link initalization
time, but do not return any data when their C<getValue()> routine is called.
Most record types support the use of constant links on their input links by
calling C<recGblInitConstantLink()> at record initialization, which results in
the constant value being loaded into the target field at that time.
Note that for most record types (the C<printf> and C<calcout> records are the
main exceptions) it is pointless to set an input link to a constant link at
@@ -70,14 +77,35 @@ converted to the desired double value at initialization, for example:
=cut
link(calc, lnkCalcIf)
=head3 Calculation Link C<"calc">
Calculation links can perform simple mathematical expressions on scalar
(double-precision floating-point) values obtained from other link types and
return a single double-precision floating-point result. The expressions are
evaluated by the EPICS Calc engine, and up to 12 inputs can be provided.
A calculation link is an input link that can evaluate mathematical expressions
on scalar (double-precision floating-point) values obtained from up to 12 child
input links, and returns a double-precision floating-point result. The
expression is evaluated by the EPICS Calc engine, and the result is returned as
the value of the link.
Two additional expressions may also be provided and are evaluated to determine
whether the record owning the link should be placed in alarm state. In both
cases the result of the main calculation is available to these expressions as
C<VAL> (attempts to assign to C<VAL> inside either expression will have no
lasting effect). If the C<major> expression evaluates to a non-zero value the
record will be placed in C<LINK/MAJOR> alarm. If not and the C<minor> expression
evaluates to non-zero the record will be placed in C<LINK/MINOR> alarm state.
A calculation link can also be an output link, with the scalar output value
being converted to a double and provided to the expression as C<VAL>. Up to 12
additional input links can also be read and provided to the expression as above.
The result of the calculation is forwarded to a child output link specified in
the link's C<out> parameter.
For an output link the main expression is actually optional; if not provided the
converted value will be forwarded to the output link unchanged. The two alarm
expressions may still be used to put the output link into alarm state as
described above.
=head4 Parameters
@@ -88,6 +116,7 @@ The link address is a JSON map with the following keys:
=item expr
The primary expression to be evaluated, given as a string.
This is optional for output links, required for input links.
=item major
@@ -104,6 +133,12 @@ to the inputs C<A>, C<B>, C<C>, ... C<L>. Each input argument may be either a
numeric literal or an embedded JSON link inside C<{}> braces. The same input
values are provided to the two alarm expressions as to the primary expression.
=item out
A JSON link inside C<{}> braces which specifies the destination of C<putValue>
operations after any expressions have been evaluated.
This key is required for output links, not used by input links.
=item units
An optional string specifying the engineering units for the result of the
@@ -129,3 +164,72 @@ atomically with the value of the input argument.
{calc: {expr:"A*B", args:[{db:"record.VAL"}, 1.5], prec:3}}
=cut
link(state, lnkStateIf)
=head3 dbState Link C<"state">
A dbState link is one that gets or puts a boolean value from/to a named global
flag as implemented by the dbState facility in C<dbstate.h>. The link type can
invert the sense of the dbState flag during the get or put if desired.
The value of the named flag is read or written at the time of the link I/O
operation. When reading a flag, the value returned by the link will be zero or
one converted to the requested data type. When writing to a flag the boolean
value of the data written is determined in the originating data type. All
strings are regarded as true other than C<""> and C<"0"> which are both false.
A link can be configured to invert the sense of the flag data by putting an
exclamation mark C<!> before the first character of the flag's name in the link
address.
These dbState flags can be accessed from the IOC Shell with various dbState
commands, and are also used by the C<"sync"> Channel-Access server-side filter
mechanism.
=head4 Parameters
The link takes a single parameter which must be a string, providing the name of
the dbState object, with an optional leading C<!> character to indicate that the
flag's value should be inverted. The dbState object will be created when the
link is initialized if it doesn't already exist.
=head4 Examples
{state:"redBeam"}
{state:"!simEnable"}
=cut
link(debug, lnkDebugIf)
variable(lnkDebug_debug, int)
=head3 Debug Link C<"debug">
The debug link type exists to enable debugging of other link types; it provides
no functionality itself other than to turn on the debug flag for the child link
that is its only parameter and pass all link operations down to that link.
=head4 Example
{debug:{state:"redBeam"}}
=cut
link(trace, lnkTraceIf)
=head3 Trace Link C<"trace">
The trace link type is a relative of the debug link type that also traces the
operation of its child link. At creation it turns on the debug flag of its child
link, then it prints the method arguments and return values of all link
operations before / after passing control down to the child link.
=head4 Example
{trace:{state:"redBeam"}}
=cut
+191 -117
View File
@@ -6,13 +6,9 @@
\*************************************************************************/
/* lnkCalc.c */
/* Current usage
* {calc:{expr:"A", args:[{...}, ...]}}
/* Usage
* {calc:{expr:"A*B", args:[{...}, ...], units:"mm"}}
* First link in 'args' is 'A', second is 'B', and so forth.
*
* TODO:
* Support setting individual input links instead of the args list.
* {calc:{expr:"K", K:{...}}}
*/
#include <string.h>
@@ -26,6 +22,7 @@
#include "epicsAssert.h"
#include "epicsString.h"
#include "epicsTypes.h"
#include "epicsTime.h"
#include "dbAccessDefs.h"
#include "dbCommon.h"
#include "dbConvertFast.h"
@@ -40,15 +37,14 @@
typedef long (*FASTCONVERT)();
#define IFDEBUG(n) if(clink->jlink.debug)
typedef struct calc_link {
jlink jlink; /* embedded object */
int nArgs;
short dbfType;
enum {
ps_init,
ps_expr, ps_major, ps_minor,
ps_args,
ps_args, ps_out,
ps_prec,
ps_units,
ps_time,
@@ -66,7 +62,9 @@ typedef struct calc_link {
char *units;
short tinp;
struct link inp[CALCPERFORM_NARGS];
struct link out;
double arg[CALCPERFORM_NARGS];
epicsTimeStamp time;
double val;
} calc_link;
@@ -77,19 +75,25 @@ static lset lnkCalc_lset;
static jlink* lnkCalc_alloc(short dbfType)
{
calc_link *clink = calloc(1, sizeof(struct calc_link));
calc_link *clink;
IFDEBUG(10)
printf("lnkCalc_alloc()\n");
if (dbfType == DBF_FWDLINK) {
errlogPrintf("lnkCalc: No support for forward links\n");
return NULL;
}
clink = calloc(1, sizeof(struct calc_link));
if (!clink) {
errlogPrintf("lnkCalc: calloc() failed.\n");
return NULL;
}
clink->nArgs = 0;
clink->dbfType = dbfType;
clink->pstate = ps_init;
clink->prec = 15; /* standard value for a double */
clink->tinp = -1;
IFDEBUG(10)
printf("lnkCalc_alloc -> calc@%p\n", clink);
return &clink->jlink;
}
@@ -98,12 +102,11 @@ static void lnkCalc_free(jlink *pjlink)
calc_link *clink = CONTAINER(pjlink, struct calc_link, jlink);
int i;
IFDEBUG(10)
printf("lnkCalc_free(calc@%p)\n", clink);
for (i = 0; i < clink->nArgs; i++)
dbJLinkFree(clink->inp[i].value.json.jlink);
dbJLinkFree(clink->out.value.json.jlink);
free(clink->expr);
free(clink->major);
free(clink->minor);
@@ -118,9 +121,6 @@ static jlif_result lnkCalc_integer(jlink *pjlink, long long num)
{
calc_link *clink = CONTAINER(pjlink, struct calc_link, jlink);
IFDEBUG(10)
printf("lnkCalc_integer(calc@%p, %lld)\n", clink, num);
if (clink->pstate == ps_prec) {
clink->prec = num;
return jlif_continue;
@@ -146,9 +146,6 @@ static jlif_result lnkCalc_double(jlink *pjlink, double num)
{
calc_link *clink = CONTAINER(pjlink, struct calc_link, jlink);
IFDEBUG(10)
printf("lnkCalc_double(calc@%p, %g)\n", clink, num);
if (clink->pstate != ps_args) {
return jlif_stop;
errlogPrintf("lnkCalc: Unexpected double %g\n", num);
@@ -171,9 +168,6 @@ static jlif_result lnkCalc_string(jlink *pjlink, const char *val, size_t len)
char *inbuf, *postbuf;
short err;
IFDEBUG(10)
printf("lnkCalc_string(calc@%p, \"%.*s\")\n", clink, (int) len, val);
if (clink->pstate == ps_units) {
clink->units = epicsStrnDup(val, len);
return jlif_continue;
@@ -181,7 +175,8 @@ static jlif_result lnkCalc_string(jlink *pjlink, const char *val, size_t len)
if (clink->pstate == ps_time) {
char tinp;
if (len != 1 || (tinp = toupper(val[0])) < 'A' || tinp > 'L') {
if (len != 1 || (tinp = toupper((int) val[0])) < 'A' || tinp > 'L') {
errlogPrintf("lnkCalc: Bad 'time' parameter \"%.*s\"\n", (int) len, val);
return jlif_stop;
}
@@ -234,11 +229,10 @@ static jlif_key_result lnkCalc_start_map(jlink *pjlink)
{
calc_link *clink = CONTAINER(pjlink, struct calc_link, jlink);
IFDEBUG(10)
printf("lnkCalc_start_map(calc@%p)\n", clink);
if (clink->pstate == ps_args)
return jlif_key_child_link;
return jlif_key_child_inlink;
if (clink->pstate == ps_out)
return jlif_key_child_outlink;
if (clink->pstate != ps_init) {
errlogPrintf("lnkCalc: Unexpected map\n");
@@ -252,10 +246,21 @@ static jlif_result lnkCalc_map_key(jlink *pjlink, const char *key, size_t len)
{
calc_link *clink = CONTAINER(pjlink, struct calc_link, jlink);
IFDEBUG(10)
printf("lnkCalc_map_key(calc@%p, \"%.*s\")\n", pjlink, (int) len, key);
/* FIXME: These errors messages are wrong when a key is duplicated.
* The key is known, we just don't allow it more than once.
*/
if (len == 4) {
if (len == 3) {
if (!strncmp(key, "out", len) &&
clink->dbfType == DBF_OUTLINK &&
clink->out.type == 0)
clink->pstate = ps_out;
else {
errlogPrintf("lnkCalc: Unknown key \"%.3s\"\n", key);
return jlif_stop;
}
}
else if (len == 4) {
if (!strncmp(key, "expr", len) && !clink->post_expr)
clink->pstate = ps_expr;
else if (!strncmp(key, "args", len) && !clink->nArgs)
@@ -293,13 +298,16 @@ static jlif_result lnkCalc_end_map(jlink *pjlink)
{
calc_link *clink = CONTAINER(pjlink, struct calc_link, jlink);
IFDEBUG(10)
printf("lnkCalc_end_map(calc@%p)\n", clink);
if (clink->pstate == ps_error)
return jlif_stop;
else if (!clink->post_expr) {
errlogPrintf("lnkCalc: no expression ('expr' key)\n");
else if (clink->dbfType == DBF_INLINK &&
!clink->post_expr) {
errlogPrintf("lnkCalc: No expression ('expr' key)\n");
return jlif_stop;
}
else if (clink->dbfType == DBF_OUTLINK &&
clink->out.type != JSON_LINK) {
errlogPrintf("lnkCalc: No output link ('out' key)\n");
return jlif_stop;
}
@@ -310,9 +318,6 @@ static jlif_result lnkCalc_start_array(jlink *pjlink)
{
calc_link *clink = CONTAINER(pjlink, struct calc_link, jlink);
IFDEBUG(10)
printf("lnkCalc_start_array(calc@%p)\n", clink);
if (clink->pstate != ps_args) {
errlogPrintf("lnkCalc: Unexpected array\n");
return jlif_stop;
@@ -325,9 +330,6 @@ static jlif_result lnkCalc_end_array(jlink *pjlink)
{
calc_link *clink = CONTAINER(pjlink, struct calc_link, jlink);
IFDEBUG(10)
printf("lnkCalc_end_array(calc@%p)\n", clink);
if (clink->pstate == ps_error)
return jlif_stop;
@@ -339,15 +341,27 @@ static void lnkCalc_end_child(jlink *parent, jlink *child)
calc_link *clink = CONTAINER(parent, struct calc_link, jlink);
struct link *plink;
if (clink->nArgs == CALCPERFORM_NARGS) {
dbJLinkFree(child);
errlogPrintf("lnkCalc: Too many input args, limit is %d\n",
CALCPERFORM_NARGS);
if (clink->pstate == ps_args) {
if (clink->nArgs == CALCPERFORM_NARGS) {
errlogPrintf("lnkCalc: Too many input args, limit is %d\n",
CALCPERFORM_NARGS);
goto errOut;
}
plink = &clink->inp[clink->nArgs++];
}
else if (clink->pstate == ps_out) {
plink = &clink->out;
}
else {
errlogPrintf("lnkCalc: Unexpected child link, parser state = %d\n",
clink->pstate);
errOut:
clink->pstate = ps_error;
dbJLinkFree(child);
return;
}
plink = &clink->inp[clink->nArgs++];
plink->type = JSON_LINK;
plink->value.json.string = NULL;
plink->value.json.jlink = child;
@@ -355,11 +369,6 @@ static void lnkCalc_end_child(jlink *parent, jlink *child)
static struct lset* lnkCalc_get_lset(const jlink *pjlink)
{
calc_link *clink = CONTAINER(pjlink, struct calc_link, jlink);
IFDEBUG(10)
printf("lnkCalc_get_lset(calc@%p)\n", pjlink);
return &lnkCalc_lset;
}
@@ -368,9 +377,6 @@ static void lnkCalc_report(const jlink *pjlink, int level, int indent)
calc_link *clink = CONTAINER(pjlink, struct calc_link, jlink);
int i;
IFDEBUG(10)
printf("lnkCalc_report(calc@%p)\n", clink);
printf("%*s'calc': \"%s\" = %.*g %s\n", indent, "",
clink->expr, clink->prec, clink->val,
clink->units ? clink->units : "");
@@ -388,9 +394,13 @@ static void lnkCalc_report(const jlink *pjlink, int level, int indent)
printf("%*s Minor expression: \"%s\"\n", indent, "",
clink->minor);
if (clink->tinp >= 0 && clink->tinp < clink->nArgs)
printf("%*s Timestamp input \"%c\"\n", indent, "",
clink->tinp + 'A');
if (clink->tinp >= 0) {
char timeStr[40];
epicsTimeToStrftime(timeStr, 40, "%Y-%m-%d %H:%M:%S.%09f",
&clink->time);
printf("%*s Timestamp input %c: %s\n", indent, "",
clink->tinp + 'A', timeStr);
}
for (i = 0; i < clink->nArgs; i++) {
struct link *plink = &clink->inp[i];
@@ -403,17 +413,20 @@ static void lnkCalc_report(const jlink *pjlink, int level, int indent)
if (child)
dbJLinkReport(child, level - 1, indent + 4);
}
if (clink->out.type == JSON_LINK) {
printf("%*s Output:\n", indent, "");
dbJLinkReport(clink->out.value.json.jlink, level - 1, indent + 4);
}
}
}
long lnkCalc_map_children(jlink *pjlink, jlink_map_fn rtn, void *ctx)
static long lnkCalc_map_children(jlink *pjlink, jlink_map_fn rtn, void *ctx)
{
calc_link *clink = CONTAINER(pjlink, struct calc_link, jlink);
int i;
IFDEBUG(10)
printf("lnkCalc_map_children(calc@%p)\n", clink);
for (i = 0; i < clink->nArgs; i++) {
struct link *child = &clink->inp[i];
long status = dbJLinkMapChildren(child, rtn, ctx);
@@ -421,6 +434,10 @@ long lnkCalc_map_children(jlink *pjlink, jlink_map_fn rtn, void *ctx)
if (status)
return status;
}
if (clink->out.type == JSON_LINK) {
return dbJLinkMapChildren(&clink->out, rtn, ctx);
}
return 0;
}
@@ -432,9 +449,6 @@ static void lnkCalc_open(struct link *plink)
struct calc_link, jlink);
int i;
IFDEBUG(10)
printf("lnkCalc_open(calc@%p)\n", clink);
for (i = 0; i < clink->nArgs; i++) {
struct link *child = &clink->inp[i];
@@ -442,6 +456,10 @@ static void lnkCalc_open(struct link *plink)
dbJLinkInit(child);
dbLoadLink(child, DBR_DOUBLE, &clink->arg[i]);
}
if (clink->out.type == JSON_LINK) {
dbJLinkInit(&clink->out);
}
}
static void lnkCalc_remove(struct dbLocker *locker, struct link *plink)
@@ -450,15 +468,16 @@ static void lnkCalc_remove(struct dbLocker *locker, struct link *plink)
struct calc_link, jlink);
int i;
IFDEBUG(10)
printf("lnkCalc_remove(calc@%p)\n", clink);
for (i = 0; i < clink->nArgs; i++) {
struct link *child = &clink->inp[i];
dbRemoveLink(locker, child);
}
if (clink->out.type == JSON_LINK) {
dbRemoveLink(locker, &clink->out);
}
free(clink->expr);
free(clink->major);
free(clink->minor);
@@ -477,9 +496,6 @@ static int lnkCalc_isConn(const struct link *plink)
int connected = 1;
int i;
IFDEBUG(10)
printf("lnkCalc_isConn(calc@%p)\n", clink);
for (i = 0; i < clink->nArgs; i++) {
struct link *child = &clink->inp[i];
@@ -488,37 +504,24 @@ static int lnkCalc_isConn(const struct link *plink)
connected = 0;
}
if (clink->out.type == JSON_LINK) {
struct link *child = &clink->out;
if (dbLinkIsVolatile(child) &&
!dbIsLinkConnected(child))
connected = 0;
}
return connected;
}
static int lnkCalc_getDBFtype(const struct link *plink)
{
calc_link *clink = CONTAINER(plink->value.json.jlink,
struct calc_link, jlink);
IFDEBUG(10) {
calc_link *clink = CONTAINER(plink->value.json.jlink,
struct calc_link, jlink);
printf("lnkCalc_getDBFtype(calc@%p)\n", clink);
}
return DBF_DOUBLE;
}
static long lnkCalc_getElements(const struct link *plink, long *nelements)
{
calc_link *clink = CONTAINER(plink->value.json.jlink,
struct calc_link, jlink);
IFDEBUG(10) {
calc_link *clink = CONTAINER(plink->value.json.jlink,
struct calc_link, jlink);
printf("lnkCalc_getElements(calc@%p, (%ld))\n",
clink, *nelements);
}
*nelements = 1;
return 0;
}
@@ -551,23 +554,22 @@ static long lnkCalc_getValue(struct link *plink, short dbrType, void *pbuffer,
long status;
FASTCONVERT conv = dbFastPutConvertRoutine[DBR_DOUBLE][dbrType];
IFDEBUG(10)
printf("lnkCalc_getValue(calc@%p, %d, ...)\n",
clink, dbrType);
/* Any link errors will trigger a LINK/INVALID alarm in the child link */
for (i = 0; i < clink->nArgs; i++) {
struct link *child = &clink->inp[i];
long nReq = 1;
if (i == clink->tinp &&
dbLinkIsConstant(&prec->tsel) &&
prec->tse == epicsTimeEventDeviceTime) {
struct lcvt vt = {&clink->arg[i], &prec->time};
if (i == clink->tinp) {
struct lcvt vt = {&clink->arg[i], &clink->time};
status = dbLinkDoLocked(child, readLocked, &vt);
if (status == S_db_noLSET)
status = readLocked(child, &vt);
if (dbLinkIsConstant(&prec->tsel) &&
prec->tse == epicsTimeEventDeviceTime) {
prec->time = clink->time;
}
}
else
dbGetLink(child, DBR_DOUBLE, &clink->arg[i], NULL, &nReq);
@@ -599,7 +601,7 @@ static long lnkCalc_getValue(struct link *plink, short dbrType, void *pbuffer,
}
}
if (!status && clink->post_minor) {
if (!status && !clink->sevr && clink->post_minor) {
double alval = clink->val;
status = calcPerform(clink->arg, &alval, clink->post_minor);
@@ -613,14 +615,79 @@ static long lnkCalc_getValue(struct link *plink, short dbrType, void *pbuffer,
return status;
}
static long lnkCalc_putValue(struct link *plink, short dbrType,
const void *pbuffer, long nRequest)
{
calc_link *clink = CONTAINER(plink->value.json.jlink,
struct calc_link, jlink);
dbCommon *prec = plink->precord;
int i;
long status;
FASTCONVERT conv = dbFastGetConvertRoutine[dbrType][DBR_DOUBLE];
/* Any link errors will trigger a LINK/INVALID alarm in the child link */
for (i = 0; i < clink->nArgs; i++) {
struct link *child = &clink->inp[i];
long nReq = 1;
if (i == clink->tinp) {
struct lcvt vt = {&clink->arg[i], &clink->time};
status = dbLinkDoLocked(child, readLocked, &vt);
if (status == S_db_noLSET)
status = readLocked(child, &vt);
if (dbLinkIsConstant(&prec->tsel) &&
prec->tse == epicsTimeEventDeviceTime) {
prec->time = clink->time;
}
}
else
dbGetLink(child, DBR_DOUBLE, &clink->arg[i], NULL, &nReq);
}
clink->stat = 0;
clink->sevr = 0;
/* Get the value being output as VAL */
status = conv(pbuffer, &clink->val, NULL);
if (!status && clink->post_expr)
status = calcPerform(clink->arg, &clink->val, clink->post_expr);
if (!status && clink->post_major) {
double alval = clink->val;
status = calcPerform(clink->arg, &alval, clink->post_major);
if (!status && alval) {
clink->stat = LINK_ALARM;
clink->sevr = MAJOR_ALARM;
recGblSetSevr(prec, clink->stat, clink->sevr);
}
}
if (!status && !clink->sevr && clink->post_minor) {
double alval = clink->val;
status = calcPerform(clink->arg, &alval, clink->post_minor);
if (!status && alval) {
clink->stat = LINK_ALARM;
clink->sevr = MINOR_ALARM;
recGblSetSevr(prec, clink->stat, clink->sevr);
}
}
if (!status) {
status = dbPutLink(&clink->out, DBR_DOUBLE, &clink->val, 1);
}
return status;
}
static long lnkCalc_getPrecision(const struct link *plink, short *precision)
{
calc_link *clink = CONTAINER(plink->value.json.jlink,
struct calc_link, jlink);
IFDEBUG(10)
printf("lnkCalc_getPrecision(calc@%p)\n", clink);
*precision = clink->prec;
return 0;
}
@@ -630,9 +697,6 @@ static long lnkCalc_getUnits(const struct link *plink, char *units, int len)
calc_link *clink = CONTAINER(plink->value.json.jlink,
struct calc_link, jlink);
IFDEBUG(10)
printf("lnkCalc_getUnits(calc@%p)\n", clink);
if (clink->units) {
strncpy(units, clink->units, --len);
units[len] = '\0';
@@ -648,9 +712,6 @@ static long lnkCalc_getAlarm(const struct link *plink, epicsEnum16 *status,
calc_link *clink = CONTAINER(plink->value.json.jlink,
struct calc_link, jlink);
IFDEBUG(10)
printf("lnkCalc_getAlarm(calc@%p)\n", clink);
if (status)
*status = clink->stat;
if (severity)
@@ -659,6 +720,19 @@ static long lnkCalc_getAlarm(const struct link *plink, epicsEnum16 *status,
return 0;
}
static long lnkCalc_getTimestamp(const struct link *plink, epicsTimeStamp *pstamp)
{
calc_link *clink = CONTAINER(plink->value.json.jlink,
struct calc_link, jlink);
if (clink->tinp >= 0) {
*pstamp = clink->time;
return 0;
}
return -1;
}
static long doLocked(struct link *plink, dbLinkUserCallback rtn, void *priv)
{
return rtn(plink, priv);
@@ -675,8 +749,8 @@ static lset lnkCalc_lset = {
lnkCalc_getValue,
NULL, NULL, NULL,
lnkCalc_getPrecision, lnkCalc_getUnits,
lnkCalc_getAlarm, NULL,
NULL, NULL,
lnkCalc_getAlarm, lnkCalc_getTimestamp,
lnkCalc_putValue, NULL,
NULL, doLocked
};
@@ -686,6 +760,6 @@ static jlif lnkCalcIf = {
lnkCalc_start_map, lnkCalc_map_key, lnkCalc_end_map,
lnkCalc_start_array, lnkCalc_end_array,
lnkCalc_end_child, lnkCalc_get_lset,
lnkCalc_report, lnkCalc_map_children
lnkCalc_report, lnkCalc_map_children, NULL
};
epicsExportAddress(jlif, lnkCalcIf);
+33 -93
View File
@@ -2,7 +2,7 @@
* Copyright (c) 2016 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.
* in file LICENSE that is included with this distribution.
\*************************************************************************/
/* lnkConst.c */
@@ -22,8 +22,6 @@
#include "epicsExport.h"
#define IFDEBUG(n) if (clink->jlink.debug)
typedef long (*FASTCONVERT)();
typedef struct const_link {
@@ -48,18 +46,23 @@ static lset lnkConst_lset;
static jlink* lnkConst_alloc(short dbfType)
{
const_link *clink = calloc(1, sizeof(*clink));
const_link *clink;
IFDEBUG(10)
printf("lnkConst_alloc()\n");
if (dbfType != DBF_INLINK) {
errlogPrintf("lnkConst: Only works with input links\n");
return NULL;
}
clink = calloc(1, sizeof(*clink));
if (!clink) {
errlogPrintf("lnkConst: calloc() failed.\n");
return NULL;
}
clink->type = s0;
clink->nElems = 0;
clink->value.pmem = NULL;
IFDEBUG(10)
printf("lnkConst_alloc -> const@%p\n", clink);
return &clink->jlink;
}
@@ -67,9 +70,6 @@ static void lnkConst_free(jlink *pjlink)
{
const_link *clink = CONTAINER(pjlink, const_link, jlink);
IFDEBUG(10)
printf("lnkConst_free(const@%p) type=%d\n", pjlink, clink->type);
switch (clink->type) {
int i;
case ac40:
@@ -95,16 +95,13 @@ static jlif_result lnkConst_integer(jlink *pjlink, long long num)
const_link *clink = CONTAINER(pjlink, const_link, jlink);
int newElems = clink->nElems + 1;
IFDEBUG(10)
printf("lnkConst_integer(const@%p, %lld)\n", pjlink, num);
switch (clink->type) {
void *buf;
case s0:
clink->type = si64;
clink->value.scalar_integer = num;
IFDEBUG(12)
if (pjlink->debug)
printf(" si64 := %lld\n", num);
break;
@@ -118,7 +115,7 @@ static jlif_result lnkConst_integer(jlink *pjlink, long long num)
clink->value.pmem = buf;
clink->value.pintegers[clink->nElems] = num;
IFDEBUG(12)
if (pjlink->debug)
printf(" ai64 += %lld\n", num);
break;
@@ -129,7 +126,7 @@ static jlif_result lnkConst_integer(jlink *pjlink, long long num)
clink->value.pmem = buf;
clink->value.pdoubles[clink->nElems] = num;
IFDEBUG(12)
if (pjlink->debug)
printf(" af64 += %lld\n", num);
break;
@@ -146,10 +143,6 @@ static jlif_result lnkConst_integer(jlink *pjlink, long long num)
static jlif_result lnkConst_boolean(jlink *pjlink, int val)
{
const_link *clink = CONTAINER(pjlink, const_link, jlink);
IFDEBUG(10)
printf("lnkConst_boolean(const@%p, %d)\n", pjlink, val);
return lnkConst_integer(pjlink, val);
}
@@ -158,9 +151,6 @@ static jlif_result lnkConst_double(jlink *pjlink, double num)
const_link *clink = CONTAINER(pjlink, const_link, jlink);
int newElems = clink->nElems + 1;
IFDEBUG(10)
printf("lnkConst_double(const@%p, %g)\n", pjlink, num);
switch (clink->type) {
epicsFloat64 *f64buf;
int i;
@@ -212,9 +202,6 @@ static jlif_result lnkConst_string(jlink *pjlink, const char *val, size_t len)
const_link *clink = CONTAINER(pjlink, const_link, jlink);
int newElems = clink->nElems + 1;
IFDEBUG(10)
printf("lnkConst_string(const@%p, \"%.*s\")\n", clink, (int) len, val);
switch (clink->type) {
char **vec, *str;
@@ -262,9 +249,6 @@ static jlif_result lnkConst_start_array(jlink *pjlink)
{
const_link *clink = CONTAINER(pjlink, const_link, jlink);
IFDEBUG(10)
printf("lnkConst_start_array(const@%p)\n", pjlink);
if (clink->type != s0) {
errlogPrintf("lnkConst: Embedded array value\n");
return jlif_stop;
@@ -276,21 +260,11 @@ static jlif_result lnkConst_start_array(jlink *pjlink)
static jlif_result lnkConst_end_array(jlink *pjlink)
{
const_link *clink = CONTAINER(pjlink, const_link, jlink);
IFDEBUG(10)
printf("lnkConst_end_array(const@%p)\n", pjlink);
return jlif_continue;
}
static struct lset* lnkConst_get_lset(const jlink *pjlink)
{
const_link *clink = CONTAINER(pjlink, const_link, jlink);
IFDEBUG(10)
printf("lnkConst_get_lset(const@%p)\n", pjlink);
return &lnkConst_lset;
}
@@ -318,9 +292,6 @@ static void lnkConst_report(const jlink *pjlink, int level, int indent)
};
const char * const dtype = type_names[clink->type & 3];
IFDEBUG(10)
printf("lnkConst_report(const@%p)\n", clink);
if (clink->type > a0) {
const char * const plural = clink->nElems > 1 ? "s" : "";
@@ -382,11 +353,6 @@ static void lnkConst_report(const jlink *pjlink, int level, int indent)
static void lnkConst_remove(struct dbLocker *locker, struct link *plink)
{
const_link *clink = CONTAINER(plink->value.json.jlink, const_link, jlink);
IFDEBUG(10)
printf("lnkConst_remove(const@%p)\n", clink);
lnkConst_free(plink->value.json.jlink);
}
@@ -395,55 +361,51 @@ static long lnkConst_loadScalar(struct link *plink, short dbrType, void *pbuffer
const_link *clink = CONTAINER(plink->value.json.jlink, const_link, jlink);
long status;
IFDEBUG(10)
printf("lnkConst_loadScalar(const@%p, %d, %p)\n",
clink, dbrType, pbuffer);
switch (clink->type) {
case si64:
IFDEBUG(12)
if (clink->jlink.debug)
printf(" si64 %lld\n", clink->value.scalar_integer);
status = dbFastPutConvertRoutine[DBF_INT64][dbrType]
(&clink->value.scalar_integer, pbuffer, NULL);
break;
case sf64:
IFDEBUG(12)
if (clink->jlink.debug)
printf(" sf64 %g\n", clink->value.scalar_double);
status = dbFastPutConvertRoutine[DBF_DOUBLE][dbrType]
(&clink->value.scalar_double, pbuffer, NULL);
break;
case sc40:
IFDEBUG(12)
if (clink->jlink.debug)
printf(" sc40 '%s'\n", clink->value.scalar_string);
status = dbFastPutConvertRoutine[DBF_STRING][dbrType]
(clink->value.scalar_string, pbuffer, NULL);
break;
case ai64:
IFDEBUG(12)
if (clink->jlink.debug)
printf(" ai64 [%lld, ...]\n", clink->value.pintegers[0]);
status = dbFastPutConvertRoutine[DBF_INT64][dbrType]
(clink->value.pintegers, pbuffer, NULL);
break;
case af64:
IFDEBUG(12)
if (clink->jlink.debug)
printf(" af64 [%g, ...]\n", clink->value.pdoubles[0]);
status = dbFastPutConvertRoutine[DBF_DOUBLE][dbrType]
(clink->value.pdoubles, pbuffer, NULL);
break;
case ac40:
IFDEBUG(12)
if (clink->jlink.debug)
printf(" ac40 ['%s', ...]\n", clink->value.pstrings[0]);
status = dbFastPutConvertRoutine[DBF_STRING][dbrType]
(clink->value.pstrings[0], pbuffer, NULL);
break;
default:
IFDEBUG(12)
if (clink->jlink.debug)
printf(" Bad type %d\n", clink->type);
status = S_db_badField;
break;
@@ -458,27 +420,23 @@ static long lnkConst_loadLS(struct link *plink, char *pbuffer, epicsUInt32 size,
const_link *clink = CONTAINER(plink->value.json.jlink, const_link, jlink);
const char *pstr;
IFDEBUG(10)
printf("lnkConst_loadLS(const@%p, %p, %d, %d)\n",
clink, pbuffer, size, *plen);
if(!size) return 0;
switch (clink->type) {
case sc40:
IFDEBUG(12)
if (clink->jlink.debug)
printf(" sc40 '%s'\n", clink->value.scalar_string);
pstr = clink->value.scalar_string;
break;
case ac40:
IFDEBUG(12)
if (clink->jlink.debug)
printf(" ac40 ['%s', ...]\n", clink->value.pstrings[0]);
pstr = clink->value.pstrings[0];
break;
default:
IFDEBUG(12)
if (clink->jlink.debug)
printf(" Bad type %d\n", clink->type);
return S_db_badField;
}
@@ -499,10 +457,6 @@ static long lnkConst_loadArray(struct link *plink, short dbrType, void *pbuffer,
FASTCONVERT conv;
long status;
IFDEBUG(10)
printf("lnkConst_loadArray(const@%p, %d, %p, (%ld))\n",
clink, dbrType, pbuffer, *pnReq);
if (nElems > *pnReq)
nElems = *pnReq;
@@ -510,28 +464,28 @@ static long lnkConst_loadArray(struct link *plink, short dbrType, void *pbuffer,
int i;
case si64:
IFDEBUG(12)
if (clink->jlink.debug)
printf(" si64 %lld\n", clink->value.scalar_integer);
status = dbFastPutConvertRoutine[DBF_INT64][dbrType]
(&clink->value.scalar_integer, pdest, NULL);
break;
case sf64:
IFDEBUG(12)
if (clink->jlink.debug)
printf(" sf64 %g\n", clink->value.scalar_double);
status = dbFastPutConvertRoutine[DBF_DOUBLE][dbrType]
(&clink->value.scalar_double, pdest, NULL);
break;
case sc40:
IFDEBUG(12)
if (clink->jlink.debug)
printf(" sc40 '%s'\n", clink->value.scalar_string);
status = dbFastPutConvertRoutine[DBF_STRING][dbrType]
(clink->value.scalar_string, pbuffer, NULL);
break;
case ai64:
IFDEBUG(12)
if (clink->jlink.debug)
printf(" ai64 [%lld, ...]\n", clink->value.pintegers[0]);
conv = dbFastPutConvertRoutine[DBF_INT64][dbrType];
for (i = 0; i < nElems; i++) {
@@ -542,7 +496,7 @@ static long lnkConst_loadArray(struct link *plink, short dbrType, void *pbuffer,
break;
case af64:
IFDEBUG(12)
if (clink->jlink.debug)
printf(" af64 [%g, ...]\n", clink->value.pdoubles[0]);
conv = dbFastPutConvertRoutine[DBF_DOUBLE][dbrType];
for (i = 0; i < nElems; i++) {
@@ -553,7 +507,7 @@ static long lnkConst_loadArray(struct link *plink, short dbrType, void *pbuffer,
break;
case ac40:
IFDEBUG(12)
if (clink->jlink.debug)
printf(" ac40 ['%s', ...]\n", clink->value.pstrings[0]);
conv = dbFastPutConvertRoutine[DBF_STRING][dbrType];
for (i = 0; i < nElems; i++) {
@@ -564,7 +518,7 @@ static long lnkConst_loadArray(struct link *plink, short dbrType, void *pbuffer,
break;
default:
IFDEBUG(12)
if (clink->jlink.debug)
printf(" Bad type %d\n", clink->type);
status = S_db_badField;
}
@@ -574,12 +528,6 @@ static long lnkConst_loadArray(struct link *plink, short dbrType, void *pbuffer,
static long lnkConst_getNelements(const struct link *plink, long *nelements)
{
const_link *clink = CONTAINER(plink->value.json.jlink, const_link, jlink);
IFDEBUG(10)
printf("lnkConst_getNelements(const@%p, (%ld))\n",
plink->value.json.jlink, *nelements);
*nelements = 0;
return 0;
}
@@ -587,13 +535,6 @@ static long lnkConst_getNelements(const struct link *plink, long *nelements)
static long lnkConst_getValue(struct link *plink, short dbrType, void *pbuffer,
long *pnRequest)
{
const_link *clink = CONTAINER(plink->value.json.jlink, const_link, jlink);
IFDEBUG(10)
printf("lnkConst_getValue(const@%p, %d, %p, ... (%ld))\n",
plink->value.json.jlink, dbrType, pbuffer,
pnRequest ? *pnRequest : 0);
if (pnRequest)
*pnRequest = 0;
return 0;
@@ -626,7 +567,6 @@ static jlif lnkConstIf = {
NULL, NULL, NULL,
lnkConst_start_array, lnkConst_end_array,
NULL, lnkConst_get_lset,
lnkConst_report, NULL
lnkConst_report, NULL, NULL
};
epicsExportAddress(jlif, lnkConstIf);
File diff suppressed because it is too large Load Diff
+230
View File
@@ -0,0 +1,230 @@
/*************************************************************************\
* Copyright (c) 2017 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.
\*************************************************************************/
/* lnkState.c */
/* Usage:
* {state:green}
*/
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include "alarm.h"
#include "dbDefs.h"
#include "errlog.h"
#include "epicsAssert.h"
#include "epicsString.h"
#include "epicsTypes.h"
#include "dbAccessDefs.h"
#include "dbCommon.h"
#include "dbConvertFast.h"
#include "dbLink.h"
#include "dbJLink.h"
#include "dbStaticLib.h"
#include "dbStaticPvt.h"
#include "dbState.h"
#include "recGbl.h"
#include "epicsExport.h"
typedef long (*FASTCONVERT)();
typedef struct state_link {
jlink jlink; /* embedded object */
char *name;
short val;
short invert;
dbStateId state;
} state_link;
static lset lnkState_lset;
/*************************** jlif Routines **************************/
static jlink* lnkState_alloc(short dbfType)
{
state_link *slink;
if (dbfType == DBF_FWDLINK) {
errlogPrintf("lnkState: DBF_FWDLINK not supported\n");
return NULL;
}
slink = calloc(1, sizeof(struct state_link));
if (!slink) {
errlogPrintf("lnkState: calloc() failed.\n");
return NULL;
}
slink->name = NULL;
slink->state = NULL;
slink->invert = 0;
slink->val = 0;
return &slink->jlink;
}
static void lnkState_free(jlink *pjlink)
{
state_link *slink = CONTAINER(pjlink, struct state_link, jlink);
free(slink->name);
free(slink);
}
static jlif_result lnkState_string(jlink *pjlink, const char *val, size_t len)
{
state_link *slink = CONTAINER(pjlink, struct state_link, jlink);
if (len > 1 && val[0] == '!') {
slink->invert = 1;
val++; len--;
}
slink->name = epicsStrnDup(val, len);
return jlif_continue;
}
static struct lset* lnkState_get_lset(const jlink *pjlink)
{
return &lnkState_lset;
}
static void lnkState_report(const jlink *pjlink, int level, int indent)
{
state_link *slink = CONTAINER(pjlink, struct state_link, jlink);
printf("%*s'state': \"%s\" = %s%s\n", indent, "",
slink->name, slink->invert ? "! " : "", slink->val ? "TRUE" : "FALSE");
}
/*************************** lset Routines **************************/
static void lnkState_open(struct link *plink)
{
state_link *slink = CONTAINER(plink->value.json.jlink,
struct state_link, jlink);
slink->state = dbStateCreate(slink->name);
}
static void lnkState_remove(struct dbLocker *locker, struct link *plink)
{
state_link *slink = CONTAINER(plink->value.json.jlink,
struct state_link, jlink);
free(slink->name);
free(slink);
plink->value.json.jlink = NULL;
}
static int lnkState_getDBFtype(const struct link *plink)
{
return DBF_SHORT;
}
static long lnkState_getElements(const struct link *plink, long *nelements)
{
*nelements = 1;
return 0;
}
static long lnkState_getValue(struct link *plink, short dbrType, void *pbuffer,
long *pnRequest)
{
state_link *slink = CONTAINER(plink->value.json.jlink,
struct state_link, jlink);
FASTCONVERT conv = dbFastPutConvertRoutine[DBR_SHORT][dbrType];
slink->val = slink->invert ^ dbStateGet(slink->state);
return conv(&slink->val, pbuffer, NULL);
}
static long lnkState_putValue(struct link *plink, short dbrType,
const void *pbuffer, long nRequest)
{
state_link *slink = CONTAINER(plink->value.json.jlink,
struct state_link, jlink);
short val;
const char *pstr;
if (nRequest == 0)
return 0;
switch(dbrType) {
case DBR_CHAR:
case DBR_UCHAR:
val = !! *(const epicsInt8 *) pbuffer;
break;
case DBR_SHORT:
case DBR_USHORT:
val = !! *(const epicsInt16 *) pbuffer;
break;
case DBR_LONG:
case DBR_ULONG:
val = !! *(const epicsInt32 *) pbuffer;
break;
case DBR_INT64:
case DBR_UINT64:
val = !! *(const epicsInt64 *) pbuffer;
break;
case DBR_FLOAT:
val = !! *(const epicsFloat32 *) pbuffer;
break;
case DBR_DOUBLE:
val = !! *(const epicsFloat64 *) pbuffer;
break;
case DBR_STRING: /* Only "" and "0" are FALSE */
pstr = (const char *) pbuffer;
val = (pstr[0] != 0) && ((pstr[0] != '0') || (pstr[1] != 0));
break;
default:
return S_db_badDbrtype;
}
slink->val = val;
val ^= slink->invert;
(val ? dbStateSet : dbStateClear)(slink->state);
return 0;
}
/************************* Interface Tables *************************/
static lset lnkState_lset = {
0, 0, /* not constant, always connected */
lnkState_open, lnkState_remove,
NULL, NULL, NULL,
NULL, lnkState_getDBFtype, lnkState_getElements,
lnkState_getValue,
NULL, NULL, NULL,
NULL, NULL,
NULL, NULL,
lnkState_putValue, NULL,
NULL, NULL
};
static jlif lnkStateIf = {
"state", lnkState_alloc, lnkState_free,
NULL, NULL, NULL, NULL, lnkState_string,
NULL, NULL, NULL,
NULL, NULL,
NULL, lnkState_get_lset,
lnkState_report, NULL, NULL
};
epicsExportAddress(jlif, lnkStateIf);
+43 -47
View File
@@ -2,7 +2,7 @@
* Copyright (c) 2002 Southeastern Universities Research Association, as
* Operator of Thomas Jefferson National Accelerator Facility.
* EPICS BASE is distributed subject to a Software License Agreement found
* in file LICENSE that is included with this distribution.
* in file LICENSE that is included with this distribution.
\*************************************************************************/
/* recAai.c */
@@ -11,7 +11,7 @@
* Original Author: Dave Barker
*
* C E B A F
*
*
* Continuous Electron Beam Accelerator Facility
* Newport News, Virginia, USA.
*
@@ -106,7 +106,6 @@ static long init_record(struct dbCommon *pcommon, int pass)
{
struct aaiRecord *prec = (struct aaiRecord *)pcommon;
struct aaidset *pdset = (struct aaidset *)(prec->dset);
long status;
/* must have dset defined */
if (!pdset) {
@@ -119,11 +118,7 @@ static long init_record(struct dbCommon *pcommon, int pass)
prec->nelm = 1;
if (prec->ftvl > DBF_ENUM)
prec->ftvl = DBF_UCHAR;
if (prec->nelm == 1) {
prec->nord = 1;
} else {
prec->nord = 0;
}
prec->nord = (prec->nelm == 1);
/* we must call pdset->init_record in pass 0
because it may set prec->bptr which must
@@ -131,8 +126,10 @@ static long init_record(struct dbCommon *pcommon, int pass)
*/
if (pdset->init_record) {
long status = pdset->init_record(prec);
/* init_record may set the bptr to point to the data */
if ((status = pdset->init_record(prec)))
if (status)
return status;
}
if (!prec->bptr) {
@@ -142,7 +139,7 @@ static long init_record(struct dbCommon *pcommon, int pass)
}
return 0;
}
recGblInitSimm(pcommon, &prec->sscn, &prec->oldsimm, &prec->simm, &prec->siml);
/* must have read_aai function defined */
@@ -167,9 +164,10 @@ static long process(struct dbCommon *pcommon)
}
status = readValue(prec); /* read the new value */
if (!pact && prec->pact) return 0;
prec->pact = TRUE;
if (!pact && prec->pact)
return 0;
prec->pact = TRUE;
prec->udf = FALSE;
recGblGetTimeStampSimm(prec, prec->simm, &prec->siol);
@@ -187,17 +185,19 @@ static long special(DBADDR *paddr, int after)
int special_type = paddr->special;
switch(special_type) {
case(SPC_MOD):
case SPC_MOD:
if (dbGetFieldIndex(paddr) == aaiRecordSIMM) {
if (!after)
recGblSaveSimm(prec->sscn, &prec->oldsimm, prec->simm);
else
recGblCheckSimm((dbCommon *)prec, &prec->sscn, prec->oldsimm, prec->simm);
return(0);
recGblCheckSimm((dbCommon *)prec, &prec->sscn, prec->oldsimm,
prec->simm);
return 0;
}
default:
recGblDbaddrError(S_db_badChoice, paddr, "aai: special");
return(S_db_badChoice);
return S_db_badChoice;
}
}
@@ -225,10 +225,14 @@ static long get_array_info(DBADDR *paddr, long *no_elements, long *offset)
static long put_array_info(DBADDR *paddr, long nNew)
{
aaiRecord *prec = (aaiRecord *)paddr->precord;
epicsUInt32 nord = prec->nord;
prec->nord = nNew;
if (prec->nord > prec->nelm)
prec->nord = prec->nelm;
if (nord != prec->nord)
db_post_events(prec, &prec->nord, DBE_VALUE | DBE_LOG);
return 0;
}
@@ -241,7 +245,7 @@ static long get_units(DBADDR *paddr, char *units)
switch (dbGetFieldIndex(paddr)) {
case indexof(VAL):
if (prec->ftvl == DBF_STRING || prec->ftvl == DBF_ENUM)
break;
break;
case indexof(HOPR):
case indexof(LOPR):
strncpy(units,prec->egu,DB_UNITS_SIZE);
@@ -336,48 +340,40 @@ static void monitor(aaiRecord *prec)
static long readValue(aaiRecord *prec)
{
struct aaidset *pdset = (struct aaidset *) prec->dset;
long status = 0;
long status;
if (!prec->pact) {
status = recGblGetSimm((dbCommon *)prec, &prec->sscn, &prec->oldsimm, &prec->simm, &prec->siml);
if (status) return status;
}
/* NB: Device support must post updates to NORD */
switch (prec->simm) {
case menuYesNoNO:
status = pdset->read_aai(prec);
break;
if (prec->pact)
goto do_read;
case menuYesNoYES: {
status = recGblGetSimm((dbCommon *)prec, &prec->sscn, &prec->oldsimm,
&prec->simm, &prec->siml);
if (status)
return status;
if (prec->simm == menuYesNoYES) {
recGblSetSevr(prec, SIMM_ALARM, prec->sims);
if (prec->pact || (prec->sdly < 0.)) {
/* Device suport is responsible for buffer
which might be read-only so we may not be
allowed to call dbGetLink on it.
Maybe also device support has an advanced
simulation mode.
Thus call device now.
Reading through SIOL is handled in Soft Channel Device Support
*/
status = pdset->read_aai(prec);
prec->pact = FALSE;
} else { /* !prec->pact && delay >= 0. */
if (prec->sdly >= 0) {
CALLBACK *pvt = prec->simpvt;
if (!pvt) {
pvt = calloc(1, sizeof(CALLBACK)); /* very lazy allocation of callback structure */
if (!pvt) { /* very lazy allocation of callback structure */
pvt = calloc(1, sizeof(CALLBACK));
prec->simpvt = pvt;
}
if (pvt) callbackRequestProcessCallbackDelayed(pvt, prec->prio, prec, prec->sdly);
if (pvt)
callbackRequestProcessCallbackDelayed(pvt, prec->prio, prec,
prec->sdly);
prec->pact = TRUE;
return 0;
}
break;
}
default:
else if (prec->simm != menuYesNoNO) {
recGblSetSevr(prec, SOFT_ALARM, INVALID_ALARM);
status = -1;
return -1;
}
return status;
do_read:
return pdset->read_aai(prec);
}
+7 -3
View File
@@ -2,7 +2,7 @@
* Copyright (c) 2002 Southeastern Universities Research Association, as
* Operator of Thomas Jefferson National Accelerator Facility.
* EPICS BASE is distributed subject to a Software License Agreement found
* in file LICENSE that is included with this distribution.
* in file LICENSE that is included with this distribution.
\*************************************************************************/
/* recAao.c */
@@ -11,7 +11,7 @@
* Original Author: Dave Barker
*
* C E B A F
*
*
* Continuous Electron Beam Accelerator Facility
* Newport News, Virginia, USA.
*
@@ -225,10 +225,14 @@ static long get_array_info(DBADDR *paddr, long *no_elements, long *offset)
static long put_array_info(DBADDR *paddr, long nNew)
{
aaoRecord *prec = (aaoRecord *)paddr->precord;
epicsUInt32 nord = prec->nord;
prec->nord = nNew;
if (prec->nord > prec->nelm)
prec->nord = prec->nelm;
if (nord != prec->nord)
db_post_events(prec, &prec->nord, DBE_VALUE | DBE_LOG);
return 0;
}
@@ -241,7 +245,7 @@ static long get_units(DBADDR *paddr, char *units)
switch (dbGetFieldIndex(paddr)) {
case indexof(VAL):
if (prec->ftvl == DBF_STRING || prec->ftvl == DBF_ENUM)
break;
break;
case indexof(HOPR):
case indexof(LOPR):
strncpy(units,prec->egu,DB_UNITS_SIZE);
-134
View File
@@ -1,134 +0,0 @@
#*************************************************************************
# Copyright (c) 2002 The University of Chicago, 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.
#*************************************************************************
recordtype(bi) {
include "dbCommon.dbd"
field(INP,DBF_INLINK) {
prompt("Input Specification")
promptgroup("40 - Input")
interest(1)
}
field(VAL,DBF_ENUM) {
prompt("Current Value")
promptgroup("40 - Input")
asl(ASL0)
pp(TRUE)
}
field(ZSV,DBF_MENU) {
prompt("Zero Error Severity")
promptgroup("70 - Alarm")
pp(TRUE)
interest(1)
menu(menuAlarmSevr)
}
field(OSV,DBF_MENU) {
prompt("One Error Severity")
promptgroup("70 - Alarm")
pp(TRUE)
interest(1)
menu(menuAlarmSevr)
}
field(COSV,DBF_MENU) {
prompt("Change of State Svr")
promptgroup("70 - Alarm")
pp(TRUE)
interest(1)
menu(menuAlarmSevr)
}
field(ZNAM,DBF_STRING) {
prompt("Zero Name")
promptgroup("80 - Display")
pp(TRUE)
interest(1)
size(26)
prop(YES)
}
field(ONAM,DBF_STRING) {
prompt("One Name")
promptgroup("80 - Display")
pp(TRUE)
interest(1)
size(26)
prop(YES)
}
field(RVAL,DBF_ULONG) {
prompt("Raw Value")
pp(TRUE)
}
field(ORAW,DBF_ULONG) {
prompt("prev Raw Value")
special(SPC_NOMOD)
interest(3)
}
field(MASK,DBF_ULONG) {
prompt("Hardware Mask")
special(SPC_NOMOD)
interest(1)
}
field(LALM,DBF_USHORT) {
prompt("Last Value Alarmed")
special(SPC_NOMOD)
interest(3)
}
field(MLST,DBF_USHORT) {
prompt("Last Value Monitored")
special(SPC_NOMOD)
interest(3)
}
field(SIOL,DBF_INLINK) {
prompt("Simulation Input Link")
promptgroup("90 - Simulate")
interest(1)
}
field(SVAL,DBF_ULONG) {
prompt("Simulation Value")
}
field(SIML,DBF_INLINK) {
prompt("Simulation Mode Link")
promptgroup("90 - Simulate")
interest(1)
}
field(SIMM,DBF_MENU) {
prompt("Simulation Mode")
special(SPC_MOD)
interest(1)
menu(menuSimm)
}
field(SIMS,DBF_MENU) {
prompt("Simulation Mode Severity")
promptgroup("90 - Simulate")
interest(2)
menu(menuAlarmSevr)
}
field(OLDSIMM,DBF_MENU) {
prompt("Prev. Simulation Mode")
special(SPC_NOMOD)
interest(4)
menu(menuSimm)
}
field(SSCN,DBF_MENU) {
prompt("Sim. Mode Scan")
promptgroup("90 - Simulate")
interest(1)
menu(menuScan)
initial("65535")
}
field(SDLY,DBF_DOUBLE) {
prompt("Sim. Mode Async Delay")
promptgroup("90 - Simulate")
interest(2)
initial("-1.0")
}
%#include "callback.h"
field(SIMPVT,DBF_NOACCESS) {
prompt("Sim. Mode Private")
special(SPC_NOMOD)
interest(4)
extra("CALLBACK *simpvt")
}
}
@@ -0,0 +1,491 @@
#*************************************************************************
# Copyright (c) 2002 The University of Chicago, 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 Binary Input Record (bi)
This record type is normally used to obtain a binary value of 0 or 1. Most
device support modules obtain values from hardware and place the value in
RVAL. For these devices, record processing sets VAL = (0,1) if RVAL is (0,
not 0). Device support modules may optionally read a value directly from
VAL.
Soft device modules are provided to obtain input via database or channel
access links via dbPutField or dbPutLink requests. Two soft device support
modules are provided: C<Soft Channel> and C<Raw Soft Channel>. The first
allows VAL to be an arbitrary unsigned short integer. The second reads the
value into RVAL just like normal hardware modules.
=head2 Parameter Fields
The binary input's fields fall into the following categories:
=over
=item *
scan Parameters
=item *
read and convert parameters
=item *
operator display parameters
=item *
alarm parameters
=item *
run-time parameters
=back
=recordtype bi
=cut
recordtype(bi) {
=head3 Scan Parameters
The binary input record has the standard fields for specifying under what
circumstances the record will be processed. These fields are listed in
L<Scan Fields>. In addition, L<Scanning Specification> explains how these
fields are used. Note that I/O event scanning is only supported for those
card types that interrupt.
=fields SCAN
=head3 Read and Convert Parameters
The read and convert fields determine where the binary input gets its
input from and how to convert the raw signal to engineering units. The INP
field contains the address from where device support retrieves the value.
If the binary input record gets its value from hardware, the address of the
card must be entered in the INP field, and the name of the device support
module must be entered in the DTYP field. See L<Address Specification> for
information on the format of the hardware address. Be aware that the format
differs between types of cards. You can see a list of device support
modules currently supported at the user's local site by using C<dbst>
utility (R3.13).
For records that specify C<Soft Channel> or C<Raw Soft Channel> device
support routines, the INP field can be a channel or a database link, or a
constant. If a constant, VAL can be changed directly by dbPuts. See
L<Address Specification> for information on the format of database and
channel access addresses. Also, see L<Device Support for Soft Records> in
this chapter for information on soft device support.
If the record gets its values from hardware or uses the C<Raw Soft Channel>
device support, the device support routines place the value in the RVAL
field which is then converted using the process described in the next
section.
=fields INP, DTYP, ZNAM, ONAM, RVAL, VAL
=head3 Conversion Fields
The VAL field is set equal to (0,1) if the RVAL field is (0, not 0), unless
the device support module reads a value directly into VAL or the
C<Soft Channel> device support is used. The value can also be fetched as one of
the strings specified in the ZNAM or ONAM fields. The ZNAM field has a
string that corresponds to the 0 state, so when the value is fetched as
this string, C<put_enum_str> will return a 0. The ONAM field hold the
string that corresponds to the 1 state, so when the value is fetched as
this string, C<put_enum_str> returns a 1.
=fields ZNAM, ONAM
=head3 Operator Display Parameters
These parameters are used to present meaningful data to the operator. The
C<get_enum_str> record support routine can retrieve the state string
corresponding to the VAL's state. If the value is 1, C<get_enum_str> will
return the string in the ONAM field; and if 0, C<get_enum_str> will return
the ZNAM string.
See L<Fields Common to All Record Types> for more on the record name (NAME)
and description (DESC) fields.
=fields ZNAM, ONAM, NAME, DESC
=head3 Alarm Parameters
These parameters are used to determine if the binary input is in alarm
condition and to determine the severity of that condition. The possible
alarm conditions for binary inputs are the SCAN, READ state alarms, and the
change of state alarm. The SCAN and READ alarms are called by the device
supprt routines.
The user can choose the severity of each state in the ZSV and OSV fields.
The possible values for these fields are C<NO_ALARM>, C<MINOR>, and
C<MAJOR>. The ZSV field holds the severity for the zero state; OSV, for
the one state. COSV causes an alarm whenever the state changes between
0 and 1 and the severity is configured as MINOR or MAJOR.
See L<Alarm Specification> for a complete explanation of the discrete alarm
states. L<Alarm Fields> lists other fields related to alarms that are
common to all record types.
=fields ZSV, OSV, COSV
=head3 Run-time Parameters and Simulation Mode Parameters
These parameters are used by the run-time code for processing the binary
input. They are not configured using a database configuration tool.
ORAW is used to determine if monitors should be triggered for RVAL at the same
time they are triggered for VAL.
MASK is given a value by ithe device support routines. This value is used to
manipulate the record's value, but is only the concern of the hardware device
support routines.
The LALM fields holds the value of the last occurence of the change of
state alarm. It is used to implement the change of state alarm, and thus
only has meaning if COSV is MAJOR or MINOR.
The MSLT field is used by the C<process> record support routine to
determine if archive and value change monitors are invoked. They are if MSLT
is not equal to VAL.
=fields ORAW, MASK, LALM, MLST
The following fields are used to operate the binary input in simulation
mode. See L<Fields Common to Many Record Types> for more information on
these fields.
=fields SIOL, SVAL, SIML, SIMM, SIMS
=cut
include "dbCommon.dbd"
field(INP,DBF_INLINK) {
prompt("Input Specification")
promptgroup("40 - Input")
interest(1)
}
field(VAL,DBF_ENUM) {
prompt("Current Value")
promptgroup("40 - Input")
asl(ASL0)
pp(TRUE)
}
field(ZSV,DBF_MENU) {
prompt("Zero Error Severity")
promptgroup("70 - Alarm")
pp(TRUE)
interest(1)
menu(menuAlarmSevr)
}
field(OSV,DBF_MENU) {
prompt("One Error Severity")
promptgroup("70 - Alarm")
pp(TRUE)
interest(1)
menu(menuAlarmSevr)
}
field(COSV,DBF_MENU) {
prompt("Change of State Svr")
promptgroup("70 - Alarm")
pp(TRUE)
interest(1)
menu(menuAlarmSevr)
}
field(ZNAM,DBF_STRING) {
prompt("Zero Name")
promptgroup("80 - Display")
pp(TRUE)
interest(1)
size(26)
prop(YES)
}
field(ONAM,DBF_STRING) {
prompt("One Name")
promptgroup("80 - Display")
pp(TRUE)
interest(1)
size(26)
prop(YES)
}
field(RVAL,DBF_ULONG) {
prompt("Raw Value")
pp(TRUE)
}
field(ORAW,DBF_ULONG) {
prompt("prev Raw Value")
special(SPC_NOMOD)
interest(3)
}
field(MASK,DBF_ULONG) {
prompt("Hardware Mask")
special(SPC_NOMOD)
interest(1)
}
field(LALM,DBF_USHORT) {
prompt("Last Value Alarmed")
special(SPC_NOMOD)
interest(3)
}
field(MLST,DBF_USHORT) {
prompt("Last Value Monitored")
special(SPC_NOMOD)
interest(3)
}
field(SIOL,DBF_INLINK) {
prompt("Simulation Input Link")
promptgroup("90 - Simulate")
interest(1)
}
field(SVAL,DBF_ULONG) {
prompt("Simulation Value")
}
field(SIML,DBF_INLINK) {
prompt("Simulation Mode Link")
promptgroup("90 - Simulate")
interest(1)
}
field(SIMM,DBF_MENU) {
prompt("Simulation Mode")
special(SPC_MOD)
interest(1)
menu(menuSimm)
}
field(SIMS,DBF_MENU) {
prompt("Simulation Mode Severity")
promptgroup("90 - Simulate")
interest(2)
menu(menuAlarmSevr)
}
field(OLDSIMM,DBF_MENU) {
prompt("Prev. Simulation Mode")
special(SPC_NOMOD)
interest(4)
menu(menuSimm)
}
field(SSCN,DBF_MENU) {
prompt("Sim. Mode Scan")
promptgroup("90 - Simulate")
interest(1)
menu(menuScan)
initial("65535")
}
field(SDLY,DBF_DOUBLE) {
prompt("Sim. Mode Async Delay")
promptgroup("90 - Simulate")
interest(2)
initial("-1.0")
}
%#include "callback.h"
field(SIMPVT,DBF_NOACCESS) {
prompt("Sim. Mode Private")
special(SPC_NOMOD)
interest(4)
extra("CALLBACK *simpvt")
}
=head2 Record Support
=head3 Record Support Routines
=head2 C<init_record>
This routine initializes SIMM with the value of SIML if SIML type is a
CONSTANT link or creates a channel access link if SIML type is PV_LINK.
SVAL is likewise initialized if SIOL is a CONSTANT or PV_LINK.
This routine next checks to see that device support is available and a
device support routine is defined. If neither exist, an error is issued and
processing is terminated.
If device support includes C<init_record>, it is called.
=head2 C<process>
See next section.
=head2 C<get_value>
Fills in the values of struct valueDes so that they refer to VAL.
=head2 C<get_enum_str>
Retrieves ASCII string corresponding to VAL.
=head2 C<get_enum_strs>
Retrieves ASCII strings for ZNAM and ONAM.
=head2 C<put_enum_str>
Check if string matches ZNAM or ONAM, and if it does, sets VAL.
=head2 Record Processing
Routine process implements the following algorithm:
=over 1
=item 1.
Check to see that the appropriate device support module exists. If it
doesn't, an error message is issued and processing is terminated with
the PACT field still set to TRUE. This ensures that processes will no
longer be called for this record. Thus error storms will not occur.
=item 2.
C<readValue> is called. See L<Input Records> for details.
=item 3.
If PACT has been changed to TRUE, the device support read routine has
started but has not completed reading a new input value. In this case, the
processing routine merely returns, leaving PACT TRUE.
=item 4.
Convert.
=back
=over 1
=item *
status = read_bi
=item *
PACT = TRUE
=item *
TIME = tslocaltime
=item *
if status is 0, then set VAL=(0,1) if RVAL is (0, not 0) and UDF = False.
=item *
if status is 2, set status = 0
=back
=over 1
=item 5.
Check alarms: This routine checks to see if the new VAL causes the alarm
status and severity to change. If so, NSEV, NSTA and LALM are set. Note
that if VAL is greater than 1, no checking is performed.
=item 6.
Check if monitors should be invoked:
=back
=over 1
=item *
Alarm monitors are invoked if the alarm status or severity has changed.
=item *
Archive and value change monitors are invoked if MSLT is not equal to VAL.
=item *
Monitors for RVAL are checked whenever other monitors are invoked.
=item *
NSEV and NSTA are reset to 0.
=back
=over 1
=item 7.
Scan forward link if necessary, set PACT FALSE, and return.
=back
=head2 Device Support
=head3 Fields of Interest to Device Support
Each binary input record must have an associated set of device support
routines. The primary resposibility of the device support routines is to
obtain a new raw input value whenever C<read_bi> is called. The device
support routines are primarily interested in the following fields:
=fields PACT, DPVT, UDF, NSEV, NSTA, VAL, INP, RVAL, MASK
=head3 Device Support routines
Device support consists of the following routines:
=head2 C<report(FILE fp, paddr)>
Not currently used.
=head2 C<init()>
This routine is called once during IOC initialization.
=head2 C<init_record(precord)>
This routine is optional. If provided, it is called by the record support
C<init_record> routine.
=head2 C<get_ioint_info(int cmd, struct dbCommon *precord, IOSCANPVT *ppvt)>
This routine is called by the C<ioEventScan> system each time the record is
added or deleted from an I/O event scan list. C<cmd> has the value (0,1) if
the record is being (added to, deleted from) and I/O event list. It must be
provided for any device type that can use the ioEvent scanner.
=head2 C<read_bi(precord)>
This routine must provide a new input value. It returns the following
values:
=over
=item 0:
Success. A new raw value is placed in RVAL. The record support module
forces VAL to be (0,1) if RVAL is (0, not 0).
=item 2:
Success, but don't modify VAL.
=item Other:
Error.
=back
=head3 Device Support for Soft Records
Two soft device support modules, Soft Channel and Raw Soft Channel, are
provided for input records not related to actual hardware devices. The INP
link type must be either CONSTANT, DB_LINK, or CA_LINK.
=head3 Soft Channel
C<read_bi> always returns a value of 2, which means that no conversion is
performed.
If the INP link type is CONSTANT, then the constant value is stored in VAL
by C<init_record>, and the UDF is set to FALSE. VAL can be changed via
C<dbPut> requests. If the INP link type is PV_LINK, the C<dbCaAddInlink> is
called by C<init_record>.
C<read_bi> calls C<recGbleGetLinkValue> to read the current value of VAL.
See L<Soft Input> for details.
If the return status of C<recGblGetLinkValue> is zero, then C<read_bi> sets
UDF to FALSE. The status of C<recGblGetLinkValue> is returned.
=head3 Raw Soft Channel
This module is like the previous except that values are read into RVAL.
C<read_bi> returns a value of 0. Thus the record processing routine will
force VAL to be 0 or 1.
=cut
}
-182
View File
@@ -1,182 +0,0 @@
#*************************************************************************
# Copyright (c) 2002 The University of Chicago, 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.
#*************************************************************************
recordtype(bo) {
include "dbCommon.dbd"
field(VAL,DBF_ENUM) {
prompt("Current Value")
promptgroup("50 - Output")
asl(ASL0)
pp(TRUE)
}
field(OMSL,DBF_MENU) {
prompt("Output Mode Select")
promptgroup("50 - Output")
interest(1)
menu(menuOmsl)
}
field(DOL,DBF_INLINK) {
prompt("Desired Output Loc")
promptgroup("40 - Input")
interest(1)
}
field(OUT,DBF_OUTLINK) {
prompt("Output Specification")
promptgroup("50 - Output")
interest(1)
}
field(HIGH,DBF_DOUBLE) {
prompt("Seconds to Hold High")
promptgroup("30 - Action")
interest(1)
}
field(ZNAM,DBF_STRING) {
prompt("Zero Name")
promptgroup("80 - Display")
pp(TRUE)
interest(1)
size(26)
prop(YES)
}
field(ONAM,DBF_STRING) {
prompt("One Name")
promptgroup("80 - Display")
pp(TRUE)
interest(1)
size(26)
prop(YES)
}
field(RVAL,DBF_ULONG) {
prompt("Raw Value")
pp(TRUE)
}
field(ORAW,DBF_ULONG) {
prompt("prev Raw Value")
special(SPC_NOMOD)
interest(3)
}
field(MASK,DBF_ULONG) {
prompt("Hardware Mask")
special(SPC_NOMOD)
interest(1)
}
field(RPVT,DBF_NOACCESS) {
prompt("Record Private")
special(SPC_NOMOD)
interest(4)
extra("void * rpvt")
}
field(WDPT,DBF_NOACCESS) {
prompt("Watch Dog Timer ID")
special(SPC_NOMOD)
interest(4)
extra("void * wdpt")
}
field(ZSV,DBF_MENU) {
prompt("Zero Error Severity")
promptgroup("70 - Alarm")
pp(TRUE)
interest(1)
menu(menuAlarmSevr)
}
field(OSV,DBF_MENU) {
prompt("One Error Severity")
promptgroup("70 - Alarm")
pp(TRUE)
interest(1)
menu(menuAlarmSevr)
}
field(COSV,DBF_MENU) {
prompt("Change of State Sevr")
promptgroup("70 - Alarm")
pp(TRUE)
interest(1)
menu(menuAlarmSevr)
}
field(RBV,DBF_ULONG) {
prompt("Readback Value")
special(SPC_NOMOD)
}
field(ORBV,DBF_ULONG) {
prompt("Prev Readback Value")
special(SPC_NOMOD)
interest(3)
}
field(MLST,DBF_USHORT) {
prompt("Last Value Monitored")
special(SPC_NOMOD)
interest(3)
}
field(LALM,DBF_USHORT) {
prompt("Last Value Alarmed")
special(SPC_NOMOD)
interest(3)
}
field(SIOL,DBF_OUTLINK) {
prompt("Simulation Output Link")
promptgroup("90 - Simulate")
interest(1)
}
field(SIML,DBF_INLINK) {
prompt("Simulation Mode Link")
promptgroup("90 - Simulate")
interest(1)
}
field(SIMM,DBF_MENU) {
prompt("Simulation Mode")
special(SPC_MOD)
interest(1)
menu(menuYesNo)
}
field(SIMS,DBF_MENU) {
prompt("Simulation Mode Severity")
promptgroup("90 - Simulate")
interest(2)
menu(menuAlarmSevr)
}
field(OLDSIMM,DBF_MENU) {
prompt("Prev. Simulation Mode")
special(SPC_NOMOD)
interest(4)
menu(menuSimm)
}
field(SSCN,DBF_MENU) {
prompt("Sim. Mode Scan")
promptgroup("90 - Simulate")
interest(1)
menu(menuScan)
initial("65535")
}
field(SDLY,DBF_DOUBLE) {
prompt("Sim. Mode Async Delay")
promptgroup("90 - Simulate")
interest(2)
initial("-1.0")
}
%#include "callback.h"
field(SIMPVT,DBF_NOACCESS) {
prompt("Sim. Mode Private")
special(SPC_NOMOD)
interest(4)
extra("CALLBACK *simpvt")
}
field(IVOA,DBF_MENU) {
prompt("INVALID outpt action")
promptgroup("50 - Output")
interest(2)
menu(menuIvoa)
}
field(IVOV,DBF_USHORT) {
prompt("INVALID output value")
promptgroup("50 - Output")
interest(2)
}
}
variable(boHIGHprecision, int)
variable(boHIGHlimit, double)
@@ -0,0 +1,611 @@
#*************************************************************************
# Copyright (c) 2002 The University of Chicago, 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 Binary Output Record (bo)
The normal use for this record type is to store a simple bit (0 or 1) value
to be sent to a Digital Output module. It can also be used to write binary
values into other records via database or channel access links. This record
can implement both latched and momentary binary outputs depending on how
the HIGH field is configured.
=head2 Parameter Fields
The binary output's fields fall into the following categories:
=over 1
=item *
scan parameters
=item *
convert and write parameters
=item *
operator display parameters
=item *
alarm parameters
=item *
run-time parameters
=back
=recordtype bo
=cut
recordtype(bo) {
=head3 Scan Parameters
The binary output record has the standard fields for specifying under what
circumstances the record will be processed. The fields are listed in
L<Scan Fields>. In addition, L<Scanning Specification> explains how these
fields are used. Note that I/O event scanning is only supported for those card
types that interrupt.
=fields SCAN
=head3 Desired Output Parameters
The binary output record must specify where its desired output originates.
The desired output needs to be in engineering units.
The first field that determines where the desired output originates is the
output mode select (OMSL) field, which can have two possible values:
C<losed_loop> or C<supervisory>. If C<supervisory> is specified, the value
in the VAL field can be set externally via dbPuts at run-time. If
C<closed_loop> is specified, the VAL field's value is obtained from the
address specified in the desired output location (DOL) field which can be a
database link, a channel access link, or a constant. To achieve continuous
control, a database link to a control algorithm record should be entered in
the DOL field.
L<Address Specification> presents more information on database addresses
and links. L<Scanning Specification> explaines the effect of database
linkage on scanning.
=fields DOL, OMSL
=head3 Convert and Write Parameters
These parameters are used to determine where the binary output writes to
and how to convert the engineering units to a raw signal. After VAL is set
and forced to be either 1 or 0, as the result of either a dbPut or a new
value being retrieved from the link in the DOL field, then what happens
next depends on which device support routine is used and how the HIGH field
is configured.
If the C<Soft Channel> device support routine is specified, then the device
support routine writes the VAL field's value to the address specified in
the OUT field. Otherwise, RVAL is the value written by the device support
routines after being converted.
If VAL is equal to 0, then the record processing routine sets RVAL equal to
zero. When VAL is not equal to 0, then RVAL is set equal to the value
contained in the MASK field. (MASK is set by the device support routines
and is of no concern to the user.) Also, when VAL is not 0 and after RVAL is
set equal to MASK, the record processing routine checks to see if the HIGH
field is greater than 0. If it is, then the routine will process the record
again with VAL set to 0 after the number of seconds specified by HIGH.
Thus, HIGH implements a momentary output which changes the state of the
device back to 0 after I<N> number of seconds.
=fields DTYP, OUT, VAL, RVAL, HIGH, ZNAM, ONAM
=head3 Conversion Parameters
The ZNAM field has the string that corresponds to the 0 state, and the ONAM
field holds the string that corresponds to the 1 state. These fields, other
than being used to tell the operator what each state represents, are used
to perform conversions if the value fetched by DOL is a string. If it is,
VAL is set to the state which corresponds to that string. For instance, if the
value fetched is the string "Off" and the ZNAM string is "Off," then VAL is
set to 0.
After VAL is set, if VAL is equal to 0, then the record processing routine
sets RVAL equal to zero. When VAL is not equal to 0, then RVAL is set equal
to the value contained in the MASK field. (Mask is set by the device
support routines and is of no concern to the user.) Also when VAL is equal
to 1 and after RVAL is set equal to MASK, the record processing routine checks
to see if the HIGH field is greater than 0. If it is, then the routine
processes the record again with VAL=0 after the number of seconds specified
by HIGH. Thus, HIGH implements a latched output which changes the state of
the device or link to 1, then changes it back to 0 after I<N> number of seconds.
=fields ZNAM, ONAM, HIGH
=head3 Output Specification
The OUT field specifies where the binary output record writes its output.
It must specify the address of an I/O card if the record sends its output
to hardware, and the DTYP field must contain the corresponding device
support module. Be aware that the address format differs according to the
I/O bus used. See L<Address Specification> for information on the format of
hardware addresses. You can see a list of device support modules currently
supported at the user's local site by using the C<dbst> utility in R3.13.
Otherwise, if the record is configured to use the soft device support
modules, then it can be either a database link, a channel access link, or a
constant. Be aware that nothing will be written when OUT is a constant. See
L<Address Specification> for information on the format of the database and
channel access addresses. Also, see L<Device Support For Soft Records> in
this chapter for more on output to other records.
=head3 Operator Display Parameters
These parameters are used to present meaningful data to the operator, The
C<get_enum_str> record support routine can retrieve the state string
corresponding to the VAL's state. So, if the value is 1, C<get_enum_str>
will return the string in the ONAM field: and if 0, C<get_enum_str> will
return the ZNAM string.
See L<Fields Common to All Record Types> for more on the record name (NAME)
and description (DESC) fields.
=fields ZNAM, ONAM, NAME, DESC
=head3 Alarm Parameters
These parameters are used to determine the binary output's alarm condition
and to determine the severity of that condition. The possible alarm
conditions for binary outputs are the SCAN, READ, INVALID and state alarms.
The user can configure the state alarm conditions using these fields.
The possible values for these fields are C<NO_ALARM>, C<MINOR>, and
C<MAJOR>. The ZSV holds the severity for the zero state; OSV for the one
state. COSV is used to cause an alarm whenever the state changes between
states (0-1, 1-0) and its severity is configured as MINOR or MAJOR.
See L<Invalid Alarm Output Action> for more information on the IVOA and
IVOV fields. L<Alarm Fields> lists other fields related to alarms that are
common to all record types.
=fields ZSV, OSV, COSV, IVOA, IVOV
=head3 Run-Time and Simulation Mode Parameters
These parameters are used by the run-time code for processiong the binary
output. They are not configurable using a configuration tool. They
represent the current state of the binary output.
ORAW is used to determine if monitors should be triggered for RVAL at the
same time they are triggered for VAL.
MASK is given a value by the device support routines and should not concern
the user.
The RBV field is also set by device support. It is the actual read back
value obtained from the hardware itself or from the associated device
driver.
The ORBV field is used to decide if monitors should be triggered
for RBV at the same time monitors are triggered for changes in VAL.
The LALM field holds the value of the last occurrence of the change of
state alarm. It is used to implement the change of state alarm, and thus
only has meaning if COSV is MINOR or MAJOR.
The MLST is used by the C<process> record support routine to determine if
archive and value change monitors are invoked. They are if MLST is not
equal to VAL.
The WPDT field is a private field for honoring seconds to hold HIGH.
=fields ORAW, MASK, RBV, ORBV, LALM, MLST, RPVT, WDPT
The following fields are used to operate the binary output in the
simulation mode. See L<Fields Common to Many Record Types> for more
information on these fields.
=fields SIOL, SIML, SIMM, SIMS
=cut
include "dbCommon.dbd"
field(VAL,DBF_ENUM) {
prompt("Current Value")
promptgroup("50 - Output")
asl(ASL0)
pp(TRUE)
}
field(OMSL,DBF_MENU) {
prompt("Output Mode Select")
promptgroup("50 - Output")
interest(1)
menu(menuOmsl)
}
field(DOL,DBF_INLINK) {
prompt("Desired Output Loc")
promptgroup("40 - Input")
interest(1)
}
field(OUT,DBF_OUTLINK) {
prompt("Output Specification")
promptgroup("50 - Output")
interest(1)
}
field(HIGH,DBF_DOUBLE) {
prompt("Seconds to Hold High")
promptgroup("30 - Action")
interest(1)
}
field(ZNAM,DBF_STRING) {
prompt("Zero Name")
promptgroup("80 - Display")
pp(TRUE)
interest(1)
size(26)
prop(YES)
}
field(ONAM,DBF_STRING) {
prompt("One Name")
promptgroup("80 - Display")
pp(TRUE)
interest(1)
size(26)
prop(YES)
}
field(RVAL,DBF_ULONG) {
prompt("Raw Value")
pp(TRUE)
}
field(ORAW,DBF_ULONG) {
prompt("prev Raw Value")
special(SPC_NOMOD)
interest(3)
}
field(MASK,DBF_ULONG) {
prompt("Hardware Mask")
special(SPC_NOMOD)
interest(1)
}
field(RPVT,DBF_NOACCESS) {
prompt("Record Private")
special(SPC_NOMOD)
interest(4)
extra("void * rpvt")
}
field(WDPT,DBF_NOACCESS) {
prompt("Watch Dog Timer ID")
special(SPC_NOMOD)
interest(4)
extra("void * wdpt")
}
field(ZSV,DBF_MENU) {
prompt("Zero Error Severity")
promptgroup("70 - Alarm")
pp(TRUE)
interest(1)
menu(menuAlarmSevr)
}
field(OSV,DBF_MENU) {
prompt("One Error Severity")
promptgroup("70 - Alarm")
pp(TRUE)
interest(1)
menu(menuAlarmSevr)
}
field(COSV,DBF_MENU) {
prompt("Change of State Sevr")
promptgroup("70 - Alarm")
pp(TRUE)
interest(1)
menu(menuAlarmSevr)
}
field(RBV,DBF_ULONG) {
prompt("Readback Value")
special(SPC_NOMOD)
}
field(ORBV,DBF_ULONG) {
prompt("Prev Readback Value")
special(SPC_NOMOD)
interest(3)
}
field(MLST,DBF_USHORT) {
prompt("Last Value Monitored")
special(SPC_NOMOD)
interest(3)
}
field(LALM,DBF_USHORT) {
prompt("Last Value Alarmed")
special(SPC_NOMOD)
interest(3)
}
field(SIOL,DBF_OUTLINK) {
prompt("Simulation Output Link")
promptgroup("90 - Simulate")
interest(1)
}
field(SIML,DBF_INLINK) {
prompt("Simulation Mode Link")
promptgroup("90 - Simulate")
interest(1)
}
field(SIMM,DBF_MENU) {
prompt("Simulation Mode")
special(SPC_MOD)
interest(1)
menu(menuYesNo)
}
field(SIMS,DBF_MENU) {
prompt("Simulation Mode Severity")
promptgroup("90 - Simulate")
interest(2)
menu(menuAlarmSevr)
}
field(OLDSIMM,DBF_MENU) {
prompt("Prev. Simulation Mode")
special(SPC_NOMOD)
interest(4)
menu(menuSimm)
}
field(SSCN,DBF_MENU) {
prompt("Sim. Mode Scan")
promptgroup("90 - Simulate")
interest(1)
menu(menuScan)
initial("65535")
}
field(SDLY,DBF_DOUBLE) {
prompt("Sim. Mode Async Delay")
promptgroup("90 - Simulate")
interest(2)
initial("-1.0")
}
%#include "callback.h"
field(SIMPVT,DBF_NOACCESS) {
prompt("Sim. Mode Private")
special(SPC_NOMOD)
interest(4)
extra("CALLBACK *simpvt")
}
field(IVOA,DBF_MENU) {
prompt("INVALID outpt action")
promptgroup("50 - Output")
interest(2)
menu(menuIvoa)
}
field(IVOV,DBF_USHORT) {
prompt("INVALID output value")
promptgroup("50 - Output")
interest(2)
}
=head2 Record Support
=head3 Record Support Routines
=head2 C<init_record>
This routine initializes SIMM if SIML is a constant or creates a channel
access link if SIML is PV_LINK. If SIOL is a PV_LINK a channel access link
is created.
This routine next checks to see that device support is available. The
routine next checks to see if the device support write routine is defined.
If either device support or the device support write routine does not
exist, and error message is issued and processing is terminated.
If DOL is a constant, then VAL is initialized to 1 if its value is nonzero
or initialzed to 0 if DOL is zero, and UDF is set to FALSE.
If device support includes C<init_record>, it is called. VAL is set using
RVAL, and UDF is set to FALSE.
=head2 C<process>
See next section.
=head2 C<get_value>
Fills in the values of struct valueDes so that they refer to VAL.
=head2 C<get_enum_str>
Retrieves ASCII string corresponding to VAL.
=head2 C<get_enum_strs>
Retrieves ASCII strings for ZNAM and ONAM.
=head2 C<put_enum_str>
Checks if string matches ZNAM or ONAM, and if it does, sets VAL.
=head2 Record Processing
Routine process implements the following algorithm:
=over 1
=item 1.
Check to see that the appropriate device support module exists. If it
doesn't, an error message is issued and processing is terminated with
the PACT field still set to TRUE. This ensures that processes will no
longer be called for this record. Thus error storms will not occur.
=item 2.
If PACT is FALSE
=back
=over
=item *
If DOL is DB_LINK and OMSL is CLOSED_LOOP
=over
=item *
get values from DOL
=item *
check for link alarm
=item *
force VAL to be 0 or 1
=item *
if MASK is defined
=over
=item *
if VAL is 0 set RVAL = 0
=back
=item *
else set RVAL = MASK
=back
=back
=over
=item 3.
Check alarms: This routine checks to see if the new VAL causes the alarm
status and severity to change. If so, NSEV, NSTA, and LALM are set.
=item 4.
Check severity and write the new value. See L<Invalid Alarm Output Action>
for more information on how INVALID alarms affect output.
=item 5.
If PACT has been changed to TRUE, the device support write output routine
has started but has not completed writing the new value. in this case, the
processing routine merely returns, leaving PACT TRUE.
=item 6.
Check WAIT. If VAL is 1 and WAIT is greater than 0, process again with a
VAL=0 after WAIT seconds.
=item 7.
Check to see if monitors should be invoked.
=back
=over 1
=item *
Alarm monitors are invoked if the alarm status or severity has changed.
=item *
Archive and value change monitors are invoked if MLST is not equal to VAL.
=item *
Monitors for RVAL and for RBV are checked whenever other monitors are
invoked.
=item *
NSEV and NSTA are reset to 0.
=back
=over
=item 8
Scan forward link if necessary, set PACT FALSE, and return
=back
=head2 Device support
=head3 Fields Of Interest To Device Support
Each binary output record must have an associated set of device support
routines. The primary responsibility of the device support routines is to
write a new value whenever C<write_bo> is called. The device support routines
are primarily interested in the following fields:
=fields PACT, DPVT, NSEV, NSTA, VAL, OUT, RVAL, MASK, RBV
=head3 Decive Support Routines
Device support consists of the following routines:
=head2 C<report(FILE fp, paddr)>
Not currently used.
=head2 C<init()>
This routine is called once during IOC initialization.
=head2 C<init_record(precord)>
This routine is optional. If provided, it is called by record support
C<init_record> routine. It should determine MASK if it is needed.
=over
=item *
0: Success. RVAL modified (VAL will be set accordingly)
=item *
2: Success. VAL modified
=item *
other: Error
=back
=head2 C<get_ioint_info(int cmd, struct dbCommon *precord, IOSCANPVT *ppvt)>
This routine is called by the ioEventScan system each time the record is
added or deleted from an I/O event scan list. C<cmd> has the value (0,1) if
the record is being (added to, deleted from) an I/O event list. It must be
provided for any device type that can use the ioEvent scanner.
=head2 C<write_bo(precord)>
This routine must output a new value. It returns the following values:
=over
=item *
0: Success
=item *
other: Error.
=back
=head2 Device Support For Soft Records
Two soft device support modules C<Soft Channel> and C<Raw Soft Channel> are
provided for output records not related to actual hardware devices. The OUT
link type must be either CONSTANT, DB_LINK, or CA_LINK.
=head3 Soft Channel
This module writes the current value of VAL.
If the OUT link type is PV_LINK, then C<dbCaAddInlink> is called by
C<init_record>. C<init_record> always returns a value of 2, which means
that no conversion will ever be attempted. C<write_bo> calls
C<recGblPutLinkValue> to write the current value of VAL. See L<Soft Output>
for details.
=head3 Raw Soft Channel
This module is like the previous except that it writes the current value of
RVAL
=cut
}
variable(boHIGHprecision, int)
variable(boHIGHlimit, double)
-324
View File
@@ -1,324 +0,0 @@
#*************************************************************************
# Copyright (c) 2007 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.
#*************************************************************************
recordtype(calc) {
include "dbCommon.dbd"
field(VAL,DBF_DOUBLE) {
prompt("Result")
promptgroup("50 - Output")
asl(ASL0)
}
field(CALC,DBF_STRING) {
prompt("Calculation")
promptgroup("30 - Action")
special(SPC_CALC)
pp(TRUE)
size(80)
initial("0")
}
field(INPA,DBF_INLINK) {
prompt("Input A")
promptgroup("41 - Input A-F")
interest(1)
}
field(INPB,DBF_INLINK) {
prompt("Input B")
promptgroup("41 - Input A-F")
interest(1)
}
field(INPC,DBF_INLINK) {
prompt("Input C")
promptgroup("41 - Input A-F")
interest(1)
}
field(INPD,DBF_INLINK) {
prompt("Input D")
promptgroup("41 - Input A-F")
interest(1)
}
field(INPE,DBF_INLINK) {
prompt("Input E")
promptgroup("41 - Input A-F")
interest(1)
}
field(INPF,DBF_INLINK) {
prompt("Input F")
promptgroup("41 - Input A-F")
interest(1)
}
field(INPG,DBF_INLINK) {
prompt("Input G")
promptgroup("42 - Input G-L")
interest(1)
}
field(INPH,DBF_INLINK) {
prompt("Input H")
promptgroup("42 - Input G-L")
interest(1)
}
field(INPI,DBF_INLINK) {
prompt("Input I")
promptgroup("42 - Input G-L")
interest(1)
}
field(INPJ,DBF_INLINK) {
prompt("Input J")
promptgroup("42 - Input G-L")
interest(1)
}
field(INPK,DBF_INLINK) {
prompt("Input K")
promptgroup("42 - Input G-L")
interest(1)
}
field(INPL,DBF_INLINK) {
prompt("Input L")
promptgroup("42 - Input G-L")
interest(1)
}
field(EGU,DBF_STRING) {
prompt("Engineering Units")
promptgroup("80 - Display")
interest(1)
size(16)
prop(YES)
}
field(PREC,DBF_SHORT) {
prompt("Display Precision")
promptgroup("80 - Display")
interest(1)
prop(YES)
}
field(HOPR,DBF_DOUBLE) {
prompt("High Operating Rng")
promptgroup("80 - Display")
interest(1)
prop(YES)
}
field(LOPR,DBF_DOUBLE) {
prompt("Low Operating Range")
promptgroup("80 - Display")
interest(1)
prop(YES)
}
field(HIHI,DBF_DOUBLE) {
prompt("Hihi Alarm Limit")
promptgroup("70 - Alarm")
pp(TRUE)
interest(1)
prop(YES)
}
field(LOLO,DBF_DOUBLE) {
prompt("Lolo Alarm Limit")
promptgroup("70 - Alarm")
pp(TRUE)
interest(1)
prop(YES)
}
field(HIGH,DBF_DOUBLE) {
prompt("High Alarm Limit")
promptgroup("70 - Alarm")
pp(TRUE)
interest(1)
prop(YES)
}
field(LOW,DBF_DOUBLE) {
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)
prop(YES)
menu(menuAlarmSevr)
}
field(LLSV,DBF_MENU) {
prompt("Lolo Severity")
promptgroup("70 - Alarm")
pp(TRUE)
interest(1)
prop(YES)
menu(menuAlarmSevr)
}
field(HSV,DBF_MENU) {
prompt("High Severity")
promptgroup("70 - Alarm")
pp(TRUE)
interest(1)
prop(YES)
menu(menuAlarmSevr)
}
field(LSV,DBF_MENU) {
prompt("Low Severity")
promptgroup("70 - Alarm")
pp(TRUE)
interest(1)
prop(YES)
menu(menuAlarmSevr)
}
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(HYST,DBF_DOUBLE) {
prompt("Alarm Deadband")
promptgroup("70 - Alarm")
interest(1)
}
field(ADEL,DBF_DOUBLE) {
prompt("Archive Deadband")
promptgroup("80 - Display")
interest(1)
}
field(MDEL,DBF_DOUBLE) {
prompt("Monitor Deadband")
promptgroup("80 - Display")
interest(1)
}
field(A,DBF_DOUBLE) {
prompt("Value of Input A")
pp(TRUE)
}
field(B,DBF_DOUBLE) {
prompt("Value of Input B")
pp(TRUE)
}
field(C,DBF_DOUBLE) {
prompt("Value of Input C")
pp(TRUE)
}
field(D,DBF_DOUBLE) {
prompt("Value of Input D")
pp(TRUE)
}
field(E,DBF_DOUBLE) {
prompt("Value of Input E")
pp(TRUE)
}
field(F,DBF_DOUBLE) {
prompt("Value of Input F")
pp(TRUE)
}
field(G,DBF_DOUBLE) {
prompt("Value of Input G")
pp(TRUE)
}
field(H,DBF_DOUBLE) {
prompt("Value of Input H")
pp(TRUE)
}
field(I,DBF_DOUBLE) {
prompt("Value of Input I")
pp(TRUE)
}
field(J,DBF_DOUBLE) {
prompt("Value of Input J")
pp(TRUE)
}
field(K,DBF_DOUBLE) {
prompt("Value of Input K")
pp(TRUE)
}
field(L,DBF_DOUBLE) {
prompt("Value of Input L")
pp(TRUE)
}
field(LA,DBF_DOUBLE) {
prompt("Prev Value of A")
special(SPC_NOMOD)
interest(3)
}
field(LB,DBF_DOUBLE) {
prompt("Prev Value of B")
special(SPC_NOMOD)
interest(3)
}
field(LC,DBF_DOUBLE) {
prompt("Prev Value of C")
special(SPC_NOMOD)
interest(3)
}
field(LD,DBF_DOUBLE) {
prompt("Prev Value of D")
special(SPC_NOMOD)
interest(3)
}
field(LE,DBF_DOUBLE) {
prompt("Prev Value of E")
special(SPC_NOMOD)
interest(3)
}
field(LF,DBF_DOUBLE) {
prompt("Prev Value of F")
special(SPC_NOMOD)
interest(3)
}
field(LG,DBF_DOUBLE) {
prompt("Prev Value of G")
special(SPC_NOMOD)
interest(3)
}
field(LH,DBF_DOUBLE) {
prompt("Prev Value of H")
special(SPC_NOMOD)
interest(3)
}
field(LI,DBF_DOUBLE) {
prompt("Prev Value of I")
special(SPC_NOMOD)
interest(3)
}
field(LJ,DBF_DOUBLE) {
prompt("Prev Value of J")
special(SPC_NOMOD)
interest(3)
}
field(LK,DBF_DOUBLE) {
prompt("Prev Value of K")
special(SPC_NOMOD)
interest(3)
}
field(LL,DBF_DOUBLE) {
prompt("Prev Value of L")
special(SPC_NOMOD)
interest(3)
}
field(LALM,DBF_DOUBLE) {
prompt("Last Value Alarmed")
special(SPC_NOMOD)
interest(3)
}
field(ALST,DBF_DOUBLE) {
prompt("Last Value Archived")
special(SPC_NOMOD)
interest(3)
}
field(MLST,DBF_DOUBLE) {
prompt("Last Val Monitored")
special(SPC_NOMOD)
interest(3)
}
%#include "postfix.h"
field(RPCL,DBF_NOACCESS) {
prompt("Reverse Polish Calc")
special(SPC_NOMOD)
interest(4)
extra("char rpcl[INFIX_TO_POSTFIX_SIZE(80)]")
}
}
@@ -0,0 +1,948 @@
#*************************************************************************
# Copyright (c) 2007 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 Calculation Record (calc)
The calculation or "Calc" record is used to perform algebraic, relational,
and logical operations on values retrieved from other records. The result
of its operations can then be accessed by another record so that it can
then be used.
=head2 Parameter Fields
The fields in the record fall into the following categories:
=over 1
=item *
scan parameters
=item *
read parameters
=item *
expression parameters
=item *
operator display parameters
=item *
alarm parameters
=item *
monitor parameters
=item *
run-time parameters
=back
=recordtype calc
=cut
recordtype(calc) {
=head3 Scan Parameters
The Calc record has the standard fields for specifying under what
circumstances the record will be processed. These fields are listed in
L<Scan Fields>. In addition, L<Scanning Specification> explains how these
fields are used. Since the Calc record supports no direct interfaces to
hardware, it cannot be scanned on I/O interrupt, so its SCAN field cannot
be C<I/O Intr>.
=fields SCAN
=head3 Read Parameters
The read parameters for the Calc record consist of 12 input links INPA,
INPB, ... INPL. The fields can be database links, channel access links, or
constants. If they are links, they must specify another record's field or a
channel access link. If they are constants, they will be initialized with
the value they are configured with and can be changed via C<dbPuts>. They
cannot be hardware addresses.
See L<Address Specification> for information on how to specify database
links.
=fields INPA, INPB, INPC, INPD, INPE, INPF, INPG, INPH, INPI, INPJ, INPK, INPL
=head3 Expression
At the core of the Calc record lies the CALC and RPCL fields. The CALC field
contains the infix expresion which the record routine will use when it
processes the record. The resulting value is placed in the VAL field and
can be accessed from there. The CALC expression is actually converted to
opcode and stored as Reverse Polish Notation in the RPCL field. It is this
expression which is actually used to calculate VAL. The Reverse Polish
expression is evaluated more efficiently during run-time than an infix
expression. CALC can be changed at run-time, and a special record routine
calls a function to convert it to Reverse Polish Notation.
The infix expressions that can be used are very similar to the C expression
syntax, but with some additions and subtle differences in operator meaning
and precedence. The string may contain a series of expressions separated by
a semi-colon character ";" any one of which may actually provide the
calculation result; however, all of the other expressions included must
assign their result to a variable. All alphabetic elements described below
are case independent, so upper and lower case letters may be used and mixed
in the variable and function names as desired. Spaces may be used anywhere
within an expression except between characters that make up a single
expression element.
The range of expressions supported by the calculation record are separated
into literals, constants, operands, algebraic operators, trigonometric operators,
relational operators, logical operators, the assignment operator,
parentheses and commas, and the question mark or '?:' operator.
=fields CALC, RPCL
=head3 Literals
=over 1
=item *
Standard double precision floating point numbers
=item *
Inf: Infinity
=item *
Nan: Not a Number
=back
=head3 Constants
=over 1
=item *
PI: returns the mathematical constant E<pi>
=item *
D2R: evaluates to E<pi>/180 which, when used as a multiplier, converts an
angle from degrees to radians
=item *
R2D: evaluates to 180/E<pi> which as a multiplier converts an angle from
radians to degrees
=back
=head3 Operands
The expression uses the values retrieved from the INPx links as operands,
though constants can be used as operands too. These values retrieved from
the input links are stored in the A-L fields. The values to be used in the
expression are simply referenced by the field letter. For instance, the
value obtained from INPA link is stored in the field A, and the value
obtained from INPB is stored in field B. The field names can be included in
the expression which will operate on their respective values, as in A+B.
Also, the RNDM nullary function can be included as an operand in the
expression in order to generate a random number between 0 and 1.
=fields A, B, C, D, E, F, G, H, I, J, K, L
The keyword VAL returns the current contents of the VAL field (which can be
written to by a CA put, so it might I<not> be the result from the last time
the expression was evaluated).
=head3 Algebraic Operators
=over 1
=item *
ABS: Absolute value (unary)
=item *
SQR: Square root (unary)
=item *
MIN: Minimum (any number of args)
=item *
MAX: Maximum (any number of args)
=item *
FINITE: returns non-zero if none of the arguments are NaN or Inf (any
number of args)
=item *
ISNAN: returns non-zero if any of the arguments is NaN or Inf (any number
of args)
=item *
CEIL: Ceiling (unary)
=item *
FLOOR: Floor (unary)
=item *
LOG: Log base 10 (unary)
=item *
LOGE: Natural log (unary)
=item *
LN: Natural log (unary)
=item *
EXP: Exponential function (unary)
=item *
^ : Exponential (binary)
=item *
** : Exponential (binary)
=item *
+ : Addition (binary)
=item *
- : Subtraction (binary)
=item *
* : Multiplication (binary)
=item *
/ : Division (binary)
=item *
% : Modulo (binary)
=item *
NOT: Negate (unary)
=back
=head3 Trigonometric Operators
=over 1
=item *
SIN: Sine
=item *
SINH: Hyperbolic sine
=item *
ASIN: Arc sine
=item *
COS: Cosine
=item *
COSH: Hyperbolic cosine
=item *
ACOS: Arc cosine
=item *
TAN: Tangent
=item *
TANH: Hyperbolic tangent
=item *
ATAN: Arc tangent
=back
=head3 Relational Operators
=over 1
=item *
>= : Greater than or equal to
=item *
> : Greater than
=item *
<= : Less than or equal to
=item *
< : Less than
=item *
# : Not equal to
=item *
= : Equal to
=back
=head3 Logical Operators
=over 1
=item *
&& : And
=item *
|| : Or
=item *
! : Not
=back
=head3 Bitwise Operators
=over 1
=item *
| : Bitwise Or
=item *
& : Bitwise And
=item *
OR : Bitwise Or
=item *
AND : Bitwise And
=item *
XOR : Bitwise Exclusive Or
=item *
~ : One's Complement
=item *
<< : Left shift
=item *
>> : Right shift
=back
=head3 Assignment Operator
=over 1
=item *
:= : assigns a value (right hand side) to a variable (i.e. field)
=back
=head3 Parantheses, Comma, and Semicolon
The open and close parentheses are supported. Nested parentheses are
supported.
The comma is supported when used to separate the arguments of a binary
function.
The semicolon is used to separate expressions. Although only one
traditional calculation expression is allowed, multiple assignment
expressions are allowed.
=head3 Conditional Expression
The C language's question mark operator is supported. The format is:
C<condition ? True result : False result>
=head3 Expression Examples
=head3 Algebraic
C<A + B + 10>
=over 1
=item *
Result is A + B + 10
=back
=head3 Relational
C<(A + B) < (C + D)>
=over 1
=item *
Result is 1 if (A + B) < (C + D)
=item *
Result is 0 if (A + B) >= (C + D)
=back
=head3 Question Mark
C<(A + B) < (C + D) ? E : F + L + 10>
=over 1
=item *
Result is E if (A + B) < (C + D)
=item *
Result is F + L + 10 if (A + B) >= (C + D)
=back
Prior to Base 3.14.9 it was legal to omit the : and the second (else) part
of the conditional, like this:
C<(A + B)<(C + D) ? E>
=over 1
=item
Result is E if (A + B)<(C + D)
=item
Result is unchanged if (A + B)>=(C + D)
From 3.14.9 onwards, this expresion must be written as
C<(A + B) < (C + D) ? E : VAL>
=back
=head3 Logical
C<A&B>
=over 1
=item *
Causes the following to occur:
=over 1
=item *
Convert A to integer
=item *
Convert B to integer
=item *
Bitwise And A and B
=item *
Convert result to floating point
=back
=back
=head3 Assignment
C<sin(a); a:=a+D2R>
=over 1
=item *
Causes the Calc record to output the successive values of a sine curve in
1 degree intervals.
=back
=head3 Operator Display Parameters
These parameters are used to present meaningful data to the operator. These
fields are used to display VAL and other parameters of the calculation
record either textually or graphically.
The EGU field contains a string of up to 16 characters which is supplied by
the user and which describes the values being operated upon. The string is
retrieved whenever the routine C<get_units> is called. The EGU string is
solely for an operator's sake and does not have to be used.
The HOPR and LOPR fields only refer to the limits of the VAL, HIHI, HIGH,
LOW and LOLO fields. PREC controls the precision of the VAL field.
See L<Fields Common to All Record Types> for more on the record name (NAME)
and description (DESC) fields.
=fields EGU, PREC, HOPR, LOPR, NAME, DESC
=head3 Alarm Parameters
The possible alarm conditions for the Calc record are the SCAN, READ,
Calculation, and limit alarms. The SCAN and READ alarms are called by the
record support routines. The Calculation alarm is called by the record
processing routine when the CALC expression is an invalid one, upon which
an error message is generated.
The following alarm parameters which are configured by the user, define the
limit alarms for the VAL field and the severity corresponding to those
conditions.
The HYST field defines an alarm deadband for each limit. See L<Alarm Specification>
for a complete explanation of alarms of these fields. L<Alarm Fields>
lists other fields related to alarms that are common to all record types.
=fields HIHI, HIGH, LOW, LOLO, HHSV, HSV, LSV, LLSV, HYST
=head3 Monitor Parameters
These paramaeters are used to determine when to send monitors for the value
fields. These monitors are sent when the value field exceeds the last
monitored field by the appropriate deadband, the ADEL for archiver monitors
and the MDEL field for all other types of monitors. If these fields have a
value of zero, everytime the value changes, monitors are triggered; if they have a
value of -1, everytime the record is scanned, monitors are triggered. See
L<Monitor Specification> for a complete explanation of monitors.
=fields ADEL, MDEL
=head3 Run-time Parameters
These fields are not configurable using a configuration tool and none are
modifiable at run-time. They are used to process the record.
The LALM field is used to implement the hysteresis factor for the alarm
limits.
The LA-LL fields are used to decide when to trigger monitors for the
corresponding fields. For instance, if LA does not equal the value A,
monitors for A are triggered. The MLST and ALST fields are used in the same
manner for the VAL field.
=fields LALM, ALST, MLST, LA, LB, LC, LD, LE, LF, LG, LH, LI, LJ, LK, LL
=cut
include "dbCommon.dbd"
field(VAL,DBF_DOUBLE) {
prompt("Result")
promptgroup("50 - Output")
asl(ASL0)
}
field(CALC,DBF_STRING) {
prompt("Calculation")
promptgroup("30 - Action")
special(SPC_CALC)
pp(TRUE)
size(80)
initial("0")
}
field(INPA,DBF_INLINK) {
prompt("Input A")
promptgroup("41 - Input A-F")
interest(1)
}
field(INPB,DBF_INLINK) {
prompt("Input B")
promptgroup("41 - Input A-F")
interest(1)
}
field(INPC,DBF_INLINK) {
prompt("Input C")
promptgroup("41 - Input A-F")
interest(1)
}
field(INPD,DBF_INLINK) {
prompt("Input D")
promptgroup("41 - Input A-F")
interest(1)
}
field(INPE,DBF_INLINK) {
prompt("Input E")
promptgroup("41 - Input A-F")
interest(1)
}
field(INPF,DBF_INLINK) {
prompt("Input F")
promptgroup("41 - Input A-F")
interest(1)
}
field(INPG,DBF_INLINK) {
prompt("Input G")
promptgroup("42 - Input G-L")
interest(1)
}
field(INPH,DBF_INLINK) {
prompt("Input H")
promptgroup("42 - Input G-L")
interest(1)
}
field(INPI,DBF_INLINK) {
prompt("Input I")
promptgroup("42 - Input G-L")
interest(1)
}
field(INPJ,DBF_INLINK) {
prompt("Input J")
promptgroup("42 - Input G-L")
interest(1)
}
field(INPK,DBF_INLINK) {
prompt("Input K")
promptgroup("42 - Input G-L")
interest(1)
}
field(INPL,DBF_INLINK) {
prompt("Input L")
promptgroup("42 - Input G-L")
interest(1)
}
field(EGU,DBF_STRING) {
prompt("Engineering Units")
promptgroup("80 - Display")
interest(1)
size(16)
prop(YES)
}
field(PREC,DBF_SHORT) {
prompt("Display Precision")
promptgroup("80 - Display")
interest(1)
prop(YES)
}
field(HOPR,DBF_DOUBLE) {
prompt("High Operating Rng")
promptgroup("80 - Display")
interest(1)
prop(YES)
}
field(LOPR,DBF_DOUBLE) {
prompt("Low Operating Range")
promptgroup("80 - Display")
interest(1)
prop(YES)
}
field(HIHI,DBF_DOUBLE) {
prompt("Hihi Alarm Limit")
promptgroup("70 - Alarm")
pp(TRUE)
interest(1)
prop(YES)
}
field(LOLO,DBF_DOUBLE) {
prompt("Lolo Alarm Limit")
promptgroup("70 - Alarm")
pp(TRUE)
interest(1)
prop(YES)
}
field(HIGH,DBF_DOUBLE) {
prompt("High Alarm Limit")
promptgroup("70 - Alarm")
pp(TRUE)
interest(1)
prop(YES)
}
field(LOW,DBF_DOUBLE) {
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)
prop(YES)
menu(menuAlarmSevr)
}
field(LLSV,DBF_MENU) {
prompt("Lolo Severity")
promptgroup("70 - Alarm")
pp(TRUE)
interest(1)
prop(YES)
menu(menuAlarmSevr)
}
field(HSV,DBF_MENU) {
prompt("High Severity")
promptgroup("70 - Alarm")
pp(TRUE)
interest(1)
prop(YES)
menu(menuAlarmSevr)
}
field(LSV,DBF_MENU) {
prompt("Low Severity")
promptgroup("70 - Alarm")
pp(TRUE)
interest(1)
prop(YES)
menu(menuAlarmSevr)
}
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(HYST,DBF_DOUBLE) {
prompt("Alarm Deadband")
promptgroup("70 - Alarm")
interest(1)
}
field(ADEL,DBF_DOUBLE) {
prompt("Archive Deadband")
promptgroup("80 - Display")
interest(1)
}
field(MDEL,DBF_DOUBLE) {
prompt("Monitor Deadband")
promptgroup("80 - Display")
interest(1)
}
field(A,DBF_DOUBLE) {
prompt("Value of Input A")
pp(TRUE)
}
field(B,DBF_DOUBLE) {
prompt("Value of Input B")
pp(TRUE)
}
field(C,DBF_DOUBLE) {
prompt("Value of Input C")
pp(TRUE)
}
field(D,DBF_DOUBLE) {
prompt("Value of Input D")
pp(TRUE)
}
field(E,DBF_DOUBLE) {
prompt("Value of Input E")
pp(TRUE)
}
field(F,DBF_DOUBLE) {
prompt("Value of Input F")
pp(TRUE)
}
field(G,DBF_DOUBLE) {
prompt("Value of Input G")
pp(TRUE)
}
field(H,DBF_DOUBLE) {
prompt("Value of Input H")
pp(TRUE)
}
field(I,DBF_DOUBLE) {
prompt("Value of Input I")
pp(TRUE)
}
field(J,DBF_DOUBLE) {
prompt("Value of Input J")
pp(TRUE)
}
field(K,DBF_DOUBLE) {
prompt("Value of Input K")
pp(TRUE)
}
field(L,DBF_DOUBLE) {
prompt("Value of Input L")
pp(TRUE)
}
field(LA,DBF_DOUBLE) {
prompt("Prev Value of A")
special(SPC_NOMOD)
interest(3)
}
field(LB,DBF_DOUBLE) {
prompt("Prev Value of B")
special(SPC_NOMOD)
interest(3)
}
field(LC,DBF_DOUBLE) {
prompt("Prev Value of C")
special(SPC_NOMOD)
interest(3)
}
field(LD,DBF_DOUBLE) {
prompt("Prev Value of D")
special(SPC_NOMOD)
interest(3)
}
field(LE,DBF_DOUBLE) {
prompt("Prev Value of E")
special(SPC_NOMOD)
interest(3)
}
field(LF,DBF_DOUBLE) {
prompt("Prev Value of F")
special(SPC_NOMOD)
interest(3)
}
field(LG,DBF_DOUBLE) {
prompt("Prev Value of G")
special(SPC_NOMOD)
interest(3)
}
field(LH,DBF_DOUBLE) {
prompt("Prev Value of H")
special(SPC_NOMOD)
interest(3)
}
field(LI,DBF_DOUBLE) {
prompt("Prev Value of I")
special(SPC_NOMOD)
interest(3)
}
field(LJ,DBF_DOUBLE) {
prompt("Prev Value of J")
special(SPC_NOMOD)
interest(3)
}
field(LK,DBF_DOUBLE) {
prompt("Prev Value of K")
special(SPC_NOMOD)
interest(3)
}
field(LL,DBF_DOUBLE) {
prompt("Prev Value of L")
special(SPC_NOMOD)
interest(3)
}
field(LALM,DBF_DOUBLE) {
prompt("Last Value Alarmed")
special(SPC_NOMOD)
interest(3)
}
field(ALST,DBF_DOUBLE) {
prompt("Last Value Archived")
special(SPC_NOMOD)
interest(3)
}
field(MLST,DBF_DOUBLE) {
prompt("Last Val Monitored")
special(SPC_NOMOD)
interest(3)
}
%#include "postfix.h"
field(RPCL,DBF_NOACCESS) {
prompt("Reverse Polish Calc")
special(SPC_NOMOD)
interest(4)
extra("char rpcl[INFIX_TO_POSTFIX_SIZE(80)]")
}
=head2 Record Support
=head3 Record Support Routines
=head2 C<init_record>
For each constant input link, the corresponding value field is initialized
with the constant value if the input link is CONSTANT or a channel access
link is created if the input link is a PV_LINK.
A routine postfix is called to convert the infix expression in CALC to
Reverse Polish Notation. The result is stored in RPCL.
=head2 C<process>
See next section.
=head2 C<special>
This is called if CALC is changed. C<special> calls postfix.
=head2 C<get_value>
Fills in the values of struct valueDes so that the refer to VAL.
=head2 C<get_units>
Retrieves EGU.
=head2 C<get_precision>
Retrieves PREC.
=head2 C<get_graphic_double>
Sets the upper display and lower display limits for a field. If the field
is VAL, HIHI, HIGH, LOW, or LOLO, the limits are set to HOPR and LOPR, else
if the field has upper and lower limits defined they will be used, else the
upper and lower maximum values for the field will be used.
=head2 C<get_control_double>
Sets the upper control and the lower control limits for a field. If the
field is VAL, HIHI, HIGH, LOW, or LOLO, the limits are set to HOPR and
LOPR, else if the field has upper and lower limits defined they will be
used, else the upper and lower maximum values for the field type will be
used.
=head2 C<get_alarm_double>
Sets the following values:
=over 1
upper_alarm_limit = HIHI
upper_warning_limit = HIGH
lower_warning_limit = LOW
lower_alarm_limit = LOLO
=back
=head3 Record Processing
Routine process implements the following algorithm:
=over 1
=item 1.
Fetch all arguments.
=item 2.
Call routine C<calcPerform>, which calculates VAL from the postfix version of
the expression given in CALC. If C<calcPerform> returns success UDF is set to
FALSE.
=item 3.
Check alarms. This routine checks to see if the new VAL causes the alarm
status and severity to change. If so, NSEV, NSTA, and LALM are set. It also
honors the alarm hysteresis factor (HYST). Thus the value must change by
at least HYST before the alarm status and severity changes.
=item 4.
Check to see if monitors should be invoked.
=back
=over 1
=item *
Alarm monitors are invoked if the alarm status or severity has changed.
=item *
Archive and values change monitors are invoked if ADEL and MDEL conditions
are met.
=item *
Monitors for A-L are checked whenever other monitors are invoked.
=item *
NSEV and NSTA are reset to 0.
=back
=over
=item 5.
Scan forward link if necessary, set PACT FALSE, and return.
=back
=cut
}
@@ -1,530 +0,0 @@
#*************************************************************************
# Copyright (c) 2007 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.
#*************************************************************************
menu(calcoutOOPT) {
choice(calcoutOOPT_Every_Time,"Every Time")
choice(calcoutOOPT_On_Change,"On Change")
choice(calcoutOOPT_When_Zero,"When Zero")
choice(calcoutOOPT_When_Non_zero,"When Non-zero")
choice(calcoutOOPT_Transition_To_Zero,"Transition To Zero")
choice(calcoutOOPT_Transition_To_Non_zero,"Transition To Non-zero")
}
menu(calcoutDOPT) {
choice(calcoutDOPT_Use_VAL,"Use CALC")
choice(calcoutDOPT_Use_OVAL,"Use OCAL")
}
menu(calcoutINAV) {
choice(calcoutINAV_EXT_NC,"Ext PV NC")
choice(calcoutINAV_EXT,"Ext PV OK")
choice(calcoutINAV_LOC,"Local PV")
choice(calcoutINAV_CON,"Constant")
}
recordtype(calcout) {
include "dbCommon.dbd"
field(RPVT,DBF_NOACCESS) {
prompt("Record Private")
special(SPC_NOMOD)
interest(4)
extra("struct rpvtStruct *rpvt")
}
field(VAL,DBF_DOUBLE) {
prompt("Result")
promptgroup("50 - Output")
asl(ASL0)
}
field(PVAL,DBF_DOUBLE) {
prompt("Previous Value")
}
field(CALC,DBF_STRING) {
prompt("Calculation")
promptgroup("30 - Action")
special(SPC_CALC)
pp(TRUE)
size(80)
initial("0")
}
field(CLCV,DBF_LONG) {
prompt("CALC Valid")
interest(1)
}
field(INPA,DBF_INLINK) {
prompt("Input A")
special(SPC_MOD)
promptgroup("41 - Input A-F")
interest(1)
}
field(INPB,DBF_INLINK) {
prompt("Input B")
special(SPC_MOD)
promptgroup("41 - Input A-F")
interest(1)
}
field(INPC,DBF_INLINK) {
prompt("Input C")
special(SPC_MOD)
promptgroup("41 - Input A-F")
interest(1)
}
field(INPD,DBF_INLINK) {
prompt("Input D")
special(SPC_MOD)
promptgroup("41 - Input A-F")
interest(1)
}
field(INPE,DBF_INLINK) {
prompt("Input E")
special(SPC_MOD)
promptgroup("41 - Input A-F")
interest(1)
}
field(INPF,DBF_INLINK) {
prompt("Input F")
special(SPC_MOD)
promptgroup("41 - Input A-F")
interest(1)
}
field(INPG,DBF_INLINK) {
prompt("Input G")
special(SPC_MOD)
promptgroup("42 - Input G-L")
interest(1)
}
field(INPH,DBF_INLINK) {
prompt("Input H")
special(SPC_MOD)
promptgroup("42 - Input G-L")
interest(1)
}
field(INPI,DBF_INLINK) {
prompt("Input I")
special(SPC_MOD)
promptgroup("42 - Input G-L")
interest(1)
}
field(INPJ,DBF_INLINK) {
prompt("Input J")
special(SPC_MOD)
promptgroup("42 - Input G-L")
interest(1)
}
field(INPK,DBF_INLINK) {
prompt("Input K")
special(SPC_MOD)
promptgroup("42 - Input G-L")
interest(1)
}
field(INPL,DBF_INLINK) {
prompt("Input L")
special(SPC_MOD)
promptgroup("42 - Input G-L")
interest(1)
}
field(OUT,DBF_OUTLINK) {
prompt("Output Specification")
special(SPC_MOD)
promptgroup("50 - Output")
interest(1)
}
field(INAV,DBF_MENU) {
prompt("INPA PV Status")
special(SPC_NOMOD)
interest(1)
menu(calcoutINAV)
initial("1")
}
field(INBV,DBF_MENU) {
prompt("INPB PV Status")
special(SPC_NOMOD)
interest(1)
menu(calcoutINAV)
initial("1")
}
field(INCV,DBF_MENU) {
prompt("INPC PV Status")
special(SPC_NOMOD)
interest(1)
menu(calcoutINAV)
initial("1")
}
field(INDV,DBF_MENU) {
prompt("INPD PV Status")
special(SPC_NOMOD)
interest(1)
menu(calcoutINAV)
initial("1")
}
field(INEV,DBF_MENU) {
prompt("INPE PV Status")
special(SPC_NOMOD)
interest(1)
menu(calcoutINAV)
initial("1")
}
field(INFV,DBF_MENU) {
prompt("INPF PV Status")
special(SPC_NOMOD)
interest(1)
menu(calcoutINAV)
initial("1")
}
field(INGV,DBF_MENU) {
prompt("INPG PV Status")
special(SPC_NOMOD)
interest(1)
menu(calcoutINAV)
initial("1")
}
field(INHV,DBF_MENU) {
prompt("INPH PV Status")
special(SPC_NOMOD)
interest(1)
menu(calcoutINAV)
initial("1")
}
field(INIV,DBF_MENU) {
prompt("INPI PV Status")
special(SPC_NOMOD)
interest(1)
menu(calcoutINAV)
initial("1")
}
field(INJV,DBF_MENU) {
prompt("INPJ PV Status")
special(SPC_NOMOD)
interest(1)
menu(calcoutINAV)
initial("1")
}
field(INKV,DBF_MENU) {
prompt("INPK PV Status")
special(SPC_NOMOD)
interest(1)
menu(calcoutINAV)
initial("1")
}
field(INLV,DBF_MENU) {
prompt("INPL PV Status")
special(SPC_NOMOD)
interest(1)
menu(calcoutINAV)
initial("1")
}
field(OUTV,DBF_MENU) {
prompt("OUT PV Status")
special(SPC_NOMOD)
interest(1)
menu(calcoutINAV)
}
field(OOPT,DBF_MENU) {
prompt("Output Execute Opt")
promptgroup("50 - Output")
interest(1)
menu(calcoutOOPT)
}
field(ODLY,DBF_DOUBLE) {
prompt("Output Execute Delay")
promptgroup("50 - Output")
asl(ASL0)
interest(1)
}
field(DLYA,DBF_USHORT) {
prompt("Output Delay Active")
special(SPC_NOMOD)
asl(ASL0)
}
field(DOPT,DBF_MENU) {
prompt("Output Data Opt")
promptgroup("30 - Action")
interest(1)
menu(calcoutDOPT)
}
field(OCAL,DBF_STRING) {
prompt("Output Calculation")
promptgroup("30 - Action")
special(SPC_CALC)
pp(TRUE)
size(80)
initial("0")
}
field(OCLV,DBF_LONG) {
prompt("OCAL Valid")
interest(1)
}
field(OEVT,DBF_STRING) {
prompt("Event To Issue")
promptgroup("30 - Action")
special(SPC_MOD)
asl(ASL0)
size(40)
}
%#include "dbScan.h"
field(EPVT, DBF_NOACCESS) {
prompt("Event private")
special(SPC_NOMOD)
interest(4)
extra("EVENTPVT epvt")
}
field(IVOA,DBF_MENU) {
prompt("INVALID output action")
promptgroup("50 - Output")
interest(2)
menu(menuIvoa)
}
field(IVOV,DBF_DOUBLE) {
prompt("INVALID output value")
promptgroup("50 - Output")
interest(2)
}
field(EGU,DBF_STRING) {
prompt("Engineering Units")
promptgroup("80 - Display")
interest(1)
size(16)
prop(YES)
}
field(PREC,DBF_SHORT) {
prompt("Display Precision")
promptgroup("80 - Display")
interest(1)
prop(YES)
}
field(HOPR,DBF_DOUBLE) {
prompt("High Operating Rng")
promptgroup("80 - Display")
interest(1)
prop(YES)
}
field(LOPR,DBF_DOUBLE) {
prompt("Low Operating Range")
promptgroup("80 - Display")
interest(1)
prop(YES)
}
field(HIHI,DBF_DOUBLE) {
prompt("Hihi Alarm Limit")
promptgroup("70 - Alarm")
pp(TRUE)
interest(1)
prop(YES)
}
field(LOLO,DBF_DOUBLE) {
prompt("Lolo Alarm Limit")
promptgroup("70 - Alarm")
pp(TRUE)
interest(1)
prop(YES)
}
field(HIGH,DBF_DOUBLE) {
prompt("High Alarm Limit")
promptgroup("70 - Alarm")
pp(TRUE)
interest(1)
prop(YES)
}
field(LOW,DBF_DOUBLE) {
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)
prop(YES)
menu(menuAlarmSevr)
}
field(LLSV,DBF_MENU) {
prompt("Lolo Severity")
promptgroup("70 - Alarm")
pp(TRUE)
interest(1)
prop(YES)
menu(menuAlarmSevr)
}
field(HSV,DBF_MENU) {
prompt("High Severity")
promptgroup("70 - Alarm")
pp(TRUE)
interest(1)
prop(YES)
menu(menuAlarmSevr)
}
field(LSV,DBF_MENU) {
prompt("Low Severity")
promptgroup("70 - Alarm")
pp(TRUE)
interest(1)
prop(YES)
menu(menuAlarmSevr)
}
field(HYST,DBF_DOUBLE) {
prompt("Alarm Deadband")
promptgroup("70 - Alarm")
interest(1)
}
field(ADEL,DBF_DOUBLE) {
prompt("Archive Deadband")
promptgroup("80 - Display")
interest(1)
}
field(MDEL,DBF_DOUBLE) {
prompt("Monitor Deadband")
promptgroup("80 - Display")
interest(1)
}
field(A,DBF_DOUBLE) {
prompt("Value of Input A")
pp(TRUE)
}
field(B,DBF_DOUBLE) {
prompt("Value of Input B")
pp(TRUE)
}
field(C,DBF_DOUBLE) {
prompt("Value of Input C")
pp(TRUE)
}
field(D,DBF_DOUBLE) {
prompt("Value of Input D")
pp(TRUE)
}
field(E,DBF_DOUBLE) {
prompt("Value of Input E")
pp(TRUE)
}
field(F,DBF_DOUBLE) {
prompt("Value of Input F")
pp(TRUE)
}
field(G,DBF_DOUBLE) {
prompt("Value of Input G")
pp(TRUE)
}
field(H,DBF_DOUBLE) {
prompt("Value of Input H")
pp(TRUE)
}
field(I,DBF_DOUBLE) {
prompt("Value of Input I")
pp(TRUE)
}
field(J,DBF_DOUBLE) {
prompt("Value of Input J")
pp(TRUE)
}
field(K,DBF_DOUBLE) {
prompt("Value of Input K")
pp(TRUE)
}
field(L,DBF_DOUBLE) {
prompt("Value of Input L")
pp(TRUE)
}
field(OVAL,DBF_DOUBLE) {
prompt("Output Value")
asl(ASL0)
}
field(LA,DBF_DOUBLE) {
prompt("Prev Value of A")
special(SPC_NOMOD)
interest(3)
}
field(LB,DBF_DOUBLE) {
prompt("Prev Value of B")
special(SPC_NOMOD)
interest(3)
}
field(LC,DBF_DOUBLE) {
prompt("Prev Value of C")
special(SPC_NOMOD)
interest(3)
}
field(LD,DBF_DOUBLE) {
prompt("Prev Value of D")
special(SPC_NOMOD)
interest(3)
}
field(LE,DBF_DOUBLE) {
prompt("Prev Value of E")
special(SPC_NOMOD)
interest(3)
}
field(LF,DBF_DOUBLE) {
prompt("Prev Value of F")
special(SPC_NOMOD)
interest(3)
}
field(LG,DBF_DOUBLE) {
prompt("Prev Value of G")
special(SPC_NOMOD)
interest(3)
}
field(LH,DBF_DOUBLE) {
prompt("Prev Value of H")
special(SPC_NOMOD)
interest(3)
}
field(LI,DBF_DOUBLE) {
prompt("Prev Value of I")
special(SPC_NOMOD)
interest(3)
}
field(LJ,DBF_DOUBLE) {
prompt("Prev Value of J")
special(SPC_NOMOD)
interest(3)
}
field(LK,DBF_DOUBLE) {
prompt("Prev Value of K")
special(SPC_NOMOD)
interest(3)
}
field(LL,DBF_DOUBLE) {
prompt("Prev Value of L")
special(SPC_NOMOD)
interest(3)
}
field(POVL,DBF_DOUBLE) {
prompt("Prev Value of OVAL")
asl(ASL0)
}
field(LALM,DBF_DOUBLE) {
prompt("Last Value Alarmed")
special(SPC_NOMOD)
interest(3)
}
field(ALST,DBF_DOUBLE) {
prompt("Last Value Archived")
special(SPC_NOMOD)
interest(3)
}
field(MLST,DBF_DOUBLE) {
prompt("Last Val Monitored")
special(SPC_NOMOD)
interest(3)
}
%#include "postfix.h"
field(RPCL,DBF_NOACCESS) {
prompt("Reverse Polish Calc")
special(SPC_NOMOD)
interest(4)
extra("char rpcl[INFIX_TO_POSTFIX_SIZE(80)]")
}
field(ORPC,DBF_NOACCESS) {
prompt("Reverse Polish OCalc")
special(SPC_NOMOD)
interest(4)
extra("char orpc[INFIX_TO_POSTFIX_SIZE(80)]")
}
}
variable(calcoutODLYprecision, int)
variable(calcoutODLYlimit, double)
File diff suppressed because it is too large Load Diff
@@ -4,12 +4,12 @@
* 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.
* in file LICENSE that is included with this distribution.
\*************************************************************************/
/*
* Original Author: Bob Dalesio
* Date: 7-14-89
* Date: 7-14-89
*/
#include <stddef.h>
@@ -439,12 +439,16 @@ static long get_array_info(DBADDR *paddr, long *no_elements, long *offset)
static long put_array_info(DBADDR *paddr, long nNew)
{
compressRecord *prec = (compressRecord *) paddr->precord;
epicsUInt32 nuse = prec->nuse;
if (prec->balg == bufferingALG_FIFO)
prec->off = (prec->off + nNew) % prec->nsam;
prec->nuse += nNew;
if (prec->nuse > prec->nsam)
prec->nuse = prec->nsam;
if (nuse != prec->nuse)
db_post_events(prec, &prec->nuse, DBE_VALUE | DBE_LOG);
return 0;
}
@@ -1,204 +0,0 @@
#*************************************************************************
# Copyright (c) 2002 The University of Chicago, 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.
#*************************************************************************
menu(dfanoutSELM) {
choice(dfanoutSELM_All,"All")
choice(dfanoutSELM_Specified,"Specified")
choice(dfanoutSELM_Mask,"Mask")
}
recordtype(dfanout) {
include "dbCommon.dbd"
field(VAL,DBF_DOUBLE) {
prompt("Desired Output")
promptgroup("40 - Input")
asl(ASL0)
pp(TRUE)
}
field(SELM,DBF_MENU) {
prompt("Select Mechanism")
promptgroup("30 - Action")
interest(1)
menu(dfanoutSELM)
}
field(SELN,DBF_USHORT) {
prompt("Link Selection")
interest(1)
initial("1")
}
field(SELL,DBF_INLINK) {
prompt("Link Selection Loc")
promptgroup("30 - Action")
interest(1)
}
field(OUTA,DBF_OUTLINK) {
prompt("Output Spec A")
promptgroup("50 - Output")
interest(1)
}
field(OUTB,DBF_OUTLINK) {
prompt("Output Spec B")
promptgroup("50 - Output")
interest(1)
}
field(OUTC,DBF_OUTLINK) {
prompt("Output Spec C")
promptgroup("50 - Output")
interest(1)
}
field(OUTD,DBF_OUTLINK) {
prompt("Output Spec D")
promptgroup("50 - Output")
interest(1)
}
field(OUTE,DBF_OUTLINK) {
prompt("Output Spec E")
promptgroup("50 - Output")
interest(1)
}
field(OUTF,DBF_OUTLINK) {
prompt("Output Spec F")
promptgroup("50 - Output")
interest(1)
}
field(OUTG,DBF_OUTLINK) {
prompt("Output Spec G")
promptgroup("50 - Output")
interest(1)
}
field(OUTH,DBF_OUTLINK) {
prompt("Output Spec H")
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("Engineering Units")
promptgroup("80 - Display")
interest(1)
size(16)
prop(YES)
}
field(PREC,DBF_SHORT) {
prompt("Display Precision")
promptgroup("80 - Display")
interest(1)
prop(YES)
}
field(HOPR,DBF_DOUBLE) {
prompt("High Operating Range")
promptgroup("80 - Display")
interest(1)
prop(YES)
}
field(LOPR,DBF_DOUBLE) {
prompt("Low Operating Range")
promptgroup("80 - Display")
interest(1)
prop(YES)
}
field(HIHI,DBF_DOUBLE) {
prompt("Hihi Alarm Limit")
promptgroup("70 - Alarm")
pp(TRUE)
interest(1)
prop(YES)
}
field(LOLO,DBF_DOUBLE) {
prompt("Lolo Alarm Limit")
promptgroup("70 - Alarm")
pp(TRUE)
interest(1)
prop(YES)
}
field(HIGH,DBF_DOUBLE) {
prompt("High Alarm Limit")
promptgroup("70 - Alarm")
pp(TRUE)
interest(1)
prop(YES)
}
field(LOW,DBF_DOUBLE) {
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)
prop(YES)
menu(menuAlarmSevr)
}
field(LLSV,DBF_MENU) {
prompt("Lolo Severity")
promptgroup("70 - Alarm")
pp(TRUE)
interest(1)
prop(YES)
menu(menuAlarmSevr)
}
field(HSV,DBF_MENU) {
prompt("High Severity")
promptgroup("70 - Alarm")
pp(TRUE)
interest(1)
prop(YES)
menu(menuAlarmSevr)
}
field(LSV,DBF_MENU) {
prompt("Low Severity")
promptgroup("70 - Alarm")
pp(TRUE)
interest(1)
prop(YES)
menu(menuAlarmSevr)
}
field(HYST,DBF_DOUBLE) {
prompt("Alarm Deadband")
promptgroup("70 - Alarm")
interest(1)
}
field(ADEL,DBF_DOUBLE) {
prompt("Archive Deadband")
promptgroup("80 - Display")
interest(1)
}
field(MDEL,DBF_DOUBLE) {
prompt("Monitor Deadband")
promptgroup("80 - Display")
interest(1)
}
field(LALM,DBF_DOUBLE) {
prompt("Last Value Alarmed")
special(SPC_NOMOD)
interest(3)
}
field(ALST,DBF_DOUBLE) {
prompt("Last Value Archived")
special(SPC_NOMOD)
interest(3)
}
field(MLST,DBF_DOUBLE) {
prompt("Last Val Monitored")
special(SPC_NOMOD)
interest(3)
}
}
@@ -0,0 +1,454 @@
#*************************************************************************
# Copyright (c) 2002 The University of Chicago, 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 Data Fanout Record (dfanout)
The Data Fanout or "dfanout" record is used to forward data to up to
eight other records. It's similar to the fanout record except that the
capability to forward data has been added to it. If has no associated
device support.
=head2 Parameter Fields
The fields in this record can be classified into the following categories:
=over
=item *
scan parameters
=item *
desired output parameters
=item *
write parameters
=item *
operator display parameters
=item *
alarm parameters
=item *
monitor parameters
=item *
run-time and simulation mode parameters
=back
=recordtype dfanout
=cut
menu(dfanoutSELM) {
choice(dfanoutSELM_All,"All")
choice(dfanoutSELM_Specified,"Specified")
choice(dfanoutSELM_Mask,"Mask")
}
recordtype(dfanout) {
=head3 Scan Parameters
The data fanout record has the standard fields for specifying under what
circumstances it will be processed. These fields are listed in
L<Scan Fields>. In addition, L<Scanning Specification> explains how these
fields are used. Since the data fanout record supports no direct interfaces
to hardware, it cannot be scanned on I/O interrupt, so its SCAN field
cannot be C<I/O Intr>.
=head3 Desired Output Parameters
The data fanout record must specify where the desired output value
originates, i.e., the data which is to be fowarded to the records in its
output links. The output mode select (OMSL) field determines whether the
output originates from another record or from run-time database access.
When set to C<closed_loop>, the desired output is retrieved from the link
specified in the desired output (DOL) field, which can specify either a
database or a channel access link, and placed into the VAL field. When set
to C<supervisory>, the desired output can be written to the VAL field via
dbPuts at run-time.
The DOL field can also be a constant in which case the VAL field is
initialized to the constant value.
Note that there are no conversion parameters, so the desired output value
undergoes no conversions before it is sent out to the output links.
=fields DOL, OMSL, VAL
=head3 Write Parameters
The OUTA-OUTH fields specify where VAL is to be sent. Each field that is to
forward data must specify an address to another record. See
L<Address Specification> for information on specifying links.
The SELL, SELM, and SELN fields specify which output links are to be
used.
=head4 Menu dfanoutSELM
SELM is a menu, with three choices:
=menu dfanoutSELM
If SELM=="All", then all output links are used, and the values of
SELL and SELN are ignored.
If SELM=="Specified", then the value of SELN is used to specify a single
link which will be used. If SELN==0, then no link will be used; if SELN==1,
then OUTA will be used, and so on.
SELN can either have its value set directly, or have its values retrieved
from another EPICS PV. If SELL is a valid PV link, then SELN will be set to
the values of the linked PV.
If SELM=="Mask", then SELN will be treated as a bit mask. If bit one of
SELN is set, then OUTA will be used, if bit two is set, OUTB will be used.
Thus if SELN==5, OUTC and OUTA will be used.
=fields OUTA, OUTB, OUTC, OUTD, OUTE, OUTF, OUTG, OUTH
=head3 Operator Display Parameters
These parameters are used to present meaningful data to the operator. They
display the value and other parameters of the data fanout record either
textually or graphically.
The EGU field can contain a string of up to 16 characters describing the
value on the VAL field.
The HOPR and LOPR fields determine the upper and lower display limits for
graphic displays and the upper and lower control limits for control
displays. They apply to the VAL, HIHI, HIGH, LOW, and LOLO fields. The
record support routines C<get_graphic_double> or C<get_control_double>
retrieve HOPR and LOPR.
See L<Fields Common to All Record Types> for more on the record name (NAME)
and description (DESC) fields.
=fields EGU, HOPR, LOPR, NAME, DESC
=head3 Alarm Parameters
The possible alarm conditions for data fanouts are the SCAN, READ, INVALID,
and limit alarms. The SCAN and READ alarms are called by the record
routines. The limit alarms are configured by the user in the HIHI, LOLO,
HIGH, and LOW fields using floating point values. The limit alarms apply
only to the VAL field. The severity for each of these limits is specified
in the corresponding field (HHSV, LLSV, HSV, LSV) and can be either
NO_ALARM, MINOR, or MAJOR. In the hysteresis field (HYST) can be entered a
number which serves as the deadband on the limit alarms.
See L<Alarm Specification> for a complete explanation of alarms and these
fields. L<Alarm Fields> lists other fields related to alarms that are
common to all record types.
=fields HIHI, HIGH, LOW, LOLO, HHSV, HSV, LSV, LLSV, HYST
=head3 Monitor Parameters
These parameters are used to determine when to send monitors placed on the
VAL field. These monitors are sent when the value field exceeds the last
monitored fields by the specified deadband, ADEL for archivers monitors and
MDEL for all other types of monitors. If these fields have a value of zero,
everytime the value changes, a monitor will be triggered; if they have a
value of -1, everytime the record is scanned, monitors are triggered. See
L<Monitor Specification> for a complete explanation of monitors.
=fields ADEL, MDEL
=head3 Run-Time Parameters and Simulation Mode Parameters
These parameters are used by the run-time code for processing the data
fanout record. Ther are not configurable. They are used to implement the
hysteresis factors for monitor callbacks.
=fields LALM, ALST, MLST
=cut
include "dbCommon.dbd"
field(VAL,DBF_DOUBLE) {
prompt("Desired Output")
promptgroup("40 - Input")
asl(ASL0)
pp(TRUE)
}
field(SELM,DBF_MENU) {
prompt("Select Mechanism")
promptgroup("30 - Action")
interest(1)
menu(dfanoutSELM)
}
field(SELN,DBF_USHORT) {
prompt("Link Selection")
interest(1)
initial("1")
}
field(SELL,DBF_INLINK) {
prompt("Link Selection Loc")
promptgroup("30 - Action")
interest(1)
}
field(OUTA,DBF_OUTLINK) {
prompt("Output Spec A")
promptgroup("50 - Output")
interest(1)
}
field(OUTB,DBF_OUTLINK) {
prompt("Output Spec B")
promptgroup("50 - Output")
interest(1)
}
field(OUTC,DBF_OUTLINK) {
prompt("Output Spec C")
promptgroup("50 - Output")
interest(1)
}
field(OUTD,DBF_OUTLINK) {
prompt("Output Spec D")
promptgroup("50 - Output")
interest(1)
}
field(OUTE,DBF_OUTLINK) {
prompt("Output Spec E")
promptgroup("50 - Output")
interest(1)
}
field(OUTF,DBF_OUTLINK) {
prompt("Output Spec F")
promptgroup("50 - Output")
interest(1)
}
field(OUTG,DBF_OUTLINK) {
prompt("Output Spec G")
promptgroup("50 - Output")
interest(1)
}
field(OUTH,DBF_OUTLINK) {
prompt("Output Spec H")
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("Engineering Units")
promptgroup("80 - Display")
interest(1)
size(16)
prop(YES)
}
field(PREC,DBF_SHORT) {
prompt("Display Precision")
promptgroup("80 - Display")
interest(1)
prop(YES)
}
field(HOPR,DBF_DOUBLE) {
prompt("High Operating Range")
promptgroup("80 - Display")
interest(1)
prop(YES)
}
field(LOPR,DBF_DOUBLE) {
prompt("Low Operating Range")
promptgroup("80 - Display")
interest(1)
prop(YES)
}
field(HIHI,DBF_DOUBLE) {
prompt("Hihi Alarm Limit")
promptgroup("70 - Alarm")
pp(TRUE)
interest(1)
prop(YES)
}
field(LOLO,DBF_DOUBLE) {
prompt("Lolo Alarm Limit")
promptgroup("70 - Alarm")
pp(TRUE)
interest(1)
prop(YES)
}
field(HIGH,DBF_DOUBLE) {
prompt("High Alarm Limit")
promptgroup("70 - Alarm")
pp(TRUE)
interest(1)
prop(YES)
}
field(LOW,DBF_DOUBLE) {
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)
prop(YES)
menu(menuAlarmSevr)
}
field(LLSV,DBF_MENU) {
prompt("Lolo Severity")
promptgroup("70 - Alarm")
pp(TRUE)
interest(1)
prop(YES)
menu(menuAlarmSevr)
}
field(HSV,DBF_MENU) {
prompt("High Severity")
promptgroup("70 - Alarm")
pp(TRUE)
interest(1)
prop(YES)
menu(menuAlarmSevr)
}
field(LSV,DBF_MENU) {
prompt("Low Severity")
promptgroup("70 - Alarm")
pp(TRUE)
interest(1)
prop(YES)
menu(menuAlarmSevr)
}
field(HYST,DBF_DOUBLE) {
prompt("Alarm Deadband")
promptgroup("70 - Alarm")
interest(1)
}
field(ADEL,DBF_DOUBLE) {
prompt("Archive Deadband")
promptgroup("80 - Display")
interest(1)
}
field(MDEL,DBF_DOUBLE) {
prompt("Monitor Deadband")
promptgroup("80 - Display")
interest(1)
}
field(LALM,DBF_DOUBLE) {
prompt("Last Value Alarmed")
special(SPC_NOMOD)
interest(3)
}
field(ALST,DBF_DOUBLE) {
prompt("Last Value Archived")
special(SPC_NOMOD)
interest(3)
}
field(MLST,DBF_DOUBLE) {
prompt("Last Val Monitored")
special(SPC_NOMOD)
interest(3)
}
=head2 Record Support
=head3 Record Support Routines
=head2 C<init_record()>
This routine initializes all output links that are defined. Then it initializes
DOL if DOL is a constant or a PV_LINK. When initializing the output links
and the DOL link, a non-zero value is returned if an error occurs.
=head2 C<process()>
See next section.
=head2 C<get_value()>
This routine fills in the members of C<struct valueDes> with the VAL fields
value and characteristics.
=head2 C<get_units()>
The routine copies the string specified in the EGU field to the location
specified by a pointer which is passed to the routine.
=head2 C<get_graphic_double()>
If the referenced field is VAL, HIHI, HIGH, LOW, or LOLO, this routine sets
the C<upper_disp_limit> member of the C<dbr_grDouble> structure to the
HOPR and the C<lower_disp_limit> member to the LOPR. If the referenced
field is not one of the above fields, then C<recGblGetControlDouble()>
routine is called.
=head2 C<get_control_double()>
Same as the C<get_graphic_double> routine except that it uses the
C<dbr_ctrlDouble> structure.
=head2 C<get_alarm_double()>
This sets the members of the C<dbr_alDouble> structure to the specified
alarm limits if the referenced field is VAL:
=over
upper_alarm_limit = HIHI
upper_warning_limit = HIGH
lower_warning_limit = LOW
lower_alarm_limit = LOLO
=back
If the referenced field is not VAL, the C<recGblGetAlarmDouble()> routine
is called.
=head3 Record Processing
=over
=item 1.
The C<process()> routine first retrieves a value for DOL and places it in
VAL if OMSL is set to colsed loop mode. If an error occurs, then UDF is set
to FALSE.
=item 2.
PACT is set TRUE
=item 3.
VAL is then sent to all the records specified in the OUTA-OUTH fields by
calling C<recGblePutLinkValue()> for each link.
=item 4.
Alarms are checked and monitors are called if conditions apply.
=item 5.
The data fanout's own forward link is then processed.
=item 6.
PACT is set FALSE, and the C<process()> routine returns. A -1 is returned
if there was an error writing values to one of the output links.
=back
=cut
}
+6 -7
View File
@@ -183,17 +183,16 @@ static long special(DBADDR *paddr, int after)
#define indexof(field) int64inRecord##field
static long get_units(DBADDR *paddr,char *units)
static long get_units(DBADDR *paddr, char *units)
{
int64inRecord *prec=(int64inRecord *)paddr->precord;
int64inRecord *prec = (int64inRecord *) paddr->precord;
if(paddr->pfldDes->field_type == DBF_LONG) {
strncpy(units,prec->egu,DB_UNITS_SIZE);
if (paddr->pfldDes->field_type == DBF_INT64) {
strncpy(units, prec->egu, DB_UNITS_SIZE);
}
return(0);
return 0;
}
static long get_graphic_double(DBADDR *paddr, struct dbr_grDouble *pgd)
{
int64inRecord *prec=(int64inRecord *)paddr->precord;
@@ -255,7 +254,7 @@ static long get_alarm_double(DBADDR *paddr, struct dbr_alDouble *pad)
static void checkAlarms(int64inRecord *prec, epicsTimeStamp *timeLast)
{
enum {
enum {
range_Lolo = 1,
range_Low,
range_Normal,
+10 -8
View File
@@ -2,17 +2,17 @@
* Copyright (c) 2002 Lawrence Berkeley Laboratory,The Control Systems
* Group, Systems Engineering Department
* EPICS BASE is distributed subject to a Software License Agreement found
* in file LICENSE that is included with this distribution.
* in file LICENSE that is included with this distribution.
\*************************************************************************/
/* recSubArray.c - Record Support Routines for SubArray records
/* recSubArray.c - Record Support Routines for SubArray records
*
*
* Author: Carl Lionberger
* Date: 090293
*
* NOTES:
* Derived from waveform record.
* Derived from waveform record.
* Modification Log:
* -----------------
*/
@@ -126,7 +126,7 @@ static long init_record(struct dbCommon *pcommon, int pass)
}
if (pdset->init_record)
return (*pdset->init_record)(prec);
return pdset->init_record(prec);
return 0;
}
@@ -194,11 +194,14 @@ static long get_array_info(DBADDR *paddr, long *no_elements, long *offset)
static long put_array_info(DBADDR *paddr, long nNew)
{
subArrayRecord *prec = (subArrayRecord *) paddr->precord;
epicsUInt32 nord = prec->nord;
if (nNew > prec->malm)
nNew = prec->malm;
prec->nord = nNew;
if (prec->nord > prec->malm)
prec->nord = prec->malm;
if (nord != prec->nord)
db_post_events(prec, &prec->nord, DBE_VALUE | DBE_LOG);
return 0;
}
@@ -211,7 +214,7 @@ static long get_units(DBADDR *paddr, char *units)
switch (dbGetFieldIndex(paddr)) {
case indexof(VAL):
if (prec->ftvl == DBF_STRING || prec->ftvl == DBF_ENUM)
break;
break;
case indexof(HOPR):
case indexof(LOPR):
strncpy(units,prec->egu,DB_UNITS_SIZE);
@@ -321,4 +324,3 @@ static long readValue(subArrayRecord *prec)
return status;
}
+43 -34
View File
@@ -4,7 +4,7 @@
* 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.
* in file LICENSE that is included with this distribution.
\*************************************************************************/
/* recWaveform.c - Record Support Routines for Waveform records */
@@ -97,18 +97,14 @@ static long init_record(struct dbCommon *pcommon, int pass)
struct waveformRecord *prec = (struct waveformRecord *)pcommon;
struct wfdset *pdset;
if (pass == 0){
if (pass == 0) {
if (prec->nelm <= 0)
prec->nelm = 1;
if (prec->ftvl > DBF_ENUM)
prec->ftvl = DBF_UCHAR;
prec->bptr = callocMustSucceed(prec->nelm, dbValueSize(prec->ftvl),
"waveform calloc failed");
if (prec->nelm == 1) {
prec->nord = 1;
} else {
prec->nord = 0;
}
prec->nord = (prec->nelm == 1);
return 0;
}
@@ -124,16 +120,18 @@ static long init_record(struct dbCommon *pcommon, int pass)
recGblRecordError(S_dev_missingSup,(void *)prec,"wf: init_record");
return S_dev_missingSup;
}
if (! pdset->init_record) return 0;
if (!pdset->init_record)
return 0;
return (*pdset->init_record)(prec);
return pdset->init_record(prec);
}
static long process(struct dbCommon *pcommon)
{
struct waveformRecord *prec = (struct waveformRecord *)pcommon;
struct wfdset *pdset = (struct wfdset *)(prec->dset);
unsigned char pact=prec->pact;
unsigned char pact=prec->pact;
long status;
if ((pdset==NULL) || (pdset->read_wf==NULL)) {
prec->pact=TRUE;
@@ -141,13 +139,14 @@ static long process(struct dbCommon *pcommon)
return S_dev_missingSup;
}
if (pact && prec->busy) return 0;
if (pact && prec->busy)
return 0;
readValue(prec); /* read the new value */
if (!pact && prec->pact) return 0;
status = readValue(prec); /* read the new value */
if (!pact && prec->pact)
return 0;
prec->udf = FALSE;
prec->pact = TRUE;
recGblGetTimeStampSimm(prec, prec->simm, &prec->siol);
monitor(prec);
@@ -156,26 +155,27 @@ static long process(struct dbCommon *pcommon)
recGblFwdLink(prec);
prec->pact=FALSE;
return 0;
return status;
}
static long special(DBADDR *paddr, int after)
{
waveformRecord *prec = (waveformRecord *)(paddr->precord);
int special_type = paddr->special;
int special_type = paddr->special;
switch(special_type) {
case(SPC_MOD):
case SPC_MOD:
if (dbGetFieldIndex(paddr) == waveformRecordSIMM) {
if (!after)
recGblSaveSimm(prec->sscn, &prec->oldsimm, prec->simm);
else
recGblCheckSimm((dbCommon *)prec, &prec->sscn, prec->oldsimm, prec->simm);
return(0);
recGblCheckSimm((dbCommon *)prec, &prec->sscn, prec->oldsimm,
prec->simm);
return 0;
}
default:
recGblDbaddrError(S_db_badChoice, paddr, "waveform: special");
return(S_db_badChoice);
return S_db_badChoice;
}
}
@@ -205,12 +205,14 @@ static long get_array_info(DBADDR *paddr, long *no_elements, long *offset)
static long put_array_info(DBADDR *paddr, long nNew)
{
waveformRecord *prec = (waveformRecord *) paddr->precord;
epicsUInt32 nord = prec->nord;
prec->nord = nNew;
if (prec->nord > prec->nelm)
prec->nord = prec->nelm;
db_post_events(prec, &prec->nord, DBE_VALUE | DBE_LOG);
if (nord != prec->nord)
db_post_events(prec, &prec->nord, DBE_VALUE | DBE_LOG);
return 0;
}
@@ -223,7 +225,7 @@ static long get_units(DBADDR *paddr, char *units)
switch (dbGetFieldIndex(paddr)) {
case indexof(VAL):
if (prec->ftvl == DBF_STRING || prec->ftvl == DBF_ENUM)
break;
break;
case indexof(HOPR):
case indexof(LOPR):
strncpy(units,prec->egu,DB_UNITS_SIZE);
@@ -323,15 +325,17 @@ static void monitor(waveformRecord *prec)
db_post_events(prec, &prec->val, monitor_mask);
}
}
static long readValue(waveformRecord *prec)
{
struct wfdset *pdset = (struct wfdset *) prec->dset;
long status = 0;
if (!prec->pact) {
status = recGblGetSimm((dbCommon *)prec, &prec->sscn, &prec->oldsimm, &prec->simm, &prec->siml);
if (status) return status;
status = recGblGetSimm((dbCommon *)prec, &prec->sscn, &prec->oldsimm,
&prec->simm, &prec->siml);
if (status)
return status;
}
switch (prec->simm) {
@@ -348,22 +352,27 @@ static long readValue(waveformRecord *prec)
long nRequest = prec->nelm;
recGblSetSevr(prec, SIMM_ALARM, prec->sims);
if (prec->pact || (prec->sdly < 0.)) {
if (prec->pact || (prec->sdly < 0)) {
status = dbGetLink(&prec->siol, prec->ftvl, prec->bptr, 0, &nRequest);
if (status == 0) prec->udf = FALSE;
/* nord set only for db links: needed for old db_access */
if (!dbLinkIsConstant(&prec->siol)) {
if (status == 0)
prec->udf = FALSE;
if (nRequest != prec->nord) {
prec->nord = nRequest;
db_post_events(prec, &prec->nord, DBE_VALUE | DBE_LOG);
}
prec->pact = FALSE;
} else { /* !prec->pact && delay >= 0. */
}
else { /* !prec->pact && delay >= 0 */
CALLBACK *pvt = prec->simpvt;
if (!pvt) {
pvt = calloc(1, sizeof(CALLBACK)); /* very lazy allocation of callback structure */
if (!pvt) { /* very lazy allocation of callback structure */
pvt = calloc(1, sizeof(CALLBACK));
prec->simpvt = pvt;
}
if (pvt) callbackRequestProcessCallbackDelayed(pvt, prec->prio, prec, prec->sdly);
if (pvt)
callbackRequestProcessCallbackDelayed(pvt, prec->prio, prec,
prec->sdly);
prec->pact = TRUE;
}
break;
@@ -1,5 +1,4 @@
eval 'exec perl -S $0 ${1+"$@"}' # -*- Mode: perl -*-
if $running_under_some_shell;
#!/usr/bin/env perl
#*************************************************************************
# Copyright (c) 2010 UChicago Argonne LLC, as Operator of Argonne
# National Laboratory.
+7 -2
View File
@@ -97,7 +97,7 @@ sub ParseDBD {
my $rtyp = $dbd->recordtype($record_type);
if (!defined $rtyp) {
$rtyp = DBD::Recordtype->new($record_type);
warn "Device using undefined record type '$record_type', place-holder created\n";
warn "Device using unknown record type '$record_type', declaration created\n";
$dbd->add($rtyp);
}
$rtyp->add_device(DBD::Device->new($link_type, $dset, $choice));
@@ -218,7 +218,12 @@ sub parse_breaktable {
sub parse_recordtype {
my ($dbd, $record_type) = @_;
pushContext("recordtype($record_type)");
my $rtyp = DBD::Recordtype->new($record_type);
# Re-use a matching declaration record type if one exists
my $rtyp = $dbd->recordtype($record_type);
if (!defined($rtyp) || $rtyp->fields) {
# Earlier record type is not a declaration, don't re-use it
$rtyp = DBD::Recordtype->new($record_type);
}
while(1) {
parseCommon($rtyp);
if (m/\G field \s* \( \s* $RXstr \s* , \s* $RXstr \s* \) \s* \{/xgc) {
+12 -1
View File
@@ -130,7 +130,18 @@ sub attribute {
}
sub equals {
dieContext("Record field objects are not comparable");
my ($l, $r) = @_;
return 1 if $l eq $r;
return 0 if
$l->{NAME} ne $r->{NAME} ||
$l->{DBF_TYPE} ne $r->{DBF_TYPE};
my ($la, $ra) = ($l->{ATTR_INDEX}, $r->{ATTR_INDEX});
my @keys = sort keys %$la;
return 0 if join(',', @keys) ne join(',', sort keys %$ra);
foreach my $k (@keys) {
return 0 if $la->{$k} ne $ra->{$k};
}
return 1;
}
sub check_valid {
+13 -3
View File
@@ -104,9 +104,19 @@ sub pod {
sub equals {
my ($new, $known) = @_;
return 0 if ! $known->fields;
return 1 if ! $new->fields;
dieContext("Duplicate definition of record type '$known->{NAME}'");
return 1 if $new eq $known;
return 0 if $new->{NAME} ne $known->{NAME};
return 1 if ! $new->fields; # Later declarations always match
# NB: Definition after declaration is handled in parse_recordtype()
my @nf = @{$new->{FIELD_LIST}};
my @kf = @{$known->{FIELD_LIST}};
return 0 if scalar @nf != scalar @kf;
while (@nf) {
my $nf = shift @nf;
my $kf = shift @kf;
return 0 if ! $nf->equals($kf);
}
return 1;
}
sub toDeclaration {
+489
View File
@@ -0,0 +1,489 @@
######################################################################
# EPICS BASE is distributed subject to a Software License Agreement
# found in file LICENSE that is included with this distribution.
#
# Original Author: Shantha Condamoor, SLAC
# Creation Date: 1-Sep-2011
# Current Author: Andrew Johnson
use strict;
use warnings;
package EPICS::IOC;
require 5.010;
=head1 NAME
EPICS::IOC - Manage an EPICS IOC
=head1 SYNOPSIS
use EPICS::IOC;
my $ioc = EPICS::IOC->new;
$ioc->debug(1); # Show IOC stdio streams
$ioc->start('bin/@ARCH@/ioc', 'iocBoot/ioc/st.cmd');
$ioc->cmd; # Wait for the iocsh prompt
my @records = $ioc->dbl;
my @values = map { $ioc->dbgf($_); } @records;
$ioc->kill;
=head1 DESCRIPTION
This module provides an object-oriented API for starting, interacting with and
stopping one or more EPICS IOCs under program control, and is generally intended
for writing test programs.
The IOC should not be configured to emit unsolicited messages on stdout as this
could interfere with the ability of the software to detect an end-of-command
from the IOC shell, which is achieved by setting the prompt to a known string
(normally C<__END__>). Unsolicited messages on stderr will not cause problems,
but can't be seen on Windows systems.
=head1 CONSTRUCTOR
=over 4
=cut
use Symbol 'gensym';
use IPC::Open3;
use IO::Select;
=item new ()
Calling C<new> creates an C<EPICS::IOC> object that can be used to start and
interact with a single IOC. After this IOC has been shut down (by calling its
C<kill> method) the C<EPICS::IOC> object may be reused for another IOC.
=back
=cut
sub new {
my $proto = shift;
my $class = ref $proto || $proto;
my $self = {
pid => undef,
stdin => gensym,
stdout => gensym,
stderr => gensym,
select => IO::Select->new(),
errbuf => '',
debug => 0,
terminator => '__END__'
};
bless $self, $class;
}
=head1 METHODS
=over 4
=item debug ( [FLAG] )
Each C<EPICS::IOC> object has its own debug flag which when non-zero causes all
IOC console traffic sent or read by other methods to be printed to stdout along
with the IOC's pid and a direction indicator. The C<debug> method optionally
sets and returns the value of this flag.
The optional FLAG is treated as a true/false value. If provided this sets the
debug flag value.
The method's return value is the current (new if given) value of the flag.
=cut
sub debug {
my $self = shift;
$self->{debug} = shift if scalar @_;
return $self->{debug};
}
=item start ( EXECUTABLE [, ARGUMENTS ...] )
Launch an IOC binary given by EXECUTABLE with ARGUMENTS. The method dies if it
can't run the program as given, or if the IOC is already running.
In most cases the C<cmd> method should be called next with no command string,
which waits for the IOC's boot process to finish and the first iocsh prompt to
be displayed.
The C<start> method sets two environment variables that control how the IOC
shell behaves: C<IOCSH_HISTEDIT_DISABLE> is set to prevent it calling the GNU
Readline library, and C<IOCSH_PS1> is set to a known string which is used as a
terminator for the previous command.
=cut
sub start {
my ($self, $exe, @args) = @_;
croak("IOC already running") if $self->started;
# Turn off readline or its equivalents
local $ENV{IOCSH_HISTEDIT_DISABLE} = "TRUE";
# The iocsh prompt marks the end of the previous command
local $ENV{IOCSH_PS1} = $self->{terminator} . "\n";
# Run the IOC as a child process
$self->{pid} = open3($self->{stdin}, $self->{stdout}, $self->{stderr},
$exe, @args)
or die "can't start $exe: $!";
$self->{select}->add($self->{stderr});
printf "#%d running %s\n", $self->{pid}, $exe if $self->{debug};
}
=item pid ()
Returns the process-ID of the IOC process, or undef if the IOC process has not
yet been started.
=cut
sub pid {
my $self = shift;
return $self->{pid};
}
=item started ()
Returns a true value if the IOC has been started and not yet killed. This state
will not change if the IOC dies by itself, it indicates that the start method
has been called without the kill method.
=cut
sub started {
my $self = shift;
return defined($self->pid);
}
=item _send ( COMMAND )
The C<_send> method is a primitive for internal use that sends a COMMAND string
to the IOC shell, and prints it to stdout if the debug flag is set.
=cut
sub _send {
my ($self, $cmd) = @_;
my $stdin = $self->{stdin};
printf "#%d << %s", $self->{pid}, $cmd if $self->{debug};
local $SIG{PIPE} = sub {
printf "#%d << <PIPE>\n", $self->{pid} if $self->{debug};
};
print $stdin $cmd;
}
=item _getline ()
The C<_getline> method is also designed for internal use, it fetches a single
line output by the IOC, and prints it to stdout if the debug flag is set.
Any CR/LF is stripped from the line before returning it. If the stream gets
closed because the IOC shuts down an C<EOF> debug message may be shown and an
undef value will be returned.
=cut
sub _getline {
my $self = shift;
my $line = readline $self->{stdout};
if (defined $line) {
$line =~ s/[\r\n]+ $//x; # chomp broken on Windows?
printf "#%d >> %s\n", $self->{pid}, $line if $self->{debug};
}
elsif (eof($self->{stdout})) {
printf "#%d >> <EOF>\n", $self->{pid} if $self->{debug};
}
else {
printf "#%d Error: %s\n", $self->{pid}, $! if $self->{debug};
}
return $line;
}
=item _getlines ( [TERM] )
Another internal method C<_getlines> fetches multiple lines from the IOC. It
takes an optional TERM string or regexp parameter which is matched against each
input line in turn to determine when the IOC's output has been completed.
Termination also occurs on an EOF from the output stream.
The return value is a list of all the lines received (with the final CR/LF
stripped) including the line that matched the terminator.
=cut
sub _getlines {
my ($self, $term) = @_;
my @response = ();
while (my $line = $self->_getline) {
push @response, $line;
last if defined $term && $line =~ $term;
}
return @response;
}
=item _geterrors ( )
Returns a list of lines output by the IOC to stderr since last called. Only
complete lines are included, and trailing newlines have been removed.
NOTE: This doesn't work on Windows because it uses select which Perl doesn't
support on that OS, but it doesn't seem to cause any problems for short-lived
IOCs at least, it just never returns any text from the IOC's stderr output.
=cut
sub _geterrors {
my ($self) = @_;
my @errors;
while ($self->{select}->can_read(0.01)) {
sysread $self->{stderr}, my $errbuf, 1024;
push @errors, split m/\n/, $self->{errbuf} . $errbuf, -1;
last unless @errors;
$self->{errbuf} = pop @errors;
}
return @errors;
}
=item cmd ( [COMMAND [, ARGUMENTS ...]] )
If the C<cmd> method is given an optional COMMAND string along with any number
of ARGUMENTS it constructs a command-line, quoting each argument as necessary.
This is sent to the IOC and a line read back and discarded if it matches the
command-line.
With no COMMAND string the method starts here; it then collects lines from the
IOC until one matches the terminator. A list of all the lines received prior to
the terminator line is returned.
=cut
sub cmd {
my ($self, $cmd, @args) = @_;
my @response;
my $term = $self->{terminator};
if (defined $cmd) {
if (@args) {
# FIXME: This quoting stuff isn't quite right
my @qargs = map {
m/^ (?: -? [0-9.eE+\-]+ ) | (?: " .* " ) $/x ? $_ : "'$_'"
} @args;
$cmd .= ' ' . join(' ', @qargs);
}
$self->_send("$cmd\n");
my $echo = $self->_getline;
return @response unless defined $echo; # undef => reached EOF
if ($echo ne $cmd) {
return @response if $echo =~ $term;
push @response, $echo;
}
}
push @response, $self->_getlines($term);
pop @response if @response and $response[-1] =~ $term;
my @errors = $self->_geterrors;
if (scalar @errors && $self->{debug}) {
my $indent = sprintf "#%d e>", $self->{pid};
print map {"$indent $_\n"} @errors;
}
return @response;
}
=item kill ()
The C<kill> method attempts to stop an IOC that is still running in several
ways. First it sends an C<exit> command to the IOC shell. Next it closes the
IOC's stdin stream which will trigger an end-of-file on that stream, and it
fetches any remaining lines from the IOC's stdout stream before closing both
that and the stderr stream. Finally (unless running on MS-Windows) it sends a
SIGTERM signal to the child process and waits for it to clean up.
=cut
sub kill {
my $self = shift;
return ()
unless $self->started;
$self->_send("exit\n"); # Don't wait
close $self->{stdin};
$self->{stdin} = gensym;
my @response = $self->_getlines; # No terminator
close $self->{stdout};
$self->{stdout} = gensym;
$self->{select}->remove($self->{stderr});
close $self->{stderr};
$self->{stderr} = gensym;
if ($^O ne "MSWin32") {
kill 'TERM', $self->{pid};
waitpid $self->{pid}, 0;
}
$self->{pid} = undef;
return @response;
}
=item DESTROY ()
C<EPICS::IOC> objects have a destructor which calls the C<kill> method, but it
is not recommended that this be relied on to terminate an IOC process. Better to
use an C<END {}> block and/or trap the necessary signals to explicitly kill the
IOC.
=cut
sub DESTROY {
shift->kill;
}
=back
=head1 CONVENIENCE METHODS
The following methods provide an easy way to perform various common IOC
operations.
=over 4
=item dbLoadRecords ( FILE [, MACROS] )
Instructs the IOC to load a database (.db) from FILE. If provided, the MACROS
parameter is a single string containing one or more comma-separated assignment
statements like C<a=1> for macros that are used in the database file.
This method can also be used to load a database definition (.dbd) file.
=cut
sub dbLoadRecords {
my ($self, $file, $macros) = @_;
$macros = '' unless defined $macros;
$self->cmd('dbLoadRecords', $file, $macros);
}
=item iocInit ()
Start the IOC executing.
=cut
sub iocInit {
shift->cmd('iocInit');
}
=item dbl ( [RECORDTYPE])
This method uses the C<dbl> command to fetch a list of all of the record names
the IOC has loaded. If a RECORDTYPE name is given, the list will only comprise
records of that type.
=cut
sub dbl {
my ($self, $rtyp) = @_;
return $self->cmd('dbl', $rtyp)
}
=item dbgf ( PV )
The C<dbgf> method returns the value of the process variable PV, or C<undef> if
the PV doesn't exist. This only works when the PV holds a scalar or an array
with one element.
=cut
# Regexps for the output from dbgf, currently supporting scalars only
my $RXdbfstr = qr/ DB[FR]_(STRING) : \s* " ( (?> \\. | [^"\\] )* ) " /x;
my $RXdbfint = qr/ DB[FR]_(U?(?:CHAR|SHORT|LONG|INT64)) : \s* ( -? [0-9]+ ) /x;
my $RXdbfflt = qr/ DB[FR]_(FLOAT|DOUBLE) : \s* ( [0-9.eE+\-]+ ) /x;
my $RXdbf = qr/ (?| $RXdbfstr | $RXdbfint | $RXdbfflt ) /x;
sub dbgf {
my ($self, $pv) = @_;
my @res = $self->cmd('dbgf', $pv);
return undef unless scalar @res;
my ($type, $result) = ($res[0] =~ m/^ $RXdbf /x);
$result =~ s/\\([\\"'])/$1/gx
if $type eq 'STRING';
return $result;
}
=item dbpf ( PV, VALUE )
This method sets PV to VALUE, and returns the new value, or C<undef> if the PV
doesn't exist. If the put fails the return value is the previous value of the
PV. As with the C<dbgf> method this only works for scalar or single-element
arrays, but PV may be an array field which will be set to one element.
=cut
sub dbpf {
my ($self, $pv, $val) = @_;
my @res = $self->cmd('dbpf', $pv, $val);
return undef unless scalar @res;
my ($type, $result) = ($res[0] =~ m/^ $RXdbf /x);
$result =~ s/\\([\\"'])/$1/gx
if $type eq 'STRING';
return $result;
}
=back
=head1 COPYRIGHT AND LICENSE
Portions Copyright (C) 2011 UChicago Argonne LLC, as Operator of Argonne
National Laboratory.
This software is distributed under the terms of the EPICS Open License.
=cut
1;
__END__
+27 -1
View File
@@ -1,5 +1,5 @@
#*************************************************************************
# Copyright (c) 2012 UChicago Argonne LLC, as Operator of Argonne
# Copyright (c) 2018 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.
@@ -24,6 +24,9 @@ PERL_MODULES += DBD/Record.pm
PERL_MODULES += DBD/Registrar.pm
PERL_MODULES += DBD/Variable.pm
PERL_MODULES += EPICS/IOC.pm
HTMLS += EPICS/IOC.html
PERL_SCRIPTS += databaseModuleDirs.pm
PERL_SCRIPTS += makeIncludeDbd.pl
@@ -37,4 +40,27 @@ PERL_SCRIPTS += registerRecordDeviceDriver.pl
HTMLS += dbdToHtml.html
# Build Package Config Files
FINAL_LOCATION ?= $(shell $(PERL) $(TOOLS)/fullPathName.pl $(INSTALL_LOCATION))
C_CFLAGS += $(filter-out -g,$(filter-out -O%,$(filter-out -W%,$(CPPFLAGS))))
C_CFLAGS += $(filter-out -g,$(filter-out -O%,$(filter-out -W%,$(CFLAGS))))
PKGVARS += FINAL_LOCATION OS_CLASS CMPLR_CLASS C_CFLAGS LDFLAGS LDLIBS
PKGVARS += EPICS_VERSION EPICS_REVISION EPICS_MODIFICATION EPICS_PATCH_LEVEL
PKGVARS += CC CCC CPP AR LD
PKGVARS += EPICS_BASE_HOST_LIBS EPICS_BASE_IOC_LIBS
EXPANDFLAGS += $(foreach var,$(PKGVARS),-D$(var)="$(strip $($(var)))")
PKGCONFIG += epics-base-$(T_A).pc
ifeq ($(T_A),$(EPICS_HOST_ARCH))
PKGCONFIG += epics-base.pc
endif
EXPAND += $(PKGCONFIG:%=%@)
CLEANS += epics-base-$(T_A).pc@
include $(TOP)/configure/RULES
epics-base-$(T_A).pc@: ../epics-base-arch.pc@
@$(RM) $@
@$(CP) $< $@
+31 -18
View File
@@ -127,6 +127,33 @@ if ($opt_D) { # Output dependencies only
open my $out, '>', $opt_o or
die "Can't create $opt_o: $!\n";
my $podHtml;
my $idify;
if ($::XHTML) {
$podHtml = Pod::Simple::XHTML->new();
$podHtml->html_doctype(<< '__END_DOCTYPE');
<?xml version='1.0' encoding='iso-8859-1'?>
<!DOCTYPE html PUBLIC '-//W3C//DTD XHTML 1.0 Transitional//EN'
'http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd'>
__END_DOCTYPE
$podHtml->html_header_tags($podHtml->html_header_tags .
"\n<link rel='stylesheet' href='style.css' type='text/css'>");
$idify = sub {
my $title = shift;
return $podHtml->idify($title, 1);
}
} else { # Fall back to HTML
$podHtml = Pod::Simple::HTML->new();
$podHtml->html_css('style.css');
$idify = sub {
my $title = shift;
return Pod::Simple::HTML::esc($podHtml->section_escape($title));
}
}
# Parse the Pod text from the root DBD object
my $pod = join "\n", '=for html <div class="pod">', '',
map {
@@ -156,22 +183,6 @@ my $pod = join "\n", '=for html <div class="pod">', '',
} $dbd->pod,
'=for html </div>', '';
my $podHtml;
if ($::XHTML) {
$podHtml = Pod::Simple::XHTML->new();
$podHtml->html_doctype(<< '__END_DOCTYPE');
<?xml version='1.0' encoding='iso-8859-1'?>
<!DOCTYPE html PUBLIC '-//W3C//DTD XHTML 1.0 Transitional//EN'
'http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd'>
__END_DOCTYPE
$podHtml->html_header_tags($podHtml->html_header_tags .
"\n<link rel='stylesheet' href='style.css' type='text/css'>");
} else { # Fall back to HTML
$podHtml = Pod::Simple::HTML->new();
$podHtml->html_css('style.css');
}
$podHtml->force_title(encode_entities($title));
$podHtml->perldoc_url_prefix('');
$podHtml->perldoc_url_postfix('.html');
@@ -249,7 +260,7 @@ sub fieldTableRow {
if ($type eq 'MENU') {
my $mn = $fld->attribute('menu');
my $menu = $dbd->menu($mn);
my $url = $menu ? "#Menu_$mn" : "${mn}.html";
my $url = $menu ? '#' . &$idify("Menu $mn") : "${mn}.html";
$html .= " (<a href='$url'>$mn</a>)";
}
$html .= '</td><td class="cell">';
@@ -377,6 +388,8 @@ can be found in the aai and aSub record types.
If you look at the L<aoRecord.dbd.pod> file you'll see that the POD there starts
by documenting a record-specific menu definition. The "menu" keyword generates a
table that lists all the choices found in the named menu.
table that lists all the choices found in the named menu. Any MENU fields in the
field tables that refer to a locally-defined menu will generate a link to a
document section which must be titled "Menu [menuName]".
=cut
@@ -0,0 +1,42 @@
# standard variables
prefix=@FINAL_LOCATION@
exec_prefix=${prefix}
bindir=${prefix}/bin/@ARCH@
libdir=${prefix}/lib/@ARCH@
# non-standard variables
# EPICS Base install location
FINAL_LOCATION=${prefix}
ARCH=@ARCH@
OS_CLASS=@OS_CLASS@
CMPLR_CLASS=@CMPLR_CLASS@
EPICS_BASE_HOST_LIBS=@EPICS_BASE_HOST_LIBS@
EPICS_BASE_IOC_LIBS=@EPICS_BASE_IOC_LIBS@
# Directories
includedir_osi=${prefix}/include
includedir_osd=${prefix}/include/os/@OS_CLASS@
includedir_comp=${prefix}/include/compiler/@CMPLR_CLASS@
includedirs=${includedir_osi} ${includedir_osd} ${includedir_comp}
dbddir=${prefix}/dbd
dbdir=${prefix}/db
# Tool chain
CC=@CC@
CXX=@CCC@
CPP=@CPP@
AR=@AR@
LD=@LD@
Name: epics-base-@ARCH@
Version: @EPICS_VERSION@.@EPICS_REVISION@.@EPICS_MODIFICATION@.@EPICS_PATCH_LEVEL@
Description: EPICS Base for @ARCH@
Cflags: -I${includedir_osi} -I${includedir_osd} -I${includedir_comp} @C_CFLAGS@
Libs: -L${libdir} @LDFLAGS@
Libs.private: @LDLIBS@
+29
View File
@@ -0,0 +1,29 @@
# standard variables
prefix=@FINAL_LOCATION@
exec_prefix=${prefix}
bindir=${prefix}/bin/@ARCH@
libdir=${prefix}/lib/@ARCH@
# non-standard variables
# EPICS Base install location
FINAL_LOCATION=${prefix}
ARCH=@ARCH@
OS_CLASS=@OS_CLASS@
CMPLR_CLASS=@CMPLR_CLASS@
# Directories
includedir_osi=${prefix}/include
includedir_osd=${prefix}/include/os/@OS_CLASS@
includedir_comp=${prefix}/include/compiler/@CMPLR_CLASS@
includedirs=${includedir_osi} ${includedir_osd} ${includedir_comp}
dbddir=${prefix}/dbd
dbdir=${prefix}/db
Name: epics-base
Version: @EPICS_VERSION@.@EPICS_REVISION@.@EPICS_MODIFICATION@.@EPICS_PATCH_LEVEL@
Description: EPICS Base for the host arch
Requires: epics-base-@ARCH@ = @EPICS_VERSION@.@EPICS_REVISION@.@EPICS_MODIFICATION@.@EPICS_PATCH_LEVEL@