Added the aSub record type, derived from Andy Foster's genSub.

This commit is contained in:
Andrew Johnson
2008-08-14 20:41:05 +00:00
parent f4728749a2
commit ee634be0e5
8 changed files with 2206 additions and 7 deletions

View File

@@ -12,6 +12,37 @@
<h2 align="center">Changes between 3.14.9 and 3.14.10</h2>
<!-- Insert new items below here ... -->
<h4>Array Subroutine Record added (aSub)</h4>
<p>A heavily modified version of Andy Foster's genSub record type has been
added, called the aSub (Array Subroutine) record type. The new name is so that
genSub records can continue to be used in IOCs that need those features that
have changed or been removed in creating the aSub record type. The main
differences between genSub and aSub are:</p>
<ul>
<li>The fields <tt>UFA..UFU</tt>, <tt>UFVA..UFVU</tt>, <tt>TOVA..TOVU</tt> and
<tt>VERS</tt> have been removed.</li>
<li>The <tt>INPA..INPU</tt> and <tt>OUTA..OUTU</tt> link fields can now be
changed at runtime.</li>
<li>A new set of fields <tt>NEA..NEU</tt> and <tt>NEVA..NEVU</tt> have been
added which hold the current element count for the associated input and ouput
fields. These count fields ensure that putting an array to the <tt>A..U</tt> or
<tt>VALA..VALU</tt> fields cannot permanently shorten the number of elements
that the array fields can hold.</li>
<li>The default value for the <tt>FTA..FTU</tt> and <tt>FTVA..FTVU</tt> fields
has been changed from "STRING" to "DOUBLE", to reduce the memory footprint of
unneeded fields.</li>
<li>aSub subroutines can be asynchronous, using the usual method of setting
prec-&gt;pact to <tt>TRUE</tt> before returning and arranging for the record's
<tt>process()</tt> routine to be called to signal completion. The output links
are not written until the asynchronous completion callback, and input links are
not read at asnychronous completion.</li>
<li>
</ul>
<h4>epicsTimeEventDeviceTime support for soft channel input records</h4>
<p>The Soft Channel device support for all input record types now supports the

View File

@@ -3,3 +3,11 @@ record(sub,"$(user):subExample")
field(INAM,"mySubInit")
field(SNAM,"mySubProcess")
}
record(aSub,"$(user):aSubExample")
{
field(INAM,"myAsubInit")
field(SNAM,"myAsubProcess")
field(FTA,"DOUBLE")
field(NOA,"10")
field(INPA,"$(user):compressExample CPP")
}

View File

@@ -3,18 +3,17 @@
#include <dbDefs.h>
#include <registryFunction.h>
#include <subRecord.h>
#include <aSubRecord.h>
#include <epicsExport.h>
int mySubDebug;
typedef long (*processMethod)(subRecord *precord);
static long mySubInit(subRecord *precord,processMethod process)
static long mySubInit(subRecord *precord)
{
if (mySubDebug)
printf("Record %s called mySubInit(%p, %p)\n",
precord->name, (void*) precord, (void*) process);
return(0);
printf("Record %s called mySubInit(%p)\n",
precord->name, (void*) precord);
return 0;
}
static long mySubProcess(subRecord *precord)
@@ -22,7 +21,23 @@ static long mySubProcess(subRecord *precord)
if (mySubDebug)
printf("Record %s called mySubProcess(%p)\n",
precord->name, (void*) precord);
return(0);
return 0;
}
static long myAsubInit(aSubRecord *precord)
{
if (mySubDebug)
printf("Record %s called myAsubInit(%p)\n",
precord->name, (void*) precord);
return 0;
}
static long myAsubProcess(aSubRecord *precord)
{
if (mySubDebug)
printf("Record %s called myAsubProcess(%p)\n",
precord->name, (void*) precord);
return 0;
}
/* Register these symbols for use by IOC code: */
@@ -30,3 +45,5 @@ static long mySubProcess(subRecord *precord)
epicsExportAddress(int, mySubDebug);
epicsRegisterFunction(mySubInit);
epicsRegisterFunction(mySubProcess);
epicsRegisterFunction(myAsubInit);
epicsRegisterFunction(myAsubProcess);

View File

@@ -1,3 +1,5 @@
variable(mySubDebug)
function(mySubInit)
function(mySubProcess)
function(myAsubInit)
function(myAsubProcess)

View File

@@ -10,6 +10,7 @@ include "menuConvert.dbd"
# Record types
include "aiRecord.dbd"
include "aoRecord.dbd"
include "aSubRecord.dbd"
include "biRecord.dbd"
include "boRecord.dbd"
include "calcRecord.dbd"

View File

