diff --git a/src/ioc/db/Makefile b/src/ioc/db/Makefile index cd015040e..c957842cb 100644 --- a/src/ioc/db/Makefile +++ b/src/ioc/db/Makefile @@ -47,6 +47,7 @@ menuGlobal_DBD += menuFtype.dbd menuGlobal_DBD += menuIvoa.dbd menuGlobal_DBD += menuOmsl.dbd menuGlobal_DBD += menuPini.dbd +menuGlobal_DBD += menuPost.dbd menuGlobal_DBD += menuPriority.dbd menuGlobal_DBD += menuScan.dbd menuGlobal_DBD += menuYesNo.dbd diff --git a/src/ioc/db/dbAccess.c b/src/ioc/db/dbAccess.c index c4661d9e1..40039b0c9 100644 --- a/src/ioc/db/dbAccess.c +++ b/src/ioc/db/dbAccess.c @@ -583,10 +583,8 @@ long dbNameToAddr(const char *pname, DBADDR *paddr) { DBENTRY dbEntry; dbFldDes *pflddes; - struct rset *prset; long status = 0; - long no_elements = 1; - short dbfType, dbrType, field_size; + short dbfType; if (!pname || !*pname || !pdbbase) return S_db_notFound; @@ -601,48 +599,49 @@ 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; - pflddes = dbEntry.pflddes; + 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]; - dbfType = pflddes->field_type; - dbrType = mapDBFToDBR[dbfType]; - field_size = pflddes->size; + if (paddr->special == SPC_DBADDR) { + struct 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; + } + } + + /* Handle field modifiers */ if (*pname++ == '$') { /* Some field types can be accessed as char arrays */ if (dbfType == DBF_STRING) { - dbfType = DBF_CHAR; - dbrType = DBR_CHAR; - no_elements = field_size; - field_size = 1; + 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) { /* Clients see a char array, but keep original dbfType */ - dbrType = DBR_CHAR; - no_elements = PVNAME_STRINGSZ + 12; - field_size = 1; + paddr->no_elements = PVNAME_STRINGSZ + 12; + paddr->field_size = 1; + paddr->dbr_field_type = DBR_CHAR; } else { status = S_dbLib_fieldNotFound; goto finish; } } - paddr->pfldDes = pflddes; - paddr->field_type = dbfType; - paddr->dbr_field_type = dbrType; - paddr->field_size = field_size; - paddr->special = pflddes->special; - paddr->no_elements = no_elements; - - if ((paddr->special == SPC_DBADDR) && - (prset = dbGetRset(paddr)) && - prset->cvt_dbaddr) - /* cvt_dbaddr routine may change any of these elements of paddr: - * pfield, no_elements, element_offset, field_type, - * dbr_field_type, field_size, and/or special. - */ - status = prset->cvt_dbaddr(paddr); - finish: dbFinishEntry(&dbEntry); return status; @@ -825,7 +824,7 @@ long dbGet(DBADDR *paddr, short dbrType, /* check for array */ if ((!pfl || pfl->type == dbfl_type_rec) && - paddr->special == SPC_DBADDR && + paddr->pfldDes->special == SPC_DBADDR && no_elements > 1 && (prset = dbGetRset(paddr)) && prset->get_array_info) { @@ -1182,7 +1181,7 @@ long dbPut(DBADDR *paddr, short dbrType, struct rset *prset = dbGetRset(paddr); long offset = 0; - if (paddr->special == SPC_DBADDR && + if (paddr->pfldDes->special == SPC_DBADDR && prset && prset->get_array_info) { long dummy; @@ -1195,7 +1194,7 @@ long dbPut(DBADDR *paddr, short dbrType, /* update array info */ if (!status && - paddr->special == SPC_DBADDR && + paddr->pfldDes->special == SPC_DBADDR && prset && prset->put_array_info) { status = prset->put_array_info(paddr, nRequest); } diff --git a/src/ioc/db/dbLink.c b/src/ioc/db/dbLink.c index 8caae22e6..54e131566 100644 --- a/src/ioc/db/dbLink.c +++ b/src/ioc/db/dbLink.c @@ -649,3 +649,62 @@ void dbScanFwdLink(struct link *plink) } } +/* Helper functions for long string support */ + +long dbLoadLinkLS(struct link *plink, char *pbuffer, epicsUInt32 size, + epicsUInt32 *plen) +{ + if (plink->type == CONSTANT && + plink->value.constantStr) { + strncpy(pbuffer, plink->value.constantStr, --size); + pbuffer[size] = 0; + *plen = strlen(pbuffer) + 1; + return 0; + } + + return S_db_notFound; +} + +long dbGetLinkLS(struct link *plink, char *pbuffer, epicsUInt32 size, + epicsUInt32 *plen) +{ + int dtyp = dbGetLinkDBFtype(plink); + long len = size; + long status; + + if (dtyp < 0) /* Not connected */ + return 0; + + if (dtyp == DBR_CHAR || dtyp == DBF_UCHAR) { + status = dbGetLink(plink, dtyp, pbuffer, 0, &len); + } + else if (size >= MAX_STRING_SIZE) + status = dbGetLink(plink, DBR_STRING, pbuffer, 0, 0); + else { + /* pbuffer is too small to fetch using DBR_STRING */ + char tmp[MAX_STRING_SIZE]; + + status = dbGetLink(plink, DBR_STRING, tmp, 0, 0); + if (!status) + strncpy(pbuffer, tmp, len - 1); + } + if (!status) { + pbuffer[--len] = 0; + *plen = strlen(pbuffer) + 1; + } + return status; +} + +long dbPutLinkLS(struct link *plink, char *pbuffer, epicsUInt32 len) +{ + int dtyp = dbGetLinkDBFtype(plink); + + if (dtyp < 0) + return 0; /* Not connected */ + + if (dtyp == DBR_CHAR || dtyp == DBF_UCHAR) + return dbPutLink(plink, dtyp, pbuffer, len); + + return dbPutLink(plink, DBR_STRING, pbuffer, 1); +} + diff --git a/src/ioc/db/dbLink.h b/src/ioc/db/dbLink.h index 213327ee2..ff0de3003 100644 --- a/src/ioc/db/dbLink.h +++ b/src/ioc/db/dbLink.h @@ -81,6 +81,13 @@ epicsShareFunc long dbPutLink(struct link *, short dbrType, const void *pbuffer, long nRequest); epicsShareFunc void dbScanFwdLink(struct link *plink); +epicsShareFunc long dbLoadLinkLS(struct link *plink, char *pbuffer, + epicsUInt32 size, epicsUInt32 *plen); +epicsShareFunc long dbGetLinkLS(struct link *plink, char *pbuffer, + epicsUInt32 buffer_size, epicsUInt32 *plen); +epicsShareFunc long dbPutLinkLS(struct link *plink, char *pbuffer, + epicsUInt32 len); + #ifdef __cplusplus } #endif diff --git a/src/ioc/db/menuPost.dbd b/src/ioc/db/menuPost.dbd new file mode 100644 index 000000000..a86b25b5e --- /dev/null +++ b/src/ioc/db/menuPost.dbd @@ -0,0 +1,11 @@ +#************************************************************************* +# Copyright (c) 2012 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. +#************************************************************************* + +menu(menuPost) { + choice(menuPost_OnChange, "On Change") + choice(menuPost_Always, "Always") +} diff --git a/src/std/dev/Makefile b/src/std/dev/Makefile index 3ee4e7608..6bdbbd997 100644 --- a/src/std/dev/Makefile +++ b/src/std/dev/Makefile @@ -30,6 +30,8 @@ dbRecStd_SRCS += devEventSoft.c dbRecStd_SRCS += devHistogramSoft.c dbRecStd_SRCS += devLiSoft.c dbRecStd_SRCS += devLoSoft.c +dbRecStd_SRCS += devLsiSoft.c +dbRecStd_SRCS += devLsoSoft.c dbRecStd_SRCS += devMbbiDirectSoft.c dbRecStd_SRCS += devMbbiDirectSoftRaw.c dbRecStd_SRCS += devMbbiSoft.c @@ -38,6 +40,7 @@ dbRecStd_SRCS += devMbboDirectSoft.c dbRecStd_SRCS += devMbboDirectSoftRaw.c dbRecStd_SRCS += devMbboSoft.c dbRecStd_SRCS += devMbboSoftRaw.c +dbRecStd_SRCS += devPrintfSoft.c dbRecStd_SRCS += devSASoft.c dbRecStd_SRCS += devSiSoft.c dbRecStd_SRCS += devSoSoft.c @@ -55,12 +58,14 @@ dbRecStd_SRCS += devAoSoftCallback.c dbRecStd_SRCS += devBoSoftCallback.c dbRecStd_SRCS += devCalcoutSoftCallback.c dbRecStd_SRCS += devLoSoftCallback.c +dbRecStd_SRCS += devLsoSoftCallback.c dbRecStd_SRCS += devMbboSoftCallback.c dbRecStd_SRCS += devMbboDirectSoftCallback.c +dbRecStd_SRCS += devPrintfSoftCallback.c dbRecStd_SRCS += devSoSoftCallback.c dbRecStd_SRCS += devTimestamp.c -dbRecStd_SRCS += devSoStdio.c +dbRecStd_SRCS += devStdio.c dbRecStd_SRCS += asSubRecordFunctions.c diff --git a/src/std/dev/devLsiSoft.c b/src/std/dev/devLsiSoft.c new file mode 100644 index 000000000..a5547cba8 --- /dev/null +++ b/src/std/dev/devLsiSoft.c @@ -0,0 +1,42 @@ +/*************************************************************************\ +* Copyright (c) 2012 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. +\*************************************************************************/ + +/* Long String Input soft device support + * + * Author: Andrew Johnson + * Date: 2012-11-28 + */ + +#include "dbAccess.h" +#include "epicsTime.h" +#include "link.h" +#include "lsiRecord.h" +#include "epicsExport.h" + +static long init_record(lsiRecord *prec) +{ + dbLoadLinkLS(&prec->inp, prec->val, prec->sizv, &prec->len); + + return 0; +} + +static long read_string(lsiRecord *prec) +{ + long status = dbGetLinkLS(&prec->inp, prec->val, prec->sizv, &prec->len); + + if (!status && + prec->tsel.type == CONSTANT && + prec->tse == epicsTimeEventDeviceTime) + dbGetTimeStamp(&prec->inp, &prec->time); + + return status; +} + +lsidset devLsiSoft = { + 5, NULL, NULL, init_record, NULL, read_string +}; +epicsExportAddress(dset, devLsiSoft); diff --git a/src/std/dev/devLsoSoft.c b/src/std/dev/devLsoSoft.c new file mode 100644 index 000000000..02079a053 --- /dev/null +++ b/src/std/dev/devLsoSoft.c @@ -0,0 +1,26 @@ +/*************************************************************************\ +* Copyright (c) 2012 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. +\*************************************************************************/ + +/* Long String Output soft device support + * + * Author: Andrew Johnson + * Date: 2012-11-29 + */ + +#include "dbAccess.h" +#include "lsoRecord.h" +#include "epicsExport.h" + +static long write_string(lsoRecord *prec) +{ + return dbPutLinkLS(&prec->out, prec->val, prec->len); +} + +lsodset devLsoSoft = { + 5, NULL, NULL, NULL, NULL, write_string +}; +epicsExportAddress(dset, devLsoSoft); diff --git a/src/std/dev/devLsoSoftCallback.c b/src/std/dev/devLsoSoftCallback.c new file mode 100644 index 000000000..4ab7ead9d --- /dev/null +++ b/src/std/dev/devLsoSoftCallback.c @@ -0,0 +1,51 @@ +/*************************************************************************\ +* Copyright (c) 2012 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$ */ +/* + * Author: Andrew Johnson + * Date: 30 Nov 2012 + */ + +#include "alarm.h" +#include "dbAccess.h" +#include "recGbl.h" +#include "lsoRecord.h" +#include "epicsExport.h" + +static long write_string(lsoRecord *prec) +{ + struct link *plink = &prec->out; + int dtyp = dbGetLinkDBFtype(plink); + long len = prec->len; + long status; + + if (prec->pact || dtyp < 0) + return 0; + + if (dtyp != DBR_CHAR && dtyp != DBF_UCHAR) { + dtyp = DBR_STRING; + len = 1; + } + + if (plink->type != CA_LINK) + return dbPutLink(plink, dtyp, prec->val, len); + + status = dbCaPutLinkCallback(plink, dtyp, prec->val, len, + dbCaCallbackProcess, plink); + if (status) { + recGblSetSevr(prec, LINK_ALARM, INVALID_ALARM); + return status; + } + + prec->pact = TRUE; + return 0; +} + +lsodset devLsoSoftCallback = { + 5, NULL, NULL, NULL, NULL, write_string +}; +epicsExportAddress(dset, devLsoSoftCallback); diff --git a/src/std/dev/devPrintfSoft.c b/src/std/dev/devPrintfSoft.c new file mode 100644 index 000000000..a61d4bbfb --- /dev/null +++ b/src/std/dev/devPrintfSoft.c @@ -0,0 +1,26 @@ +/*************************************************************************\ +* Copyright (c) 2012 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$ */ +/* + * Author: Andrew Johnson + * Date: 28 Sept 2012 + */ + +#include "dbAccess.h" +#include "printfRecord.h" +#include "epicsExport.h" + +static long write_string(printfRecord *prec) +{ + return dbPutLinkLS(&prec->out, prec->val, prec->len); +} + +printfdset devPrintfSoft = { + 5, NULL, NULL, NULL, NULL, write_string +}; +epicsExportAddress(dset, devPrintfSoft); + diff --git a/src/std/dev/devPrintfSoftCallback.c b/src/std/dev/devPrintfSoftCallback.c new file mode 100644 index 000000000..7f9314d9e --- /dev/null +++ b/src/std/dev/devPrintfSoftCallback.c @@ -0,0 +1,51 @@ +/*************************************************************************\ +* Copyright (c) 2012 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$ */ +/* + * Author: Andrew Johnson + * Date: 28 Sept 2012 + */ + +#include "alarm.h" +#include "dbAccess.h" +#include "recGbl.h" +#include "printfRecord.h" +#include "epicsExport.h" + +static long write_string(printfRecord *prec) +{ + struct link *plink = &prec->out; + int dtyp = dbGetLinkDBFtype(plink); + long len = prec->len; + long status; + + if (prec->pact || dtyp < 0) + return 0; + + if (dtyp != DBR_CHAR && dtyp != DBF_UCHAR) { + dtyp = DBR_STRING; + len = 1; + } + + if (plink->type != CA_LINK) + return dbPutLink(plink, dtyp, prec->val, len); + + status = dbCaPutLinkCallback(plink, dtyp, prec->val, len, + dbCaCallbackProcess, plink); + if (status) { + recGblSetSevr(prec, LINK_ALARM, INVALID_ALARM); + return status; + } + + prec->pact = TRUE; + return 0; +} + +printfdset devPrintfSoftCallback = { + 5, NULL, NULL, NULL, NULL, write_string +}; +epicsExportAddress(dset, devPrintfSoftCallback); diff --git a/src/std/dev/devSoStdio.c b/src/std/dev/devSoStdio.c deleted file mode 100644 index e7a2f738a..000000000 --- a/src/std/dev/devSoStdio.c +++ /dev/null @@ -1,108 +0,0 @@ -/*************************************************************************\ -* 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$ */ - -#include -#include - -#include "dbCommon.h" -#include "devSup.h" -#include "errlog.h" -#include "recGbl.h" -#include "recSup.h" -#include "stringoutRecord.h" -#include "epicsExport.h" - -typedef int (*PRINTFFUNC)(const char *fmt, ...); - -static int stderrPrintf(const char *fmt, ...); -static int logPrintf(const char *fmt, ...); - - -static struct outStream { - const char *name; - PRINTFFUNC print; -} outStreams[] = { - {"stdout", printf}, - {"stderr", stderrPrintf}, - {"errlog", logPrintf}, - {NULL, NULL} -}; - -static int stderrPrintf(const char *fmt, ...) { - va_list pvar; - int retval; - - va_start(pvar, fmt); - retval = vfprintf(stderr, fmt, pvar); - va_end (pvar); - - return retval; -} - -static int logPrintf(const char *fmt, ...) { - va_list pvar; - int retval; - - va_start(pvar, fmt); - retval = errlogVprintf(fmt, pvar); - va_end (pvar); - - return retval; -} - -static long add(dbCommon *pcommon) { - stringoutRecord *prec = (stringoutRecord *) pcommon; - struct outStream *pstream; - - if (prec->out.type != INST_IO) - return S_dev_badOutType; - - for (pstream = outStreams; pstream->name; ++pstream) { - if (strcmp(prec->out.value.instio.string, pstream->name) == 0) { - prec->dpvt = pstream; - return 0; - } - } - prec->dpvt = NULL; - return -1; -} - -static long del(dbCommon *pcommon) { - stringoutRecord *prec = (stringoutRecord *) pcommon; - - prec->dpvt = NULL; - return 0; -} - -static struct dsxt dsxtSoStdio = { - add, del -}; - -static long init(int pass) -{ - if (pass == 0) devExtend(&dsxtSoStdio); - return 0; -} - -static long write_string(stringoutRecord *prec) -{ - struct outStream *pstream = (struct outStream *)prec->dpvt; - if (pstream) - pstream->print("%s\n", prec->val); - return 0; -} - -/* Create the dset for devSoStdio */ -static struct { - dset common; - DEVSUPFUN write; -} devSoStdio = { - {5, NULL, init, NULL, NULL}, write_string -}; -epicsExportAddress(dset, devSoStdio); diff --git a/src/std/dev/devSoft.dbd b/src/std/dev/devSoft.dbd index 9072245ca..d6cb14de9 100644 --- a/src/std/dev/devSoft.dbd +++ b/src/std/dev/devSoft.dbd @@ -9,10 +9,13 @@ device(event,CONSTANT,devEventSoft,"Soft Channel") device(histogram,CONSTANT,devHistogramSoft,"Soft Channel") device(longin,CONSTANT,devLiSoft,"Soft Channel") device(longout,CONSTANT,devLoSoft,"Soft Channel") +device(lsi,CONSTANT,devLsiSoft,"Soft Channel") +device(lso,CONSTANT,devLsoSoft,"Soft Channel") device(mbbi,CONSTANT,devMbbiSoft,"Soft Channel") device(mbbiDirect,CONSTANT,devMbbiDirectSoft,"Soft Channel") device(mbbo,CONSTANT,devMbboSoft,"Soft Channel") device(mbboDirect,CONSTANT,devMbboDirectSoft,"Soft Channel") +device(printf,CONSTANT,devPrintfSoft,"Soft Channel") device(stringin,CONSTANT,devSiSoft,"Soft Channel") device(stringout,CONSTANT,devSoSoft,"Soft Channel") device(subArray,CONSTANT,devSASoft,"Soft Channel") @@ -34,10 +37,12 @@ device(bo,CONSTANT,devBoSoftCallback,"Async Soft Channel") device(calcout,CONSTANT,devCalcoutSoftCallback,"Async Soft Channel") device(longin,CONSTANT,devLiSoftCallback,"Async Soft Channel") device(longout,CONSTANT,devLoSoftCallback,"Async Soft Channel") +device(lso,CONSTANT,devLsoSoftCallback,"Async Soft Channel") device(mbbi,CONSTANT,devMbbiSoftCallback,"Async Soft Channel") device(mbbiDirect,CONSTANT,devMbbiDirectSoftCallback,"Async Soft Channel") device(mbbo,CONSTANT,devMbboSoftCallback,"Async Soft Channel") device(mbboDirect,CONSTANT,devMbboDirectSoftCallback,"Async Soft Channel") +device(printf,CONSTANT,devPrintfSoftCallback,"Async Soft Channel") device(stringin,CONSTANT,devSiSoftCallback,"Async Soft Channel") device(stringout,CONSTANT,devSoSoftCallback,"Async Soft Channel") @@ -49,6 +54,8 @@ device(bo, INST_IO,devBoGeneralTime,"General Time") device(longin, INST_IO,devLiGeneralTime,"General Time") device(stringin,INST_IO,devSiGeneralTime,"General Time") +device(lso,INST_IO,devLsoStdio,"stdio") +device(printf,INST_IO,devPrintfStdio,"stdio") device(stringout,INST_IO,devSoStdio,"stdio") device(bi, INST_IO, devBiDbState, "Db State") diff --git a/src/std/dev/devStdio.c b/src/std/dev/devStdio.c new file mode 100644 index 000000000..a4ebd64e7 --- /dev/null +++ b/src/std/dev/devStdio.c @@ -0,0 +1,212 @@ +/*************************************************************************\ +* 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$ */ + +#include +#include + +#include "dbCommon.h" +#include "devSup.h" +#include "errlog.h" +#include "recGbl.h" +#include "recSup.h" +#include "lsoRecord.h" +#include "printfRecord.h" +#include "stringoutRecord.h" +#include "epicsExport.h" + +typedef int (*PRINTFFUNC)(const char *fmt, ...); + +static int stderrPrintf(const char *fmt, ...); +static int logPrintf(const char *fmt, ...); + + +static struct outStream { + const char *name; + PRINTFFUNC print; +} outStreams[] = { + {"stdout", printf}, + {"stderr", stderrPrintf}, + {"errlog", logPrintf}, + {NULL, NULL} +}; + +static int stderrPrintf(const char *fmt, ...) { + va_list pvar; + int retval; + + va_start(pvar, fmt); + retval = vfprintf(stderr, fmt, pvar); + va_end (pvar); + + return retval; +} + +static int logPrintf(const char *fmt, ...) { + va_list pvar; + int retval; + + va_start(pvar, fmt); + retval = errlogVprintf(fmt, pvar); + va_end (pvar); + + return retval; +} + + +/* lso device support */ + +static long add_lso(dbCommon *pcommon) { + lsoRecord *prec = (lsoRecord *) pcommon; + struct outStream *pstream; + + if (prec->out.type != INST_IO) + return S_dev_badOutType; + + for (pstream = outStreams; pstream->name; ++pstream) { + if (strcmp(prec->out.value.instio.string, pstream->name) == 0) { + prec->dpvt = pstream; + return 0; + } + } + prec->dpvt = NULL; + return -1; +} + +static long del_lso(dbCommon *pcommon) { + lsoRecord *prec = (lsoRecord *) pcommon; + + prec->dpvt = NULL; + return 0; +} + +static struct dsxt dsxtLsoStdio = { + add_lso, del_lso +}; + +static long init_lso(int pass) +{ + if (pass == 0) devExtend(&dsxtLsoStdio); + return 0; +} + +static long write_lso(lsoRecord *prec) +{ + struct outStream *pstream = (struct outStream *)prec->dpvt; + if (pstream) + pstream->print("%s\n", prec->val); + return 0; +} + +lsodset devLsoStdio = { + 5, NULL, init_lso, NULL, NULL, write_lso +}; +epicsExportAddress(dset, devLsoStdio); + + +/* printf device support */ + +static long add_printf(dbCommon *pcommon) { + printfRecord *prec = (printfRecord *) pcommon; + struct outStream *pstream; + + if (prec->out.type != INST_IO) + return S_dev_badOutType; + + for (pstream = outStreams; pstream->name; ++pstream) { + if (strcmp(prec->out.value.instio.string, pstream->name) == 0) { + prec->dpvt = pstream; + return 0; + } + } + prec->dpvt = NULL; + return -1; +} + +static long del_printf(dbCommon *pcommon) { + printfRecord *prec = (printfRecord *) pcommon; + + prec->dpvt = NULL; + return 0; +} + +static struct dsxt dsxtPrintfStdio = { + add_printf, del_printf +}; + +static long init_printf(int pass) +{ + if (pass == 0) devExtend(&dsxtPrintfStdio); + return 0; +} + +static long write_printf(printfRecord *prec) +{ + struct outStream *pstream = (struct outStream *)prec->dpvt; + if (pstream) + pstream->print("%s\n", prec->val); + return 0; +} + +printfdset devPrintfStdio = { + 5, NULL, init_printf, NULL, NULL, write_printf +}; +epicsExportAddress(dset, devPrintfStdio); + + +/* stringout device support */ + +static long add_stringout(dbCommon *pcommon) { + stringoutRecord *prec = (stringoutRecord *) pcommon; + struct outStream *pstream; + + if (prec->out.type != INST_IO) + return S_dev_badOutType; + + for (pstream = outStreams; pstream->name; ++pstream) { + if (strcmp(prec->out.value.instio.string, pstream->name) == 0) { + prec->dpvt = pstream; + return 0; + } + } + prec->dpvt = NULL; + return -1; +} + +static long del_stringout(dbCommon *pcommon) { + stringoutRecord *prec = (stringoutRecord *) pcommon; + + prec->dpvt = NULL; + return 0; +} + +static struct dsxt dsxtSoStdio = { + add_stringout, del_stringout +}; + +static long init_stringout(int pass) +{ + if (pass == 0) devExtend(&dsxtSoStdio); + return 0; +} + +static long write_stringout(stringoutRecord *prec) +{ + struct outStream *pstream = (struct outStream *)prec->dpvt; + if (pstream) + pstream->print("%s\n", prec->val); + return 0; +} + +static struct { + dset common; + DEVSUPFUN write; +} devSoStdio = { + {5, NULL, init_stringout, NULL, NULL}, write_stringout +}; +epicsExportAddress(dset, devSoStdio); diff --git a/src/std/rec/Makefile b/src/std/rec/Makefile index 96cf66244..c2d44ddb9 100644 --- a/src/std/rec/Makefile +++ b/src/std/rec/Makefile @@ -11,67 +11,43 @@ SRC_DIRS += $(STDDIR)/rec -DBDINC += aaiRecord -DBDINC += aaoRecord -DBDINC += aiRecord -DBDINC += aoRecord -DBDINC += aSubRecord -DBDINC += biRecord -DBDINC += boRecord -DBDINC += calcRecord -DBDINC += calcoutRecord -DBDINC += compressRecord -DBDINC += dfanoutRecord -DBDINC += eventRecord -DBDINC += fanoutRecord -DBDINC += histogramRecord -DBDINC += longinRecord -DBDINC += longoutRecord -DBDINC += mbbiRecord -DBDINC += mbbiDirectRecord -DBDINC += mbboRecord -DBDINC += mbboDirectRecord -DBDINC += permissiveRecord -DBDINC += selRecord -DBDINC += seqRecord -DBDINC += stateRecord -DBDINC += stringinRecord -DBDINC += stringoutRecord -DBDINC += subRecord -DBDINC += subArrayRecord -DBDINC += waveformRecord +stdRecords += aaiRecord +stdRecords += aaoRecord +stdRecords += aiRecord +stdRecords += aoRecord +stdRecords += aSubRecord +stdRecords += biRecord +stdRecords += boRecord +stdRecords += calcRecord +stdRecords += calcoutRecord +stdRecords += compressRecord +stdRecords += dfanoutRecord +stdRecords += eventRecord +stdRecords += fanoutRecord +stdRecords += histogramRecord +stdRecords += longinRecord +stdRecords += longoutRecord +stdRecords += lsiRecord +stdRecords += lsoRecord +stdRecords += mbbiRecord +stdRecords += mbbiDirectRecord +stdRecords += mbboRecord +stdRecords += mbboDirectRecord +stdRecords += permissiveRecord +stdRecords += printfRecord +stdRecords += selRecord +stdRecords += seqRecord +stdRecords += stateRecord +stdRecords += stringinRecord +stdRecords += stringoutRecord +stdRecords += subRecord +stdRecords += subArrayRecord +stdRecords += waveformRecord +DBDINC += $(stdRecords) DBD += stdRecords.dbd -stdRecords_DBD = $(patsubst %,%.dbd,$(DBDINC)) +stdRecords_DBD = $(patsubst %,%.dbd,$(stdRecords)) -dbRecStd_SRCS += aaiRecord.c -dbRecStd_SRCS += aaoRecord.c -dbRecStd_SRCS += aiRecord.c -dbRecStd_SRCS += aoRecord.c -dbRecStd_SRCS += aSubRecord.c -dbRecStd_SRCS += biRecord.c -dbRecStd_SRCS += boRecord.c -dbRecStd_SRCS += calcRecord.c -dbRecStd_SRCS += calcoutRecord.c -dbRecStd_SRCS += compressRecord.c -dbRecStd_SRCS += dfanoutRecord.c -dbRecStd_SRCS += eventRecord.c -dbRecStd_SRCS += fanoutRecord.c -dbRecStd_SRCS += histogramRecord.c -dbRecStd_SRCS += longinRecord.c -dbRecStd_SRCS += longoutRecord.c -dbRecStd_SRCS += mbbiRecord.c -dbRecStd_SRCS += mbbiDirectRecord.c -dbRecStd_SRCS += mbboRecord.c -dbRecStd_SRCS += mbboDirectRecord.c -dbRecStd_SRCS += permissiveRecord.c -dbRecStd_SRCS += selRecord.c -dbRecStd_SRCS += seqRecord.c -dbRecStd_SRCS += stateRecord.c -dbRecStd_SRCS += stringinRecord.c -dbRecStd_SRCS += stringoutRecord.c -dbRecStd_SRCS += subRecord.c -dbRecStd_SRCS += subArrayRecord.c -dbRecStd_SRCS += waveformRecord.c +dbRecStd_SRCS += $(patsubst %,%.c,$(stdRecords)) diff --git a/src/std/rec/lsiRecord.c b/src/std/rec/lsiRecord.c new file mode 100644 index 000000000..1cbff9185 --- /dev/null +++ b/src/std/rec/lsiRecord.c @@ -0,0 +1,284 @@ +/*************************************************************************\ +* Copyright (c) 2012 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. +\*************************************************************************/ + +/* Long String Input record type */ +/* + * Author: Andrew Johnson + * Date: 2012-11-27 + */ + +#include +#include +#include + +#include "dbDefs.h" +#include "errlog.h" +#include "alarm.h" +#include "cantProceed.h" +#include "dbAccess.h" +#include "dbEvent.h" +#include "dbFldTypes.h" +#include "errMdef.h" +#include "menuPost.h" +#include "menuYesNo.h" +#include "recSup.h" +#include "recGbl.h" +#include "special.h" +#define GEN_SIZE_OFFSET +#include "lsiRecord.h" +#undef GEN_SIZE_OFFSET +#include "epicsExport.h" + +static void monitor(lsiRecord *); +static long readValue(lsiRecord *); + +static long init_record(lsiRecord *prec, int pass) +{ + lsidset *pdset; + + if (pass == 0) { + size_t sizv = prec->sizv; + + if (sizv < 16) { + sizv = 16; /* Enforce a minimum size for the VAL field */ + prec->sizv = sizv; + } + + prec->val = callocMustSucceed(1, sizv, "lsi::init_record"); + prec->len = 0; + prec->oval = callocMustSucceed(1, sizv, "lsi::init_record"); + prec->olen = 0; + return 0; + } + + dbLoadLink(&prec->siml, DBF_USHORT, &prec->simm); + + pdset = (lsidset *) prec->dset; + if (!pdset) { + recGblRecordError(S_dev_noDSET, prec, "lsi: init_record"); + return S_dev_noDSET; + } + + /* must have a read_string function */ + if (pdset->number < 5 || !pdset->read_string) { + recGblRecordError(S_dev_missingSup, prec, "lsi: init_record"); + return S_dev_missingSup; + } + + if (pdset->init_record) { + long status = pdset->init_record(prec); + + if (status) + return status; + } + + if (prec->len) { + strcpy(prec->oval, prec->val); + prec->olen = prec->len; + prec->udf = FALSE; + } + + return 0; +} + +static long process(lsiRecord *prec) +{ + int pact = prec->pact; + lsidset *pdset = (lsidset *) prec->dset; + long status = 0; + + if (!pdset || !pdset->read_string) { + prec->pact = TRUE; + recGblRecordError(S_dev_missingSup, prec, "lsi: read_string"); + return S_dev_missingSup; + } + + status = readValue(prec); /* read the new value */ + if (!pact && prec->pact) + return 0; + + prec->pact = TRUE; + recGblGetTimeStamp(prec); + + monitor(prec); + + /* Wrap up */ + recGblFwdLink(prec); + prec->pact = FALSE; + return status; +} + +static long cvt_dbaddr(DBADDR *paddr) +{ + lsiRecord *prec = (lsiRecord *) paddr->precord; + int fieldIndex = dbGetFieldIndex(paddr); + + if (fieldIndex == lsiRecordVAL) { + paddr->pfield = prec->val; + paddr->special = SPC_MOD; + } + else if (fieldIndex == lsiRecordOVAL) { + paddr->pfield = prec->oval; + paddr->special = SPC_NOMOD; + } + else { + errlogPrintf("lsiRecord::cvt_dbaddr called for %s.%s\n", + prec->name, paddr->pfldDes->name); + return -1; + } + + paddr->no_elements = 1; + paddr->field_type = DBF_STRING; + paddr->dbr_field_type = DBF_STRING; + paddr->field_size = prec->sizv; + return 0; +} + +static long get_array_info(DBADDR *paddr, long *no_elements, long *offset) +{ + lsiRecord *prec = (lsiRecord *) paddr->precord; + int fieldIndex = dbGetFieldIndex(paddr); + + if (fieldIndex == lsiRecordVAL) + *no_elements = prec->len; + else if (fieldIndex == lsiRecordOVAL) + *no_elements = prec->olen; + else + return -1; + + *offset = 0; + return 0; +} + +static long put_array_info(DBADDR *paddr, long nNew) +{ + lsiRecord *prec = (lsiRecord *) paddr->precord; + + if (nNew == prec->sizv) + --nNew; /* truncated string */ + prec->val[nNew] = 0; /* ensure data is terminated */ + + return 0; +} + +static long special(DBADDR *paddr, int after) +{ + lsiRecord *prec = (lsiRecord *) paddr->precord; + + if (!after) + return 0; + + /* We set prec->len here and not in put_array_info() + * because that does not get called if the put was + * done using a DBR_STRING type. + */ + prec->len = strlen(prec->val) + 1; + db_post_events(prec, &prec->len, DBE_VALUE | DBE_LOG); + + return 0; +} + +static void monitor(lsiRecord *prec) +{ + epicsUInt16 events = recGblResetAlarms(prec); + + if (prec->len != prec->olen || + memcmp(prec->oval, prec->val, prec->len)) { + events |= DBE_VALUE | DBE_LOG; + memcpy(prec->oval, prec->val, prec->len); + } + + if (prec->len != prec->olen) { + prec->olen = prec->len; + db_post_events(prec, &prec->len, DBE_VALUE | DBE_LOG); + } + + if (prec->mpst == menuPost_Always) + events |= DBE_VALUE; + if (prec->apst == menuPost_Always) + events |= DBE_LOG; + + if (events) + db_post_events(prec, prec->val, events); +} + +static long readValue(lsiRecord *prec) +{ + long status; + lsidset *pdset = (lsidset *) prec->dset; + + if (prec->pact) + goto read; + + status = dbGetLink(&prec->siml, DBR_USHORT, &prec->simm, 0, 0); + if (status) + return status; + + switch (prec->simm) { + case menuYesNoNO: +read: + status = pdset->read_string(prec); + break; + + case menuYesNoYES: + recGblSetSevr(prec, SIMM_ALARM, prec->sims); + status = dbGetLinkLS(&prec->siol, prec->val, prec->sizv, &prec->len); + break; + + default: + recGblSetSevr(prec, SOFT_ALARM, INVALID_ALARM); + status = -1; + } + + if (!status) + prec->udf = FALSE; + + return status; +} + + +/* Create Record Support Entry Table*/ + +#define report NULL +#define initialize NULL +/* init_record */ +/* process */ +/* special */ +#define get_value NULL +/* cvt_dbaddr */ +/* get_array_info */ +/* put_array_info */ +#define get_units NULL +#define get_precision NULL +#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 lsiRSET = { + 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, lsiRSET); diff --git a/src/std/rec/lsiRecord.dbd b/src/std/rec/lsiRecord.dbd new file mode 100644 index 000000000..be14cf552 --- /dev/null +++ b/src/std/rec/lsiRecord.dbd @@ -0,0 +1,88 @@ +#************************************************************************* +# Copyright (c) 2012 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. +#************************************************************************* + +recordtype(lsi) { + include "dbCommon.dbd" + %#include "devSup.h" + % + %/* Declare Device Support Entry Table */ + %typedef struct lsidset { + % long number; + % DEVSUPFUN report; + % DEVSUPFUN init; + % DEVSUPFUN init_record; + % DEVSUPFUN get_ioint_info; + % DEVSUPFUN read_string; + %} lsidset; + % + field(VAL,DBF_NOACCESS) { + prompt("Current Value") + asl(ASL0) + pp(TRUE) + special(SPC_DBADDR) + extra("char *val") + } + field(OVAL,DBF_NOACCESS) { + prompt("Old Value") + special(SPC_DBADDR) + interest(3) + extra("char *oval") + } + field(SIZV,DBF_USHORT) { + prompt("Size of buffers") + promptgroup(GUI_OUTPUT) + special(SPC_NOMOD) + interest(1) + initial("41") + } + field(LEN,DBF_ULONG) { + prompt("Length of VAL") + special(SPC_NOMOD) + } + field(OLEN,DBF_ULONG) { + prompt("Length of OVAL") + special(SPC_NOMOD) + } + field(INP,DBF_INLINK) { + prompt("Input Specification") + promptgroup(GUI_INPUTS) + interest(1) + } + field(MPST,DBF_MENU) { + prompt("Post Value Monitors") + promptgroup(GUI_DISPLAY) + interest(1) + menu(menuPost) + } + field(APST,DBF_MENU) { + prompt("Post Archive Monitors") + promptgroup(GUI_DISPLAY) + interest(1) + menu(menuPost) + } + field(SIML,DBF_INLINK) { + prompt("Simulation Mode Link") + promptgroup(GUI_INPUTS) + interest(2) + } + field(SIMM,DBF_MENU) { + prompt("Simulation Mode") + interest(2) + menu(menuYesNo) + } + field(SIMS,DBF_MENU) { + prompt("Simulation Mode Severity") + promptgroup(GUI_INPUTS) + interest(2) + menu(menuAlarmSevr) + } + field(SIOL,DBF_INLINK) { + prompt("Sim Input Specifctn") + promptgroup(GUI_INPUTS) + interest(2) + } +} diff --git a/src/std/rec/lsoRecord.c b/src/std/rec/lsoRecord.c new file mode 100644 index 000000000..271a0c3f9 --- /dev/null +++ b/src/std/rec/lsoRecord.c @@ -0,0 +1,322 @@ +/*************************************************************************\ +* Copyright (c) 2012 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. +\*************************************************************************/ + +/* Long String Output record type */ +/* + * Author: Andrew Johnson + * Date: 2012-11-28 + */ + + +#include +#include +#include + +#include "dbDefs.h" +#include "errlog.h" +#include "alarm.h" +#include "cantProceed.h" +#include "dbAccess.h" +#include "dbEvent.h" +#include "dbFldTypes.h" +#include "devSup.h" +#include "errMdef.h" +#include "menuIvoa.h" +#include "menuOmsl.h" +#include "menuPost.h" +#include "menuYesNo.h" +#include "recSup.h" +#include "recGbl.h" +#include "special.h" +#define GEN_SIZE_OFFSET +#include "lsoRecord.h" +#undef GEN_SIZE_OFFSET +#include "epicsExport.h" + +static void monitor(lsoRecord *); +static long writeValue(lsoRecord *); + +static long init_record(lsoRecord *prec, int pass) +{ + lsodset *pdset; + + if (pass == 0) { + size_t sizv = prec->sizv; + + if (sizv < 16) { + sizv = 16; /* Enforce a minimum size for the VAL field */ + prec->sizv = sizv; + } + + prec->val = callocMustSucceed(1, sizv, "lso::init_record"); + prec->len = 0; + prec->oval = callocMustSucceed(1, sizv, "lso::init_record"); + prec->olen = 0; + return 0; + } + + dbLoadLink(&prec->siml, DBF_USHORT, &prec->simm); + + pdset = (lsodset *) prec->dset; + if (!pdset) { + recGblRecordError(S_dev_noDSET, prec, "lso: init_record"); + return S_dev_noDSET; + } + + /* must have a write_string function defined */ + if (pdset->number < 5 || !pdset->write_string) { + recGblRecordError(S_dev_missingSup, prec, "lso: init_record"); + return S_dev_missingSup; + } + + dbLoadLinkLS(&prec->dol, prec->val, prec->sizv, &prec->len); + + if (pdset->init_record) { + long status = pdset->init_record(prec); + + if (status) + return status; + } + + if (prec->len) { + strcpy(prec->oval, prec->val); + prec->olen = prec->len; + prec->udf = FALSE; + } + + return 0; +} + +static long process(lsoRecord *prec) +{ + int pact = prec->pact; + lsodset *pdset = (lsodset *) prec->dset; + long status = 0; + + if (!pdset || !pdset->write_string) { + prec->pact = TRUE; + recGblRecordError(S_dev_missingSup, prec, "lso: write_string"); + return S_dev_missingSup; + } + + if (!pact && prec->omsl == menuOmslclosed_loop) + if (!dbGetLinkLS(&prec->dol, prec->val, prec->sizv, &prec->len)) + prec->udf = FALSE; + + if (prec->udf) + recGblSetSevr(prec, UDF_ALARM, INVALID_ALARM); + + if (prec->nsev < INVALID_ALARM ) + status = writeValue(prec); /* write the new value */ + else { + switch (prec->ivoa) { + case menuIvoaContinue_normally: + status = writeValue(prec); /* write the new value */ + break; + + case menuIvoaDon_t_drive_outputs: + break; + + case menuIvoaSet_output_to_IVOV: + if (!prec->pact) { + size_t size = prec->sizv - 1; + + strncpy(prec->val, prec->ivov, size); + prec->val[size] = 0; + prec->len = strlen(prec->val) + 1; + } + status = writeValue(prec); /* write the new value */ + break; + + default: + status = -1; + recGblRecordError(S_db_badField, prec, + "lso:process Bad IVOA choice"); + } + } + + /* Asynchronous if device support set pact */ + if (!pact && prec->pact) + return status; + + prec->pact = TRUE; + recGblGetTimeStamp(prec); + + monitor(prec); + + /* Wrap up */ + recGblFwdLink(prec); + prec->pact = FALSE; + return status; +} + +static long cvt_dbaddr(DBADDR *paddr) +{ + lsoRecord *prec = (lsoRecord *) paddr->precord; + int fieldIndex = dbGetFieldIndex(paddr); + + if (fieldIndex == lsoRecordVAL) { + paddr->pfield = prec->val; + paddr->special = SPC_MOD; + } + else if (fieldIndex == lsoRecordOVAL) { + paddr->pfield = prec->oval; + paddr->special = SPC_NOMOD; + } + else { + errlogPrintf("lsoRecord::cvt_dbaddr called for %s.%s\n", + prec->name, paddr->pfldDes->name); + return -1; + } + + paddr->no_elements = 1; + paddr->field_type = DBF_STRING; + paddr->dbr_field_type = DBF_STRING; + paddr->field_size = prec->sizv; + return 0; +} + +static long get_array_info(DBADDR *paddr, long *no_elements, long *offset) +{ + lsoRecord *prec = (lsoRecord *) paddr->precord; + int fieldIndex = dbGetFieldIndex(paddr); + + if (fieldIndex == lsoRecordVAL) + *no_elements = prec->len; + else if (fieldIndex == lsoRecordOVAL) + *no_elements = prec->olen; + else + return -1; + + *offset = 0; + return 0; +} + +static long put_array_info(DBADDR *paddr, long nNew) +{ + lsoRecord *prec = (lsoRecord *) paddr->precord; + + if (nNew == prec->sizv) + --nNew; /* truncated string */ + prec->val[nNew] = 0; /* ensure data is terminated */ + + return 0; +} + +static long special(DBADDR *paddr, int after) +{ + lsoRecord *prec = (lsoRecord *) paddr->precord; + + if (!after) + return 0; + + /* We set prec->len here and not in put_array_info() + * because that does not get called if the put was + * done using a DBR_STRING type. + */ + prec->len = strlen(prec->val) + 1; + db_post_events(prec, &prec->len, DBE_VALUE | DBE_LOG); + + return 0; +} + +static void monitor(lsoRecord *prec) +{ + epicsUInt16 events = recGblResetAlarms(prec); + + if (prec->len != prec->olen || + memcmp(prec->oval, prec->val, prec->len)) { + events |= DBE_VALUE | DBE_LOG; + memcpy(prec->oval, prec->val, prec->len); + } + + if (prec->len != prec->olen) { + prec->olen = prec->len; + db_post_events(prec, &prec->len, DBE_VALUE | DBE_LOG); + } + + if (prec->mpst == menuPost_Always) + events |= DBE_VALUE; + if (prec->apst == menuPost_Always) + events |= DBE_LOG; + + if (events) + db_post_events(prec, prec->val, events); +} + +static long writeValue(lsoRecord *prec) +{ + long status; + lsodset *pdset = (lsodset *) prec->dset; + + if (prec->pact) + goto write; + + status = dbGetLink(&prec->siml, DBR_USHORT, &prec->simm, 0, 0); + if (status) + return(status); + + switch (prec->simm) { + case menuYesNoNO: +write: + status = pdset->write_string(prec); + break; + + case menuYesNoYES: + recGblSetSevr(prec, SIMM_ALARM, prec->sims); + status = dbPutLink(&prec->siol,DBR_STRING, prec->val,1); + break; + + default: + recGblSetSevr(prec, SOFT_ALARM, INVALID_ALARM); + status = -1; + } + + return status; +} + +/* Create Record Support Entry Table*/ + +#define report NULL +#define initialize NULL +/* init_record */ +/* process */ +/* special */ +#define get_value NULL +/* cvt_dbaddr */ +/* get_array_info */ +/* put_array_info */ +#define get_units NULL +#define get_precision NULL +#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 lsoRSET = { + 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, lsoRSET); diff --git a/src/std/rec/lsoRecord.dbd b/src/std/rec/lsoRecord.dbd new file mode 100644 index 000000000..48ddea810 --- /dev/null +++ b/src/std/rec/lsoRecord.dbd @@ -0,0 +1,112 @@ +#************************************************************************* +# Copyright (c) 2012 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. +#************************************************************************* + +recordtype(lso) { + include "dbCommon.dbd" + %#include "devSup.h" + % + %/* Declare Device Support Entry Table */ + %typedef struct lsodset { + % long number; + % DEVSUPFUN report; + % DEVSUPFUN init; + % DEVSUPFUN init_record; + % DEVSUPFUN get_ioint_info; + % DEVSUPFUN write_string; + %} lsodset; + % + field(VAL,DBF_NOACCESS) { + prompt("Current Value") + asl(ASL0) + pp(TRUE) + special(SPC_DBADDR) + extra("char *val") + } + field(OVAL,DBF_NOACCESS) { + prompt("Previous Value") + special(SPC_DBADDR) + interest(3) + extra("char *oval") + } + field(SIZV,DBF_USHORT) { + prompt("Size of buffers") + promptgroup(GUI_OUTPUT) + special(SPC_NOMOD) + interest(1) + initial("41") + } + field(LEN,DBF_ULONG) { + prompt("Length of VAL") + special(SPC_NOMOD) + } + field(OLEN,DBF_ULONG) { + prompt("Length of OVAL") + special(SPC_NOMOD) + interest(3) + } + field(DOL,DBF_INLINK) { + prompt("Desired Output Link") + promptgroup(GUI_OUTPUT) + interest(1) + } + field(IVOA,DBF_MENU) { + prompt("INVALID Output Action") + promptgroup(GUI_OUTPUT) + interest(2) + menu(menuIvoa) + } + field(IVOV,DBF_STRING) { + prompt("INVALID Output Value") + promptgroup(GUI_OUTPUT) + interest(2) + size(40) + } + field(OMSL,DBF_MENU) { + prompt("Output Mode Select") + promptgroup(GUI_OUTPUT) + interest(1) + menu(menuOmsl) + } + field(OUT,DBF_OUTLINK) { + prompt("Output Specification") + promptgroup(GUI_OUTPUT) + interest(1) + } + field(MPST,DBF_MENU) { + prompt("Post Value Monitors") + promptgroup(GUI_DISPLAY) + interest(1) + menu(menuPost) + } + field(APST,DBF_MENU) { + prompt("Post Archive Monitors") + promptgroup(GUI_DISPLAY) + interest(1) + menu(menuPost) + } + field(SIML,DBF_INLINK) { + prompt("Sim Mode link") + promptgroup(GUI_INPUTS) + interest(1) + } + field(SIMM,DBF_MENU) { + prompt("Simulation Mode") + interest(1) + menu(menuYesNo) + } + field(SIMS,DBF_MENU) { + prompt("Sim mode Alarm Svrty") + promptgroup(GUI_INPUTS) + interest(2) + menu(menuAlarmSevr) + } + field(SIOL,DBF_OUTLINK) { + prompt("Sim Output Specifctn") + promptgroup(GUI_INPUTS) + interest(1) + } +} diff --git a/src/std/rec/printfRecord.c b/src/std/rec/printfRecord.c new file mode 100644 index 000000000..91558b3ff --- /dev/null +++ b/src/std/rec/printfRecord.c @@ -0,0 +1,438 @@ +/*************************************************************************\ +* Copyright (c) 2012 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. +\*************************************************************************/ + +/* Printf record type */ +/* + * Author: Andrew Johnson + * Date: 2012-09-18 + */ + +#include +#include + +#include "dbDefs.h" +#include "errlog.h" +#include "alarm.h" +#include "cantProceed.h" +#include "dbAccess.h" +#include "dbEvent.h" +#include "dbFldTypes.h" +#include "epicsMath.h" +#include "epicsStdio.h" +#include "errMdef.h" +#include "recSup.h" +#include "recGbl.h" +#include "special.h" +#define GEN_SIZE_OFFSET +#include "printfRecord.h" +#undef GEN_SIZE_OFFSET +#include "epicsExport.h" + + +/* Flag bits */ +#define F_CHAR 1 +#define F_SHORT 2 +#define F_LONG 4 +#define F_LEFT 8 +#define F_BADFMT 0x10 +#define F_BADLNK 0x20 +#define F_BAD (F_BADFMT | F_BADLNK) + +#define GET_PRINT(VALTYPE, DBRTYPE) \ + VALTYPE val; \ + int ok; \ +\ + if (plink->type == CONSTANT) \ + ok = recGblInitConstantLink(plink++, DBRTYPE, &val); \ + else \ + ok = ! dbGetLink(plink++, DBRTYPE, &val, 0, 0); \ + if (ok) \ + added = epicsSnprintf(pval, vspace + 1, format, val); \ + else \ + flags |= F_BADLNK + +static void doPrintf(printfRecord *prec) +{ + const char *pfmt = prec->fmt; + DBLINK *plink = &prec->inp0; + int linkn = 0; + char *pval = prec->val; + int vspace = prec->sizv - 1; + int ch; + + while (vspace > 0 && (ch = *pfmt++)) { + if (ch != '%') { + /* Copy literal strings directly into prec->val */ + *pval++ = ch; + --vspace; + } + else { + char format[20]; + char *pformat = format; + int width = 0; + int precision = 0; + int *pnum = &width; + int flags = 0; + int added = 0; + int cont = 1; + + /* The format directive parsing here is not comprehensive, + * in most cases we just copy each directive into format[] + * and get epicsSnprintf() do all the work. We do replace + * all variable-length field width or precision '*' chars + * with an integer read from the next input link, and we + * also convert %ls (long string) directives ourself, so + * we need to know the width, precision and justification. + */ + + *pformat++ = ch; /* '%' */ + while (cont && (ch = *pfmt++)) { + *pformat++ = ch; + switch (ch) { + case '+': case ' ': case '#': + break; + case '-': + flags |= F_LEFT; + break; + case '.': + pnum = &precision; + break; + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + *pnum = *pnum * 10 + ch - '0'; + break; + case '*': + if (*pnum) { + flags |= F_BADFMT; + } + else if (linkn++ < PRINTF_NLINKS) { + epicsInt16 i; + int ok; + + if (plink->type == CONSTANT) + ok = recGblInitConstantLink(plink++, DBR_SHORT, &i); + else + ok = ! dbGetLink(plink++, DBR_SHORT, &i, 0, 0); + if (ok) { + *pnum = i; + added = epicsSnprintf(--pformat, 6, "%d", i); + pformat += added; + } + else /* No more LNKn fields */ + flags |= F_BADLNK; + } + else + flags |= F_BADLNK; + break; + case 'h': + if (flags & F_SHORT) + flags = (flags & ~F_SHORT) | F_CHAR; + else + flags |= F_SHORT; + break; + case 'l': + flags |= F_LONG; + break; + default: + if (strchr("diouxXeEfFgGcs%", ch) == NULL) + flags |= F_BADFMT; + cont = 0; + break; + } + } + if (!ch) /* End of format string */ + break; + + if (flags & F_BAD) + goto bad_format; + + *pformat = 0; /* Terminate our format string */ + + if (width < 0) { + width = -width; + flags |= F_LEFT; + } + if (precision < 0) + precision = 0; + + if (ch == '%') { + added = epicsSnprintf(pval, vspace + 1, format); + } + else if (linkn++ >= PRINTF_NLINKS) { + /* No more LNKn fields */ + flags |= F_BADLNK; + } + else + switch (ch) { /* Conversion character */ + case 'c': case 'd': case 'i': + if (ch == 'c' || flags & F_CHAR) { + GET_PRINT(epicsInt8, DBR_CHAR); + } + else if (flags & F_SHORT) { + GET_PRINT(epicsInt16, DBR_SHORT); + } + else { /* F_LONG has no real effect */ + GET_PRINT(epicsInt32, DBR_LONG); + } + break; + + case 'o': case 'x': case 'X': case 'u': + if (flags & F_CHAR) { + GET_PRINT(epicsUInt8, DBR_UCHAR); + } + else if (flags & F_SHORT) { + GET_PRINT(epicsUInt16, DBR_USHORT); + } + else { /* F_LONG has no real effect */ + GET_PRINT(epicsUInt32, DBR_ULONG); + } + break; + + case 'e': case 'E': + case 'f': case 'F': + case 'g': case 'G': + if (flags & F_SHORT) { + GET_PRINT(epicsFloat32, DBR_FLOAT); + } + else { + GET_PRINT(epicsFloat64, DBR_DOUBLE); + } + break; + + case 's': + if (flags & F_LONG && plink->type != CONSTANT) { + long n = vspace + 1; + + if (precision && n > precision) + n = precision + 1; + /* If set, precision is the maximum number of + * characters to be printed from the string. + * It does not limit the field width however. + */ + if (dbGetLink(plink++, DBR_CHAR, pval, 0, &n)) + flags |= F_BADLNK; + else { + int padding; + + /* Terminate string and measure its length */ + pval[n] = 0; + added = strlen(pval); + padding = width - added; + + if (padding > 0) { + if (flags & F_LEFT) { + /* add spaces on RHS */ + if (width > vspace) + padding = vspace - added; + memset(pval + added, ' ', padding); + } + else { + /* insert spaces on LHS */ + int trunc = width - vspace; + + if (trunc < added) { + added -= trunc; + memmove(pval + padding, pval, added); + } + else { + padding = vspace; + added = 0; + } + memset(pval, ' ', padding); + } + added += padding; + } + } + } + else { + char val[MAX_STRING_SIZE]; + int ok; + + if (plink->type == CONSTANT) + ok = recGblInitConstantLink(plink++, DBR_STRING, val); + else + ok = ! dbGetLink(plink++, DBR_STRING, val, 0, 0); + if (ok) + added = epicsSnprintf(pval, vspace + 1, format, val); + else + flags |= F_BADLNK; + } + break; + + default: + errlogPrintf("printfRecord: Unexpected conversion '%s'\n", + format); + flags |= F_BADFMT; + break; + } + + if (flags & F_BAD) { + bad_format: + added = epicsSnprintf(pval, vspace + 1, "%s", + flags & F_BADLNK ? prec->ivls : format); + } + + if (added <= vspace) { + pval += added; + vspace -= added; + } + else { + /* Output was truncated */ + pval += vspace; + vspace = 0; + } + } + } + *pval++ = 0; /* Terminate the VAL string */ + prec->len = pval - prec->val; +} + + +static long init_record(printfRecord *prec, int pass) +{ + printfdset *pdset; + + if (pass == 0) { + size_t sizv = prec->sizv; + + if (sizv < 16) { + sizv = 16; /* Enforce a minimum size for the VAL field */ + prec->sizv = sizv; + } + + prec->val = callocMustSucceed(1, sizv, "printf::init_record"); + prec->len = 0; + return 0; + } + + pdset = (printfdset *) prec->dset; + if (!pdset) + return 0; /* Device support is optional */ + + if (pdset->number < 5) { + recGblRecordError(S_dev_missingSup, prec, "printf::init_record"); + return S_dev_missingSup; + } + + if (pdset->init_record) { + long status = pdset->init_record(prec); + if (status) + return status; + } + + return 0; +} + +static long process(printfRecord *prec) +{ + int pact = prec->pact; + printfdset *pdset; + long status = 0; + epicsUInt16 events; + + if (!pact) { + doPrintf(prec); + + prec->udf = FALSE; + recGblGetTimeStamp(prec); + } + + /* Call device support */ + pdset = (printfdset *) prec->dset; + if (pdset && + pdset->number >= 5 && + pdset->write_string) { + status = pdset->write_string(prec); + + /* Asynchronous if device support set pact */ + if (!pact && prec->pact) + return status; + } + + prec->pact = TRUE; + + /* Post monitor */ + events = recGblResetAlarms(prec); + db_post_events(prec, prec->val, events | DBE_VALUE | DBE_LOG); + db_post_events(prec, &prec->len, events | DBE_VALUE | DBE_LOG); + + /* Wrap up */ + recGblFwdLink(prec); + prec->pact = FALSE; + return status; +} + +static long cvt_dbaddr(DBADDR *paddr) +{ + printfRecord *prec = (printfRecord *)paddr->precord; + int fieldIndex = dbGetFieldIndex(paddr); + + if (fieldIndex == printfRecordVAL) { + paddr->pfield = prec->val; + paddr->no_elements = 1; + paddr->field_type = DBF_STRING; + paddr->dbr_field_type = DBF_STRING; + paddr->field_size = prec->sizv; + } + else + errlogPrintf("printfRecord::cvt_dbaddr called for %s.%s\n", + prec->name, paddr->pfldDes->name); + return 0; +} + +static long get_array_info(DBADDR *paddr, long *no_elements, long *offset) +{ + printfRecord *prec = (printfRecord *) paddr->precord; + + *no_elements = prec->len; + *offset = 0; + return 0; +} + + +/* Create Record Support Entry Table */ + +#define report NULL +#define initialize NULL +/* init_record */ +/* process */ +#define special NULL +#define get_value NULL +/* cvt_dbaddr */ +/* get_array_info */ +#define put_array_info NULL +#define get_units NULL +#define get_precision NULL +#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 printfRSET = { + 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, printfRSET); + diff --git a/src/std/rec/printfRecord.dbd b/src/std/rec/printfRecord.dbd new file mode 100644 index 000000000..37beccebf --- /dev/null +++ b/src/std/rec/printfRecord.dbd @@ -0,0 +1,109 @@ +#************************************************************************* +# Copyright (c) 2012 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. +#************************************************************************* + +recordtype(printf) { + include "dbCommon.dbd" + %#include "devSup.h" + % + %/* Declare Device Support Entry Table */ + %typedef struct printfdset { + % long number; + % DEVSUPFUN report; + % DEVSUPFUN init; + % DEVSUPFUN init_record; + % DEVSUPFUN get_ioint_info; + % DEVSUPFUN write_string; + %} printfdset; + % + field(VAL,DBF_NOACCESS) { + prompt("Result") + asl(ASL0) + pp(TRUE) + special(SPC_DBADDR) + extra("char *val") + } + field(SIZV,DBF_USHORT) { + prompt("Size of VAL buffer") + promptgroup(GUI_OUTPUT) + special(SPC_NOMOD) + interest(1) + initial("41") + } + field(LEN,DBF_ULONG) { + prompt("Length of VAL") + special(SPC_NOMOD) + } + field(OUT,DBF_OUTLINK) { + prompt("Output Specification") + promptgroup(GUI_OUTPUT) + interest(1) + } + field(FMT,DBF_STRING) { + prompt("Format String") + promptgroup(GUI_CALC) + pp(TRUE) + size(81) + } + field(IVLS,DBF_STRING) { + prompt("Invalid Link String") + promptgroup(GUI_CALC) + size(16) + initial("LNK") + } + field(INP0,DBF_INLINK) { + prompt("Input 0") + promptgroup(GUI_INPUTS) + interest(1) + } + field(INP1,DBF_INLINK) { + prompt("Input 1") + promptgroup(GUI_INPUTS) + interest(1) + } + field(INP2,DBF_INLINK) { + prompt("Input 2") + promptgroup(GUI_INPUTS) + interest(1) + } + field(INP3,DBF_INLINK) { + prompt("Input 3") + promptgroup(GUI_INPUTS) + interest(1) + } + field(INP4,DBF_INLINK) { + prompt("Input 4") + promptgroup(GUI_INPUTS) + interest(1) + } + field(INP5,DBF_INLINK) { + prompt("Input 5") + promptgroup(GUI_INPUTS) + interest(1) + } + field(INP6,DBF_INLINK) { + prompt("Input 6") + promptgroup(GUI_INPUTS) + interest(1) + } + field(INP7,DBF_INLINK) { + prompt("Input 7") + promptgroup(GUI_INPUTS) + interest(1) + } + field(INP8,DBF_INLINK) { + prompt("Input 8") + promptgroup(GUI_INPUTS) + interest(1) + } + field(INP9,DBF_INLINK) { + prompt("Input 9") + promptgroup(GUI_INPUTS) + interest(1) + } + %/* Number of INPx fields defined */ + %#define PRINTF_NLINKS 10 +}