Files
epics-base/modules/database/src/std/rec/seqRecord.c
Michael Davidsaver 0f8876de67 Merge branch '3.15' into 7.0
* 3.15: (28 commits)
  update RELEASE_NOTES
  add option EPICS_NO_CALLBACK
  replace CALLBACK -> epicsCallback
  Update dbTest.c
  Remove links to wiki-ext
  Add POD annotations from Wiki to subArrayRecord and menuAlarmStat
  Rename subArrayRecord.dbd and menuAlarmStat.dbd to .pod
  Add POD annotations to longoutRecord from Wiki
  Rename longoutRecord.dbd longoutRecord.dbd.pod
  Add POD annotations to longinRecord from Wiki
  Rename longinRecord.dbd longinRecord.dbd.pod
  Add POD annotations to subRecord from Wiki
  Rename subRecord.dbd subRecord.dbd.pod
  Add POD annotations to selRecord from Wiki
  Rename selRecord.dbd selRecord.dbd.pod
  Add POD annotations to seqRecord from Wiki
  Rename seqRecord.dbd seqRecord.dbd.pod
  Fix menu declaration test too
  Add redefinition guard to menu-generated typedefs
  Updates to existing .dbd.pod texts, add event and fanout from wiki
  ...

# Conflicts:
#	documentation/README.1st
#	documentation/README.html
#	modules/database/src/ioc/db/callback.h
#	modules/database/src/ioc/db/dbNotify.c
#	modules/database/src/ioc/db/menuAlarmStat.dbd
#	modules/database/src/ioc/db/menuFtype.dbd
#	modules/database/src/std/rec/compressRecord.dbd.pod
#	modules/database/src/std/rec/eventRecord.dbd
#	modules/database/src/std/rec/fanoutRecord.dbd
#	modules/database/src/std/rec/longinRecord.dbd
#	modules/database/src/std/rec/longoutRecord.dbd
#	modules/database/src/std/rec/selRecord.dbd
#	modules/database/src/std/rec/seqRecord.dbd
#	modules/database/src/std/rec/subArrayRecord.dbd
#	modules/database/src/std/rec/subRecord.dbd
#	modules/libcom/src/iocsh/menuAlarmStat.dbd.pod
#	modules/libcom/src/iocsh/menuFtype.dbd.pod
#	src/ioc/db/menuAlarmStat.dbd
#	src/ioc/db/menuFtype.dbd

Manually fix some move+rename
Make additional CALLBACK -> epicsCallback
preserve INT64 in menuFtype
preserve OLDSIM et al
2019-09-09 19:29:58 -07:00

369 lines
9.7 KiB
C

