From d8812cbee351d86af9694e465d093ebc368d8371 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Fri, 28 Sep 2012 18:00:13 -0500 Subject: [PATCH] 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 +}