Skip to content

Commit

Permalink
add large int -> string LUT
Browse files Browse the repository at this point in the history
avoid mbbi limit of 16 states
  • Loading branch information
mdavidsaver committed Jul 28, 2017
1 parent 753557e commit 78f1daa
Show file tree
Hide file tree
Showing 9 changed files with 358 additions and 2 deletions.
4 changes: 3 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
#Makefile at top of application tree
TOP = .
include $(TOP)/configure/CONFIG
DIRS := configure mrfCommon evrApp mrmShared evgMrmApp evrMrmApp mrfApp evrFRIBApp iocBoot
DIRS := configure mrfCommon evrApp mrmShared evgMrmApp evrMrmApp mrfApp evrFRIBApp testApp iocBoot

define DIR_template
$(1)_DEPEND_DIRS = configure
Expand All @@ -23,4 +23,6 @@ evrFRIBApp_DEPEND_DIRS += evrApp

mrfApp_DEPEND_DIRS += evrMrmApp evgMrmApp evrFRIBApp

testApp_DEPEND_DIRS += mrfCommon

include $(TOP)/configure/RULES_TOP
1 change: 1 addition & 0 deletions mrfCommon/src/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ mrfCommon_SRCS += devObjString.cpp
mrfCommon_SRCS += devObjCommand.cpp
mrfCommon_SRCS += devObjWf.cpp
mrfCommon_SRCS += devMbboDirectSoft.c
mrfCommon_SRCS += devlutstring.cpp
mrfCommon_SRCS += databuf.cpp
mrfCommon_SRCS += mrfCommon.cpp

Expand Down
182 changes: 182 additions & 0 deletions mrfCommon/src/devlutstring.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
/*************************************************************************\
* Copyright (c) 2017 Michael Davidsaver
* mrfioc2 is distributed subject to a Software License Agreement found
* in file LICENSE that is included with this distribution.
\*************************************************************************/
/** Arbitrary size lookup table mapping integer to string.
*
* record(stringin, "blah") {
* field(INP , "some:int")
* info(lut0, " 0 = zero")
* info(lut1, " 1 = one")
* info(lut2, " 3 = three")
* info(lutX, "unknown")
*/

#include <stdexcept>
#include <map>
#include <string>
#include <sstream>

#include <stdio.h>
#include <errno.h>

#include <epicsTypes.h>
#include <dbAccess.h>
#include <dbStaticLib.h>
#include <dbScan.h>
#include <link.h>
#include <devSup.h>
#include <recGbl.h>
#include <devLib.h> // For S_dev_*
#include <alarm.h>
#include <errlog.h>

#include <stringinRecord.h>

#include "mrfCommon.h"
#include "devObj.h"

#include <epicsExport.h>

namespace {

std::string strip(const std::string& inp)
{
size_t S = inp.find_first_not_of(" \t"),
E = inp.find_last_not_of(" \t");
if(S==inp.npos)
return std::string(); // empty
else
return inp.substr(S, E-S+1);
}

struct DBENT {
DBENTRY entry;
template<typename REC>
DBENT(REC *prec)
{
dbInitEntry(pdbbase, &entry);
if(dbFindRecord(&entry, prec->name))
throw std::logic_error("Failed to lookup DBENTRY from dbCommon");
}
~DBENT() {
dbFinishEntry(&entry);
}
};

struct LUTPriv {
typedef std::map<epicsInt32, std::string> lut_t;
lut_t lut;
std::string unknown;
};

static
long init_record_lut(stringinRecord *prec)
{
try {
mrf::auto_ptr<LUTPriv> priv(new LUTPriv);

priv->unknown = "<unknown>";

DBENT entry(prec);

for(long status = dbFirstInfo(&entry.entry); status==0; status = dbNextInfo(&entry.entry))
{
const char * const name = dbGetInfoName(&entry.entry);

std::string line(dbGetInfoString(&entry.entry));

if(strcmp(name, "lutX")==0) {
priv->unknown = strip(line);
if(prec->tpro>1)
printf("%s : LUT <fallback> -> \"%s\"\n", prec->name, priv->unknown.c_str());
continue;

} else if(strncmp(name, "lut", 3)!=0) {
continue;
}

size_t eq = line.find_first_of('=');
if(eq==line.npos) {
fprintf(stderr, "%s : info %s value missing '=' : %s\n", prec->name, name, line.c_str());
return 0;
}

epicsUInt32 raw;
{
std::string num(line.substr(0, eq));
if(epicsParseUInt32(num.c_str(), &raw, 0, 0)) {
fprintf(stderr, "%s : info %s index not number \"%s\"\n", prec->name, name, num.c_str());
throw std::runtime_error("Invalid LUT entry");
}
}

std::pair<LUTPriv::lut_t::iterator, bool> ret =
priv->lut.insert(std::make_pair(raw, strip(line.substr(eq+1))));

if(prec->tpro>1)
printf("%s : LUT %u -> \"%s\"\n", prec->name, ret.first->first, ret.first->second.c_str());
}

/* initialize w/ unknown value */
strncpy(prec->val, priv->unknown.c_str(), sizeof(prec->val));
prec->val[sizeof(prec->val)-1] = '\0';

prec->dpvt = priv.release();

return 0;
}CATCH(-ENODEV)
}

static
long read_lut(stringinRecord *prec)
{
const LUTPriv * const priv = static_cast<LUTPriv*>(prec->dpvt);
if(!priv) {
(void)recGblSetSevr(prec, COMM_ALARM, INVALID_ALARM);
return 0;
}
try {
const std::string *val = &priv->unknown;

epicsUInt32 raw;
long status = dbGetLink(&prec->inp, DBR_LONG, &raw, 0, 0);

if(status) {
(void)recGblSetSevr(prec, LINK_ALARM, INVALID_ALARM);

} else {
LUTPriv::lut_t::const_iterator it = priv->lut.find(raw);
if(it==priv->lut.end()) {
(void)recGblSetSevr(prec, READ_ALARM, INVALID_ALARM);
} else {
val = &it->second;
}
}

if(prec->tpro>2)
printf("%s : LUT status=%ld select=%u VAL=\"%s\"\n",
prec->name, status, (unsigned)raw, val->c_str());

strncpy(prec->val, val->c_str(), sizeof(prec->val));
prec->val[sizeof(prec->val)-1] = '\0';

return 0;
}CATCH(-ENODEV)
}

common_dset devLUTSI = {
5,
0,
0,
(DEVSUPFUN)&init_record_lut,
0,
(DEVSUPFUN)&read_lut
};

} // namespace

