From d8812cbee351d86af9694e465d093ebc368d8371 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Fri, 28 Sep 2012 18:00:13 -0500 Subject: [PATCH 01/15] std: Added new printf record type --- src/std/rec/Makefile | 2 + src/std/rec/printfRecord.c | 426 +++++++++++++++++++++++++++++++++++ src/std/rec/printfRecord.dbd | 105 +++++++++ 3 files changed, 533 insertions(+) create mode 100644 src/std/rec/printfRecord.c create mode 100644 src/std/rec/printfRecord.dbd diff --git a/src/std/rec/Makefile b/src/std/rec/Makefile index 96cf66244..a7098591c 100644 --- a/src/std/rec/Makefile +++ b/src/std/rec/Makefile @@ -32,6 +32,7 @@ DBDINC += mbbiDirectRecord DBDINC += mbboRecord DBDINC += mbboDirectRecord DBDINC += permissiveRecord +DBDINC += printfRecord DBDINC += selRecord DBDINC += seqRecord DBDINC += stateRecord @@ -66,6 +67,7 @@ dbRecStd_SRCS += mbbiDirectRecord.c dbRecStd_SRCS += mbboRecord.c dbRecStd_SRCS += mbboDirectRecord.c dbRecStd_SRCS += permissiveRecord.c +dbRecStd_SRCS += printfRecord.c dbRecStd_SRCS += selRecord.c dbRecStd_SRCS += seqRecord.c dbRecStd_SRCS += stateRecord.c diff --git a/src/std/rec/printfRecord.c b/src/std/rec/printfRecord.c new file mode 100644 index 000000000..155df803c --- /dev/null +++ b/src/std/rec/printfRecord.c @@ -0,0 +1,426 @@ +/*************************************************************************\ +* 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 size_t 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 */ + return 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->val = callocMustSucceed(1, sizv, "printf::init_record"); + 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; + size_t len = 0; + printfdset *pdset; + long status = 0; + epicsUInt16 events; + + if (!pact) { + len = 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, len); + + /* 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); + + /* 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; +} + + +/* Create Record Support Entry Table */ + +#define report NULL +#define initialize NULL +/* init_record */ +/* process */ +#define special NULL +#define get_value NULL +/* cvt_dbaddr */ +#define get_array_info NULL +#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..2a1bf0be3 --- /dev/null +++ b/src/std/rec/printfRecord.dbd @@ -0,0 +1,105 @@ +#************************************************************************* +# 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") + promptgroup(GUI_OUTPUT) + special(SPC_NOMOD) + interest(1) + initial("41") + } + 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 +} From e94ffd48fa6b6c753b11dbefa50dc4a791116bf5 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Fri, 28 Sep 2012 18:00:42 -0500 Subject: [PATCH 02/15] std: Device support for new printf record type --- src/std/dev/Makefile | 3 + src/std/dev/devPrintfSoft.c | 35 ++++++++++ src/std/dev/devPrintfSoftCallback.c | 50 +++++++++++++ src/std/dev/devPrintfStdio.c | 105 ++++++++++++++++++++++++++++ src/std/dev/devSoft.dbd | 3 + 5 files changed, 196 insertions(+) create mode 100644 src/std/dev/devPrintfSoft.c create mode 100644 src/std/dev/devPrintfSoftCallback.c create mode 100644 src/std/dev/devPrintfStdio.c diff --git a/src/std/dev/Makefile b/src/std/dev/Makefile index 3ee4e7608..2a037ea42 100644 --- a/src/std/dev/Makefile +++ b/src/std/dev/Makefile @@ -38,6 +38,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 @@ -57,9 +58,11 @@ dbRecStd_SRCS += devCalcoutSoftCallback.c dbRecStd_SRCS += devLoSoftCallback.c dbRecStd_SRCS += devMbboSoftCallback.c dbRecStd_SRCS += devMbboDirectSoftCallback.c +dbRecStd_SRCS += devPrintfSoftCallback.c dbRecStd_SRCS += devSoSoftCallback.c dbRecStd_SRCS += devTimestamp.c +dbRecStd_SRCS += devPrintfStdio.c dbRecStd_SRCS += devSoStdio.c dbRecStd_SRCS += asSubRecordFunctions.c diff --git a/src/std/dev/devPrintfSoft.c b/src/std/dev/devPrintfSoft.c new file mode 100644 index 000000000..f88db01ec --- /dev/null +++ b/src/std/dev/devPrintfSoft.c @@ -0,0 +1,35 @@ +/*************************************************************************\ +* 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, size_t len) +{ + struct link *plink = &prec->out; + int dtyp = dbGetLinkDBFtype(plink); + + if (dtyp < 0) + return 0; /* Not connected */ + + if (dtyp == DBR_CHAR || dtyp == DBF_UCHAR) + return dbPutLink(plink, dtyp, prec->val, len); + + return dbPutLink(plink, DBR_STRING, prec->val, 1); +} + +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..09eb2494d --- /dev/null +++ b/src/std/dev/devPrintfSoftCallback.c @@ -0,0 +1,50 @@ +/*************************************************************************\ +* 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, size_t len) +{ + struct link *plink = &prec->out; + int dtyp = dbGetLinkDBFtype(plink); + 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/devPrintfStdio.c b/src/std/dev/devPrintfStdio.c new file mode 100644 index 000000000..b109c750f --- /dev/null +++ b/src/std/dev/devPrintfStdio.c @@ -0,0 +1,105 @@ +/*************************************************************************\ +* 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$ */ + +#include +#include + +#include "dbCommon.h" +#include "devSup.h" +#include "errlog.h" +#include "recGbl.h" +#include "recSup.h" +#include "printfRecord.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) { + 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(dbCommon *pcommon) { + printfRecord *prec = (printfRecord *) 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(printfRecord *prec, size_t len) +{ + struct outStream *pstream = (struct outStream *)prec->dpvt; + if (pstream) + pstream->print("%s\n", prec->val); + return 0; +} + +/* Create the dset for devSoStdio */ +printfdset devPrintfStdio = { + 5, NULL, init, NULL, NULL, write_string +}; +epicsExportAddress(dset, devPrintfStdio); diff --git a/src/std/dev/devSoft.dbd b/src/std/dev/devSoft.dbd index 9072245ca..da2f21ad4 100644 --- a/src/std/dev/devSoft.dbd +++ b/src/std/dev/devSoft.dbd @@ -13,6 +13,7 @@ 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") @@ -38,6 +39,7 @@ 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 +51,7 @@ device(bo, INST_IO,devBoGeneralTime,"General Time") device(longin, INST_IO,devLiGeneralTime,"General Time") device(stringin,INST_IO,devSiGeneralTime,"General Time") +device(printf,INST_IO,devPrintfStdio,"stdio") device(stringout,INST_IO,devSoStdio,"stdio") device(bi, INST_IO, devBiDbState, "Db State") From e3f59fd5c291591863732062c10bce1c59a4fe87 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Mon, 12 Nov 2012 13:45:23 -0600 Subject: [PATCH 03/15] Support variable array size Added LEN field and get_array_info() routine. Removed len arg from device support write_string() routine. Modified device support to match. --- src/std/dev/devPrintfSoft.c | 4 ++-- src/std/dev/devPrintfSoftCallback.c | 3 ++- src/std/dev/devPrintfStdio.c | 2 +- src/std/rec/printfRecord.c | 21 +++++++++++++++------ src/std/rec/printfRecord.dbd | 4 ++++ 5 files changed, 24 insertions(+), 10 deletions(-) diff --git a/src/std/dev/devPrintfSoft.c b/src/std/dev/devPrintfSoft.c index f88db01ec..4ed16ae15 100644 --- a/src/std/dev/devPrintfSoft.c +++ b/src/std/dev/devPrintfSoft.c @@ -14,7 +14,7 @@ #include "printfRecord.h" #include "epicsExport.h" -static long write_string(printfRecord *prec, size_t len) +static long write_string(printfRecord *prec) { struct link *plink = &prec->out; int dtyp = dbGetLinkDBFtype(plink); @@ -23,7 +23,7 @@ static long write_string(printfRecord *prec, size_t len) return 0; /* Not connected */ if (dtyp == DBR_CHAR || dtyp == DBF_UCHAR) - return dbPutLink(plink, dtyp, prec->val, len); + return dbPutLink(plink, dtyp, prec->val, prec->len); return dbPutLink(plink, DBR_STRING, prec->val, 1); } diff --git a/src/std/dev/devPrintfSoftCallback.c b/src/std/dev/devPrintfSoftCallback.c index 09eb2494d..7f9314d9e 100644 --- a/src/std/dev/devPrintfSoftCallback.c +++ b/src/std/dev/devPrintfSoftCallback.c @@ -16,10 +16,11 @@ #include "printfRecord.h" #include "epicsExport.h" -static long write_string(printfRecord *prec, size_t len) +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) diff --git a/src/std/dev/devPrintfStdio.c b/src/std/dev/devPrintfStdio.c index b109c750f..bf14ed817 100644 --- a/src/std/dev/devPrintfStdio.c +++ b/src/std/dev/devPrintfStdio.c @@ -90,7 +90,7 @@ static long init(int pass) return 0; } -static long write_string(printfRecord *prec, size_t len) +static long write_string(printfRecord *prec) { struct outStream *pstream = (struct outStream *)prec->dpvt; if (pstream) diff --git a/src/std/rec/printfRecord.c b/src/std/rec/printfRecord.c index 155df803c..5dfe557af 100644 --- a/src/std/rec/printfRecord.c +++ b/src/std/rec/printfRecord.c @@ -55,7 +55,7 @@ else \ flags |= F_BADLNK -static size_t doPrintf(printfRecord *prec) +static void doPrintf(printfRecord *prec) { const char *pfmt = prec->fmt; DBLINK *plink = &prec->inp0; @@ -288,7 +288,7 @@ static size_t doPrintf(printfRecord *prec) } } *pval++ = 0; /* Terminate the VAL string */ - return pval - prec->val; + prec->len = pval - prec->val; } @@ -327,13 +327,12 @@ static long init_record(printfRecord *prec, int pass) static long process(printfRecord *prec) { int pact = prec->pact; - size_t len = 0; printfdset *pdset; long status = 0; epicsUInt16 events; if (!pact) { - len = doPrintf(prec); + doPrintf(prec); prec->udf = FALSE; recGblGetTimeStamp(prec); @@ -344,7 +343,7 @@ static long process(printfRecord *prec) if (pdset && pdset->number >= 5 && pdset->write_string) { - status = pdset->write_string(prec, len); + status = pdset->write_string(prec); /* Asynchronous if device support set pact */ if (!pact && prec->pact) @@ -356,6 +355,7 @@ static long process(printfRecord *prec) /* 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); @@ -381,6 +381,15 @@ static long cvt_dbaddr(DBADDR *paddr) 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 */ @@ -391,7 +400,7 @@ static long cvt_dbaddr(DBADDR *paddr) #define special NULL #define get_value NULL /* cvt_dbaddr */ -#define get_array_info NULL +/* get_array_info */ #define put_array_info NULL #define get_units NULL #define get_precision NULL diff --git a/src/std/rec/printfRecord.dbd b/src/std/rec/printfRecord.dbd index 2a1bf0be3..f45022f23 100644 --- a/src/std/rec/printfRecord.dbd +++ b/src/std/rec/printfRecord.dbd @@ -33,6 +33,10 @@ recordtype(printf) { 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) From 493d6014b951c08c5f0f5f924ee1d5e535fb6134 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Thu, 29 Nov 2012 12:53:21 -0600 Subject: [PATCH 04/15] Add new global menuPost To reduce duplication. --- src/ioc/db/Makefile | 1 + src/ioc/db/menuPost.dbd | 11 +++++++++++ 2 files changed, 12 insertions(+) create mode 100644 src/ioc/db/menuPost.dbd 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/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") +} From 2e4f6443e012d392be905dd21a07b1273bf6eb10 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Thu, 29 Nov 2012 13:20:02 -0600 Subject: [PATCH 05/15] dbNameToAddr() changes for long string support Modified dbNameToAddr() to match how dbChannelOpen() calls prset->cvt_dbaddr() before handling the '$' field modifier. This lets DB links and the dbgf/dbpf commands work with '$'. Elsewhere changed array handling to check fldDes->special for SPC_DBADDR instead of paddr->special to allow long string records cvt_dbaddr() routines to change paddr->special without breaking the array handling stuff. --- src/ioc/db/dbAccess.c | 65 +++++++++++++++++++++---------------------- 1 file changed, 32 insertions(+), 33 deletions(-) diff --git a/src/ioc/db/dbAccess.c b/src/ioc/db/dbAccess.c index dd9094fe1..0686af799 100644 --- a/src/ioc/db/dbAccess.c +++ b/src/ioc/db/dbAccess.c @@ -589,10 +589,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; @@ -607,48 +605,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; @@ -823,7 +822,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) { @@ -1175,7 +1174,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; @@ -1187,7 +1186,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); } From d27747a7f350d4057f23b99fe6ecdaaae07293f7 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Thu, 29 Nov 2012 13:21:11 -0600 Subject: [PATCH 06/15] Added helper functions dbGetLinkLS() and dbPutLinkLS() --- src/ioc/db/dbLink.c | 45 +++++++++++++++++++++++++++++++++++++++++++++ src/ioc/db/dbLink.h | 4 ++++ 2 files changed, 49 insertions(+) diff --git a/src/ioc/db/dbLink.c b/src/ioc/db/dbLink.c index 8caae22e6..914252862 100644 --- a/src/ioc/db/dbLink.c +++ b/src/ioc/db/dbLink.c @@ -649,3 +649,48 @@ void dbScanFwdLink(struct link *plink) } } +/* Helper functions for long string support */ + +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..cb4b412ed 100644 --- a/src/ioc/db/dbLink.h +++ b/src/ioc/db/dbLink.h @@ -80,6 +80,10 @@ epicsShareFunc long dbGetTimeStamp(const struct link *plink, epicsShareFunc long dbPutLink(struct link *, short dbrType, const void *pbuffer, long nRequest); epicsShareFunc void dbScanFwdLink(struct link *plink); +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 } From 02348e770b5b5b6eb102a77186e413f4a6bf0bde Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Thu, 29 Nov 2012 13:40:42 -0600 Subject: [PATCH 07/15] Use new helper function. --- src/std/dev/devPrintfSoft.c | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/src/std/dev/devPrintfSoft.c b/src/std/dev/devPrintfSoft.c index 4ed16ae15..a61d4bbfb 100644 --- a/src/std/dev/devPrintfSoft.c +++ b/src/std/dev/devPrintfSoft.c @@ -16,16 +16,7 @@ static long write_string(printfRecord *prec) { - struct link *plink = &prec->out; - int dtyp = dbGetLinkDBFtype(plink); - - if (dtyp < 0) - return 0; /* Not connected */ - - if (dtyp == DBR_CHAR || dtyp == DBF_UCHAR) - return dbPutLink(plink, dtyp, prec->val, prec->len); - - return dbPutLink(plink, DBR_STRING, prec->val, 1); + return dbPutLinkLS(&prec->out, prec->val, prec->len); } printfdset devPrintfSoft = { From 77c837bcbe1eecbc4e72f0c5cd83ae2c0261293f Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Thu, 29 Nov 2012 13:41:46 -0600 Subject: [PATCH 08/15] printf record: Fix bug in minimum buffer size logic. --- src/std/rec/printfRecord.c | 5 ++++- src/std/rec/printfRecord.dbd | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/std/rec/printfRecord.c b/src/std/rec/printfRecord.c index 5dfe557af..91558b3ff 100644 --- a/src/std/rec/printfRecord.c +++ b/src/std/rec/printfRecord.c @@ -299,10 +299,13 @@ static long init_record(printfRecord *prec, int pass) if (pass == 0) { size_t sizv = prec->sizv; - if (sizv < 16) + 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; } diff --git a/src/std/rec/printfRecord.dbd b/src/std/rec/printfRecord.dbd index f45022f23..37beccebf 100644 --- a/src/std/rec/printfRecord.dbd +++ b/src/std/rec/printfRecord.dbd @@ -27,7 +27,7 @@ recordtype(printf) { extra("char *val") } field(SIZV,DBF_USHORT) { - prompt("Size of VAL") + prompt("Size of VAL buffer") promptgroup(GUI_OUTPUT) special(SPC_NOMOD) interest(1) From e01a428059d1c2217ec73660503c0ae81be0aa7b Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Thu, 29 Nov 2012 13:45:13 -0600 Subject: [PATCH 09/15] Added new Long String Input record type "lsi" With Soft Channel device support. --- src/std/dev/Makefile | 1 + src/std/dev/devLsiSoft.c | 60 +++++++++ src/std/dev/devSoft.dbd | 1 + src/std/rec/Makefile | 2 + src/std/rec/lsiRecord.c | 277 ++++++++++++++++++++++++++++++++++++++ src/std/rec/lsiRecord.dbd | 88 ++++++++++++ 6 files changed, 429 insertions(+) create mode 100644 src/std/dev/devLsiSoft.c create mode 100644 src/std/rec/lsiRecord.c create mode 100644 src/std/rec/lsiRecord.dbd diff --git a/src/std/dev/Makefile b/src/std/dev/Makefile index 2a037ea42..f4f8f690f 100644 --- a/src/std/dev/Makefile +++ b/src/std/dev/Makefile @@ -30,6 +30,7 @@ dbRecStd_SRCS += devEventSoft.c dbRecStd_SRCS += devHistogramSoft.c dbRecStd_SRCS += devLiSoft.c dbRecStd_SRCS += devLoSoft.c +dbRecStd_SRCS += devLsiSoft.c dbRecStd_SRCS += devMbbiDirectSoft.c dbRecStd_SRCS += devMbbiDirectSoftRaw.c dbRecStd_SRCS += devMbbiSoft.c diff --git a/src/std/dev/devLsiSoft.c b/src/std/dev/devLsiSoft.c new file mode 100644 index 000000000..10dceb359 --- /dev/null +++ b/src/std/dev/devLsiSoft.c @@ -0,0 +1,60 @@ +/*************************************************************************\ +* 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 +#include +#include + +#include "alarm.h" +#include "dbDefs.h" +#include "dbAccess.h" +#include "epicsTime.h" +#include "recGbl.h" +#include "devSup.h" +#include "link.h" +#include "lsiRecord.h" +#include "epicsExport.h" + +static long init_record(lsiRecord *prec) +{ + /* Handle CONSTANT links */ + if (prec->inp.type == CONSTANT) { + char tmp[MAX_STRING_SIZE]; + + if (recGblInitConstantLink(&prec->inp, DBF_STRING, tmp)) { + long len = prec->sizv; + + strncpy(prec->val, tmp, len); + prec->len = strlen(prec->val) + 1; + prec->udf = FALSE; + } + } + 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/devSoft.dbd b/src/std/dev/devSoft.dbd index da2f21ad4..babad358a 100644 --- a/src/std/dev/devSoft.dbd +++ b/src/std/dev/devSoft.dbd @@ -9,6 +9,7 @@ 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(mbbi,CONSTANT,devMbbiSoft,"Soft Channel") device(mbbiDirect,CONSTANT,devMbbiDirectSoft,"Soft Channel") device(mbbo,CONSTANT,devMbboSoft,"Soft Channel") diff --git a/src/std/rec/Makefile b/src/std/rec/Makefile index a7098591c..d16b20b93 100644 --- a/src/std/rec/Makefile +++ b/src/std/rec/Makefile @@ -27,6 +27,7 @@ DBDINC += fanoutRecord DBDINC += histogramRecord DBDINC += longinRecord DBDINC += longoutRecord +DBDINC += lsiRecord DBDINC += mbbiRecord DBDINC += mbbiDirectRecord DBDINC += mbboRecord @@ -62,6 +63,7 @@ dbRecStd_SRCS += fanoutRecord.c dbRecStd_SRCS += histogramRecord.c dbRecStd_SRCS += longinRecord.c dbRecStd_SRCS += longoutRecord.c +dbRecStd_SRCS += lsiRecord.c dbRecStd_SRCS += mbbiRecord.c dbRecStd_SRCS += mbbiDirectRecord.c dbRecStd_SRCS += mbboRecord.c diff --git a/src/std/rec/lsiRecord.c b/src/std/rec/lsiRecord.c new file mode 100644 index 000000000..6e025fe51 --- /dev/null +++ b/src/std/rec/lsiRecord.c @@ -0,0 +1,277 @@ +/*************************************************************************\ +* 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; + } + + if (prec->siml.type == CONSTANT) { + recGblInitConstantLink(&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 == NULL) ) { + 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); + } + + return 0; +} + +static long process(lsiRecord *prec) +{ + int pact = prec->pact; + lsidset *pdset = (lsidset *) prec->dset; + long status = 0; + + if ((pdset == NULL) || (pdset->read_string == NULL)) { + 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; + + return 0; +} + +static long special(DBADDR *paddr, int after) +{ + lsiRecord *prec = (lsiRecord *) paddr->precord; + + if (!after) + return 0; + + 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 menuYesNoYES: + recGblSetSevr(prec, SIMM_ALARM, prec->sims); + status = dbGetLinkLS(&prec->siol, prec->val, prec->sizv, &prec->len); + break; + + case menuYesNoNO: +read: + status = pdset->read_string(prec); + 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) + } +} From d741c106354675a647d680dbda92e2b4986b493a Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Fri, 30 Nov 2012 16:43:16 -0600 Subject: [PATCH 10/15] Another helper, dbLoadLinkLS Replaces recGblInitConstantLink(..., DBF_STRING, ...) --- src/ioc/db/dbLink.c | 14 ++++++++++++++ src/ioc/db/dbLink.h | 3 +++ 2 files changed, 17 insertions(+) diff --git a/src/ioc/db/dbLink.c b/src/ioc/db/dbLink.c index 914252862..54e131566 100644 --- a/src/ioc/db/dbLink.c +++ b/src/ioc/db/dbLink.c @@ -651,6 +651,20 @@ 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) { diff --git a/src/ioc/db/dbLink.h b/src/ioc/db/dbLink.h index cb4b412ed..ff0de3003 100644 --- a/src/ioc/db/dbLink.h +++ b/src/ioc/db/dbLink.h @@ -80,6 +80,9 @@ epicsShareFunc long dbGetTimeStamp(const struct link *plink, 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, From f2716f88f3bb1e60f5ed536df9d56c5b0080a8d8 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Fri, 30 Nov 2012 16:47:11 -0600 Subject: [PATCH 11/15] stdRecords.dbd depends on rec/Makefile --- src/std/rec/RULES | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/std/rec/RULES b/src/std/rec/RULES index 702ff7009..134731157 100644 --- a/src/std/rec/RULES +++ b/src/std/rec/RULES @@ -9,7 +9,7 @@ # This is a Makefile fragment, see src/std/Makefile. -$(COMMON_DIR)/stdRecords.dbd: +$(COMMON_DIR)/stdRecords.dbd: ../rec/Makefile $(RM) $@ $(PERL) $(TOOLS)/makeIncludeDbd.pl $(stdRecords_DBD) $@ From 01819b0b02fdde48e96b83f7d570f8adb59cb86d Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Fri, 30 Nov 2012 16:48:58 -0600 Subject: [PATCH 12/15] Use new helper. --- src/std/dev/devLsiSoft.c | 22 ++-------------------- 1 file changed, 2 insertions(+), 20 deletions(-) diff --git a/src/std/dev/devLsiSoft.c b/src/std/dev/devLsiSoft.c index 10dceb359..a5547cba8 100644 --- a/src/std/dev/devLsiSoft.c +++ b/src/std/dev/devLsiSoft.c @@ -2,7 +2,7 @@ * 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. +* in file LICENSE that is included with this distribution. \*************************************************************************/ /* Long String Input soft device support @@ -11,34 +11,16 @@ * Date: 2012-11-28 */ -#include -#include -#include - -#include "alarm.h" -#include "dbDefs.h" #include "dbAccess.h" #include "epicsTime.h" -#include "recGbl.h" -#include "devSup.h" #include "link.h" #include "lsiRecord.h" #include "epicsExport.h" static long init_record(lsiRecord *prec) { - /* Handle CONSTANT links */ - if (prec->inp.type == CONSTANT) { - char tmp[MAX_STRING_SIZE]; + dbLoadLinkLS(&prec->inp, prec->val, prec->sizv, &prec->len); - if (recGblInitConstantLink(&prec->inp, DBF_STRING, tmp)) { - long len = prec->sizv; - - strncpy(prec->val, tmp, len); - prec->len = strlen(prec->val) + 1; - prec->udf = FALSE; - } - } return 0; } From 080e362743c64ad628ec47bc5330de2e7227614c Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Fri, 30 Nov 2012 16:49:31 -0600 Subject: [PATCH 13/15] Cleanup. --- src/std/rec/lsiRecord.c | 37 ++++++++++++++++++++++--------------- 1 file changed, 22 insertions(+), 15 deletions(-) diff --git a/src/std/rec/lsiRecord.c b/src/std/rec/lsiRecord.c index 6e025fe51..1cbff9185 100644 --- a/src/std/rec/lsiRecord.c +++ b/src/std/rec/lsiRecord.c @@ -2,7 +2,7 @@ * 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. +* in file LICENSE that is included with this distribution. \*************************************************************************/ /* Long String Input record type */ @@ -36,7 +36,6 @@ static void monitor(lsiRecord *); static long readValue(lsiRecord *); - static long init_record(lsiRecord *prec, int pass) { lsidset *pdset; @@ -56,9 +55,7 @@ static long init_record(lsiRecord *prec, int pass) return 0; } - if (prec->siml.type == CONSTANT) { - recGblInitConstantLink(&prec->siml, DBF_USHORT, &prec->simm); - } + dbLoadLink(&prec->siml, DBF_USHORT, &prec->simm); pdset = (lsidset *) prec->dset; if (!pdset) { @@ -67,7 +64,7 @@ static long init_record(lsiRecord *prec, int pass) } /* must have a read_string function */ - if ((pdset->number < 5) || (pdset->read_string == NULL) ) { + if (pdset->number < 5 || !pdset->read_string) { recGblRecordError(S_dev_missingSup, prec, "lsi: init_record"); return S_dev_missingSup; } @@ -76,7 +73,13 @@ static long init_record(lsiRecord *prec, int pass) long status = pdset->init_record(prec); if (status) - return(status); + return status; + } + + if (prec->len) { + strcpy(prec->oval, prec->val); + prec->olen = prec->len; + prec->udf = FALSE; } return 0; @@ -88,7 +91,7 @@ static long process(lsiRecord *prec) lsidset *pdset = (lsidset *) prec->dset; long status = 0; - if ((pdset == NULL) || (pdset->read_string == NULL)) { + if (!pdset || !pdset->read_string) { prec->pact = TRUE; recGblRecordError(S_dev_missingSup, prec, "lsi: read_string"); return S_dev_missingSup; @@ -156,8 +159,8 @@ 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; + --nNew; /* truncated string */ + prec->val[nNew] = 0; /* ensure data is terminated */ return 0; } @@ -169,6 +172,10 @@ static long special(DBADDR *paddr, int after) 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); @@ -212,16 +219,16 @@ static long readValue(lsiRecord *prec) return status; switch (prec->simm) { - case menuYesNoYES: - recGblSetSevr(prec, SIMM_ALARM, prec->sims); - status = dbGetLinkLS(&prec->siol, prec->val, prec->sizv, &prec->len); - break; - 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; From b64bc41d6b0c5fc7efb64393a67125ee0c68b7c6 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Fri, 30 Nov 2012 16:50:31 -0600 Subject: [PATCH 14/15] Add new long string output record type. --- src/std/rec/Makefile | 98 +++++------- src/std/rec/lsoRecord.c | 322 ++++++++++++++++++++++++++++++++++++++ src/std/rec/lsoRecord.dbd | 112 +++++++++++++ 3 files changed, 469 insertions(+), 63 deletions(-) create mode 100644 src/std/rec/lsoRecord.c create mode 100644 src/std/rec/lsoRecord.dbd diff --git a/src/std/rec/Makefile b/src/std/rec/Makefile index d16b20b93..c2d44ddb9 100644 --- a/src/std/rec/Makefile +++ b/src/std/rec/Makefile @@ -11,71 +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 += lsiRecord -DBDINC += mbbiRecord -DBDINC += mbbiDirectRecord -DBDINC += mbboRecord -DBDINC += mbboDirectRecord -DBDINC += permissiveRecord -DBDINC += printfRecord -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 += lsiRecord.c -dbRecStd_SRCS += mbbiRecord.c -dbRecStd_SRCS += mbbiDirectRecord.c -dbRecStd_SRCS += mbboRecord.c -dbRecStd_SRCS += mbboDirectRecord.c -dbRecStd_SRCS += permissiveRecord.c -dbRecStd_SRCS += printfRecord.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/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) + } +} From fc12c72809f8a80d410787963431429905b80a41 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Fri, 30 Nov 2012 16:51:37 -0600 Subject: [PATCH 15/15] Device support for new long string out record type --- src/std/dev/Makefile | 5 +- src/std/dev/devLsoSoft.c | 26 ++++ src/std/dev/devLsoSoftCallback.c | 51 ++++++++ src/std/dev/devPrintfStdio.c | 105 --------------- src/std/dev/devSoStdio.c | 108 ---------------- src/std/dev/devSoft.dbd | 3 + src/std/dev/devStdio.c | 212 +++++++++++++++++++++++++++++++ 7 files changed, 295 insertions(+), 215 deletions(-) create mode 100644 src/std/dev/devLsoSoft.c create mode 100644 src/std/dev/devLsoSoftCallback.c delete mode 100644 src/std/dev/devPrintfStdio.c delete mode 100644 src/std/dev/devSoStdio.c create mode 100644 src/std/dev/devStdio.c diff --git a/src/std/dev/Makefile b/src/std/dev/Makefile index f4f8f690f..6bdbbd997 100644 --- a/src/std/dev/Makefile +++ b/src/std/dev/Makefile @@ -31,6 +31,7 @@ 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 @@ -57,14 +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 += devPrintfStdio.c -dbRecStd_SRCS += devSoStdio.c +dbRecStd_SRCS += devStdio.c dbRecStd_SRCS += asSubRecordFunctions.c 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/devPrintfStdio.c b/src/std/dev/devPrintfStdio.c deleted file mode 100644 index bf14ed817..000000000 --- a/src/std/dev/devPrintfStdio.c +++ /dev/null @@ -1,105 +0,0 @@ -/*************************************************************************\ -* 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$ */ - -#include -#include - -#include "dbCommon.h" -#include "devSup.h" -#include "errlog.h" -#include "recGbl.h" -#include "recSup.h" -#include "printfRecord.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) { - 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(dbCommon *pcommon) { - printfRecord *prec = (printfRecord *) 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(printfRecord *prec) -{ - struct outStream *pstream = (struct outStream *)prec->dpvt; - if (pstream) - pstream->print("%s\n", prec->val); - return 0; -} - -/* Create the dset for devSoStdio */ -printfdset devPrintfStdio = { - 5, NULL, init, NULL, NULL, write_string -}; -epicsExportAddress(dset, devPrintfStdio); 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 babad358a..d6cb14de9 100644 --- a/src/std/dev/devSoft.dbd +++ b/src/std/dev/devSoft.dbd @@ -10,6 +10,7 @@ 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") @@ -36,6 +37,7 @@ 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") @@ -52,6 +54,7 @@ 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") 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);