@@ -14,6 +14,7 @@ DBDINC += aaiRecord
DBDINC += aaoRecord
DBDINC += aiRecord
DBDINC += aoRecord
DBDINC += aSubRecord
DBDINC += biRecord
DBDINC += boRecord
DBDINC += calcRecord
@@ -43,6 +44,7 @@ LIBSRCS += aaiRecord.c
LIBSRCS += aaoRecord.c
LIBSRCS += aiRecord.c
LIBSRCS += aoRecord.c
LIBSRCS += aSubRecord.c
LIBSRCS += biRecord.c
LIBSRCS += boRecord.c
LIBSRCS += calcRecord.c

501
src/rec/aSubRecord.c Normal file
View File

@@ -0,0 +1,501 @@
/*************************************************************************\
* Copyright (c) 2008 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.
\*************************************************************************/
/* $Id$
*
* Record Support Routines for the Array Subroutine Record type,
* derived from Andy Foster's genSub record, with some features
* removed and asynchronous support added.
*
* Original Author: Andy Foster
* Revised by: Andrew Johnson
*
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "alarm.h"
#include "cantProceed.h"
#include "dbDefs.h"
#include "dbEvent.h"
#include "dbAccess.h"
#include "dbFldTypes.h"
#include "dbStaticLib.h"
#include "errMdef.h"
#include "recSup.h"
#include "devSup.h"
#include "special.h"
#include "registryFunction.h"
#include "epicsExport.h"
#include "recGbl.h"
#define GEN_SIZE_OFFSET
#include "aSubRecord.h"
#undef GEN_SIZE_OFFSET
typedef long (*GENFUNCPTR)(struct aSubRecord *);
/* Create RSET - Record Support Entry Table*/
#define report NULL
#define initialize NULL
static long init_record(aSubRecord *, int);
static long process(aSubRecord *);
static long special(DBADDR *, int);
static long get_value(aSubRecord *, struct valueDes *);
static long cvt_dbaddr(DBADDR *);
static long get_array_info(DBADDR *, long *, long *);
static long put_array_info(DBADDR *, long );
#define get_units NULL
static long get_precision(DBADDR *, long *);
#define get_enum_str NULL
#define get_enum_strs NULL
#define put_enum_str NULL
#define get_graphic_double NULL
#define get_control_double NULL
#define get_alarm_double NULL
rset aSubRSET = {
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, aSubRSET);
static long initFields(epicsEnum16 *pft, epicsUInt32 *pno, epicsUInt32 *pne,
const char **fldnames, void **pval, void **povl);
static long fetch_values(aSubRecord *prec);
static void monitor(aSubRecord *);
static long do_sub(aSubRecord *);
#define NUM_ARGS 21
#define MAX_ARRAY_SIZE 10000000
/* These are the names of the Input fields */
static const char *Ifldnames[] = {
"A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K",
"L", "M", "N", "O", "P", "Q", "R", "S", "T", "U"
};
/* These are the names of the Output fields */
static const char *Ofldnames[] = {
"VALA", "VALB", "VALC", "VALD", "VALE", "VALF", "VALG",
"VALH", "VALI", "VALJ", "VALK", "VALL", "VALM", "VALN",
"VALO", "VALP", "VALQ", "VALR", "VALS", "VALT", "VALU"
};
static long init_record(aSubRecord *prec, int pass)
{
GENFUNCPTR pfunc;
long status;
int i;
status = 0;
if (pass == 0) {
/* Allocate memory for arrays */
initFields(&prec->fta, &prec->noa, &prec->nea, Ifldnames,
&prec->a, NULL);
initFields(&prec->ftva, &prec->nova, &prec->neva, Ofldnames,
&prec->vala, &prec->ovla);
return 0;
}
/* Initialize the Subroutine Name Link */
switch (prec->subl.type) {
case CONSTANT:
recGblInitConstantLink(&prec->subl, DBF_STRING, prec->snam);
break;
case PV_LINK:
case DB_LINK:
case CA_LINK:
break;
default:
recGblRecordError(S_db_badField, (void *)prec,
"aSubRecord(init_record) Bad SUBL link type");
return S_db_badField;
}
/* Initialize Input Links */
for (i = 0; i < NUM_ARGS; i++) {
struct link *plink = &(&prec->inpa)[i];
switch (plink->type) {
case CONSTANT:
if ((&prec->noa)[i] < 2) {
if (recGblInitConstantLink(plink, (&prec->fta)[i], (&prec->a)[i])) {
prec->udf = FALSE;
} else
prec->udf = TRUE;
}
break;
case PV_LINK:
case CA_LINK:
case DB_LINK:
break;
default:
recGblRecordError(S_db_badField, (void *)prec,
"aSubRecord(init_record) Illegal INPUT LINK");
status = S_db_badField;
break;
}
}
if (status)
return status;
/* Initialize Output Links */
for (i = 0; i < NUM_ARGS; i++) {
switch ((&prec->outa)[i].type) {
case CONSTANT:
case PV_LINK:
case CA_LINK:
case DB_LINK:
break;
default:
recGblRecordError(S_db_badField, (void *)prec,
"aSubRecord(init_record) Illegal OUTPUT LINK");
status = S_db_badField;
}
}
if (status)
return status;
/* Call the user initialization routine if there is one */
if (prec->inam[0] != 0) {
pfunc = (GENFUNCPTR)registryFunctionFind(prec->inam);
if (pfunc) {
pfunc(prec);
} else {
recGblRecordError(S_db_BadSub, (void *)prec,
"aSubRecord::init_record - INAM subr not found");
return S_db_BadSub;
}
}
if (prec->lflg == aSubLFLG_IGNORE &&
prec->snam[0] != 0) {
pfunc = (GENFUNCPTR)registryFunctionFind(prec->snam);
if (pfunc)
prec->sadr = pfunc;
else {
recGblRecordError(S_db_BadSub, (void *)prec,
"aSubRecord::init_record - SNAM subr not found");
return S_db_BadSub;
}
}
return 0;
}
static long initFields(epicsEnum16 *pft, epicsUInt32 *pno, epicsUInt32 *pne,
const char **fldnames, void **pval, void **povl)
{
int i;
long status = 0;
for (i = 0; i < NUM_ARGS; i++, pft++, pno++, pne++, pval++) {
epicsUInt32 num;
epicsUInt32 flen;
if (*pft > DBF_ENUM)
*pft = DBF_CHAR;
if (*pno == 0)
*pno = 1;
flen = dbValueSize(*pft);
num = *pno * flen;
if (num > MAX_ARRAY_SIZE) {
epicsPrintf("Link %s - Array too large! %d Bytes\n", fldnames[i], num);
*pno = num = 0;
status = S_db_errArg;
} else
*pval = (char *)callocMustSucceed(*pno, flen,
"aSubRecord::init_record");
*pne = *pno;
if (povl) {
if (num)
*povl = (char *)callocMustSucceed(*pno, flen,
"aSubRecord::init_record");
povl++;
}
}
return status;
}
static long process(aSubRecord *prec)
{
int pact = prec->pact;
long status = 0;
if (!pact) {
prec->pact = TRUE;
status = fetch_values(prec);
prec->pact = FALSE;
}
if (!status) {
status = do_sub(prec);
prec->val = status;
}
if (!pact && prec->pact)
return 0;
prec->pact = TRUE;
/* Push the output link values */
if (!status) {
int i;
for (i = 0; i < NUM_ARGS; i++)
dbPutLink(&(&prec->outa)[i], (&prec->ftva)[i], (&prec->vala)[i],
(&prec->neva)[i]);
}
recGblGetTimeStamp(prec);
monitor(prec);
recGblFwdLink(prec);
prec->pact = FALSE;
return 0;
}
static long fetch_values(aSubRecord *prec)
{
long status;
int i;
if (prec->lflg == aSubLFLG_READ) {
/* Get the Subroutine Name and look it up if changed */
status = dbGetLink(&prec->subl, DBR_STRING, prec->snam, 0, 0);
if (status)
return status;
if (prec->snam[0] != 0 &&
strcmp(prec->snam, prec->onam)) {
GENFUNCPTR pfunc = (GENFUNCPTR)registryFunctionFind(prec->snam);
if (!pfunc)
return S_db_BadSub;
prec->sadr = pfunc;
strcpy(prec->onam, prec->snam);
}
}
/* Get the input link values */
for (i = 0; i < NUM_ARGS; i++) {
long nRequest = (&prec->noa)[i];
status = dbGetLink(&(&prec->inpa)[i], (&prec->fta)[i], (&prec->a)[i], 0,
&nRequest);
if (nRequest > 0)
(&prec->nea)[i] = nRequest;
if (status)
return status;
}
return 0;
}
static long get_precision(DBADDR *paddr, long *precision)
{
aSubRecord *prec = (aSubRecord *)paddr->precord;
int fieldIndex = dbGetFieldIndex(paddr);
if (fieldIndex == aSubRecordVAL) {
*precision = prec->prec;
return 0;
}
recGblGetPrec(paddr, precision);
return 0;
}
static long get_value(aSubRecord *prec, struct valueDes *pvdes)
{
pvdes->no_elements = 1;
pvdes->pvalue = (void *)(&prec->val);
pvdes->field_type = DBF_LONG;
return 0;
}
static void monitor(aSubRecord *prec)
{
int i;
unsigned short monitor_mask;
monitor_mask = recGblResetAlarms(prec) | DBE_VALUE | DBE_LOG;
/* Post events for VAL field */
if (prec->val != prec->oval) {
db_post_events(prec, &prec->val, monitor_mask);
prec->oval = prec->val;
}
/* Event posting on VAL arrays depends on the setting of prec->eflg */
switch (prec->eflg) {
case aSubEFLG_NEVER:
break;
case aSubEFLG_ON_CHANGE:
for (i = 0; i < NUM_ARGS; i++) {
epicsUInt32 alen = dbValueSize((&prec->ftva)[i]) * (&prec->neva)[i];
void *povl = (&prec->ovla)[i];
void *pval = (&prec->vala)[i];
if (memcmp(povl, pval, alen)) {
memcpy(povl, pval, alen);
db_post_events(prec, pval, monitor_mask);
}
}
break;
case aSubEFLG_ALWAYS:
for (i = 0; i < NUM_ARGS; i++)
db_post_events(prec, (&prec->vala)[i], monitor_mask);
break;
}
return;
}
static long do_sub(aSubRecord *prec)
{
GENFUNCPTR pfunc = prec->sadr;
long status;
if (prec->snam[0] == 0)
return 0;
if (pfunc == NULL) {
recGblSetSevr(prec, BAD_SUB_ALARM, INVALID_ALARM);
return S_db_BadSub;
}
status = pfunc(prec);
if (status < 0)
recGblSetSevr(prec, SOFT_ALARM, prec->brsv);
else
prec->udf = FALSE;
return status;
}
static long cvt_dbaddr(DBADDR *paddr)
{
aSubRecord *prec = (aSubRecord *)paddr->precord;
int fieldIndex = dbGetFieldIndex(paddr);
if (fieldIndex >= aSubRecordA &&
fieldIndex <= aSubRecordU) {
int offset = fieldIndex - aSubRecordA;
paddr->pfield = (&prec->a )[offset];
paddr->no_elements = (&prec->noa)[offset];
paddr->field_type = (&prec->fta)[offset];
}
else if (fieldIndex >= aSubRecordVALA &&
fieldIndex <= aSubRecordVALU) {
int offset = fieldIndex - aSubRecordVALA;
paddr->pfield = (&prec->vala)[offset];
paddr->no_elements = (&prec->nova)[offset];
paddr->field_type = (&prec->ftva)[offset];
}
else {
errlogPrintf("aSubRecord::cvt_dbaddr called for %s.%s\n",
prec->name, ((dbFldDes *)paddr->pfldDes)->name);
return 0;
}
paddr->dbr_field_type = paddr->field_type;
paddr->field_size = dbValueSize(paddr->field_type);
return 0;
}
static long get_array_info(DBADDR *paddr, long *no_elements, long *offset)
{
aSubRecord *prec = (aSubRecord *)paddr->precord;
int fieldIndex = dbGetFieldIndex(paddr);
if (fieldIndex >= aSubRecordA &&
fieldIndex <= aSubRecordU) {
*no_elements = (&prec->nea)[fieldIndex - aSubRecordA];
}
else if (fieldIndex >= aSubRecordVALA &&
fieldIndex <= aSubRecordVALU) {
*no_elements = (&prec->neva)[fieldIndex - aSubRecordVALA];
}
else {
errlogPrintf("aSubRecord::get_array_info called for %s.%s\n",
prec->name, ((dbFldDes *)paddr->pfldDes)->name);
}
*offset = 0;
return 0;
}
static long put_array_info(DBADDR *paddr, long nNew)
{
aSubRecord *prec = (aSubRecord *)paddr->precord;
int fieldIndex = dbGetFieldIndex(paddr);
if (fieldIndex >= aSubRecordA &&
fieldIndex <= aSubRecordU) {
(&prec->nea)[fieldIndex - aSubRecordA] = nNew;
}
else if (fieldIndex >= aSubRecordVALA &&
fieldIndex <= aSubRecordVALU) {
(&prec->neva)[fieldIndex - aSubRecordVALA] = nNew;
}
else {
errlogPrintf("aSubRecord::put_array_info called for %s.%s\n",
prec->name, ((dbFldDes *)paddr->pfldDes)->name);
}
return 0;
}
static long special(DBADDR *paddr, int after)
{
aSubRecord *prec = (aSubRecord *)paddr->precord;
if (after &&
prec->lflg == aSubLFLG_IGNORE) {
if (prec->snam[0] == 0)
return 0;
prec->sadr = (GENFUNCPTR)registryFunctionFind(prec->snam);
if (!prec->sadr) {
recGblRecordError(S_db_BadSub, (void *)prec, prec->snam);
return S_db_BadSub;
}
}
return 0;
}

1637
src/rec/aSubRecord.dbd Normal file

File diff suppressed because it is too large Load Diff