merged with upstream
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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) ||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 */
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -13,6 +13,8 @@
|
||||
#ifndef INCdbLockh
|
||||
#define INCdbLockh
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include "ellLib.h"
|
||||
#include "shareLib.h"
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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];
|
||||
}
|
||||
@@ -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 */
|
||||
+38
-1
@@ -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")
|
||||
@@ -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;
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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 */
|
||||
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -26,3 +26,4 @@ dbCore_SRCS += miscIocRegister.c
|
||||
dbCore_SRCS += dlload.c
|
||||
dbCore_SRCS += iocshRegisterCommon.c
|
||||
|
||||
miscIocRegister_CFLAGS_iOS = -DSYSTEM_UNAVAILABLE
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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,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;
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
@@ -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);
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -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)
|
||||
@@ -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
|
||||
}
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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__
|
||||
@@ -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) $< $@
|
||||
|
||||
@@ -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@
|
||||
@@ -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@
|
||||
Reference in New Issue
Block a user