extern "C" {
epicsExportAddress(dset, devLUTSI);
}
57 changes: 56 additions & 1 deletion mrfCommon/src/mrfCommon.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
#include <stdlib.h>
#include <stdexcept>

#include <limits.h>
#include <stdlib.h>

#include <epicsStdio.h>
#include <epicsExport.h>
#include "mrfCommon.h"
Expand Down Expand Up @@ -39,3 +41,56 @@ char *allocSNPrintf(size_t N, const char *fmt, ...)

return mem;
}

#if (EPICS_VERSION_INT < VERSION_INT(3,15,0,2))

static
epicsParseULong(const char *str, unsigned long *to, int base, char **units)
{
int c;
char *endp;
unsigned long value;

while ((c = *str) && isspace(c))
++str;

errno = 0;
value = strtoul(str, &endp, base);

if (endp == str)
return S_stdlib_noConversion;
if (errno == EINVAL) /* Not universally supported */
return S_stdlib_badBase;
if (errno == ERANGE)
return S_stdlib_overflow;

while ((c = *endp) && isspace(c))
++endp;
if (c && !units)
return S_stdlib_extraneous;

*to = value;
if (units)
*units = endp;
return 0;
}

int
epicsParseUInt32(const char *str, epicsUInt32 *to, int base, char **units)
{
unsigned long value;
int status = epicsParseULong(str, &value, base, units);

if (status)
return status;

#if (ULONG_MAX > 0xffffffffULL)
if (value > 0xffffffffUL && value <= ~0xffffffffUL)
return S_stdlib_overflow;
#endif

*to = (epicsUInt32) value;
return 0;
}

#endif
2 changes: 2 additions & 0 deletions mrfCommon/src/mrfCommon.dbd
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@ device(bo, INST_IO, devBOCommand, "Obj Prop command")
device(waveform , INST_IO, devWFIn, "Obj Prop waveform in")
device(waveform , INST_IO, devWFOut, "Obj Prop waveform out")

# from devlutstring.cpp
device(stringin , CONSTANT, devLUTSI, "LUT uint32 -> string")

# Special version of normal mbboDirect soft dset which restores bit fields from VAL
device(mbboDirect, CONSTANT, devMbboDirectRestore, "Soft and restore")
13 changes: 13 additions & 0 deletions mrfCommon/src/mrfCommon.h
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ using std::auto_ptr;
#include <epicsTime.h> /* EPICS Time definitions */
#include <epicsMath.h> /* EPICS Common math functions & definitions */
#include <epicsInterrupt.h>
#include <epicsStdlib.h>

#include <alarm.h> /* EPICS Alarm status and severity definitions */
#include <dbAccess.h> /* EPICS Database Access definitions */
Expand Down Expand Up @@ -274,6 +275,18 @@ epicsShareFunc char *allocSNPrintf(size_t N, const char *fmt, ...) EPICS_PRINTF_
# endif

#endif /*EPICS 64-bit integer types need defining*/

#define S_stdlib_noConversion (M_stdlib | 1) /* No digits to convert */
#define S_stdlib_extraneous (M_stdlib | 2) /* Extraneous characters */
#define S_stdlib_underflow (M_stdlib | 3) /* Too small to represent */
#define S_stdlib_overflow (M_stdlib | 4) /* Too large to represent */
#define S_stdlib_badBase (M_stdlib | 5) /* Number base not supported */

extern "C" {
epicsShareFunc int
epicsParseUInt32(const char *str, epicsUInt32 *to, int base, char **units);
}

#endif


Expand Down
33 changes: 33 additions & 0 deletions testApp/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
TOP=..

include $(TOP)/configure/CONFIG
#----------------------------------------
# ADD MACRO DEFINITIONS AFTER THIS LINE
#=============================

ifdef BASE_3_15

TARGETS += $(COMMON_DIR)/testmrf.dbd
DBDDEPENDS_FILES += testmrf.dbd$(DEP)

testmrf_DBD += base.dbd
testmrf_DBD += mrfCommon.dbd

TESTPROD_HOST += testlut
testlut_SRCS += testlut.c
testlut_SRCS += testmrf_registerRecordDeviceDriver.cpp
TESTS += testlut

PROD_LIBS += mrfCommon
PROD_LIBS += $(EPICS_BASE_IOC_LIBS)

TESTSCRIPTS_HOST += $(TESTS:%=%.t)

endif # BASE_3_15

#===========================

include $(TOP)/configure/RULES
#----------------------------------------
# ADD RULES AFTER THIS LINE

11 changes: 11 additions & 0 deletions testApp/lut.db
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
record(longout, "input") {
field(FLNK, "output")
}
record(stringin, "output") {
field(DTYP, "LUT uint32 -> string")
field(INP , "input NPP MSI")
info(lutX , "unknown")
info(lut0 , " 0 = zero")
info(lut1 , " 0x5 = five")
field(TPRO, "2")
}
Loading

0 comments on commit 78f1daa

Please sign in to comment.