/*************************************************************************\ * 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. \*************************************************************************/ /* Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd * * 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 #include #include #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 "errlog.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); #define get_value NULL 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) { STATIC_ASSERT(sizeof(prec->onam)==sizeof(prec->snam)); 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; } } strcpy(prec->onam, prec->snam); prec->oval = prec->val; 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; *precision = prec->prec; recGblGetPrec(paddr, precision); 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, 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, 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, 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; }