/*************************************************************************\
* Copyright (c) 2012 UChicago Argonne LLC, as Operator of Argonne
* National Laboratory.
* Copyright (c) 2002 The Regents of the University of California, as
* Operator of Los Alamos National Laboratory.
* EPICS BASE is distributed subject to a Software License Agreement found
* in file LICENSE that is included with this distribution.
\*************************************************************************/
/*
* Author: John Winans
* Date: 09-21-92
*/
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include "alarm.h"
#include "callback.h"
#include "dbAccess.h"
#include "dbEvent.h"
#include "epicsTypes.h"
#include "link.h"
#include "recSup.h"
#include "recGbl.h"
#define GEN_SIZE_OFFSET
#include "seqRecord.h"
#undef GEN_SIZE_OFFSET
#include "epicsExport.h"
static void processNextLink(seqRecord *prec);
static long asyncFinish(seqRecord *prec);
static void processCallback(epicsCallback *arg);
/* Create RSET - Record Support Entry Table*/
#define report NULL
#define initialize NULL
static long init_record(struct dbCommon *prec, int pass);
static long process(struct dbCommon *prec);
#define special NULL
#define get_value NULL
#define cvt_dbaddr NULL
#define get_array_info NULL
#define put_array_info NULL
static long get_units(DBADDR *, char *);
static long get_precision(const DBADDR *paddr, long *);
#define get_enum_str NULL
#define get_enum_strs NULL
#define put_enum_str NULL
static long get_graphic_double(DBADDR *, struct dbr_grDouble *);
static long get_control_double(DBADDR *, struct dbr_ctrlDouble *);
static long get_alarm_double(DBADDR *, struct dbr_alDouble *);
rset seqRSET = {
RSETNUMBER,
report,
initialize,
init_record,
process,
special,
get_value,
cvt_dbaddr,
get_array_info,
put_array_info,
get_units,
get_precision,
get_enum_str,
get_enum_strs,
put_enum_str,
get_graphic_double,
get_control_double,
get_alarm_double
};
epicsExportAddress(rset, seqRSET);
int seqDLYprecision = 2;
epicsExportAddress(int, seqDLYprecision);
double seqDLYlimit = 100000;
epicsExportAddress(double, seqDLYlimit);
/* Total number of link-groups */
#define NUM_LINKS 16
/* Each link-group looks like this */
typedef struct linkGrp {
double dly; /* Delay in seconds */
DBLINK dol; /* Input link */
double dov; /* Value storage */
DBLINK lnk; /* Output link */
} linkGrp;
/* The list of link-groups for processing */
typedef struct seqRecPvt {
epicsCallback callback;
seqRecord *prec;
linkGrp *grps[NUM_LINKS + 1]; /* List of link-groups */
int index; /* Where we are now */
} seqRecPvt;
static long init_record(struct dbCommon *pcommon, int pass)
{
struct seqRecord *prec = (struct seqRecord *)pcommon;
int index;
linkGrp *grp;
seqRecPvt *pseqRecPvt;
if (pass == 0)
return 0;
pseqRecPvt = (seqRecPvt *)calloc(1, sizeof(seqRecPvt));
pseqRecPvt->prec = prec;
callbackSetCallback(processCallback, &pseqRecPvt->callback);
callbackSetUser(pseqRecPvt, &pseqRecPvt->callback);
prec->dpvt = pseqRecPvt;
recGblInitConstantLink(&prec->sell, DBF_USHORT, &prec->seln);
grp = (linkGrp *) &prec->dly0;
for (index = 0; index < NUM_LINKS; index++, grp++) {
recGblInitConstantLink(&grp->dol, DBF_DOUBLE, &grp->dov);
}
prec->oldn = prec->seln;
return 0;
}
static long process(struct dbCommon *pcommon)
{
struct seqRecord *prec = (struct seqRecord *)pcommon;
seqRecPvt *pcb = (seqRecPvt *) prec->dpvt;
linkGrp *pgrp;
epicsUInt16 lmask;
int i;
if (prec->pact)
return asyncFinish(prec);
prec->pact = TRUE;
/* Set callback from PRIO */
callbackSetPriority(prec->prio, &pcb->callback);
if (prec->selm == seqSELM_All)
lmask = (1 << NUM_LINKS) - 1;
else {
/* Get SELN value */
dbGetLink(&prec->sell, DBR_USHORT, &prec->seln, 0, 0);
if (prec->selm == seqSELM_Specified) {
int grpn = prec->seln + prec->offs;
if (grpn < 0 || grpn >= NUM_LINKS) {
recGblSetSevr(prec, SOFT_ALARM, INVALID_ALARM);
return asyncFinish(prec);
}
if (grpn == 0)
return asyncFinish(prec);
lmask = 1 << grpn;
}
else if (prec->selm == seqSELM_Mask) {
int shft = prec->shft;
if (shft < -15 || shft > 15) {
/* Shifting by more than the number of bits in the
* value produces undefined behavior in C */
recGblSetSevr(prec, SOFT_ALARM, INVALID_ALARM);
return asyncFinish(prec);
}
lmask = (shft >= 0) ? prec->seln >> shft : prec->seln << -shft;
}
else {
recGblSetSevr(prec, SOFT_ALARM, INVALID_ALARM);
return asyncFinish(prec);
}
}
/* Figure out which groups are to be processed */
pcb->index = 0;
pgrp = (linkGrp *) &prec->dly0;
for (i = 0; lmask; lmask >>= 1) {
if ((lmask & 1) &&
(!dbLinkIsConstant(&pgrp->lnk) ||
!dbLinkIsConstant(&pgrp->dol))) {
pcb->grps[i++] = pgrp;
}
pgrp++;
}
pcb->grps[i] = NULL; /* mark the end of the list */
if (!i)
return asyncFinish(prec);
/* Start processing link groups (we have at least one) */
processNextLink(prec);
return 0;
}
static void processNextLink(seqRecord *prec)
{
seqRecPvt *pcb = (seqRecPvt *) prec->dpvt;
linkGrp *pgrp = pcb->grps[pcb->index];
if (pgrp == NULL) {
/* None left, finish up. */
prec->rset->process((dbCommon *)prec);
return;
}
/* Always use the callback task to avoid recursion */
if (pgrp->dly > 0.0)
callbackRequestDelayed(&pcb->callback, pgrp->dly);
else
callbackRequest(&pcb->callback);
}
static long asyncFinish(seqRecord *prec)
{
epicsUInt16 events;
prec->udf = FALSE;
recGblGetTimeStamp(prec);
/* post monitors */
events = recGblResetAlarms(prec);
if (events)
db_post_events(prec, &prec->val, events);
if (prec->seln != prec->oldn) {
db_post_events(prec, &prec->seln, events | DBE_VALUE | DBE_LOG);
prec->oldn = prec->seln;
}
/* process the forward scan link record */
recGblFwdLink(prec);
prec->pact = FALSE;
return 0;
}
static void processCallback(epicsCallback *arg)
{
seqRecPvt *pcb;
seqRecord *prec;
linkGrp *pgrp;
double odov;
callbackGetUser(pcb, arg);
prec = pcb->prec;
dbScanLock((struct dbCommon *)prec);
pgrp = pcb->grps[pcb->index];
/* Save the old value */
odov = pgrp->dov;
dbGetLink(&pgrp->dol, DBR_DOUBLE, &pgrp->dov, 0, 0);
recGblGetTimeStamp(prec);
/* Dump the value to the destination field */
dbPutLink(&pgrp->lnk, DBR_DOUBLE, &pgrp->dov, 1);
if (odov != pgrp->dov) {
db_post_events(prec, &pgrp->dov, DBE_VALUE | DBE_LOG);
}
/* Start the next link-group */
pcb->index++;
processNextLink(prec);
dbScanUnlock((struct dbCommon *)prec);
}
#define indexof(field) seqRecord##field
#define get_dol(prec, fieldOffset) \
&((linkGrp *) &prec->dly0)[fieldOffset >> 2].dol
static long get_units(DBADDR *paddr, char *units)
{
seqRecord *prec = (seqRecord *) paddr->precord;
int fieldOffset = dbGetFieldIndex(paddr) - indexof(DLY1);
if (fieldOffset >= 0)
switch (fieldOffset & 2) {
case 0: /* DLYn */
strcpy(units, "s");
break;
case 2: /* DOn */
dbGetUnits(get_dol(prec, fieldOffset),
units, DB_UNITS_SIZE);
}
return 0;
}
static long get_precision(const DBADDR *paddr, long *pprecision)
{
seqRecord *prec = (seqRecord *) paddr->precord;
int fieldOffset = dbGetFieldIndex(paddr) - indexof(DLY1);
short precision;
if (fieldOffset >= 0)
switch (fieldOffset & 2) {
case 0: /* DLYn */
*pprecision = seqDLYprecision;
return 0;
case 2: /* DOn */
if (dbGetPrecision(get_dol(prec, fieldOffset), &precision) == 0) {
*pprecision = precision;
return 0;
}
}
*pprecision = prec->prec;
recGblGetPrec(paddr, pprecision);
return 0;
}
static long get_graphic_double(DBADDR *paddr, struct dbr_grDouble *pgd)
{
seqRecord *prec = (seqRecord *) paddr->precord;
int fieldOffset = dbGetFieldIndex(paddr) - indexof(DLY1);
if (fieldOffset >= 0)
switch (fieldOffset & 2) {
case 0: /* DLYn */
pgd->lower_disp_limit = 0.0;
pgd->lower_disp_limit = 10.0;
return 0;
case 2: /* DOn */
dbGetGraphicLimits(get_dol(prec, fieldOffset),
&pgd->lower_disp_limit,
&pgd->upper_disp_limit);
return 0;
}
recGblGetGraphicDouble(paddr, pgd);
return 0;
}
static long get_control_double(DBADDR *paddr, struct dbr_ctrlDouble *pcd)
{
int fieldOffset = dbGetFieldIndex(paddr) - indexof(DLY1);
if (fieldOffset >= 0 && (fieldOffset & 2) == 0) { /* DLYn */
pcd->lower_ctrl_limit = 0.0;
pcd->upper_ctrl_limit = seqDLYlimit;
}
else
recGblGetControlDouble(paddr, pcd);
return 0;
}
static long get_alarm_double(DBADDR *paddr, struct dbr_alDouble *pad)
{
seqRecord *prec = (seqRecord *) paddr->precord;
int fieldOffset = dbGetFieldIndex(paddr) - indexof(DLY1);
if (fieldOffset >= 0 && (fieldOffset & 2) == 2) /* DOn */
dbGetAlarmLimits(get_dol(prec, fieldOffset),
&pad->lower_alarm_limit, &pad->lower_warning_limit,
&pad->upper_warning_limit, &pad->upper_alarm_limit);
else
recGblGetAlarmDouble(paddr, pad);
return 0;
}